feat(ai): add my-skills marketplace with personal skills
Add a new "my-skills" plugin marketplace under common/home-manager/ai-agents/plugins (adriano-voice, adriano-voice-code-comments, version-control, create/update-personal-skill), enable it on zw, and replace the flat jujutsu-vcs skill with the version-control plugin. Plugins live in a sibling dir of skills/ so the flat skill compiler doesn't double-install them.
This commit is contained in:
parent
83b870a61f
commit
7e639e1a03
12 changed files with 543 additions and 22 deletions
43
.claude-plugin/marketplace.json
Normal file
43
.claude-plugin/marketplace.json
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "my-skills",
|
||||
"owner": {
|
||||
"name": "Adriano Caloiaro"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Personal, system-agnostic skills shared across all of Adriano's machines",
|
||||
"version": "1.0.0",
|
||||
"pluginRoot": "./common/home-manager/ai-agents/plugins"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "adriano-voice",
|
||||
"source": "./common/home-manager/ai-agents/plugins/adriano-voice",
|
||||
"description": "Rewrite prose in Adriano Caloiaro's communication style",
|
||||
"version": "1.0.1"
|
||||
},
|
||||
{
|
||||
"name": "adriano-voice-code-comments",
|
||||
"source": "./common/home-manager/ai-agents/plugins/adriano-voice-code-comments",
|
||||
"description": "Write code comments in Adriano Caloiaro's code-comment voice",
|
||||
"version": "1.0.2"
|
||||
},
|
||||
{
|
||||
"name": "version-control",
|
||||
"source": "./common/home-manager/ai-agents/plugins/version-control",
|
||||
"description": "Whenever version control is used, use jujutsu instead of git",
|
||||
"version": "1.0.4"
|
||||
},
|
||||
{
|
||||
"name": "create-personal-skill",
|
||||
"source": "./common/home-manager/ai-agents/plugins/create-personal-skill",
|
||||
"description": "Scaffold a new skill in the my-skills marketplace",
|
||||
"version": "2.0.0"
|
||||
},
|
||||
{
|
||||
"name": "update-personal-skill",
|
||||
"source": "./common/home-manager/ai-agents/plugins/update-personal-skill",
|
||||
"description": "Update an existing skill in the my-skills marketplace",
|
||||
"version": "2.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "adriano-voice-code-comments",
|
||||
"description": "Write code comments in Adriano Caloiaro's code-comment voice",
|
||||
"version": "1.0.2"
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
---
|
||||
name: adriano-voice-code-comments
|
||||
description: Write code comments that sound like Adriano Caloiaro wrote them. Use whenever authoring or editing code comments — in any language (Ruby, Go, JS/TS, nix, etc.) — while writing or modifying code, so comments match Adriano's own voice rather than a generic AI style. This is a passive, coding-time pass, not a deliberate "rewrite this text" invocation. For prose (Slack, PR descriptions, review feedback) use the adriano-voice skill instead.
|
||||
---
|
||||
|
||||
# Adriano's Code-Comment Voice
|
||||
|
||||
Apply this whenever you write or edit a comment in source code. It governs *how* a comment reads, and helps decide *whether* a line deserves a comment at all. It is not a prose-rewriting tool — for Slack, PR descriptions, or review feedback, use [[adriano-voice]].
|
||||
|
||||
This guide was distilled from 800+ comment lines Adriano (`acaloiaro`) authored across his work repositories, tagged by altitude (class/module, method, inline). To re-derive it, see [Refreshing this guide](#refreshing-this-guide).
|
||||
|
||||
## The one rule that matters most
|
||||
|
||||
**A comment earns its place by explaining what the code cannot say itself — almost always the *why*, the *contract*, or the *gotcha*.** Adriano does not narrate mechanics that the code already states. He comments:
|
||||
|
||||
- the **reason** a non-obvious line exists ("…because AMS wants `ApplicationsSerializer` to be an array serializer"),
|
||||
- the **contract** of a method (what it returns, what it assumes, what it mutates),
|
||||
- the **caveat** — a misnomer, a temporary hack, a backward-compat constraint, a performance footgun.
|
||||
|
||||
If a comment would only restate the code, drop it.
|
||||
|
||||
**Only assert a *why* you have verified in the source.** A confidently wrong rationale is worse than no comment — it outlives the code and misleads every reader after you. When you haven't confirmed the mechanism (especially one that lives in another service or repo), say less, or pitch the *why* at the altitude you can actually vouch for. "The worker never reads `linked_data_id`" is something this file proves; "`linked_data_id` is the Kafka partition key" is a claim about a producer you'd have to go read first.
|
||||
|
||||
## Shared core (inherited from adriano-voice)
|
||||
|
||||
These carry over verbatim from [[adriano-voice]] — apply them in comments too:
|
||||
|
||||
- **Rationale is never optional.** The "because / so that / to ensure" clause is the spine of the comment.
|
||||
- **Concrete over abstract — but only for symbols a reader of this file can resolve.** Name the real method, type, setting, flag, ticket. Never "the helper" when you can write `param_to_person_attachment_id`. The exception is code that lives in *another codebase*: name the *system*, not its internal class — write "link's producer", not `GlobalSyncEventProducer` (that symbol is invisible and unstable from where the comment sits). Reach for a foreign class name only when you genuinely must.
|
||||
- **State the subject; full sentences.** Comments are sentences with subjects and verbs, not headline fragments. "This method name is a misnomer." — not "misnomer here."
|
||||
- **Low ceremony, short.** No filler, no apology, no restating the obvious. Say the point once.
|
||||
- **Honest about debt.** Name misnomers, hacks, and temporary code plainly. Don't dress them up.
|
||||
|
||||
## By altitude
|
||||
|
||||
The voice shifts with what the comment documents. Match the altitude.
|
||||
|
||||
### Class / module level — *what this thing is and what it owns*
|
||||
|
||||
Open by naming the thing and its responsibility, in present tense, full sentence. Then document defaults, config knobs, and the gotchas a caller needs before they reach for it.
|
||||
|
||||
```ruby
|
||||
# AuthenticationTokenService provides expiring authentication token functionality for API clients. Tokens
|
||||
# generated by this service should be refreshed periodically by clients, as they may expire.
|
||||
#
|
||||
# Token expiry is _off_ by default. However, expiration may be enabled in two ways: 1. By setting
|
||||
# +Settings.mobile_auth_token_service.token_lifetime+ (either with the environment variable, or in YAML), or
|
||||
# 2. by enabling an organization's +mobile_use_org_session_timeout+ feature flag.
|
||||
```
|
||||
|
||||
```ruby
|
||||
# This service module separates permission concerns from the +User+ model.
|
||||
```
|
||||
|
||||
- Document defaults and config by **exact name** (`+Settings.…+`, the flag, the env var), not "configurable."
|
||||
- Cross-reference collaborators: "Model merges occur in `People::MergeService` when Person and Application records are merged."
|
||||
- For workers/jobs/tasks, embed a **usage example** in the doc block so callers can copy it:
|
||||
```ruby
|
||||
# Toggle the status of a single product flag across multiple orgs.
|
||||
#
|
||||
# BulkProductFlagWorker.toggle_async([1,2,3,4], :product_flag_name, false, run_at: Time.zone.now + 1.day)
|
||||
```
|
||||
|
||||
### Method level — *the contract and the why*
|
||||
|
||||
Lead with a third-person verb describing what the method does ("Returns…", "Renders…", "Authorizes…", "Generates…"), or an imperative for commands ("Create a new event"). Then state the contract precisely — what comes back, in what shape, under what conditions.
|
||||
|
||||
**A single line is fine when one line says it all — don't inflate it into a block.** Split into a summary line and details only when there's more to say. When you do split, structure it as: one summary line, a blank comment line, then the details. The first line stands alone as the synopsis (params, edge cases, and contract go below the break) — never run the summary straight into the explanation.
|
||||
|
||||
```ruby
|
||||
# Claim Checks the event's payload via `GET /v1/sync_events/:id`.
|
||||
#
|
||||
# The first call against an `announced` event transitions it `announced -> processing` in Link. Returns
|
||||
# `[bytes, headers]`, or the `:already_synced` sentinel when there is nothing left to process.
|
||||
```
|
||||
|
||||
When the surrounding error class and a log line already make a `rescue`/branch self-evident, don't restate it inline — lift the contract that *is* worth keeping up to the method's doc comment instead of narrating each branch.
|
||||
|
||||
```ruby
|
||||
# Returns a hash where keys are HiringPlanInterviewStage IDs and values are the number of applications pending
|
||||
# review for the corresponding HPIS in +hiring_plan+.
|
||||
```
|
||||
|
||||
```ruby
|
||||
# CRC32 fingerprints are _not_ to be used for cryptographic purposes or ensuring global uniqueness.
|
||||
# Uses of CRC32 fingerprints should be localized and used only for performance reasons.
|
||||
```
|
||||
|
||||
- Use RDoc params when the file already does: `# @param [User] user The user being authorized` / `# @return an EventProspectRequest representing the answers`.
|
||||
- Reference params and identifiers inline. In legacy Ruby that's RDoc `+user+`; **in modern code use backticks** (`` `user` ``) unless the surrounding file already uses `+…+`. The principle — always mark identifiers — matters more than the delimiter.
|
||||
- Use `_emphasis_` (RDoc italics) for the stressed word: "asked at _all_ events", "_off_ by default".
|
||||
- Link external docs with a bare `# See https://example.com/api/#approve`.
|
||||
|
||||
### Inline / statement level — *why this line, right here*
|
||||
|
||||
This is where the voice is sharpest. Explain the surprising line, almost always with a causal clause.
|
||||
|
||||
```ruby
|
||||
# Cannot use `render :json => apps, ...` here because AMS wants `ApplicationsSerializer` to be an array serializer.
|
||||
```
|
||||
|
||||
```ruby
|
||||
# redis stores all values as strings; convert row ids back to integers
|
||||
```
|
||||
|
||||
```ruby
|
||||
# This only adds the role to the user temporarily because the `user` is not `saved`.
|
||||
```
|
||||
|
||||
- **Guards/preconditions** read as imperatives: "Ensure organization has Prospects access", "Ensure user has correct permissions".
|
||||
- **Flag misnomers and debt candidly**: "This method name is a misnomer. There is nothing mobile-specific about this URL."
|
||||
- **Test mechanics** explain the stub's purpose: "stubbing out `transaction` allows permissions to be tested without testing the entirety of `ApplicationService.move`."
|
||||
|
||||
### Language conventions
|
||||
|
||||
- **Go**: follow godoc — the comment opens with the identifier name. "`ErrorRecovery` is a Negroni middleware that recovers from any panics…", "`NewRecovery` returns a new instance of `ErrorRecovery`."
|
||||
- **Ruby**: RDoc-flavored as above; `#` comments, `+ident+` in legacy files, backticks in new ones.
|
||||
- **Always match the surrounding file's existing comment convention** over this guide when they conflict.
|
||||
|
||||
## TODO discipline
|
||||
|
||||
A TODO without a condition is noise. Adriano's TODOs carry either a **ticket** or a **concrete unblocking condition** — ideally both:
|
||||
|
||||
```ruby
|
||||
# TODO: change to job_post.touch after <tracker>/browse/TICKET-17245 bakes in
|
||||
# TODO: When all environments are running new code, remove the second parameter and rename `param` `request_json`
|
||||
# TODO: Refactor so as not to use Float::Infinity. The semantics are not clear to new developers.
|
||||
```
|
||||
|
||||
Write `# TODO: <action> <when/why it's unblocked>`. If there's a ticket, cite it.
|
||||
|
||||
## Mechanics checklist
|
||||
|
||||
- Identifiers in backticks (or `+…+` in legacy RDoc files). Never bare.
|
||||
- `_word_` for emphasis on the one word that carries the caveat.
|
||||
- Numbered enumerations for multi-case logic: "two conditions under which a worker should be drained, 1. … or 2. …".
|
||||
- Examples embedded in doc blocks — call examples for workers, "Example response: …" for endpoints.
|
||||
- `# See <url>` for external references; cite the tracker ticket for behavior tied to one.
|
||||
|
||||
## Strip list (these are not in Adriano's voice)
|
||||
|
||||
- Restating the code: `# increment the counter` above `counter += 1`. Delete it.
|
||||
- Flattery / hedging / AI throat-clearing: "Note that", "It's worth mentioning", "Simply", "Just".
|
||||
- Inflated adjectives: "robust", "seamless", "powerful", "leverage". Say what it does.
|
||||
- Bare TODOs with no ticket and no condition.
|
||||
- Headline fragments with no subject ("misnomer here", "perf hack"). Write the sentence.
|
||||
- Decorative banners and section separators unless the file already uses them.
|
||||
|
||||
## Refreshing this guide
|
||||
|
||||
The patterns above are distilled, not live. To re-derive from current code (uses `jj`, per Adriano's no-`git` rule; assumes the repos are jj-colocated):
|
||||
|
||||
```sh
|
||||
cd <a repo where you've authored substantial code> # repeat for each
|
||||
# Adriano's identities, excluding coworker "juan" and pairing (+juan) emails:
|
||||
REV='author_date(before:"2025-01-01") & (author(substring:"caloiaro") | author(substring:"adriano.fyi")) ~ author(substring:"+juan")'
|
||||
# Dump his diffs, then pull added comment lines and the code each one anchors to.
|
||||
for id in $(jj log --no-graph -r "$REV" -T 'commit_id ++ "\n"'); do jj diff --git -r "$id"; done > /tmp/diffs.txt
|
||||
```
|
||||
|
||||
Then classify each added comment by the next code construct it precedes (`class`/`module` → class-level, `def` → method-level, otherwise → inline) and re-distill the altitude sections. Drop the pre-2025 date bound to include recent work. Note: `author()` defaults to *exact* match in jj — use `substring:` for partial matches.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "adriano-voice",
|
||||
"description": "Rewrite prose in Adriano Caloiaro's communication style",
|
||||
"version": "1.0.1"
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
name: adriano-voice
|
||||
description: Rewrite or humanize prose so it sounds like Adriano Caloiaro — direct, pragmatic, low-ceremony, with rationale always attached. Use when drafting or editing text that should read as Adriano's own words: PR review feedback, design opinions, Slack messages, PR descriptions, status updates. Also invoke as a final voice pass from other skills (e.g. a draft code-review skill) by passing the draft text and the target register.
|
||||
---
|
||||
|
||||
# Adriano's Voice
|
||||
|
||||
Rewrite prose into Adriano Caloiaro's communication style. Use this standalone ("say this like me") or as a final pass from another skill that has drafted content and wants it to sound like Adriano.
|
||||
|
||||
This style guide was distilled from real Slack messages, GitHub PR review comments, and PR descriptions. To refresh it, see [Refreshing the style guide](#refreshing-the-style-guide).
|
||||
|
||||
## How to use this skill
|
||||
|
||||
1. Identify the **register** from the context (review feedback, design opinion, Slack/casual, or PR description). If a calling skill names it, use that. If unsure, infer from where the text will land.
|
||||
2. Apply the **core voice** rules (always) plus the **register-specific** rules.
|
||||
3. Run the **strip list** to remove AI tells.
|
||||
4. Return only the rewritten text — no preamble, no "here's your text," unless the caller asked for explanation.
|
||||
|
||||
## Core voice (applies to every register)
|
||||
|
||||
- **Decisive, but collaborative.** Make the call. Frame shared work as "Let's…" and alternatives as questions ("Could we…?", "Couldn't we…?"), but don't waffle on the actual recommendation.
|
||||
- **Rationale is never optional.** Every ask, cut, or opinion carries its *why* — usually one clause. "The `--local` is intentional here so we don't have to fetch from the rubygems repo."
|
||||
- **Concrete over abstract.** Name the real identifier, method, path, type, error, or ticket. Put code identifiers in backticks. Show a code snippet instead of describing one.
|
||||
- **Own opinions as opinions.** Taste is stated as taste and then justified: "I don't like the overall design here", "feels wrong as we add more models." Don't dress preference as fact.
|
||||
- **State the subject; never imply it.** Write full subject-verb sentences, not headline-style fragments. Openers and assessments name what they're about: "This is a solid refactor, and the test coverage is good." — *never* "Solid refactor and the test coverage is good." The subject-dropped fragment is the single most common tell that a rewrite isn't in Adriano's voice. (Exception: casual Slack, where fragments are fine — see that register.)
|
||||
- **Low ceremony.** No flattery, no warm-up, no "great work," no "I hope this helps." Get to the point in the first sentence.
|
||||
- **Pragmatic.** Favor what's deliberate and maintainable over what's clever. "I'm not opposed to X as long as it's deliberate."
|
||||
- **Short.** Prefer the shortest version that keeps the rationale. Cut filler words ("just", "simply", "actually", "really") unless they carry weight.
|
||||
|
||||
## Register: PR review feedback
|
||||
|
||||
The most important register — it's what composes into a code-review skill.
|
||||
|
||||
- **Clear cut → terse imperative.** When something should be deleted, say so in one line: "Drop this whole comment." / "Drop the return types commentary and use concrete sorbet types."
|
||||
- **Requested change → "Let's…" + a concrete snippet.** Prefix examples with `e.g.` and show real code:
|
||||
> Let's set the manager/legal_entity `EntityReference`s.
|
||||
>
|
||||
> e.g.
|
||||
> ```ruby
|
||||
> if (manager_id = hris_payload["manager_id"]).present?
|
||||
> employee.manager = vendor_reference(manager_id, :ENTITY_TYPE_VENDOR_EMPLOYEE)
|
||||
> end
|
||||
> ```
|
||||
- **Design pushback → a question that proposes the alternative.** "Couldn't our connector service have some form of *registry* of all models it's capable of syncing?" / "Could we use an enumerator here, so as to not require the entire array to be loaded into memory?"
|
||||
- **Be specific about the fix.** Name the destination and signature: "It seems like this should be a new top-level method on `app/services/connector_service.rb`, named `fetch_employment_types`, returning `T::Enumerator[Greenhouse::V1::EmploymentType]`."
|
||||
- **Nice-to-haves → "Maybe consider…"** "Maybe consider a formatter spec exercising both `manager` and `legal_entity` once added."
|
||||
- **Out-of-scope work → a follow-up story.** "Let's create a followup story in our cleanup epic: TICKET-XXXXX."
|
||||
- **Conventions stated flatly, no hedging.** "Don't use bare http status codes in code. Use an http status code name."
|
||||
- **No praise padding, no nit-apologizing.** Skip "Great PR! Just a tiny nit…". State the point.
|
||||
- **Lead an approval with a full sentence, not a verdict fragment.** When opening with positive assessment, give it a subject. "This is a solid refactor, and the test coverage is good." — *not* "Solid refactor and the test coverage is good." Then pivot to the concern: "Approving in principle, but I don't want to ship it as a single deploy."
|
||||
|
||||
## Register: design opinions / longer reasoning
|
||||
|
||||
- Lead with the concern, own it, justify it: "I also don't like the overall design here when we add more models. Having everything live in one method feels wrong as we add more."
|
||||
- Trace causation explicitly — quote the exact error or condition that produces the problem.
|
||||
- It's fine to flag that a thing is fine *today* but won't scale: "This comment is only true *today*."
|
||||
|
||||
## Register: Slack / casual
|
||||
|
||||
- Lowercase is fine, contractions throughout, sentences can fragment.
|
||||
- Dry humor and light emoji are in-character (`:smile:`, `:shrugguy:`), but sparingly.
|
||||
- Agreement opens with "Yeah": "Yeah, go ahead and do the requirement." / "Yeah, I don't think the jq requirement is unreasonable."
|
||||
- Decisive but soft framing: "My one thought is that…", "it might be nice to…", "I'm not opposed to … as long as it's deliberate."
|
||||
- Still concrete and reasoned even when casual — explain the why in a clause.
|
||||
|
||||
## Register: PR descriptions
|
||||
|
||||
- Use section headers as needed: `## Problem`, `## Why`, `## Summary`, `## Change`, `### Root cause`.
|
||||
- Lead with the problem stated concretely; quote the exact error in a fenced block.
|
||||
- Bullet the summary of what changed. Plain, precise, identifier-accurate.
|
||||
- Explain the mechanism and the why, not just the what.
|
||||
|
||||
## Strip list (remove these AI tells)
|
||||
|
||||
- Flattery and warm-ups: "Great question", "Certainly", "I hope this helps", "Happy to help".
|
||||
- Inflated adjectives: "robust", "seamless", "powerful", "comprehensive", "leverage", "delve".
|
||||
- Rule-of-three padding and parallel triplets.
|
||||
- Em-dash overuse — Adriano uses them, but don't sprinkle. One per paragraph at most.
|
||||
- Hedging filler before feedback: "I just wanted to", "It might be worth perhaps".
|
||||
- Restating the obvious or summarizing what was just said.
|
||||
- Closing summaries ("In summary…", "Overall…") on short messages.
|
||||
|
||||
## Composition (being called from another skill)
|
||||
|
||||
When another skill invokes this as a pass, expect it to hand over: the **draft text** and the **target register**. Rewrite in place and return only the rewritten text. Preserve any code blocks, identifiers, ticket IDs, and links verbatim — voice changes wording, not facts. If the draft is review feedback, default to the PR review feedback register.
|
||||
|
||||
## Refreshing the style guide
|
||||
|
||||
The patterns above are distilled, not live. To re-derive from current writing:
|
||||
|
||||
1. Slack: search your recent messages from yourself (`from:@me` or your own user ID) via the Slack search tool, `sort=timestamp`.
|
||||
2. PR review feedback: list PRs reviewed by `acaloiaro`, then pull inline comments authored by him:
|
||||
```sh
|
||||
gh search prs --reviewed-by=acaloiaro --limit100 --json url,repository,title
|
||||
gh api "repos/<owner>/<repo>/pulls/<n>/comments" --paginate \
|
||||
--jq '.[] | select(.user.login=="acaloiaro") | .body'
|
||||
```
|
||||
3. PR descriptions: `gh search prs --author=acaloiaro --json url,title,body`.
|
||||
4. Re-distill the register sections and replace the exemplars with fresh verbatim lines.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "create-personal-skill",
|
||||
"description": "Scaffold a new skill in the my-skills marketplace",
|
||||
"version": "2.0.0"
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
name: create-personal-skill
|
||||
description: Scaffold a new skill plugin in the personal my-skills marketplace. Use when asked to create a personal skill, add a new skill, or /create-personal-skill.
|
||||
argument-hint: "[skill-name] [description of what the skill should do]"
|
||||
---
|
||||
|
||||
# Create Personal Skill
|
||||
|
||||
Create a new skill plugin in the `my-skills` marketplace. These skills are system-agnostic and shared across every machine, so they live in the `nixos-system` repo and **must contain no machine-specific absolute paths or work-specific content**.
|
||||
|
||||
## Marketplace location
|
||||
|
||||
The marketplace lives in the `nixos-system` repo (remote `github:acaloiaro/nixos-system`). Work in your local checkout — commonly `~/proj/nixos-system`, but confirm the path before writing. Inside that repo:
|
||||
|
||||
- Marketplace registry: `.claude-plugin/marketplace.json` (at the repo root)
|
||||
- Plugin root: `common/home-manager/ai-agents/plugins/`
|
||||
|
||||
Note: the marketplace is resolved by Claude from GitHub, so a new skill is only installable after it is committed and pushed (see step 7).
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Gather information
|
||||
|
||||
Ask the user for (if not provided as arguments):
|
||||
- **Skill name**: kebab-case identifier (e.g., `my-cool-skill`)
|
||||
- **Description**: What the skill does and when Claude should use it
|
||||
- **Skill content**: The instructions, workflows, or procedures the skill should contain
|
||||
|
||||
### 2. Create the plugin directory structure
|
||||
|
||||
Relative to the `nixos-system` repo root:
|
||||
|
||||
```
|
||||
common/home-manager/ai-agents/plugins/{skill-name}/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json
|
||||
└── skills/
|
||||
└── {skill-name}/
|
||||
└── SKILL.md
|
||||
```
|
||||
|
||||
### 3. Write `.claude-plugin/plugin.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "{skill-name}",
|
||||
"description": "{short description}",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Write `skills/{skill-name}/SKILL.md`
|
||||
|
||||
Use this template:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: {skill-name}
|
||||
description: {description — be specific about when Claude should invoke this skill}
|
||||
---
|
||||
|
||||
{skill content}
|
||||
```
|
||||
|
||||
Guidelines for the SKILL.md:
|
||||
- Write clear, procedural instructions that Claude can follow.
|
||||
- Include specific tool/CLI commands where applicable.
|
||||
- Add edge case handling for common failure modes.
|
||||
- Keep instructions actionable — avoid vague guidance.
|
||||
- **Stay generic.** No machine-specific absolute paths (no `/Users/...` or `/home/...`), no work-specific identifiers. This marketplace is shared across all systems.
|
||||
|
||||
### 5. Register in marketplace.json
|
||||
|
||||
Read the current `.claude-plugin/marketplace.json`, then append a new entry to the `plugins` array:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "{skill-name}",
|
||||
"source": "./common/home-manager/ai-agents/plugins/{skill-name}",
|
||||
"description": "{short description}",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Enable the plugin per consuming system
|
||||
|
||||
Each machine that should load the skill enables it in its own `programs.claude-code.settings.enabledPlugins`, in `plugin@marketplace` form:
|
||||
|
||||
```nix
|
||||
"{skill-name}@my-skills" = true;
|
||||
```
|
||||
|
||||
The marketplace itself must also be known to that machine (usually already configured once):
|
||||
|
||||
```nix
|
||||
extraKnownMarketplaces.my-skills.source = {
|
||||
source = "github";
|
||||
repo = "acaloiaro/nixos-system";
|
||||
};
|
||||
```
|
||||
|
||||
- On `nixos-system` machines, that config lives in the system's home file (e.g. `systems/zw/home/adriano.nix`).
|
||||
- On any other machine, it lives in that machine's own Claude Code config.
|
||||
|
||||
Ask the user which systems should get the new skill, and edit each one's `enabledPlugins`.
|
||||
|
||||
### 7. Commit, push, and rebuild
|
||||
|
||||
After the user confirms:
|
||||
|
||||
1. Create a jujutsu change in the `nixos-system` repo with a descriptive message (e.g., `feat: add {skill-name} skill`) — use the `version-control` skill's jj workflow, never `git`.
|
||||
2. `jj git push` so the GitHub-sourced marketplace picks up the new plugin.
|
||||
3. Remind the user to rebuild the nix config on each system where the plugin was enabled for the `enabledPlugins` change to take effect.
|
||||
|
||||
### 8. Confirm
|
||||
|
||||
Show the user a summary of what was created and where it was enabled.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "update-personal-skill",
|
||||
"description": "Update an existing skill in the my-skills marketplace",
|
||||
"version": "2.0.0"
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
name: update-personal-skill
|
||||
description: Update an existing skill in the personal my-skills marketplace. Use when asked to update, edit, or modify a personal skill, or /update-personal-skill.
|
||||
argument-hint: "[skill-name] [description of changes]"
|
||||
---
|
||||
|
||||
# Update Personal Skill
|
||||
|
||||
Modify an existing skill plugin in the `my-skills` marketplace.
|
||||
|
||||
## Marketplace location
|
||||
|
||||
The marketplace lives in the `nixos-system` repo (remote `github:acaloiaro/nixos-system`). Work in your local checkout — commonly `~/proj/nixos-system`, but confirm the path before writing. Inside that repo:
|
||||
|
||||
- Marketplace registry: `.claude-plugin/marketplace.json` (at the repo root)
|
||||
- Plugin root: `common/home-manager/ai-agents/plugins/`
|
||||
|
||||
Note: the marketplace is resolved by Claude from GitHub, so edits are only picked up after they are committed and pushed (see step 6).
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Identify the skill
|
||||
|
||||
If no skill name is provided, list the available skills by reading `.claude-plugin/marketplace.json` and present them to the user.
|
||||
|
||||
If a skill name is provided, verify it exists under `common/home-manager/ai-agents/plugins/{skill-name}/`.
|
||||
|
||||
### 2. Read the current skill
|
||||
|
||||
Read these files (relative to the repo root):
|
||||
- `common/home-manager/ai-agents/plugins/{skill-name}/.claude-plugin/plugin.json`
|
||||
- `common/home-manager/ai-agents/plugins/{skill-name}/skills/{skill-name}/SKILL.md`
|
||||
|
||||
Present the current state to the user if they haven't specified what to change.
|
||||
|
||||
### 3. Apply changes
|
||||
|
||||
Based on user instructions, update the relevant files:
|
||||
|
||||
- **SKILL.md content changes**: Edit the skill instructions, workflows, or procedures. Keep it generic — no machine-specific absolute paths, no work-specific content.
|
||||
- **Description changes**: Update the `description` field in all three:
|
||||
- `common/home-manager/ai-agents/plugins/{skill-name}/.claude-plugin/plugin.json`
|
||||
- The YAML frontmatter in `SKILL.md`
|
||||
- The corresponding entry in `.claude-plugin/marketplace.json`
|
||||
- **Name changes** (rename): This requires:
|
||||
1. Creating the new plugin directory structure under `common/home-manager/ai-agents/plugins/{new-name}/`
|
||||
2. Moving content to the new location
|
||||
3. Updating `plugin.json`, `SKILL.md` frontmatter, and `marketplace.json`
|
||||
4. Updating `enabledPlugins` (old → new) in each consuming system's Claude Code config
|
||||
5. Removing the old directory
|
||||
|
||||
### 4. Version bump
|
||||
|
||||
When updating a skill, bump the patch version (e.g., `1.0.0` → `1.0.1`) in both:
|
||||
- `common/home-manager/ai-agents/plugins/{skill-name}/.claude-plugin/plugin.json`
|
||||
- The matching entry in `.claude-plugin/marketplace.json`
|
||||
|
||||
### 5. Confirm
|
||||
|
||||
Show the user a diff or summary of what changed.
|
||||
|
||||
### 6. Commit, push, and rebuild
|
||||
|
||||
After the user confirms the changes:
|
||||
|
||||
1. Create a jujutsu change in the `nixos-system` repo with a descriptive message (e.g., `feat: update {skill-name} skill — <brief summary>`) — use the `version-control` skill's jj workflow, never `git`.
|
||||
2. `jj git push` so the GitHub-sourced marketplace picks up the change.
|
||||
3. Remind the user to rebuild the nix config on each system that enables the skill for the updated version to take effect.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "version-control",
|
||||
"description": "Whenever version control is used, use jujutsu instead of git",
|
||||
"version": "1.0.4"
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
---
|
||||
name: jujutsu-vcs
|
||||
description: Version control workflow using jujutsu (jj) exclusively. Use when managing version control, committing changes, viewing history, working with bookmarks, rebasing, squashing, or syncing with remote repositories. DO NOT use git commands - use jj commands instead. Jujutsu uses bookmarks (not branches), changes (not commits), and has automatic working copy snapshotting.
|
||||
name: version-control
|
||||
description: Version control workflow using jujutsu (jj) exclusively. Use for any version control operation — commits, bookmarks, diffs, history, rebasing, squashing, or syncing with remotes. DO NOT use git commands; use jj commands instead. Jujutsu uses bookmarks (not branches), changes (not commits), and automatically snapshots the working copy.
|
||||
---
|
||||
|
||||
# Jujutsu Version Control
|
||||
# Version Control (Jujutsu)
|
||||
|
||||
> **Note**: This documentation is current as of jujutsu version **0.37.0**. Run `jj --version` to check your version and `jj help` for the latest command reference.
|
||||
> **Note**: Current as of jujutsu version **0.37.0**. Run `jj --version` to check your version and `jj help` for the latest command reference.
|
||||
|
||||
**Forget everything you know about git.**
|
||||
|
||||
Use jujutsu exclusively. NEVER use git commands.
|
||||
Use jujutsu exclusively. NEVER use git commands directly — always use `jj`.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
|
|
@ -20,12 +20,24 @@ Use jujutsu exclusively. NEVER use git commands.
|
|||
- Working copy is automatically snapshotted (no staging area)
|
||||
- Changes are immutable; rewriting creates new change IDs
|
||||
|
||||
## Bookmark Naming Convention
|
||||
|
||||
When creating bookmarks (which become branch names when pushed to the git remote), use:
|
||||
|
||||
```
|
||||
acaloiaro/<task-id>/<generated-bookmark-name>
|
||||
```
|
||||
|
||||
- `acaloiaro` — always prefix with the user's identifier.
|
||||
- `<task-id>` — the issue/ticket ID **only when you're already aware of one** in the current context. Use whatever tracker is in play: a Jira key (`GREEN-12345`), a Trello card ID, a Linear/GitHub issue number, etc. If no task ID is in context, **omit this segment entirely** and use `acaloiaro/<generated-bookmark-name>`. Never invent or guess an ID.
|
||||
- `<generated-bookmark-name>` — a short, kebab-case summary of the work (e.g., `fix-auth-redirect`, `add-bulk-export`).
|
||||
|
||||
## Interactive Commands
|
||||
|
||||
Some jj subcommands require a TTY — they open `$EDITOR`, a TUI diff selector, or a merge tool. Run these inside a floating multiplexer pane with `run-in-mux` (works in both zellij and tmux), otherwise they will fail or hang:
|
||||
|
||||
```bash
|
||||
run-in-mux -- <interactive jj command>
|
||||
run-in-mux -- hookable --interactive --no-exit-code --cmd '<interactive jj command>'
|
||||
```
|
||||
|
||||
Interactive commands that require `run-in-mux`:
|
||||
|
|
@ -51,7 +63,7 @@ Non-interactive alternative: whenever you already know the description, pass `-m
|
|||
```bash
|
||||
# Make your changes to files (automatically snapshotted)
|
||||
jj describe -m "your commit message"
|
||||
jj bookmark create your-bookmark-name
|
||||
jj bookmark create acaloiaro/your-bookmark-name
|
||||
jj git push
|
||||
```
|
||||
|
||||
|
|
@ -62,7 +74,7 @@ jj git fetch
|
|||
jj new main
|
||||
# Make changes...
|
||||
jj describe -m "message"
|
||||
jj bookmark create feature-name
|
||||
jj bookmark create acaloiaro/feature-name
|
||||
jj git push
|
||||
```
|
||||
|
||||
|
|
@ -126,8 +138,6 @@ jj bookmark list # List all bookmarks (alias: jj b l)
|
|||
jj bookmark track name --remote origin # Track remote bookmark (alias: jj b t)
|
||||
```
|
||||
|
||||
For complete bookmark reference, see [references/bookmarks.md](references/bookmarks.md).
|
||||
|
||||
### History Rewriting
|
||||
|
||||
```bash
|
||||
|
|
@ -167,8 +177,6 @@ jj git push --bookmark new-name # Push new name
|
|||
jj git push --deleted # Delete old name from remote
|
||||
```
|
||||
|
||||
For complete git push reference, see [references/git-push.md](references/git-push.md).
|
||||
|
||||
## Revset Basics
|
||||
|
||||
Common revset expressions:
|
||||
|
|
@ -184,7 +192,7 @@ Common revset expressions:
|
|||
| `::x` | x and all ancestors |
|
||||
| `x::` | x and all descendants |
|
||||
|
||||
For complete revset syntax, see [references/revsets.md](references/revsets.md) or run `jj help -k revsets`.
|
||||
For complete revset syntax, run `jj help -k revsets`.
|
||||
|
||||
## Important Notes
|
||||
|
||||
|
|
@ -192,12 +200,3 @@ For complete revset syntax, see [references/revsets.md](references/revsets.md) o
|
|||
- Working copy is always automatically snapshotted
|
||||
- Use `jj help <command>` for detailed command help
|
||||
- Use `jj help -k <keyword>` for topic help (e.g., `jj help -k revsets`)
|
||||
|
||||
## Detailed References
|
||||
|
||||
For exhaustive command documentation, see:
|
||||
|
||||
- [references/commands.md](references/commands.md) - Complete list of all commands and subcommands
|
||||
- [references/bookmarks.md](references/bookmarks.md) - Complete bookmark command reference
|
||||
- [references/git-push.md](references/git-push.md) - Complete git push command reference
|
||||
- [references/revsets.md](references/revsets.md) - Complete revset syntax reference
|
||||
|
|
@ -274,11 +274,20 @@
|
|||
source = "git";
|
||||
url = "https://git.sr.ht/~jcmuller/lsp-mux";
|
||||
};
|
||||
my-skills.source = {
|
||||
source = "github";
|
||||
repo = "acaloiaro/nixos-system";
|
||||
};
|
||||
};
|
||||
enabledPlugins = {
|
||||
"lsp-mux-go-nix@lsp-mux" = true;
|
||||
"lsp-mux-nix-nix@lsp-mux" = true;
|
||||
"lsp-mux-python@lsp-mux" = true;
|
||||
"adriano-voice@my-skills" = true;
|
||||
"adriano-voice-code-comments@my-skills" = true;
|
||||
"create-personal-skill@my-skills" = true;
|
||||
"update-personal-skill@my-skills" = true;
|
||||
"version-control@my-skills" = true;
|
||||
};
|
||||
mcpServers = config.ai-agents.mcpServers;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue