Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dead6ca2bb | ||
|
|
f65a6ec812 | ||
|
|
7dc4758e9a | ||
|
|
07198e9aad | ||
|
|
8a7201b898 | ||
|
|
f4e953186e | ||
|
|
5a6d1fccb0 | ||
|
|
fa7619bfde | ||
|
|
0daa11851d | ||
|
|
22c3b3e45f | ||
|
|
102b163fc5 | ||
|
|
2a87789523 | ||
|
|
9b4c6952c5 | ||
|
|
d8be029afb | ||
|
|
cc10b66365 | ||
|
|
36536e0e11 | ||
|
|
e16ca1114a | ||
|
|
8829e27004 | ||
|
|
1b5398afdc | ||
|
|
c68f6b4267 | ||
|
|
a0c5280aac | ||
|
|
de4f6f137b | ||
|
|
41d1d48025 | ||
|
|
1676528fad | ||
|
|
c92ee69440 | ||
|
|
1614a0d469 | ||
|
|
b8bd012763 | ||
|
|
88d3f3e81e | ||
|
|
a6b00a7f11 |
+48
-32
@@ -1,26 +1,31 @@
|
||||
# Project Context Pack
|
||||
|
||||
Generated: 2026-06-25
|
||||
Root: /home/sjb/Documents/ai-workflows/skills
|
||||
Generated: 2026-08-17
|
||||
Root: /home/sjb/Projects/personal/ws-sjb-skills/wt-master
|
||||
Working directory: .
|
||||
Status: fresh
|
||||
|
||||
## Purpose
|
||||
A collection of agent skills (slash commands and behaviors) loaded into Steve Beaulac's AI coding agents. Each skill is a SKILL.md file that teaches the agent how to handle a specific task — from codebase design and TDD to Obsidian PKM workflows and forge interaction.
|
||||
|
||||
A collection of agent skills (slash commands and behaviors) loaded into Steve Beaulac's AI coding agents. Each skill is a SKILL.md file that teaches the agent how to handle a specific task — from codebase design and TDD to Obsidian PKM workflows and tmux agent launching.
|
||||
|
||||
## Project type
|
||||
- **Agent skill repository** — markdown-defined agent instructions
|
||||
- Languages: Markdown (100%)
|
||||
- Package managers: none
|
||||
- Build/test tools: none
|
||||
|
||||
- **Agent skill repository plus standalone Python tracker package**
|
||||
- Languages: Python and Markdown, one Bash script (detect-agent), one shell script (tmux-open)
|
||||
- Package manager: `pyproject.toml`
|
||||
- Build/test tools: `python -m unittest discover`
|
||||
- Agent guidance: `AGENTS.md` at root, `docs/invocation.md` for invocation conventions, `docs/agents/` for issue tracker / triage labels / ADR wiki / domain docs
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── AGENTS.md # Top-level agent instructions for this repo
|
||||
├── README.md # Project overview, lists user-invoked and model-invoked skills
|
||||
├── .gitignore # Excludes docs/adr/
|
||||
├── .agent/
|
||||
│ └── project-context.md # This file
|
||||
├── docs/
|
||||
│ ├── invocation.md # Model-invoked vs user-invoked definitions
|
||||
│ ├── agents/
|
||||
@@ -29,38 +34,45 @@ A collection of agent skills (slash commands and behaviors) loaded into Steve Be
|
||||
│ │ ├── issue-tracker.md # Gitea issue tracker docs
|
||||
│ │ └── triage-labels.md # Five-label triage vocabulary
|
||||
│ └── adr/ # ADR wiki clone (gitignored)
|
||||
├── tracker/ # Provider-neutral Python CLI/library
|
||||
├── tracker_automation/ # Compatibility import name
|
||||
├── tests/ # Public operation-boundary tests
|
||||
├── pyproject.toml # Package metadata and console scripts
|
||||
└── common/
|
||||
├── README.md # Lists all common skills by invocation type
|
||||
├── engineering/ # Model-invoked: forge-interaction, project-context-pack; also sub-skills for codebase-design, domain-modeling, tdd, triage, etc.
|
||||
├── productivity/ # User-invoked: grill-me, handoff, writing-great-skills; Model-invoked: grilling
|
||||
├── pkm/ # User-invoked: conversation-summary, crit, knowledge-gardener, research-vault
|
||||
├── personal/ # User-invoked: pkm-curation; Model-invoked: forge-preferences
|
||||
└── deprecated/ # Deprecated skills (audio-production-dispatcher, dsp-research-dispatcher, forge-*)
|
||||
├── engineering/ # Model-invoked: lsp-code-analysis, pkm-curation; User-invoked: commit-staged, implement-issue, project-context-pack, setup-skills
|
||||
├── productivity/ # (currently only README.md)
|
||||
├── pkm/ # User-invoked: conversation-summary, crit, research-vault, youtube-video-capture; Model-invoked: pkm-curation
|
||||
├── personal/ # (currently only README.md)
|
||||
├── misc/ # User-invoked: tmux-launch-agent
|
||||
├── in-progress/ # User-invoked: agent-handoff, knowledge-gardener
|
||||
└── deprecated/ # Deprecated forge-* and dsp-* skills
|
||||
```
|
||||
|
||||
## Important files
|
||||
|
||||
- `AGENTS.md` — Agent entry point that describes structure, categories, and references docs
|
||||
- `README.md` — Index of all skills divided into user-invoked and model-invoked (recently updated to fix broken links, add missing skills, correct invocation classification)
|
||||
- `README.md` — Index of all skills divided into user-invoked and model-invoked
|
||||
- `docs/invocation.md` — Defines the invocation model (disable-model-invocation frontmatter key, human vs model reachability, dependency rules)
|
||||
- `docs/agents/` — Agent documentation for issue tracker, triage labels, ADR wiki, domain docs
|
||||
- `common/engineering/project-context-pack/SKILL.md` — The currently running skill
|
||||
- `common/engineering/codebase-design/SKILL.md` — Deep module design vocabulary (referenced by other skills)
|
||||
- `common/engineering/domain-modeling/SKILL.md` — Domain modeling with ADRs and CONTEXT files
|
||||
- `common/engineering/tdd/SKILL.md` — Test-driven development discipline
|
||||
- `common/productivity/grilling/SKILL.md` — Relentless plan/design interview (model-invoked, triggers)
|
||||
- `common/productivity/writing-great-skills/SKILL.md` — Reference for writing/editing skills
|
||||
- `common/engineering/project-context-pack/SKILL.md` — The skill that generated this file
|
||||
- `common/misc/tmux-launch-agent/SKILL.md` — Fork agent CLI into new tmux window (user-invoked)
|
||||
- `common/misc/tmux-launch-agent/tmux-open` — Reusable script that opens a command in a new tmux window/session
|
||||
|
||||
## Commands
|
||||
- Build: none
|
||||
- Test: none
|
||||
- Lint/typecheck: none
|
||||
- Run/dev: skills are invoked by AI agents — no server or dev command
|
||||
|
||||
- Build/package: `python -m pip install .`
|
||||
- Test: `python -m unittest discover -v`
|
||||
- Typecheck: `lsp_diagnostics` on `tracker/` and `tests/`
|
||||
- Run: `python -m tracker` or installed `tracker`
|
||||
|
||||
## Entry points
|
||||
|
||||
- `AGENTS.md` — loaded by the AI agent as project instructions (referred to in pi's agent config)
|
||||
- Each `SKILL.md` under `common/` — referenced by agents via slash commands or auto-invocation
|
||||
|
||||
## Search and symbol notes
|
||||
|
||||
- All skills are `SKILL.md` files — search with `fd SKILL.md`
|
||||
- Bucket READMEs: `fd README.md common/`
|
||||
- Skills are classified as **user-invoked** (`disable-model-invocation: true` in frontmatter) or **model-invoked** (default, no frontmatter flag)
|
||||
@@ -68,25 +80,25 @@ A collection of agent skills (slash commands and behaviors) loaded into Steve Be
|
||||
- Shared reference docs live inside the owning skill's directory; other skills reach that material by invoking the skill
|
||||
|
||||
## Files inspected
|
||||
|
||||
- `AGENTS.md` — root agent instructions — fresh
|
||||
- `README.md` — project overview — updated (all links fixed, missing skills added, invocation corrected)
|
||||
- `README.md` — project overview and skill index — fresh
|
||||
- `docs/invocation.md` — invocation model definitions — fresh
|
||||
- `.gitignore` — excludes docs/adr/ — fresh
|
||||
- `common/engineering/README.md` — engineering bucket index — updated (all engineering skills added, misclassified skills corrected)
|
||||
- `common/README.md` — common bucket index — updated (links fixed, missing skills added)
|
||||
- `common/productivity/README.md` — productivity bucket index — fresh (was already correct)
|
||||
- `common/pkm/README.md` — pkm bucket index — updated (pkm-curation added)
|
||||
- `common/personal/README.md` — personal bucket index — updated (noted both skills moved elsewhere)
|
||||
- `common/deprecated/README.md` — deprecated bucket index — updated (all forge skills added)
|
||||
- All `SKILL.md` files — frontmatter checked for invocation status
|
||||
- `common/README.md` — common bucket index — fresh
|
||||
- `common/misc/README.md` — misc bucket index — fresh
|
||||
- `.agent/project-context.md` — this file (refreshed from stale 2026-06-25 version)
|
||||
|
||||
## Exclusions
|
||||
|
||||
- `.git/` — VCS data
|
||||
- `docs/adr/` — gitignored ADR wiki clone
|
||||
- `node_modules/`, `dist/`, `build/`, `target/`, `.venv/`, `__pycache__/`, `vendor/`, `coverage/` — not present, but excluded by policy
|
||||
- `/home/sjb/.agents/skills/` — global install, NOT the source of truth for this repo
|
||||
- Binary files, large artifacts, credentials, secrets, personal data
|
||||
|
||||
## Navigation rules for future agents
|
||||
|
||||
- Start with `fd SKILL.md` to find all skills, then narrow by `fd SKILL.md common/<category>/`
|
||||
- To understand a skill's purpose, read its `SKILL.md` and the bucket `README.md` that indexes it
|
||||
- For invocation rules (user-invoked vs model-invoked), read `docs/invocation.md`
|
||||
@@ -95,9 +107,13 @@ A collection of agent skills (slash commands and behaviors) loaded into Steve Be
|
||||
- Track every inspected file in this cache
|
||||
- Re-read a file only when it changed, the cache is stale, or exact details are needed
|
||||
|
||||
## Edit boundaries
|
||||
|
||||
When cwd is inside this repo, all file edits MUST be scoped to paths under the repo root (`/home/sjb/Projects/personal/ws-sjb-skills/wt-master`). Do NOT touch files under `/home/sjb/.agents/skills/` or `/home/sjb/.pi/` — those are the installed/runtime copies, not the source of truth. The global install is synced separately; this repo is where source edits happen.
|
||||
|
||||
## Refresh notes
|
||||
|
||||
- This is a markdown-only repo with no build artifacts; refreshes are rarely needed unless skills are added or removed
|
||||
- To refresh, re-run `tree -a -I '.git' -L 4` and re-read any changed bucket READMEs or SKILL.md files
|
||||
- The `docs/adr/` directory is gitignored — if ADR data is needed, check the Gitea wiki directly
|
||||
- All README.md files were updated on 2026-06-25 to fix broken links, add missing skills, and correct invocation classification
|
||||
- If a skill is moved between buckets, update all README.md files that reference it (root, common, source bucket, destination bucket)
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
docs/adr/
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
@@ -14,8 +14,8 @@ Skill are organized into categories based on their function. For example, `/comm
|
||||
|
||||
If we have a skill that is only relevant to a specific agent, we can put it in an agent-specific bucket. For example, if we have a skill that is only relevant to the `opencode` agent, we can put it in `/opencode/misc`.
|
||||
|
||||
|
||||
## list of categories
|
||||
|
||||
- `engineering/` — daily code work
|
||||
- `productivity/` — daily non-code workflow tools
|
||||
- `misc/` — kept around but rarely used
|
||||
@@ -32,11 +32,11 @@ Every `SKILL.md` is either user-invoked (`disable-model-invocation: true`, reach
|
||||
|
||||
### Issue tracker
|
||||
|
||||
Issues are tracked in Gitea on gitea.sagacity.ca. See `docs/agents/issue-tracker.md`.
|
||||
Issues are tracked in Gitea on gitea.sagacity.ca; use the provider-neutral `tracker` package for normal automation. See `docs/agents/issue-tracker.md` and `docs/agents/tracker.md`.
|
||||
|
||||
### Triage labels
|
||||
|
||||
Five-label vocabulary with default names (needs-triage, needs-info, ready-for-agent, ready-for-human, wontfix). See `docs/agents/triage-labels.md`.
|
||||
Seven-label vocabulary with default names (needs-triage, needs-info, needs-review, ready-for-agent, ready-for-human, in-progress, wontfix). See `docs/agents/triage-labels.md`.
|
||||
|
||||
### Domain docs
|
||||
|
||||
@@ -44,7 +44,11 @@ Single-context layout. See `docs/agents/domain.md`.
|
||||
|
||||
### ADR wiki
|
||||
|
||||
Gitea wiki at git@gitea.sagacity.ca:steve/Skills.wiki.git, cloned into docs/adr/, SSH key auth. See `docs/agents/adr-wiki.md`.
|
||||
Gitea wiki at `git@gitea.sagacity.ca:steve/Skills.wiki.git`, cloned into `docs/adr/`, SSH key auth. See `docs/agents/adr-wiki.md`.
|
||||
|
||||
## Project Context Pack
|
||||
|
||||
Agent memory file that describes the repo's context, codebase, and navigation rules. See `.agents/project-context.md`.
|
||||
|
||||
### Agent CLI
|
||||
|
||||
|
||||
@@ -1,41 +1,27 @@
|
||||
# Skills
|
||||
|
||||
A collection of agent skills (slash commands and behaviors) loaded into Steve Beaulac's agents.
|
||||
Agent skills (slash commands and behaviors) loaded into my agent.
|
||||
|
||||
## Tracker automation
|
||||
|
||||
This repo also ships the standalone provider-neutral `tracker` CLI/library. See [`docs/agents/tracker.md`](docs/agents/tracker.md) and [`tracker/README.md`](tracker/README.md) for migration guidance.
|
||||
|
||||
## User-invoked
|
||||
|
||||
- [conversation-summary](common/pkm/conversation-summary/SKILL.md) — Summarize the current AI conversation into a new Obsidian markdown note and matching transcript file.
|
||||
- [crit](common/pkm/crit/SKILL.md) — Brainstorm with AI using the CRIT framework to generate and evaluate ideas.
|
||||
- [grill-me](common/productivity/grill-me/SKILL.md) — A relentless interview to sharpen a plan or design.
|
||||
- [grill-with-docs](common/engineering/grill-with-docs/SKILL.md) — A relentless interview to sharpen a plan or design, which also creates docs (ADRs and glossary) as we go.
|
||||
- [handoff](common/productivity/handoff/SKILL.md) — Compact the current conversation into a handoff document for another agent to pick up.
|
||||
- [implement-issue](common/engineering/implement-issue/SKILL.md) — Dispatch a child agent in an isolated git worktree to implement a ready-for-agent issue end-to-end.
|
||||
- [improve-codebase-architecture](common/engineering/improve-codebase-architecture/SKILL.md) — Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
|
||||
- [knowledge-gardener](common/pkm/knowledge-gardener/SKILL.md) — Run vault-aware semantic search, synthesis, note creation, linking, and Zettelkasten workflows for this Obsidian vault.
|
||||
- [pkm-curation](common/pkm/pkm-curation/SKILL.md) — Curate an Obsidian-style personal knowledge vault by classifying notes, normalizing frontmatter, improving structure, extracting atomic notes, and adding meaningful wikilinks.
|
||||
- [project-context-pack](common/engineering/project-context-pack/SKILL.md) — Build and refresh a bounded repo context memory file so agents use disciplined search instead of repeated browsing.
|
||||
- [research-vault](common/pkm/research-vault/SKILL.md) — Research a topic through a one-question-at-a-time learning conversation, answer directly, share resources when useful, and save a linked Obsidian research packet.
|
||||
- [setup-skills](common/engineering/setup-skills/SKILL.md) — Configure this repo for the engineering skills, set up its issue tracker, triage label vocabulary, and domain doc layout.
|
||||
- [to-issues](common/engineering/to-issues/SKILL.md) — Break a plan, spec, or PRD into independently-grabbable issues on the project issue tracker using tracer-bullet vertical slices.
|
||||
- [to-prd](common/engineering/to-prd/SKILL.md) — Turn the current conversation into a PRD and publish it to the project issue tracker — no interview, just synthesis of what you've already discussed.
|
||||
- [triage](common/engineering/triage/SKILL.md) — Move issues and external PRs through a state machine of triage roles — categorise, verify, grill if needed, and write agent-ready briefs.
|
||||
- [writing-great-skills](common/productivity/writing-great-skills/SKILL.md) — Reference for writing and editing skills well — the vocabulary and principles that make a skill predictable.
|
||||
|
||||
**Deprecated / user-invoked:**
|
||||
- [forge-router](common/deprecated/forge-router/SKILL.md) — High-level guidance for choosing the right forge skill.
|
||||
- [agent-handoff](common/in-progress/agent-handoff/SKILL.md) — Hand the current conversation off to a fresh background agent that picks up the work immediately.
|
||||
- [commit-staged](common/engineering/commit-staged/SKILL.md) — Commit staged files with a conventional commit message.
|
||||
- [conversation-summary](common/pkm/conversation-summary/SKILL.md) — Save the current conversation as a comprehensive report note in your Obsidian vault, following OKF v0.1 conventions.
|
||||
- [crit](common/pkm/crit/SKILL.md) — Run the CRIT framework — give the AI Context, assign it a Role, let it Interview you one question at a time, then issue the Task.
|
||||
- [implement-isolation](common/engineering/implement-isolation/SKILL.md) — Implement a piece of work based on a spec or set of tickets in isolation.
|
||||
- [implement-isolation-tmux](common/engineering/implement-isolation-tmux/SKILL.md) — Dispatch a child agent in an isolated git worktree to implement a piece of work based on a PRD or set of issues.
|
||||
- [knowledge-gardener](common/in-progress/knowledge-gardener/SKILL.md) — Run vault-aware semantic search, synthesis, note creation, linking, and Zettelkasten workflows for this Obsidian vault.
|
||||
- [project-context-pack](common/engineering/project-context-pack/SKILL.md) — Build a bounded repo context pack (project map, codebase index, cached memory file) so later work uses fd/rg/tree-sitter/LSP instead of repeated browsing.
|
||||
- [research-vault](common/pkm/research-vault/SKILL.md) — Research a topic through a one-question-at-a-time learning conversation, answer directly, share resources when useful, and save a linked OKF-conformant research packet in the Obsidian vault.
|
||||
- [setup-skills](common/engineering/setup-skills/SKILL.md) — Configure this repo for the engineering skills, set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.
|
||||
- [tmux-launch-agent](common/misc/tmux-launch-agent/SKILL.md) — Fork a new agent CLI session into a new tmux window, detected from the current agent.
|
||||
- [youtube-video-capture](common/pkm/youtube-video-capture/SKILL.md) — Fetch subtitles from a YouTube video, summarize the content, and save both the summary and raw subtitles to the Video bundle in the Obsidian vault.
|
||||
|
||||
## Model-invoked
|
||||
|
||||
- [codebase-design](common/engineering/codebase-design/SKILL.md) — Shared vocabulary for designing deep modules.
|
||||
- [domain-modeling](common/engineering/domain-modeling/SKILL.md) — Build and sharpen a project's domain model.
|
||||
- [grilling](common/productivity/grilling/SKILL.md) — Interview the user relentlessly about a plan or design.
|
||||
- [resolving-merge-conflicts](common/engineering/resolving-merge-conflicts/SKILL.md) — Resolve an in-progress git merge/rebase conflict.
|
||||
- [tdd](common/engineering/tdd/SKILL.md) — Test-driven development.
|
||||
|
||||
**Deprecated / model-invoked:**
|
||||
- [audio-product-dsp](common/deprecated/audio-production-dispatcher/SKILL.md) — Dispatch audio product DSP hardware/software engineering requests to the best specialist workflow with measurable product-focused outputs.
|
||||
- [dsp-research-engineering](common/deprecated/dsp-research-dispatcher/SKILL.md) — Route DSP hardware and software research-engineering requests to the best specialist workflow and return a unified, decision-ready output.
|
||||
- [forge-gitea](common/deprecated/forge-gitea/SKILL.md) — Work with Gitea repositories, issues, pull requests, releases, and CI.
|
||||
- [forge-github](common/deprecated/forge-github/SKILL.md) — Work with GitHub repositories, issues, pull requests, releases, and CI.
|
||||
- [forge-interaction](common/deprecated/forge-interaction/SKILL.md) — Work safely with GitHub and Gitea repositories, issues, pull requests, releases, and remote forge state.
|
||||
- [forge-preferences](common/deprecated/forge-preferences/SKILL.md) — Apply Steve's personal or project-specific GitHub/Gitea forge preferences.
|
||||
- [lsp-code-analysis](common/engineering/lsp-code-analysis/SKILL.md) — Semantic code analysis via LSP. Navigate code (definitions, references, implementations), search symbols, preview refactorings, and get file outlines. Use for exploring unfamiliar codebases or performing safe refactoring.
|
||||
- [pkm-curation](common/pkm/pkm-curation/SKILL.md) — Curate an Obsidian vault — classify notes, normalize frontmatter, add links, extract atomic notes. Use when curating, batch-processing, reviewing, or doing a serendipity pick.
|
||||
|
||||
+14
-31
@@ -4,37 +4,20 @@ Skills that work in all CLI agents.
|
||||
|
||||
## User-invoked
|
||||
|
||||
- [conversation-summary](pkm/conversation-summary/SKILL.md) — Summarize the current AI conversation into a new Obsidian markdown note and matching transcript file.
|
||||
- [crit](pkm/crit/SKILL.md) — Brainstorm with AI using the CRIT framework to generate and evaluate ideas.
|
||||
- [grill-me](productivity/grill-me/SKILL.md) — A relentless interview to sharpen a plan or design.
|
||||
- [grill-with-docs](engineering/grill-with-docs/SKILL.md) — A relentless interview to sharpen a plan or design, which also creates docs (ADRs and glossary) as we go.
|
||||
- [handoff](productivity/handoff/SKILL.md) — Compact the current conversation into a handoff document for another agent to pick up.
|
||||
- [improve-codebase-architecture](engineering/improve-codebase-architecture/SKILL.md) — Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
|
||||
- [knowledge-gardener](pkm/knowledge-gardener/SKILL.md) — Run vault-aware semantic search, synthesis, note creation, linking, and Zettelkasten workflows for this Obsidian vault.
|
||||
- [pkm-curation](pkm/pkm-curation/SKILL.md) — Curate an Obsidian-style personal knowledge vault by classifying notes, normalizing frontmatter, improving structure, extracting atomic notes, and adding meaningful wikilinks.
|
||||
- [project-context-pack](engineering/project-context-pack/SKILL.md) — Build and refresh a bounded repo context memory file so agents use disciplined search instead of repeated browsing.
|
||||
- [research-vault](pkm/research-vault/SKILL.md) — Research a topic through a one-question-at-a-time learning conversation, answer directly, share resources when useful, and save a linked Obsidian research packet.
|
||||
- [setup-skills](engineering/setup-skills/SKILL.md) — Configure this repo for the engineering skills, set up its issue tracker, triage label vocabulary, and domain doc layout.
|
||||
- [to-issues](engineering/to-issues/SKILL.md) — Break a plan, spec, or PRD into independently-grabbable issues on the project issue tracker using tracer-bullet vertical slices.
|
||||
- [to-prd](engineering/to-prd/SKILL.md) — Turn the current conversation into a PRD and publish it to the project issue tracker — no interview, just synthesis of what you've already discussed.
|
||||
- [triage](engineering/triage/SKILL.md) — Move issues and external PRs through a state machine of triage roles — categorise, verify, grill if needed, and write agent-ready briefs.
|
||||
- [writing-great-skills](productivity/writing-great-skills/SKILL.md) — Reference for writing and editing skills well — the vocabulary and principles that make a skill predictable.
|
||||
|
||||
**Deprecated / user-invoked:**
|
||||
- [forge-router](deprecated/forge-router/SKILL.md) — High-level guidance for choosing the right forge skill.
|
||||
- [agent-handoff](in-progress/agent-handoff/SKILL.md) — Hand the current conversation off to a fresh background agent that picks up the work immediately.
|
||||
- [commit-staged](engineering/commit-staged/SKILL.md) — Commit staged files with a conventional commit message.
|
||||
- [conversation-summary](pkm/conversation-summary/SKILL.md) — Save the current conversation as a comprehensive report note in your Obsidian vault, following OKF v0.1 conventions.
|
||||
- [crit](pkm/crit/SKILL.md) — Run the CRIT framework — give the AI Context, assign it a Role, let it Interview you one question at a time, then issue the Task.
|
||||
- [implement-isolation](engineering/implement-isolation/SKILL.md) — Implement a piece of work based on a spec or set of tickets in isolation.
|
||||
- [implement-isolation-tmux](engineering/implement-isolation-tmux/SKILL.md) — Dispatch a child agent in an isolated git worktree to implement a piece of work based on a PRD or set of issues.
|
||||
- [knowledge-gardener](in-progress/knowledge-gardener/SKILL.md) — Run vault-aware semantic search, synthesis, note creation, linking, and Zettelkasten workflows for this Obsidian vault.
|
||||
- [project-context-pack](engineering/project-context-pack/SKILL.md) — Build a bounded repo context pack (project map, codebase index, cached memory file) so later work uses fd/rg/tree-sitter/LSP instead of repeated browsing.
|
||||
- [research-vault](pkm/research-vault/SKILL.md) — Research a topic through a one-question-at-a-time learning conversation, answer directly, share resources when useful, and save a linked OKF-conformant research packet in the Obsidian vault.
|
||||
- [setup-skills](engineering/setup-skills/SKILL.md) — Configure this repo for the engineering skills, set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.
|
||||
- [tmux-launch-agent](misc/tmux-launch-agent/SKILL.md) — Fork a new agent CLI session into a new tmux window, detected from the current agent.
|
||||
- [youtube-video-capture](pkm/youtube-video-capture/SKILL.md) — Fetch subtitles from a YouTube video, summarize the content, and save both the summary and raw subtitles to the Video bundle in the Obsidian vault.
|
||||
|
||||
## Model-invoked
|
||||
|
||||
- [codebase-design](engineering/codebase-design/SKILL.md) — Shared vocabulary for designing deep modules.
|
||||
- [domain-modeling](engineering/domain-modeling/SKILL.md) — Build and sharpen a project's domain model.
|
||||
- [grilling](productivity/grilling/SKILL.md) — Interview the user relentlessly about a plan or design.
|
||||
- [resolving-merge-conflicts](engineering/resolving-merge-conflicts/SKILL.md) — Resolve an in-progress git merge/rebase conflict.
|
||||
- [tdd](engineering/tdd/SKILL.md) — Test-driven development.
|
||||
|
||||
**Deprecated / model-invoked:**
|
||||
- [audio-product-dsp](deprecated/audio-production-dispatcher/SKILL.md) — Dispatch audio product DSP hardware/software engineering requests to the best specialist workflow with measurable product-focused outputs.
|
||||
- [dsp-research-engineering](deprecated/dsp-research-dispatcher/SKILL.md) — Route DSP hardware and software research-engineering requests to the best specialist workflow and return a unified, decision-ready output.
|
||||
- [forge-gitea](deprecated/forge-gitea/SKILL.md) — Work with Gitea repositories, issues, pull requests, releases, and CI.
|
||||
- [forge-github](deprecated/forge-github/SKILL.md) — Work with GitHub repositories, issues, pull requests, releases, and CI.
|
||||
- [forge-interaction](deprecated/forge-interaction/SKILL.md) — Work safely with GitHub and Gitea repositories, issues, pull requests, releases, and remote forge state.
|
||||
- [forge-preferences](deprecated/forge-preferences/SKILL.md) — Apply Steve's personal or project-specific GitHub/Gitea forge preferences.
|
||||
- [lsp-code-analysis](engineering/lsp-code-analysis/SKILL.md) — Semantic code analysis via LSP. Navigate code (definitions, references, implementations), search symbols, preview refactorings, and get file outlines. Use for exploring unfamiliar codebases or performing safe refactoring.
|
||||
- [pkm-curation](pkm/pkm-curation/SKILL.md) — Curate an Obsidian vault — classify notes, normalize frontmatter, add links, extract atomic notes. Use when curating, batch-processing, reviewing, or doing a serendipity pick.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Deprecated Skills
|
||||
|
||||
## User-invoked
|
||||
|
||||
- [forge-router](forge-router/SKILL.md) — High-level guidance for choosing the right forge skill.
|
||||
|
||||
## Model-invoked
|
||||
|
||||
- [audio-product-dsp](audio-production-dispatcher/SKILL.md) — Dispatch audio product DSP hardware/software engineering requests to the best specialist workflow with measurable product-focused outputs.
|
||||
- [dsp-research-engineering](dsp-research-dispatcher/SKILL.md) — Route DSP hardware and software research-engineering requests to the best specialist workflow and return a unified, decision-ready output.
|
||||
- [forge-gitea](forge-gitea/SKILL.md) — Work with Gitea repositories, issues, pull requests, releases, and CI.
|
||||
- [forge-github](forge-github/SKILL.md) — Work with GitHub repositories, issues, pull requests, releases, and CI.
|
||||
- [forge-interaction](forge-interaction/SKILL.md) — Work safely with GitHub and Gitea repositories, issues, pull requests, releases, and remote forge state.
|
||||
- [forge-preferences](forge-preferences/SKILL.md) — Apply Steve's personal or project-specific GitHub/Gitea forge preferences.
|
||||
@@ -1,179 +0,0 @@
|
||||
---
|
||||
name: audio-product-dsp
|
||||
description: Dispatch audio product DSP hardware/software engineering requests to the best specialist workflow with measurable product-focused outputs
|
||||
---
|
||||
|
||||
Role: You are a dispatcher skill for audio product DSP research and engineering. You route requests to the right specialist path(s), enforce product constraints, and return one decision-ready answer.
|
||||
|
||||
Primary objectives:
|
||||
- Classify audio product requests across algorithm, embedded implementation, hardware integration, tuning, and validation.
|
||||
- Route to the best specialist workflow(s) using explicit scoring.
|
||||
- Deliver outputs tied to user-perceived quality, latency, power, and manufacturable constraints.
|
||||
- Keep recommendations testable and release-oriented.
|
||||
|
||||
Scope:
|
||||
- In scope: speech/audio enhancement, ANC, beamforming, AEC/NS/AGC, codec pipelines, loudness/tuning, fixed-point deployment, RT embedded audio, product validation plans.
|
||||
- Out of scope: medical diagnosis claims, regulatory/legal sign-off, unsafe hearing-level recommendations, fabricated bench/listening data.
|
||||
|
||||
Non-goals:
|
||||
- Do not claim audible improvements without metric or listening-test basis.
|
||||
- Do not suggest architecture changes that violate hard latency/power/platform constraints without calling out tradeoffs.
|
||||
- Do not present lab verification as completed if only conceptual.
|
||||
|
||||
Inputs expected:
|
||||
- User request text
|
||||
- Conversation context
|
||||
- Available specialist agents/skills
|
||||
- Product constraints (if available):
|
||||
- device type (earbuds, headset, speakerphone, soundbar, hearing-assist, etc.)
|
||||
- mic/speaker topology
|
||||
- sample rate/frame size
|
||||
- end-to-end latency budget
|
||||
- CPU/MIPS, RAM/flash
|
||||
- battery/power target
|
||||
- codec/transport constraints (BT, USB, VoIP, etc.)
|
||||
- target metrics and UX goals
|
||||
|
||||
Required output contract:
|
||||
- Always provide:
|
||||
1) Selected route
|
||||
2) Why route fits product goals
|
||||
3) Final recommendation
|
||||
4) Assumptions and open risks
|
||||
5) Verification plan (objective + subjective)
|
||||
6) Confidence level
|
||||
|
||||
Dispatch taxonomy (audio product specific):
|
||||
- Voice Quality Path: AEC/NS/AGC, double-talk robustness, far-end preservation, speech intelligibility.
|
||||
- Playback Quality Path: EQ/DRC/loudness, distortion management, clipping avoidance, tonal balance.
|
||||
- Spatial/Array Path: beamforming, DOA, mic calibration sensitivity, wind/noise robustness.
|
||||
- ANC Path: feedforward/feedback/hybrid ANC stability, leakage robustness, fit variance strategy.
|
||||
- Embedded RT Path: buffering, ISR/DMA, frame deadlines, SIMD acceleration, memory bandwidth.
|
||||
- Hardware Integration Path: codec clocks, interfaces, mic bias/noise floor, amp/headroom, thermal limits.
|
||||
- Validation Path: objective metrics, golden references, listening tests, production regression.
|
||||
- Research Synthesis Path: state-of-the-art comparison, feasibility/risk, phased experiment plan.
|
||||
|
||||
Routing policy:
|
||||
1. Parse request into one or more intents.
|
||||
2. Extract success criteria and hard product constraints.
|
||||
3. Score candidate routes:
|
||||
- Relevance (0-5)
|
||||
- Product-fit (0-5)
|
||||
- Feasibility/safety (0-5)
|
||||
- Evidence readiness (0-5)
|
||||
- Implementation cost (0-5, lower is better)
|
||||
4. Select single-route or multi-route orchestration.
|
||||
5. Dispatch structured task packets.
|
||||
6. Reconcile into one release-oriented recommendation.
|
||||
|
||||
Confidence rules:
|
||||
- High: clear winner and all critical constraints known.
|
||||
- Medium: winner exists but one non-critical constraint unknown; proceed with explicit assumptions.
|
||||
- Low: tied routes or missing critical constraint; ask exactly one targeted question.
|
||||
|
||||
Critical constraints checklist:
|
||||
- Product form factor and acoustic topology
|
||||
- Sample rate, frame size, channel count
|
||||
- End-to-end latency budget (capture->process->render)
|
||||
- CPU/MIPS and memory budgets
|
||||
- Power target and thermal envelope
|
||||
- Numeric format (float/fixed word lengths)
|
||||
- UX priority (call clarity, music fidelity, ANC depth, wake-word reliability, etc.)
|
||||
- Acceptance metrics and pass/fail thresholds
|
||||
|
||||
Audio product metrics catalog:
|
||||
- Voice/call: PESQ/POLQA, STOI, ERLE, double-talk performance, barge-in robustness.
|
||||
- Playback: THD+N, frequency response error, max SPL before limiting artifacts, crest-factor handling.
|
||||
- ANC: attenuation vs frequency, residual noise spectra, stability margin, fit-leak sensitivity.
|
||||
- System: RTL latency, glitch/dropout rate, CPU load, memory headroom, battery impact.
|
||||
- Subjective: MUSHRA/AB preference tests, panel notes, artifact taxonomy.
|
||||
|
||||
Safety and integrity gates:
|
||||
- Never fabricate measurements, listening outcomes, or citations.
|
||||
- If hearing safety could be impacted, require explicit level limits and verification steps.
|
||||
- If irreversible hardware actions are requested, require explicit confirmation and safe fallback path.
|
||||
- Protect credentials and proprietary parameters.
|
||||
|
||||
Specialist route mapping:
|
||||
- "Improve call quality" -> Voice Quality + Validation paths
|
||||
- "Reduce earbud power while keeping ANC" -> ANC + Embedded RT + Hardware Integration
|
||||
- "Fix audio glitches" -> Embedded RT + Hardware Integration + Validation
|
||||
- "Compare beamforming methods" -> Spatial/Array + Research Synthesis
|
||||
- "Ship-ready tuning plan" -> Playback/Voice/ANC (as relevant) + Validation
|
||||
|
||||
Task packet format for downstream specialists:
|
||||
```json
|
||||
{
|
||||
"objective": "<single product outcome>",
|
||||
"constraints": {
|
||||
"latency_ms": "<value or unknown>",
|
||||
"cpu_budget": "<value or unknown>",
|
||||
"power_budget": "<value or unknown>",
|
||||
"platform": "<SoC/DSP/MCU>",
|
||||
"sample_rate_hz": "<value>",
|
||||
"frame_size": "<value>
|
||||
"
|
||||
},
|
||||
"required_output": [
|
||||
"Recommended approach",
|
||||
"Why it fits product goals",
|
||||
"Tradeoffs",
|
||||
"Top 3 risks",
|
||||
"Objective metrics to track",
|
||||
"Subjective listening checks",
|
||||
"Implementation next steps"
|
||||
],
|
||||
"limits": [
|
||||
"No fabricated data",
|
||||
"State assumptions explicitly"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Orchestration rules:
|
||||
- Split only when subproblems are independent and interfaces are clear.
|
||||
- Normalize units (ms, dB, Hz, mW, MIPS) and definitions across outputs.
|
||||
- Resolve conflicts by preferring measured evidence > validated simulation > reasoned estimate.
|
||||
- If conflict remains, present it as a decision fork with verification to break the tie.
|
||||
|
||||
Fallback behavior:
|
||||
- If selected specialist fails, retry once with narrower objective and stricter output schema.
|
||||
- If retry fails, route to a generalist technical path and lower confidence.
|
||||
- If critical constraints are missing, provide best-effort baseline + one blocking question.
|
||||
|
||||
Response template:
|
||||
```text
|
||||
Route Selected:
|
||||
- <specialist path(s)>
|
||||
|
||||
Why This Route:
|
||||
- <1-3 product-focused bullets>
|
||||
|
||||
Recommendation:
|
||||
<final user-facing answer>
|
||||
|
||||
Assumptions and Risks:
|
||||
- <bullets>
|
||||
|
||||
Verification Plan:
|
||||
- Objective: <3-7 checks with metrics and thresholds>
|
||||
- Subjective: <2-5 listening test checks>
|
||||
|
||||
Confidence:
|
||||
- <High|Medium|Low> with one-line rationale
|
||||
```
|
||||
|
||||
Clarification template (only when blocked):
|
||||
```text
|
||||
I can dispatch this accurately, but I need one detail:
|
||||
- <single targeted question>
|
||||
|
||||
Default I will assume for speed:
|
||||
- <recommended default>
|
||||
```
|
||||
|
||||
Quality bar:
|
||||
- Product impact over algorithm novelty.
|
||||
- Verifiable claims over qualitative promises.
|
||||
- Fast experiment loops over broad rewrites.
|
||||
- Explicit uncertainty over false precision.
|
||||
@@ -1,149 +0,0 @@
|
||||
---
|
||||
name: research-engineering
|
||||
description: Route DSP hardware and software research-engineering requests to the best specialist workflow and return a unified, decision-ready output
|
||||
---
|
||||
|
||||
Role: You are a dispatcher skill for DSP hardware and software research engineering. You triage requests, select the right specialist path(s), enforce safety and reproducibility constraints, and return one coherent response.
|
||||
|
||||
Primary objectives:
|
||||
- Identify technical intent across algorithms, embedded implementation, hardware architecture, tooling, and validation.
|
||||
- Route work to the most appropriate specialist workflow(s) with explicit assumptions.
|
||||
- Produce practical, testable outputs for research engineering decisions.
|
||||
- Minimize unnecessary handoffs and avoid over-engineering.
|
||||
|
||||
Scope:
|
||||
- In scope: signal analysis, DSP algorithm design, fixed-point strategy, embedded audio/DSP implementation, architecture tradeoffs, measurement plans, benchmarking, verification strategy, literature-grounded research synthesis.
|
||||
- Out of scope: legal/compliance claims, medical claims, fabrication process sign-off, irreversible production actions.
|
||||
|
||||
Non-goals:
|
||||
- Do not pretend to run lab measurements that were not run.
|
||||
- Do not claim numerical performance without source, simulation, or measurement basis.
|
||||
- Do not bypass hardware safety, power, thermal, EMC, or hearing-safety constraints.
|
||||
|
||||
Inputs expected:
|
||||
- User request text
|
||||
- Current conversation context
|
||||
- Available specialist agents/skills
|
||||
- Environment/tooling constraints
|
||||
- Optional project constraints (sample rate, latency budget, CPU target, memory budget, power target, BOM constraints)
|
||||
|
||||
Required output contract:
|
||||
- Always provide:
|
||||
1) Selected route
|
||||
2) Why this route
|
||||
3) Final user-facing result
|
||||
4) Assumptions and unknowns
|
||||
5) Verification plan (how to confirm correctness/performance)
|
||||
|
||||
Dispatch taxonomy:
|
||||
- Algorithm Design: filters, adaptive processing, beamforming, detection/classification front-ends, denoising, dynamics, time-frequency methods.
|
||||
- Numerical Implementation: fixed-point, quantization noise, saturation behavior, scaling, coefficient sensitivity, stability under finite precision.
|
||||
- Embedded Software: RT constraints, DMA/ISR design, buffering, scheduling, memory layout, SIMD/accelerators, portability.
|
||||
- Hardware/Platform: MCU/DSP/FPGA partitioning, codec/interface constraints, clocking, throughput, latency, power/thermal tradeoffs.
|
||||
- Validation and Measurement: objective metrics, stimulus design, golden references, regression tests, bench/lab measurement plans.
|
||||
- Research Synthesis: literature scan, method comparison, risk/novelty assessment, experiment roadmap.
|
||||
|
||||
Routing policy:
|
||||
1. Parse request into one or more intents.
|
||||
2. Extract hard constraints and success criteria.
|
||||
3. Score candidate routes on:
|
||||
- Relevance (0-5)
|
||||
- Capability fit (0-5)
|
||||
- Safety/feasibility (0-5)
|
||||
- Evidence availability (0-5)
|
||||
- Execution cost (0-5, lower is better)
|
||||
4. Select route:
|
||||
- Single-route if one clear winner.
|
||||
- Multi-route if subproblems are separable and independent.
|
||||
5. Dispatch with structured task packets.
|
||||
6. Reconcile outputs into a single final response.
|
||||
|
||||
Confidence rules:
|
||||
- High: top route exceeds second by >= 3 and all hard constraints are known.
|
||||
- Medium: top route exceeds second by 1-2 or one non-critical constraint missing; proceed with explicit assumptions.
|
||||
- Low: tie score or missing critical constraint (platform, sample rate, latency, safety limit); ask exactly one targeted question.
|
||||
|
||||
Critical constraints checklist:
|
||||
- Target platform (e.g., Cortex-M4/M7, SHARC, FPGA family)
|
||||
- Sample rate and channel count
|
||||
- End-to-end latency budget
|
||||
- CPU/memory budget
|
||||
- Power/thermal envelope (if embedded/portable)
|
||||
- Numeric format (float/fixed, word lengths)
|
||||
- Required performance metrics (SNR, THD+N, PESQ/STOI, detection F1, etc.)
|
||||
|
||||
Safety and integrity gates (must run before dispatch):
|
||||
- If safety-critical or human-impacting audio claims are requested, include explicit uncertainty and verification requirements.
|
||||
- If destructive hardware actions are requested, require explicit confirmation and safe fallback.
|
||||
- Never expose secrets, proprietary keys, or internal credentials.
|
||||
- Never fabricate measurement data or citations.
|
||||
|
||||
Specialist route mapping:
|
||||
- Signal characterization question -> Signal Analysis specialist
|
||||
- Embedded DSP implementation/debug -> Embedded DSP specialist
|
||||
- Hardware/software partitioning -> Embedded hardware architect path
|
||||
- Literature-heavy "state of the art" request -> Research Assistant or literature path
|
||||
- Cross-domain request (algorithm + embedded + validation) -> Multi-route orchestration with unified recommendation
|
||||
|
||||
Task packet format for downstream specialists:
|
||||
```json
|
||||
{
|
||||
"objective": "<single clear objective>",
|
||||
"context": ["<key constraints>", "<known assumptions>"],
|
||||
"required_output": [
|
||||
"Approach",
|
||||
"Tradeoffs",
|
||||
"Risks",
|
||||
"Verification steps",
|
||||
"Confidence"
|
||||
],
|
||||
"limits": ["No fabricated data", "State unknowns explicitly"]
|
||||
}
|
||||
```
|
||||
|
||||
Multi-route orchestration rules:
|
||||
- Split only when interfaces between subproblems are clear.
|
||||
- Normalize units and terminology across outputs.
|
||||
- Resolve disagreements by preferring: measured evidence > validated simulation > reasoned estimate.
|
||||
- If unresolved conflict remains, surface it as a decision risk.
|
||||
|
||||
Fallback behavior:
|
||||
- If selected specialist fails, retry once with narrowed objective and stricter output format.
|
||||
- If retry fails, route to a generalist technical path and label confidence reduced.
|
||||
- If key constraints are missing, provide a best-effort scaffold plus one blocking question.
|
||||
|
||||
Response template:
|
||||
```text
|
||||
Route Selected:
|
||||
- <specialist path(s)>
|
||||
|
||||
Why This Route:
|
||||
- <1-3 concise bullets>
|
||||
|
||||
Result:
|
||||
<final user-facing answer>
|
||||
|
||||
Assumptions and Unknowns:
|
||||
- <bullet list or "None">
|
||||
|
||||
Verification Plan:
|
||||
- <3-7 concrete checks/tests/measurements>
|
||||
|
||||
Confidence:
|
||||
- <High|Medium|Low> with one-line rationale
|
||||
```
|
||||
|
||||
Clarification template (only when blocked):
|
||||
```text
|
||||
I can dispatch this precisely, but I need one detail:
|
||||
- <single targeted question>
|
||||
|
||||
Default I will assume if you prefer speed:
|
||||
- <recommended default>
|
||||
```
|
||||
|
||||
Quality bar:
|
||||
- Actionable over theoretical.
|
||||
- Reproducible over vague.
|
||||
- Explicit uncertainty over false precision.
|
||||
- Deliver the smallest valid plan that can be tested quickly.
|
||||
@@ -1,146 +0,0 @@
|
||||
---
|
||||
name: forge-gitea
|
||||
---
|
||||
|
||||
# Gitea Forge Interaction
|
||||
|
||||
Use this skill for Gitea forge work when the user wants to interact with Gitea repositories, issues, pull requests, releases, or CI.
|
||||
|
||||
## Purpose
|
||||
|
||||
Choose the correct Gitea CLI (`tea`) and use it to interact with Gitea issues, pull requests, releases, CI, and repository state.
|
||||
|
||||
This skill is intended to perform Gitea forge actions, including mutating actions, when the user asks for them. Do not turn every requested forge action into a confirmation loop; if the user clearly asks to create, edit, comment, publish, or release, do the requested action after selecting the correct CLI.
|
||||
|
||||
## Strict Decision Tree
|
||||
|
||||
Follow this order every time.
|
||||
|
||||
### 1. Determine the target remote
|
||||
|
||||
1. If the user explicitly says which remote or forge to use, use that.
|
||||
2. Else, if user/project memory or repo guidance states where to find the forge, use that.
|
||||
3. Else, if a remote named `forge` exists, use `forge`.
|
||||
4. Else, if the user has configured a default remote, use that.
|
||||
5. Else, use `origin`.
|
||||
|
||||
Useful inspection commands:
|
||||
|
||||
```bash
|
||||
git remote -v
|
||||
git config --get checkout.defaultRemote
|
||||
git config --get clone.defaultRemoteName
|
||||
git config --get branch.$(git branch --show-current).remote
|
||||
```
|
||||
|
||||
Interpretation:
|
||||
|
||||
- A remote named `forge` is the preferred convention for the canonical forge remote.
|
||||
- If no `forge` remote exists, `origin` is the fallback.
|
||||
- If the current branch has an upstream remote and no stronger rule applies, treat that as the user's configured default for the current work.
|
||||
|
||||
Only mention this selection if there is ambiguity or a conflict.
|
||||
|
||||
### 2. Choose the CLI
|
||||
|
||||
Use `tea` for Gitea interactions.
|
||||
|
||||
Check whether the chosen CLI is available:
|
||||
|
||||
```bash
|
||||
command -v tea >/dev/null 2>&1 && tea --version
|
||||
```
|
||||
|
||||
If the needed CLI is missing:
|
||||
|
||||
- Tell the user which CLI is required: `tea` for Gitea.
|
||||
- Tell the user to install it.
|
||||
- If it may already be installed but not discoverable, tell the user to add it to their `PATH`.
|
||||
- Do not use the wrong CLI as a fallback.
|
||||
|
||||
### 3. Check authentication/context
|
||||
|
||||
Use read-only checks for the selected tool:
|
||||
|
||||
```bash
|
||||
tea login list
|
||||
tea repos ls
|
||||
```
|
||||
|
||||
If authentication is missing, tell the user which CLI needs login/configuration. Do not ask the user to paste tokens or secrets.
|
||||
|
||||
### 4. Do the requested forge task
|
||||
|
||||
Perform the requested action with `tea` commands such as:
|
||||
|
||||
```bash
|
||||
tea issues list
|
||||
tea issues create
|
||||
tea issues comment
|
||||
tea pulls list
|
||||
tea pulls create
|
||||
tea pulls view
|
||||
tea pulls comment
|
||||
tea pulls merge
|
||||
tea releases list
|
||||
tea releases create
|
||||
```
|
||||
|
||||
Use exact command syntax supported by the installed CLI version; inspect help when needed:
|
||||
|
||||
```bash
|
||||
tea help
|
||||
```
|
||||
|
||||
## Mutating Actions
|
||||
|
||||
Mutating forge actions are allowed when clearly requested by the user, including:
|
||||
|
||||
- create an issue;
|
||||
- modify an issue;
|
||||
- comment on an issue;
|
||||
- create a pull request;
|
||||
- modify a pull request;
|
||||
- comment on a pull request;
|
||||
- push a branch;
|
||||
- publish changes;
|
||||
- create a release.
|
||||
|
||||
Still be careful with destructive or high-impact actions:
|
||||
|
||||
- Ask before deleting branches, tags, releases, issues, or repositories.
|
||||
- Ask before force-pushing.
|
||||
- Ask before merging a PR unless the user explicitly asked to merge it.
|
||||
- Ask before overwriting existing release assets or tags.
|
||||
|
||||
## Branch and PR Preparation
|
||||
|
||||
Before creating or updating a PR:
|
||||
|
||||
1. Select the remote using the strict decision tree.
|
||||
2. Inspect branch state and upstream tracking.
|
||||
3. Inspect the diff against the target branch.
|
||||
4. Read the PR template if one exists.
|
||||
5. Push the branch if needed and requested by the workflow.
|
||||
6. Create or update the PR.
|
||||
|
||||
Useful local inspection:
|
||||
|
||||
```bash
|
||||
git status --short --branch
|
||||
git branch -vv
|
||||
git diff --stat
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Output Style
|
||||
|
||||
Normally, do not over-explain. If the remote/tool selection is straightforward, just complete the task and summarize the result.
|
||||
|
||||
Report detection details only when there is ambiguity, conflict, missing tooling, or failure. In those cases include:
|
||||
|
||||
- selected remote;
|
||||
- detected forge;
|
||||
- selected CLI;
|
||||
- reason for the choice;
|
||||
- what the user needs to fix, if anything.
|
||||
@@ -1,149 +0,0 @@
|
||||
---
|
||||
name: forge-github
|
||||
---
|
||||
|
||||
# GitHub Forge Interaction
|
||||
|
||||
Use this skill for GitHub forge work when the user wants to interact with GitHub repositories, issues, pull requests, releases, or CI.
|
||||
|
||||
## Purpose
|
||||
|
||||
Choose the correct GitHub CLI (`gh`) and use it to interact with GitHub issues, pull requests, releases, CI, and repository state.
|
||||
|
||||
This skill is intended to perform GitHub forge actions, including mutating actions, when the user asks for them. Do not turn every requested forge action into a confirmation loop; if the user clearly asks to create, edit, comment, publish, or release, do the requested action after selecting the correct CLI.
|
||||
|
||||
## Strict Decision Tree
|
||||
|
||||
Follow this order every time.
|
||||
|
||||
### 1. Determine the target remote
|
||||
|
||||
1. If the user explicitly says which remote or forge to use, use that.
|
||||
2. Else, if user/project memory or repo guidance states where to find the forge, use that.
|
||||
3. Else, if a remote named `forge` exists, use `forge`.
|
||||
4. Else, if the user has configured a default remote, use that.
|
||||
5. Else, use `origin`.
|
||||
|
||||
Useful inspection commands:
|
||||
|
||||
```bash
|
||||
git remote -v
|
||||
git config --get checkout.defaultRemote
|
||||
git config --get clone.defaultRemoteName
|
||||
git config --get branch.$(git branch --show-current).remote
|
||||
```
|
||||
|
||||
Interpretation:
|
||||
|
||||
- A remote named `forge` is the preferred convention for the canonical forge remote.
|
||||
- If no `forge` remote exists, `origin` is the fallback.
|
||||
- If the current branch has an upstream remote and no stronger rule applies, treat that as the user's configured default for the current work.
|
||||
|
||||
Only mention this selection if there is ambiguity or a conflict.
|
||||
|
||||
### 2. Choose the CLI
|
||||
|
||||
Use `gh` for GitHub interactions.
|
||||
|
||||
Check whether the chosen CLI is available:
|
||||
|
||||
```bash
|
||||
command -v gh >/dev/null 2>&1 && gh --version
|
||||
```
|
||||
|
||||
If the needed CLI is missing:
|
||||
|
||||
- Tell the user which CLI is required: `gh` for GitHub.
|
||||
- Tell the user to install it.
|
||||
- If it may already be installed but not discoverable, tell the user to add it to their `PATH`.
|
||||
- Do not use the wrong CLI as a fallback.
|
||||
|
||||
### 3. Check authentication/context
|
||||
|
||||
Use read-only checks for the selected tool:
|
||||
|
||||
```bash
|
||||
gh auth status
|
||||
gh repo view
|
||||
```
|
||||
|
||||
If authentication is missing, tell the user which CLI needs login/configuration. Do not ask the user to paste tokens or secrets.
|
||||
|
||||
### 4. Do the requested forge task
|
||||
|
||||
Perform the requested action with `gh` commands such as:
|
||||
|
||||
```bash
|
||||
gh issue list
|
||||
gh issue create
|
||||
gh issue comment
|
||||
gh pr list
|
||||
gh pr create
|
||||
gh pr view
|
||||
gh pr comment
|
||||
gh pr edit
|
||||
gh pr merge
|
||||
gh run list
|
||||
gh run view
|
||||
gh release list
|
||||
gh release create
|
||||
```
|
||||
|
||||
Use exact command syntax supported by the installed CLI version; inspect help when needed:
|
||||
|
||||
```bash
|
||||
gh help
|
||||
```
|
||||
|
||||
## Mutating Actions
|
||||
|
||||
Mutating forge actions are allowed when clearly requested by the user, including:
|
||||
|
||||
- create an issue;
|
||||
- modify an issue;
|
||||
- comment on an issue;
|
||||
- create a pull request;
|
||||
- modify a pull request;
|
||||
- comment on a pull request;
|
||||
- push a branch;
|
||||
- publish changes;
|
||||
- create a release.
|
||||
|
||||
Still be careful with destructive or high-impact actions:
|
||||
|
||||
- Ask before deleting branches, tags, releases, issues, or repositories.
|
||||
- Ask before force-pushing.
|
||||
- Ask before merging a PR unless the user explicitly asked to merge it.
|
||||
- Ask before overwriting existing release assets or tags.
|
||||
|
||||
## Branch and PR Preparation
|
||||
|
||||
Before creating or updating a PR:
|
||||
|
||||
1. Select the remote using the strict decision tree.
|
||||
2. Inspect branch state and upstream tracking.
|
||||
3. Inspect the diff against the target branch.
|
||||
4. Read the PR template if one exists.
|
||||
5. Push the branch if needed and requested by the workflow.
|
||||
6. Create or update the PR.
|
||||
|
||||
Useful local inspection:
|
||||
|
||||
```bash
|
||||
git status --short --branch
|
||||
git branch -vv
|
||||
git diff --stat
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Output Style
|
||||
|
||||
Normally, do not over-explain. If the remote/tool selection is straightforward, just complete the task and summarize the result.
|
||||
|
||||
Report detection details only when there is ambiguity, conflict, missing tooling, or failure. In those cases include:
|
||||
|
||||
- selected remote;
|
||||
- detected forge;
|
||||
- selected CLI;
|
||||
- reason for the choice;
|
||||
- what the user needs to fix, if anything.
|
||||
@@ -1,227 +0,0 @@
|
||||
---
|
||||
name: forge-interaction
|
||||
description: Use when the user wants forge work such as opening a PR, creating or listing issues, checking CI, looking at the repo, pushing a branch, publishing changes, or making a release on GitHub or Gitea. This skill now delegates to specialized skills for better predictability.
|
||||
---
|
||||
|
||||
# Forge Interaction (Router)
|
||||
|
||||
This skill has been refactored into specialized skills for better predictability and maintainability:
|
||||
|
||||
- **GitHub**: Use `/forge-github` for GitHub repositories, issues, pull requests, releases, or CI
|
||||
- **Gitea**: Use `/forge-gitea` for Gitea repositories, issues, pull requests, releases, or CI
|
||||
|
||||
## Purpose
|
||||
|
||||
This router skill delegates to the appropriate specialized forge interaction skill based on the target forge. Each specialized skill handles one forge type completely, making them more predictable and easier to maintain.
|
||||
|
||||
## When to use:
|
||||
|
||||
- If you're unsure which forge you're working with, use this skill and it will guide you
|
||||
- For general forge work without specifying the forge type
|
||||
- When you want the system to figure out which specialized skill to use
|
||||
|
||||
## Delegation Logic
|
||||
|
||||
The router analyzes your request and determines whether to delegate to:
|
||||
|
||||
1. **GitHub skill** (`/forge-github`) - for GitHub repositories and workflows
|
||||
2. **Gitea skill** (`/forge-gitea`) - for Gitea repositories and workflows
|
||||
|
||||
Each specialized skill contains the complete decision tree and implementation for its respective forge type.
|
||||
|
||||
## Recommendation
|
||||
|
||||
For most predictable results, use the specialized skills directly:
|
||||
- `/forge-github` when working with GitHub
|
||||
- `/forge-gitea` when working with Gitea
|
||||
|
||||
This separation follows the principle of single responsibility and makes each skill more focused and reliable.
|
||||
|
||||
## Strict Decision Tree
|
||||
|
||||
Follow this order every time.
|
||||
|
||||
### 1. Determine the target remote
|
||||
|
||||
1. If the user explicitly says which remote or forge to use, use that.
|
||||
2. Else, if user/project memory or repo guidance states where to find the forge, use that.
|
||||
3. Else, if a remote named `forge` exists, use `forge`.
|
||||
4. Else, if the user has configured a default remote, use that.
|
||||
5. Else, use `origin`.
|
||||
|
||||
Useful inspection commands:
|
||||
|
||||
```bash
|
||||
git remote -v
|
||||
git config --get checkout.defaultRemote
|
||||
git config --get clone.defaultRemoteName
|
||||
git config --get branch.$(git branch --show-current).remote
|
||||
```
|
||||
|
||||
Interpretation:
|
||||
|
||||
- A remote named `forge` is the preferred convention for the canonical forge remote.
|
||||
- If no `forge` remote exists, `origin` is the fallback.
|
||||
- If the current branch has an upstream remote and no stronger rule applies, treat that as the user's configured default for the current work.
|
||||
|
||||
Only mention this selection if there is ambiguity or a conflict.
|
||||
|
||||
### 2. Determine the forge type from the chosen remote
|
||||
|
||||
Inspect the selected remote URL:
|
||||
|
||||
```bash
|
||||
git remote get-url <remote>
|
||||
```
|
||||
|
||||
Classify it:
|
||||
|
||||
- URLs containing `github.com` are GitHub.
|
||||
- URLs containing `gitea`, or a known Gitea host from memory/project guidance, are Gitea.
|
||||
- If the remote is self-hosted and ambiguous, inspect repo guidance, memory, and web/API/tool configuration before asking.
|
||||
|
||||
Do not choose based on which CLI is installed. The remote determines the forge; the forge determines the CLI.
|
||||
|
||||
### 3. Choose the CLI
|
||||
|
||||
- If the forge is GitHub, use `gh`.
|
||||
- If the forge is Gitea, use `tea`.
|
||||
|
||||
Check whether the chosen CLI is available:
|
||||
|
||||
```bash
|
||||
command -v gh >/dev/null 2>&1 && gh --version
|
||||
command -v tea >/dev/null 2>&1 && tea --version
|
||||
```
|
||||
|
||||
If the needed CLI is missing:
|
||||
|
||||
- Tell the user which CLI is required: `gh` for GitHub, `tea` for Gitea.
|
||||
- Tell the user to install it.
|
||||
- If it may already be installed but not discoverable, tell the user to add it to their `PATH`.
|
||||
- Do not use the wrong CLI as a fallback.
|
||||
|
||||
### 4. Check authentication/context
|
||||
|
||||
Use read-only checks for the selected tool:
|
||||
|
||||
```bash
|
||||
# GitHub
|
||||
gh auth status
|
||||
gh repo view
|
||||
|
||||
# Gitea
|
||||
tea login list
|
||||
tea repos ls
|
||||
```
|
||||
|
||||
If authentication is missing, tell the user which CLI needs login/configuration. Do not ask the user to paste tokens or secrets.
|
||||
|
||||
### 5. Do the requested forge task
|
||||
|
||||
Perform the requested action with the selected CLI.
|
||||
|
||||
For GitHub, use `gh` commands such as:
|
||||
|
||||
```bash
|
||||
gh issue list
|
||||
gh issue create
|
||||
gh issue comment
|
||||
gh pr list
|
||||
gh pr create
|
||||
gh pr view
|
||||
gh pr comment
|
||||
gh pr edit
|
||||
gh pr merge
|
||||
gh run list
|
||||
gh run view
|
||||
gh release list
|
||||
gh release create
|
||||
```
|
||||
|
||||
For Gitea, use `tea` commands such as:
|
||||
|
||||
```bash
|
||||
tea issues list
|
||||
tea issues create
|
||||
tea issues comment
|
||||
tea pulls list
|
||||
tea pulls create
|
||||
tea pulls view
|
||||
tea pulls comment
|
||||
tea pulls merge
|
||||
tea releases list
|
||||
tea releases create
|
||||
```
|
||||
|
||||
Use exact command syntax supported by the installed CLI version; inspect help when needed:
|
||||
|
||||
```bash
|
||||
gh help
|
||||
tea help
|
||||
```
|
||||
|
||||
## Mutating Actions
|
||||
|
||||
Mutating forge actions are allowed when clearly requested by the user, including:
|
||||
|
||||
- create an issue;
|
||||
- modify an issue;
|
||||
- comment on an issue;
|
||||
- create a pull request;
|
||||
- modify a pull request;
|
||||
- comment on a pull request;
|
||||
- push a branch;
|
||||
- publish changes;
|
||||
- create a release.
|
||||
|
||||
Still be careful with destructive or high-impact actions:
|
||||
|
||||
- Ask before deleting branches, tags, releases, issues, or repositories.
|
||||
- Ask before force-pushing.
|
||||
- Ask before merging a PR unless the user explicitly asked to merge it.
|
||||
- Ask before overwriting existing release assets or tags.
|
||||
|
||||
## Branch and PR Preparation
|
||||
|
||||
Before creating or updating a PR:
|
||||
|
||||
1. Select the remote using the strict decision tree.
|
||||
2. Select the CLI from the remote's forge type.
|
||||
3. Inspect branch state and upstream tracking.
|
||||
4. Inspect the diff against the target branch.
|
||||
5. Read the PR template if one exists.
|
||||
6. Push the branch if needed and requested by the workflow.
|
||||
7. Create or update the PR.
|
||||
|
||||
Useful local inspection:
|
||||
|
||||
```bash
|
||||
git status --short --branch
|
||||
git branch -vv
|
||||
git diff --stat
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Output Style
|
||||
|
||||
Normally, do not over-explain. If the remote/tool selection is straightforward, just complete the task and summarize the result.
|
||||
|
||||
Report detection details only when there is ambiguity, conflict, missing tooling, or failure. In those cases include:
|
||||
|
||||
- selected remote;
|
||||
- detected forge;
|
||||
- selected CLI;
|
||||
- reason for the choice;
|
||||
- what the user needs to fix, if anything.
|
||||
|
||||
## Companion Personal Skill
|
||||
|
||||
Personal forge preferences should live in a separate skill or memory entry, not in this general skill. That companion skill/memory may specify things like:
|
||||
|
||||
- preferred default remote conventions;
|
||||
- known personal Gitea or GitHub hosts;
|
||||
- preferred issue/PR/release styles;
|
||||
- project-specific forge rules.
|
||||
|
||||
This general skill should consume that memory/guidance when present, but keep the generic decision tree above as the fallback.
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: forge-preferences
|
||||
description: Use with forge-interaction to apply Steve's personal or project-specific GitHub/Gitea remote, CLI, issue, PR, and release preferences.
|
||||
---
|
||||
|
||||
# Forge Preferences
|
||||
|
||||
Use this companion skill with `/forge-interaction` when personal or project-specific forge preferences are relevant.
|
||||
|
||||
## Purpose
|
||||
|
||||
Store personal conventions that should not live in the generic forge interaction skill.
|
||||
|
||||
## Current Preferences
|
||||
|
||||
- If a remote named `forge` exists, treat it as the canonical forge remote.
|
||||
- If no `forge` remote exists, use the user's configured default remote when available.
|
||||
- If no default remote is configured, use `origin`.
|
||||
- Use the forge type of the selected remote to choose the CLI:
|
||||
- GitHub → `gh`
|
||||
- Gitea → `tea`
|
||||
- If the correct CLI is missing, tell the user to install it or add it to `PATH`.
|
||||
- Do not use the wrong CLI as a fallback.
|
||||
|
||||
## Derived Preferences (auto-detected at runtime)
|
||||
|
||||
- Canonical forge remote: prefer `forge`, then default remote, then `origin`.
|
||||
- Forge CLI: determined from remote URL (GitHub → `gh`, Gitea → `tea`).
|
||||
- Known personal Gitea hosts and GitHub usernames/orgs are collected from the remote URL and git CLI at time of use — no hardcoded list.
|
||||
|
||||
## Issue Labels
|
||||
|
||||
See `triage-labels.md` in this directory for the canonical-to-actual label mapping.
|
||||
|
||||
## PR Conventions
|
||||
|
||||
- PR titles follow Conventional Commits: `type(scope): description`.
|
||||
- Common types: `feat`, `fix`, `refactor`, `docs`, `chore`, `test`, `style`.
|
||||
- PR body is freeform but should summarise the change and any breaking or noteworthy details.
|
||||
|
||||
## Release Conventions
|
||||
|
||||
- Tags follow semver: `v{major}.{minor}.{patch}` (e.g. `v1.2.3`).
|
||||
- Releases are created from the tag with auto-generated or manually curated notes.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Triage Labels
|
||||
|
||||
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
|
||||
|
||||
| Label in mattpocock/skills | Label in our tracker | Meaning |
|
||||
| -------------------------- | -------------------- | ---------------------------------------- |
|
||||
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
|
||||
| `needs-info` | `needs-info` | Waiting on reporter for more information |
|
||||
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
|
||||
| `ready-for-human` | `ready-for-human` | Requires human implementation |
|
||||
| `wontfix` | `wontfix` | Will not be actioned |
|
||||
|
||||
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
|
||||
|
||||
Edit the right-hand column to match whatever vocabulary you actually use.
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
name: forge-router
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Forge Router
|
||||
|
||||
Choose the right forge interaction skill for your task.
|
||||
|
||||
## When to use:
|
||||
|
||||
- **GitHub**: Use `/forge-github` when working with GitHub repositories, issues, pull requests, releases, or CI.
|
||||
- **Gitea**: Use `/forge-gitea` when working with Gitea repositories, issues, pull requests, releases, or CI.
|
||||
|
||||
## How it works:
|
||||
|
||||
The router delegates to specialized skills that handle each forge type separately. This keeps each skill focused and predictable.
|
||||
|
||||
**GitHub skill** (`/forge-github`):
|
||||
- Handles GitHub-specific interactions using the `gh` CLI
|
||||
- Follows GitHub's API patterns and conventions
|
||||
- Optimized for GitHub's feature set
|
||||
|
||||
**Gitea skill** (`/forge-gitea`):
|
||||
- Handles Gitea-specific interactions using the `tea` CLI
|
||||
- Follows Gitea's API patterns and conventions
|
||||
- Optimized for Gitea's feature set
|
||||
|
||||
## Why separate:
|
||||
|
||||
- **Predictability**: Each skill knows exactly one forge type
|
||||
- **Maintainability**: Changes to GitHub or Gitea logic stay isolated
|
||||
- **Clarity**: Users can see which forge a skill handles at a glance
|
||||
- **Testing**: Each skill can be tested independently
|
||||
|
||||
## Usage examples:
|
||||
|
||||
```
|
||||
# For GitHub work
|
||||
/forge-github create an issue in this repo
|
||||
/forge-github list pull requests
|
||||
/forge-github check CI status
|
||||
|
||||
# For Gitea work
|
||||
/forge-gitea create an issue in this repo
|
||||
/forge-gitea list pull requests
|
||||
/forge-gitea check CI status
|
||||
```
|
||||
|
||||
The router ensures you always use the right tool for the right forge.
|
||||
@@ -1,86 +0,0 @@
|
||||
# Forge Skills
|
||||
|
||||
A collection of specialized forge interaction skills for GitHub and Gitea.
|
||||
|
||||
## Overview
|
||||
|
||||
This directory contains focused skills for interacting with different code hosting platforms:
|
||||
|
||||
- **GitHub**: Use `/forge-github` for GitHub repositories, issues, pull requests, releases, or CI
|
||||
- **Gitea**: Use `/forge-gitea` for Gitea repositories, issues, pull requests, releases, or CI
|
||||
- **Router**: Use `/forge-interaction` to let the system choose the right skill automatically
|
||||
|
||||
## Skill Structure
|
||||
|
||||
Each forge-specific skill is designed with:
|
||||
|
||||
1. **Single Responsibility**: Handles only one forge type (GitHub or Gitea)
|
||||
2. **Predictable Behavior**: Clear decision tree and completion criteria
|
||||
3. **Complete Coverage**: All requested forge actions are supported
|
||||
4. **Error Handling**: Specific guidance for missing tools or authentication
|
||||
|
||||
## Choosing the Right Skill
|
||||
|
||||
### Use `/forge-github` when:
|
||||
- Working with GitHub repositories (`github.com` URLs)
|
||||
- Using the `gh` CLI tool
|
||||
- Interacting with GitHub-specific features (issues, PRs, releases, CI)
|
||||
|
||||
### Use `/forge-gitea` when:
|
||||
- Working with Gitea repositories (`gitea.com` or self-hosted Gitea)
|
||||
- Using the `tea` CLI tool
|
||||
- Interacting with Gitea-specific features (issues, PRs, releases, CI)
|
||||
|
||||
### Use `/forge-interaction` when:
|
||||
- You're unsure which forge you're working with
|
||||
- You want the system to automatically choose the right skill
|
||||
- You prefer a unified interface that handles both platforms
|
||||
|
||||
## Skill Comparison
|
||||
|
||||
| Feature | GitHub Skill | Gitea Skill |
|
||||
|---------|--------------|-------------|
|
||||
| CLI Tool | `gh` | `tea` |
|
||||
| Platform | GitHub | Gitea |
|
||||
| Focus | GitHub-specific | Gitea-specific |
|
||||
| Predictability | High | High |
|
||||
| Maintenance | Isolated | Isolated |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```
|
||||
# For GitHub work
|
||||
/forge-github create an issue in this repo
|
||||
/forge-github list pull requests
|
||||
/forge-github check CI status
|
||||
/forge-github publish a new release
|
||||
|
||||
# For Gitea work
|
||||
/forge-gitea create an issue in this repo
|
||||
/forge-gitea list pull requests
|
||||
/forge-gitea check CI status
|
||||
/forge-gitea publish a new release
|
||||
|
||||
# Let the system decide
|
||||
/forge-interaction create a PR for this feature
|
||||
```
|
||||
|
||||
## Why Separate Skills?
|
||||
|
||||
1. **Predictability**: Each skill knows exactly one forge type
|
||||
2. **Maintainability**: Changes to GitHub or Gitea logic stay isolated
|
||||
3. **Clarity**: Users can see which forge a skill handles at a glance
|
||||
4. **Testing**: Each skill can be tested independently
|
||||
5. **Cognitive Load**: Users remember fewer skills and their specific purposes
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `/forge-router` - High-level guidance for choosing the right forge skill
|
||||
- `/forge-preferences` - Personal or project-specific forge configurations
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Choose the appropriate skill based on your forge platform
|
||||
2. Ensure the required CLI tool (`gh` or `tea`) is installed
|
||||
3. Configure authentication for your forge platform
|
||||
4. Start with simple actions and build up to more complex workflows
|
||||
@@ -1,19 +1,15 @@
|
||||
# Engineering Skills
|
||||
|
||||
Daily code work.
|
||||
|
||||
## User-invoked
|
||||
|
||||
- [implement-issue](implement-issue/SKILL.md) — Dispatch a child agent in an isolated git worktree to implement a ready-for-agent issue end-to-end.
|
||||
- [grill-with-docs](grill-with-docs/SKILL.md) — A relentless interview to sharpen a plan or design, which also creates docs (ADRs and glossary) as we go.
|
||||
- [improve-codebase-architecture](improve-codebase-architecture/SKILL.md) — Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
|
||||
- [project-context-pack](project-context-pack/SKILL.md) — Build and refresh a bounded repo context memory file so agents use disciplined search instead of repeated browsing.
|
||||
- [setup-skills](setup-skills/SKILL.md) — Configure this repo for the engineering skills, set up its issue tracker, triage label vocabulary, and domain doc layout.
|
||||
- [to-issues](to-issues/SKILL.md) — Break a plan, spec, or PRD into independently-grabbable issues on the project issue tracker using tracer-bullet vertical slices.
|
||||
- [to-prd](to-prd/SKILL.md) — Turn the current conversation into a PRD and publish it to the project issue tracker — no interview, just synthesis of what you've already discussed.
|
||||
- [triage](triage/SKILL.md) — Move issues and external PRs through a state machine of triage roles — categorise, verify, grill if needed, and write agent-ready briefs.
|
||||
- [commit-staged](commit-staged/SKILL.md) — Commit staged files with a conventional commit message.
|
||||
- [implement-isolation](implement-isolation/SKILL.md) — Implement a piece of work based on a spec or set of tickets in isolation.
|
||||
- [implement-isolation-tmux](implement-isolation-tmux/SKILL.md) — Dispatch a child agent in an isolated git worktree to implement a piece of work based on a PRD or set of issues.
|
||||
- [project-context-pack](project-context-pack/SKILL.md) — Build a bounded repo context pack (project map, codebase index, cached memory file) so later work uses fd/rg/tree-sitter/LSP instead of repeated browsing.
|
||||
- [setup-skills](setup-skills/SKILL.md) — Configure this repo for the engineering skills, set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.
|
||||
|
||||
## Model-invoked
|
||||
|
||||
- [codebase-design](codebase-design/SKILL.md) — Shared vocabulary for designing deep modules.
|
||||
- [domain-modeling](domain-modeling/SKILL.md) — Build and sharpen a project's domain model.
|
||||
- [resolving-merge-conflicts](resolving-merge-conflicts/SKILL.md) — Resolve an in-progress git merge/rebase conflict.
|
||||
- [tdd](tdd/SKILL.md) — Test-driven development.
|
||||
- [lsp-code-analysis](lsp-code-analysis/SKILL.md) — Semantic code analysis via LSP. Navigate code (definitions, references, implementations), search symbols, preview refactorings, and get file outlines. Use for exploring unfamiliar codebases or performing safe refactoring.
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
# Deepening
|
||||
|
||||
How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [SKILL.md](SKILL.md) — **module**, **interface**, **seam**, **adapter**.
|
||||
|
||||
## Dependency categories
|
||||
|
||||
When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.
|
||||
|
||||
### 1. In-process
|
||||
|
||||
Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed.
|
||||
|
||||
### 2. Local-substitutable
|
||||
|
||||
Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.
|
||||
|
||||
### 3. Remote but owned (Ports & Adapters)
|
||||
|
||||
Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.
|
||||
|
||||
Recommendation shape: *"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."*
|
||||
|
||||
### 4. True external (Mock)
|
||||
|
||||
Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.
|
||||
|
||||
## Seam discipline
|
||||
|
||||
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
|
||||
- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.
|
||||
|
||||
## Testing strategy: replace, don't layer
|
||||
|
||||
- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them.
|
||||
- Write new tests at the deepened module's interface. The **interface is the test surface**.
|
||||
- Tests assert on observable outcomes through the interface, not internal state.
|
||||
- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.
|
||||
@@ -1,44 +0,0 @@
|
||||
# Design It Twice
|
||||
|
||||
When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best.
|
||||
|
||||
Uses the vocabulary in [SKILL.md](SKILL.md) — **module**, **interface**, **seam**, **adapter**, **leverage**.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Frame the problem space
|
||||
|
||||
Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate:
|
||||
|
||||
- The constraints any new interface would need to satisfy
|
||||
- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md))
|
||||
- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete
|
||||
|
||||
Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel.
|
||||
|
||||
### 2. Spawn sub-agents
|
||||
|
||||
Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module.
|
||||
|
||||
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
|
||||
|
||||
- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point."
|
||||
- Agent 2: "Maximise flexibility — support many use cases and extension."
|
||||
- Agent 3: "Optimise for the most common caller — make the default case trivial."
|
||||
- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies."
|
||||
|
||||
Include both [SKILL.md](SKILL.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language.
|
||||
|
||||
Each sub-agent outputs:
|
||||
|
||||
1. Interface (types, methods, params — plus invariants, ordering, error modes)
|
||||
2. Usage example showing how callers use it
|
||||
3. What the implementation hides behind the seam
|
||||
4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md))
|
||||
5. Trade-offs — where leverage is high, where it's thin
|
||||
|
||||
### 3. Present and compare
|
||||
|
||||
Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**.
|
||||
|
||||
After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu.
|
||||
@@ -1,114 +0,0 @@
|
||||
---
|
||||
name: codebase-design
|
||||
description: Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
|
||||
---
|
||||
|
||||
# Codebase Design
|
||||
|
||||
Design **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this language and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone.
|
||||
|
||||
## Glossary
|
||||
|
||||
Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point.
|
||||
|
||||
**Module** — anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. _Avoid_: unit, component, service.
|
||||
|
||||
**Interface** — everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. _Avoid_: API, signature (too narrow — they refer only to the type-level surface).
|
||||
|
||||
**Implementation** — what's inside a module, its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise.
|
||||
|
||||
**Depth** — leverage at the interface: the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface, **shallow** when the interface is nearly as complex as the implementation.
|
||||
|
||||
**Seam** _(Michael Feathers)_ — a place where you can alter behaviour without editing in that place; the *location* at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. _Avoid_: boundary (overloaded with DDD's bounded context).
|
||||
|
||||
**Adapter** — a concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside).
|
||||
|
||||
**Leverage** — what callers get from depth: more capability per unit of interface they learn. One implementation pays back across N call sites and M tests.
|
||||
|
||||
**Locality** — what maintainers get from depth: change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers. Fix once, fixed everywhere.
|
||||
|
||||
## Deep vs shallow
|
||||
|
||||
**Deep module** = small interface + lots of implementation:
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ Small Interface │ ← Few methods, simple params
|
||||
├─────────────────────┤
|
||||
│ │
|
||||
│ Deep Implementation│ ← Complex logic hidden
|
||||
│ │
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
**Shallow module** = large interface + little implementation (avoid):
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ Large Interface │ ← Many methods, complex params
|
||||
├─────────────────────────────────┤
|
||||
│ Thin Implementation │ ← Just passes through
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
When designing an interface, ask:
|
||||
|
||||
- Can I reduce the number of methods?
|
||||
- Can I simplify the parameters?
|
||||
- Can I hide more complexity inside?
|
||||
|
||||
## Principles
|
||||
|
||||
- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface.
|
||||
- **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.
|
||||
- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test *past* the interface, the module is probably the wrong shape.
|
||||
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it.
|
||||
|
||||
## Designing for testability
|
||||
|
||||
Good interfaces make testing natural:
|
||||
|
||||
1. **Accept dependencies, don't create them.**
|
||||
|
||||
```typescript
|
||||
// Testable
|
||||
function processOrder(order, paymentGateway) {}
|
||||
|
||||
// Hard to test
|
||||
function processOrder(order) {
|
||||
const gateway = new StripeGateway();
|
||||
}
|
||||
```
|
||||
|
||||
2. **Return results, don't produce side effects.**
|
||||
|
||||
```typescript
|
||||
// Testable
|
||||
function calculateDiscount(cart): Discount {}
|
||||
|
||||
// Hard to test
|
||||
function applyDiscount(cart): void {
|
||||
cart.total -= discount;
|
||||
}
|
||||
```
|
||||
|
||||
3. **Small surface area.** Fewer methods = fewer tests needed. Fewer params = simpler test setup.
|
||||
|
||||
## Relationships
|
||||
|
||||
- A **Module** has exactly one **Interface** (the surface it presents to callers and tests).
|
||||
- **Depth** is a property of a **Module**, measured against its **Interface**.
|
||||
- A **Seam** is where a **Module**'s **Interface** lives.
|
||||
- An **Adapter** sits at a **Seam** and satisfies the **Interface**.
|
||||
- **Depth** produces **Leverage** for callers and **Locality** for maintainers.
|
||||
|
||||
## Rejected framings
|
||||
|
||||
- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead.
|
||||
- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know.
|
||||
- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**.
|
||||
|
||||
## Going deeper
|
||||
|
||||
- **Deepening a cluster given its dependencies** — see [DEEPENING.md](DEEPENING.md): dependency categories, seam discipline, and replace-don't-layer testing.
|
||||
- **Exploring alternative interfaces** — see [DESIGN-IT-TWICE.md](DESIGN-IT-TWICE.md): spin up parallel sub-agents to design the interface several radically different ways, then compare on depth, locality, and seam placement.
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: commit-staged
|
||||
description: Commit staged files with a conventional commit message.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
## Process
|
||||
|
||||
1. **Check the staging area** — Run `git diff --cached --stat`. If empty, report "nothing staged" and stop.
|
||||
|
||||
Completion criterion: Staged files are listed, or the skill terminates.
|
||||
|
||||
2. **Draft the message** — Inspect `git diff --cached` to understand what changed. Write a [conventional commit](https://www.conventionalcommits.org/) message:
|
||||
|
||||
- Format: `type(scope): summary` — scope is optional.
|
||||
- Common types: `feat`, `fix`, `refactor`, `docs`, `chore`, `test`, `style`, `perf`, `ci`, `build`.
|
||||
- Summary: imperative mood, lowercase, no period, ≤72 characters.
|
||||
- Body (if needed): wrap at 72 characters, explain _what_ and _why_, not _how_.
|
||||
|
||||
Completion criterion: A conventional commit message is composed.
|
||||
|
||||
3. **Commit** — Run `git commit -m "<message>"`. If the commit fails, report the error and stop.
|
||||
|
||||
Completion criterion: `git commit` exits 0.
|
||||
|
||||
4. **Report** — Print the short commit hash and the first line of the message.
|
||||
|
||||
Completion criterion: The hash and message are printed.
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Commit Staged"
|
||||
short_description: "Commit staged changes to the repository"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,47 +0,0 @@
|
||||
# ADR Format
|
||||
|
||||
ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc.
|
||||
|
||||
Create the `docs/adr/` directory lazily — only when the first ADR is needed.
|
||||
|
||||
## Template
|
||||
|
||||
```md
|
||||
# {Short title of the decision}
|
||||
|
||||
{1-3 sentences: what's the context, what did we decide, and why.}
|
||||
```
|
||||
|
||||
That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why* — not in filling out sections.
|
||||
|
||||
## Optional sections
|
||||
|
||||
Only include these when they add genuine value. Most ADRs won't need them.
|
||||
|
||||
- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — useful when decisions are revisited
|
||||
- **Considered Options** — only when the rejected alternatives are worth remembering
|
||||
- **Consequences** — only when non-obvious downstream effects need to be called out
|
||||
|
||||
## Numbering
|
||||
|
||||
Scan `docs/adr/` for the highest existing number and increment by one.
|
||||
|
||||
## When to offer an ADR
|
||||
|
||||
All three of these must be true:
|
||||
|
||||
1. **Hard to reverse** — the cost of changing your mind later is meaningful
|
||||
2. **Surprising without context** — a future reader will look at the code and wonder "why on earth did they do it this way?"
|
||||
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
|
||||
|
||||
If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."
|
||||
|
||||
### What qualifies
|
||||
|
||||
- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres."
|
||||
- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP."
|
||||
- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out.
|
||||
- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s.
|
||||
- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate.
|
||||
- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract."
|
||||
- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months.
|
||||
@@ -1,60 +0,0 @@
|
||||
# CONTEXT.md Format
|
||||
|
||||
## Structure
|
||||
|
||||
```md
|
||||
# {Context Name}
|
||||
|
||||
{One or two sentence description of what this context is and why it exists.}
|
||||
|
||||
## Language
|
||||
|
||||
**Order**:
|
||||
{A one or two sentence description of the term}
|
||||
_Avoid_: Purchase, transaction
|
||||
|
||||
**Invoice**:
|
||||
A request for payment sent to a customer after delivery.
|
||||
_Avoid_: Bill, payment request
|
||||
|
||||
**Customer**:
|
||||
A person or organization that places orders.
|
||||
_Avoid_: Client, buyer, account
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others under `_Avoid_`.
|
||||
- **Keep definitions tight.** One or two sentences max. Define what it IS, not what it does.
|
||||
- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs.
|
||||
- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine.
|
||||
|
||||
## Single vs multi-context repos
|
||||
|
||||
**Single context (most repos):** One `CONTEXT.md` at the repo root.
|
||||
|
||||
**Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
|
||||
|
||||
```md
|
||||
# Context Map
|
||||
|
||||
## Contexts
|
||||
|
||||
- [Ordering](./src/ordering/CONTEXT.md) — receives and tracks customer orders
|
||||
- [Billing](./src/billing/CONTEXT.md) — generates invoices and processes payments
|
||||
- [Fulfillment](./src/fulfillment/CONTEXT.md) — manages warehouse picking and shipping
|
||||
|
||||
## Relationships
|
||||
|
||||
- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking
|
||||
- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices
|
||||
- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money`
|
||||
```
|
||||
|
||||
The skill infers which structure applies:
|
||||
|
||||
- If `CONTEXT-MAP.md` exists, read it to find contexts
|
||||
- If only a root `CONTEXT.md` exists, single context
|
||||
- If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved
|
||||
|
||||
When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.
|
||||
@@ -1,74 +0,0 @@
|
||||
---
|
||||
name: domain-modeling
|
||||
description: Build and sharpen a project's domain model. Use when the user wants to pin down domain terminology or a ubiquitous language, record an architectural decision, or when another skill needs to maintain the domain model.
|
||||
---
|
||||
|
||||
# Domain Modeling
|
||||
|
||||
Actively build and sharpen the project's domain model as you design. This is the *active* discipline — challenging terms, inventing edge-case scenarios, and writing the glossary and decisions down the moment they crystallise. (Merely *reading* `CONTEXT.md` for vocabulary is not this skill — that's a one-line habit any skill can do. This skill is for when you're changing the model, not just consuming it.)
|
||||
|
||||
## File structure
|
||||
|
||||
Most repos have a single context:
|
||||
|
||||
```
|
||||
/
|
||||
├── CONTEXT.md
|
||||
├── docs/
|
||||
│ └── adr/
|
||||
│ ├── 0001-event-sourced-orders.md
|
||||
│ └── 0002-postgres-for-write-model.md
|
||||
└── src/
|
||||
```
|
||||
|
||||
If a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives:
|
||||
|
||||
```
|
||||
/
|
||||
├── CONTEXT-MAP.md
|
||||
├── docs/
|
||||
│ └── adr/ ← system-wide decisions
|
||||
├── src/
|
||||
│ ├── ordering/
|
||||
│ │ ├── CONTEXT.md
|
||||
│ │ └── docs/adr/ ← context-specific decisions
|
||||
│ └── billing/
|
||||
│ ├── CONTEXT.md
|
||||
│ └── docs/adr/
|
||||
```
|
||||
|
||||
Create files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed.
|
||||
|
||||
## During the session
|
||||
|
||||
### Challenge against the glossary
|
||||
|
||||
When the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?"
|
||||
|
||||
### Sharpen fuzzy language
|
||||
|
||||
When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account' — do you mean the Customer or the User? Those are different things."
|
||||
|
||||
### Discuss concrete scenarios
|
||||
|
||||
When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts.
|
||||
|
||||
### Cross-reference with code
|
||||
|
||||
When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?"
|
||||
|
||||
### Update CONTEXT.md inline
|
||||
|
||||
When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md).
|
||||
|
||||
`CONTEXT.md` should be totally devoid of implementation details. Do not treat `CONTEXT.md` as a spec, a scratch pad, or a repository for implementation decisions. It is a glossary and nothing else.
|
||||
|
||||
### Offer ADRs sparingly
|
||||
|
||||
Only offer to create an ADR when all three are true:
|
||||
|
||||
1. **Hard to reverse** — the cost of changing your mind later is meaningful
|
||||
2. **Surprising without context** — a future reader will wonder "why did they do it this way?"
|
||||
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
|
||||
|
||||
If any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md).
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
name: grill-with-docs
|
||||
description: A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Run a `/grilling` session, using the `/domain-modeling` skill.
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
name: implement-isolation-tmux
|
||||
description: "Dispatch a child agent in an isolated git worktree to implement a piece of work based on a PRD or set of issues."
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
## Invocation
|
||||
|
||||
/skill:implement-isolation-tmux <N> [--base <branch>] [--force]
|
||||
|
||||
- `<N>` — required, the issue number
|
||||
- `--base <branch>` — optional, target base branch (default: repo default branch)
|
||||
- `--force` — optional, remove the existing worktree directory first, then recreate
|
||||
|
||||
Triage labels follow the vocabulary in `docs/agents/triage-labels.md`.
|
||||
|
||||
## Process
|
||||
|
||||
## 1. Claim and isolate
|
||||
|
||||
Change the issue triage label to `in-progress`, remove all other labels. Derive `<issue-slug>` from the issue title: lowercase, replace spaces with hyphens, strip non-alphanumeric characters (keep hyphens). Then create a git worktree for the ticket as a sibling directory named `../issue-<N>-<issue-slug>`, checked out from the base branch. Switch to the worktree. The branch name matches the directory: `issue-<N>-<issue-slug>`.
|
||||
|
||||
```bash
|
||||
git worktree add -b issue-<N>-<issue-slug> ../issue-<N>-<issue-slug> <base>
|
||||
```
|
||||
|
||||
Stay in the worktree directory for the rest of the process.
|
||||
|
||||
Completion criterion: The issue label is confirmed as `in-progress` and the worktree exists at `../issue-<N>-<issue-slug>` with branch `issue-<N>-<issue-slug>` checked out.
|
||||
|
||||
### 2. Compose the child prompt
|
||||
|
||||
Assemble a single prompt that the child agent will receive. Include:
|
||||
|
||||
- **Issue body** — the full markdown body of the issue.
|
||||
- **Comments** — all comments, if any
|
||||
- **Standing instruction**: "Implement the work described by the user in the spec or tickets, using tdd skill where possible. Run typechecking regularly, run single test files regularly, and run the full test suite once at the end. Once done, use code-review skill to review the work. Commit your work and push the new branch and create a PR with a link to the issue, one-sentence summary, and short key-changes list. Comment on the issue with the PR link: `PR opened: <url>`. Change the issue triage label to needs-review when done. Remove all other labels"
|
||||
|
||||
- **Failure instruction**: "If any step fails, report where you stopped and what remains for manual recovery. Print the exact commands needed."
|
||||
|
||||
Completion criterion: The prompt is composed with all required sections (issue body, comments, standing instruction, failure instruction).
|
||||
|
||||
### 3. Launch the child via tmux-launch-agent
|
||||
|
||||
Use the `tmux-launch-agent` skill to fork the child agent into a new tmux window. Tmux-launch-agent handles agent detection, config lookup, command building (prompt-file or stdin-pipe), mise/SHELL wrapping, and `tmux new-window` creation.
|
||||
|
||||
Pass these parameters:
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| `--name` | `"issue-<N>-<issue-slug>"` — tmux window title |
|
||||
| Remaining args | The full prompt text composed in step 2 |
|
||||
|
||||
Ensure the child agent starts in the worktree. If the current pane's working directory is already inside `<absolute-worktree-path>` (from step 1), tmux-launch-agent's new window inherits it. Otherwise, pass `-c <absolute-worktree-path>` when invoking tmux-launch-agent so the child agent starts in the worktree directory.
|
||||
|
||||
Let tmux-launch-agent handle prompt-file creation — it writes a temp file automatically when the target agent uses prompt-file mode. Pass the prompt text inline.
|
||||
|
||||
The window stays open after the child completes so the user can review the output.
|
||||
|
||||
Completion criterion: `tmux-launch-agent` exits 0 and a new tmux window appears with the child agent session active.
|
||||
|
||||
### 4. Print summary
|
||||
|
||||
After launching, print:
|
||||
|
||||
- Issue number and title
|
||||
- Worktree path
|
||||
- Branch name
|
||||
- Base branch
|
||||
- Agent used (detected by tmux-launch-agent)
|
||||
- Tmux window name (so the user can find it)
|
||||
- Cleanup command: `git worktree remove <worktree-path> && git worktree prune`
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Implement Isolation Tmux"
|
||||
short_description: "Implement isolation using tmux for the agent"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
name: implement-isolation
|
||||
description: "Implement a piece of work based on a spec or set of tickets in isolation."
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
## Invocation
|
||||
|
||||
/skill:implement-isolation <N> [--base <branch>] [--force]
|
||||
|
||||
- `<N>` — required, the ticket/issue number
|
||||
- `--base <branch>` — optional, target base branch (default: repo default branch)
|
||||
- `--force` — optional, remove the existing worktree directory first, then recreate
|
||||
|
||||
Triage labels follow the vocabulary in `docs/agents/triage-labels.md`.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Claim and isolate
|
||||
|
||||
Change the issue triage label to `in-progress`, remove all other labels. Derive `<issue-slug>` from the issue title: lowercase, replace spaces with hyphens, strip non-alphanumeric characters (keep hyphens). Then create a git worktree for the ticket as a sibling directory named `../issue-<N>-<issue-slug>`, checked out from the base branch. Switch to the worktree. The branch name matches the directory: `issue-<N>-<issue-slug>`.
|
||||
|
||||
```bash
|
||||
git worktree add -b issue-<N>-<issue-slug> ../issue-<N>-<issue-slug> <base>
|
||||
```
|
||||
|
||||
Stay in the worktree directory for the rest of the process.
|
||||
|
||||
Completion criterion: The issue label is confirmed as `in-progress` and the worktree exists at `../issue-<N>-<issue-slug>` with branch `issue-<N>-<issue-slug>` checked out.
|
||||
|
||||
### 2. Implement
|
||||
|
||||
Start a subagent to implement the work in isolation. The subagent receives a single prompt that includes:
|
||||
|
||||
- Issue body: the full markdown body of the issue.
|
||||
- Comments: all comments, if any
|
||||
- Implement: the work described by the user in the spec or tickets, using tdd skill where possible. Run typechecking regularly, run single test files regularly, and run the full test suite once at the end.
|
||||
|
||||
Completion criterion: The subagent exits with code 0 and all changes are committed.
|
||||
|
||||
### 3. Review
|
||||
|
||||
Start a reviewer subagent that runs the `code-review` skill against the base branch to identify findings in the diff. Wait for the reviewer to finish.
|
||||
|
||||
Review each finding and fix the code until zero findings remain. Commit fixes as you go.
|
||||
|
||||
Completion criterion: All code-review findings are resolved (zero open findings) and fixes are committed.
|
||||
|
||||
### 4. Ship
|
||||
|
||||
Push the branch, open a PR. The PR description must include a link to the ticket and a key-changes list.
|
||||
|
||||
Comment on the issue: `PR opened: <url>`. Label the issue `needs-review`, remove all other labels.
|
||||
|
||||
Print a cleanup command for the user: `git worktree remove ../issue-<N>-<issue-slug> && git worktree prune`
|
||||
|
||||
### Failure recovery
|
||||
|
||||
If the implement subagent exits non-zero or the review finds issues that cannot be fully resolved, stop. Report to the user: issue number, what was completed, what failed, and the exact commands needed to resume or clean up (including `git worktree remove ...`). Do not push partial work.
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Implement Isolation"
|
||||
short_description: "Implement isolation for the agent"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,225 +0,0 @@
|
||||
---
|
||||
name: implement-issue
|
||||
description: Dispatch a child agent in an isolated git worktree to implement a `ready-for-agent` issue end-to-end.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Implement Issue
|
||||
|
||||
Dispatch a child agent in an isolated git worktree to implement a `ready-for-agent` issue end-to-end — explore, implement with TDD, run the full quality gate, push a branch, create a PR, comment on the issue, and update labels.
|
||||
|
||||
The issue tracker conventions live in [`docs/agents/issue-tracker.md`](../../../docs/agents/issue-tracker.md) and the triage label vocabulary in [`docs/agents/triage-labels.md`](../../../docs/agents/triage-labels.md). Both should have been provided to you already.
|
||||
|
||||
## Invocation
|
||||
|
||||
```
|
||||
/skill:implement-issue <N> [--agent <name>] [--base <branch>] [--force]
|
||||
```
|
||||
|
||||
- `<N>` — required, the issue number
|
||||
- `--agent <name>` — **DEPRECATED**: this argument is ignored. The agent CLI is read from `docs/agents/agent-cli.md`, which is set up by `/setup-skills` Section E. Rerun `/setup-skills` to change the agent.
|
||||
- `--base <branch>` — optional, target base branch (default: repo default branch).
|
||||
- `--force` — optional, allow overwriting an existing worktree.
|
||||
|
||||
## Agent CLI Configuration
|
||||
|
||||
The CLI agent for child agents is configured in `docs/agents/agent-cli.md`. This file is written by `/setup-skills` Section E and contains:
|
||||
|
||||
```yaml
|
||||
selected: <agent-name>
|
||||
binary: <binary-name>
|
||||
args: "<arguments including @{prompt}>"
|
||||
```
|
||||
|
||||
Supported agents and their configurations are defined in `agents-seed.md` in the setup-skills skill directory.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Pre-flight checks
|
||||
|
||||
Stop on any failure and report clearly what needs fixing. Do not proceed.
|
||||
|
||||
#### 1b. Issue exists and is open
|
||||
|
||||
If the issue is not found or state is not `open`, report and stop.
|
||||
|
||||
#### 1c. Issue is labeled `ready-for-agent`
|
||||
|
||||
Check that the issue's labels include `ready-for-agent`. If not, report the current state and point the user to `/triage`. Stop.
|
||||
|
||||
#### 1d. No open blockers
|
||||
|
||||
Read the issue body for "Blocked by #M" references. For each referenced issue, check whether it is open:
|
||||
|
||||
If any blocker is open, report the blocking issues and stop.
|
||||
|
||||
#### 1e. Remote exists and base branch is reachable
|
||||
|
||||
Default base is the repo's default branch — infer from `git ls-remote --heads origin` (look for `main` or `master`). Allow an explicit `--base <branch>` override.
|
||||
|
||||
```bash
|
||||
git ls-remote --heads origin <base>
|
||||
```
|
||||
|
||||
If the remote is unreachable or the base branch doesn't exist, report and stop.
|
||||
|
||||
#### 1f. Branch name is free
|
||||
|
||||
Derive the branch name from the issue title:
|
||||
|
||||
- Format: `issue-<N>-<slug>`
|
||||
- Slug: lowercase the title, strip to `[a-z0-9-]`, truncate to ~4–5 short words (max ~50 chars), collapse double hyphens
|
||||
- If the title yields no usable slug, fall back to `issue-<N>`
|
||||
|
||||
Check that the branch doesn't already exist on the remote:
|
||||
|
||||
```bash
|
||||
git ls-remote --heads origin <branch-name>
|
||||
```
|
||||
|
||||
If it exists, report and stop. (No auto-suffix, no force-push.)
|
||||
|
||||
#### 1g. Worktree path is free
|
||||
|
||||
The worktree path is a sibling to the main repo: `../<repo-name>-issue-<N>`. Check that it doesn't already exist:
|
||||
|
||||
```bash
|
||||
ls -d ../<repo-name>-issue-<N>
|
||||
```
|
||||
|
||||
If it exists, report and stop. Let the user override with `--force`.
|
||||
|
||||
#### 1h. Running inside a tmux session
|
||||
|
||||
Confirm `$TMUX` is set. If not, report "This skill requires a tmux session. Start tmux and rerun." and stop.
|
||||
|
||||
#### 1i. Read agent CLI config
|
||||
|
||||
Read `docs/agents/agent-cli.md` and parse the `selected`, `binary`, and `args` fields. If the file is missing or malformed, report:
|
||||
|
||||
> Agent CLI not configured. Run `/setup-skills` Section E to configure the CLI agent for child agents.
|
||||
|
||||
Stop.
|
||||
|
||||
#### 1j. Validate binary on PATH
|
||||
|
||||
Confirm the configured `binary` is on PATH:
|
||||
|
||||
```bash
|
||||
command -v <binary>
|
||||
```
|
||||
|
||||
If the binary is not found, fail with:
|
||||
|
||||
> Agent '<binary>' not found on PATH — rerun /setup-skills to reconfigure.
|
||||
|
||||
Stop.
|
||||
|
||||
### 2. Setup
|
||||
|
||||
#### 2a. Label the issue `in-progress`
|
||||
|
||||
Apply the `in-progress` triage label.
|
||||
|
||||
#### 2b. Create the worktree
|
||||
|
||||
```bash
|
||||
git worktree add -b <branch-name> ../<repo-name>-issue-<N> <base>
|
||||
```
|
||||
|
||||
### 3. Compose and write the child prompt
|
||||
|
||||
Assemble a single prompt that the child agent will receive. Include:
|
||||
|
||||
- **Issue body** — the full markdown body from `tea issue <N> -o json`
|
||||
- **Comments** — all comments, if any
|
||||
- **Standing instruction**: "Implement using TDD: write a failing test first, make it pass, refactor. Explore the codebase before writing code. Respect ADRs and the project domain glossary. Set up the project (install dependencies, build) before starting. The issue body may contain Implementation Decisions and Testing Decisions sections — treat these as constraints."
|
||||
- **Context**: "Base branch: `<base>`. Target your PR at `<base>`. Branch name: `<branch-name>`."
|
||||
- **Checklist** — a numbered checklist the child should work through:
|
||||
1. Explore the codebase and read the issue body + comments thoroughly
|
||||
2. Install dependencies and build the project
|
||||
3. Implement the change using TDD (red-green-refactor)
|
||||
4. Run the full project quality gate (infer from package.json, Makefile, Cargo.toml, etc.), fix until green
|
||||
5. Commit with conventional commits (`feat:` for enhancement, `fix:` for bug) referencing `(#<N>)`
|
||||
6. Push the branch: `git push origin <branch-name>`
|
||||
7. Create a PR with: link to the issue, one-sentence summary, short key-changes list
|
||||
8. Comment on the issue with the PR link: `tea comment <N> "PR opened: <url>"`
|
||||
9. Change the issue label to `needs-review`: `tea issues edit <N> --add-labels "needs-review" --remove-labels "in-progress"`
|
||||
10. Print `DONE — issue #<N>`
|
||||
- **Failure instruction**: "If any step fails, report where you stopped and what remains for manual recovery. Print the exact commands needed."
|
||||
|
||||
Write the full prompt to a temp file:
|
||||
|
||||
```bash
|
||||
cat > /tmp/issue-<N>-prompt.md <<'PROMPT_EOF'
|
||||
<full-composed-prompt>
|
||||
PROMPT_EOF
|
||||
```
|
||||
|
||||
### 4. Launch the child
|
||||
|
||||
#### 4a. Detect runtime environment
|
||||
|
||||
Check if `mise` is available:
|
||||
|
||||
```bash
|
||||
command -v mise
|
||||
```
|
||||
|
||||
If available, use `mise x --allow-env='*' --` as the runner prefix. Otherwise, fall back to `$SHELL -c`.
|
||||
|
||||
#### 4b. Compose the agent command
|
||||
|
||||
Substitute `@{prompt}` in the configured `args` with the absolute path to the temp prompt file:
|
||||
|
||||
- If `args` is non-empty: the agent command is `<binary> <substituted-args>`
|
||||
- If `args` is empty (stdin-piping agents): the agent command is `cat <prompt-file> | <binary>`
|
||||
|
||||
#### 4c. Compose the full tmux command
|
||||
|
||||
```bash
|
||||
tmux new-window -n "issue-<N>-<repo-slug>" -c <absolute-worktree-path> "<runner-prefix> <agent-command>"
|
||||
```
|
||||
|
||||
**With mise:**
|
||||
```bash
|
||||
tmux new-window -n "issue-<N>-<repo>" -c /path/to/worktree "mise x --allow-env='*' -- <binary> <args-with-prompt-subbed>"
|
||||
```
|
||||
|
||||
**Without mise (fallback):**
|
||||
```bash
|
||||
tmux new-window -n "issue-<N>-<repo>" -c /path/to/worktree "$SHELL -c '<binary> <args-with-prompt-subbed>'"
|
||||
```
|
||||
|
||||
For stdin-piping agents (empty args), wrap the pipe command:
|
||||
|
||||
**With mise:**
|
||||
```bash
|
||||
tmux new-window -n "issue-<N>-<repo>" -c /path/to/worktree "mise x --allow-env='*' -- sh -c 'cat <prompt-file> | <binary>'"
|
||||
```
|
||||
|
||||
**Without mise:**
|
||||
```bash
|
||||
tmux new-window -n "issue-<N>-<repo>" -c /path/to/worktree "$SHELL -c 'cat <prompt-file> | <binary>'"
|
||||
```
|
||||
|
||||
The window stays open after the child completes so the user can review the output.
|
||||
|
||||
After tmux launches, clean up the temp file:
|
||||
|
||||
```bash
|
||||
rm /tmp/issue-<N>-prompt.md
|
||||
```
|
||||
|
||||
### 5. Print summary
|
||||
|
||||
After launching, print:
|
||||
- Issue number and title
|
||||
- Worktree path
|
||||
- Branch name
|
||||
- Base branch
|
||||
- Agent used (from `docs/agents/agent-cli.md`)
|
||||
- Tmux window name (so the user can find it)
|
||||
- Cleanup command: `git worktree remove <worktree-path> && git worktree prune`
|
||||
|
||||
The parent's turn ends here. The user can dispatch another issue immediately.
|
||||
@@ -1,161 +0,0 @@
|
||||
# Agent CLI Tests for implement-issue
|
||||
|
||||
These tests verify the tmux command composition logic in `implement-issue`. They intercept the generated shell command string rather than the tmux invocation itself.
|
||||
|
||||
The seam being tested: given a `docs/agents/agent-cli.md` config and a `mise` availability state, `implement-issue` must compose the correct tmux launch command.
|
||||
|
||||
## Test fixtures
|
||||
|
||||
### Fixture: pi agent config (stdin-piped via @{prompt})
|
||||
|
||||
```yaml
|
||||
# docs/agents/agent-cli.md
|
||||
selected: pi
|
||||
binary: pi
|
||||
args: "-p @{prompt}"
|
||||
```
|
||||
|
||||
### Fixture: opencode agent config
|
||||
|
||||
```yaml
|
||||
# docs/agents/agent-cli.md
|
||||
selected: opencode
|
||||
binary: opencode
|
||||
args: "run -f @{prompt}"
|
||||
```
|
||||
|
||||
### Fixture: codex agent config (empty args = stdin pipe)
|
||||
|
||||
```yaml
|
||||
# docs/agents/agent-cli.md
|
||||
selected: codex
|
||||
binary: codex
|
||||
args: ""
|
||||
```
|
||||
|
||||
### Fixture: missing binary config
|
||||
|
||||
```yaml
|
||||
# docs/agents/agent-cli.md
|
||||
selected: missing-agent
|
||||
binary: totally-not-on-path
|
||||
args: ""
|
||||
```
|
||||
|
||||
## Test cases
|
||||
|
||||
### Test: composes correct tmux command for pi with mise available
|
||||
|
||||
**Given:**
|
||||
- `docs/agents/agent-cli.md` contains the pi fixture
|
||||
- `command -v mise` succeeds
|
||||
|
||||
**When:** `implement-issue` composes the tmux launch command
|
||||
|
||||
**Then:** the generated command string is:
|
||||
|
||||
```
|
||||
tmux new-window -n "issue-42-ws-sjb-skills" -c /home/sjb/Projects/personal/ws-sjb-skills/ws-sjb-skills-issue-42 "mise x --allow-env='*' -- pi -p /tmp/issue-42-prompt.md"
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- `@{prompt}` is substituted with the actual temp file path
|
||||
- Window name includes issue number and repo slug
|
||||
- Worktree path is absolute
|
||||
- `mise x --allow-env='*'` is prepended when mise is available
|
||||
|
||||
---
|
||||
|
||||
### Test: composes correct tmux command for pi without mise (fallback to $SHELL -c)
|
||||
|
||||
**Given:**
|
||||
- `docs/agents/agent-cli.md` contains the pi fixture
|
||||
- `command -v mise` fails (no mise on PATH)
|
||||
|
||||
**When:** `implement-issue` composes the tmux launch command
|
||||
|
||||
**Then:** the generated command string is:
|
||||
|
||||
```
|
||||
tmux new-window -n "issue-42-ws-sjb-skills" -c /home/sjb/Projects/personal/ws-sjb-skills/ws-sjb-skills-issue-42 "$SHELL -c 'pi -p /tmp/issue-42-prompt.md'"
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Falls back to `$SHELL -c` when mise is absent
|
||||
- The agent command is wrapped inside `$SHELL -c '...'`
|
||||
|
||||
---
|
||||
|
||||
### Test: composes correct tmux command for codex (stdin piping, no @{prompt})
|
||||
|
||||
**Given:**
|
||||
- `docs/agents/agent-cli.md` contains the codex fixture (args: "")
|
||||
- `command -v mise` succeeds
|
||||
|
||||
**When:** `implement-issue` composes the tmux launch command
|
||||
|
||||
**Then:** the generated command string is:
|
||||
|
||||
```
|
||||
tmux new-window -n "issue-42-ws-sjb-skills" -c /home/sjb/Projects/personal/ws-sjb-skills/ws-sjb-skills-issue-42 "mise x --allow-env='*' -- sh -c 'cat /tmp/issue-42-prompt.md | codex'"
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- When args is empty, prompt is piped via `cat <prompt> | <binary>`
|
||||
- The pipe syntax is always `cat <prompt> | <binary>` for stdin-based agents
|
||||
|
||||
---
|
||||
|
||||
### Test: fails fast with clear message when binary not on PATH
|
||||
|
||||
**Given:**
|
||||
- `docs/agents/agent-cli.md` contains the missing binary fixture
|
||||
- `command -v totally-not-on-path` fails
|
||||
|
||||
**When:** `implement-issue` validates the binary at invocation time
|
||||
|
||||
**Then:** it fails immediately with:
|
||||
|
||||
```
|
||||
Agent 'totally-not-on-path' not found on PATH — rerun /setup-skills to reconfigure.
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Validation happens before any tmux launch
|
||||
- Error message directs user to re-run setup-skills
|
||||
|
||||
---
|
||||
|
||||
### Test: reads selected agent from docs/agents/agent-cli.md
|
||||
|
||||
**Given:**
|
||||
- `docs/agents/agent-cli.md` exists and contains valid YAML with `selected`, `binary`, and `args` fields
|
||||
|
||||
**When:** `implement-issue` reads the config at invocation time
|
||||
|
||||
**Then:** it parses the three fields correctly and uses them for dispatch
|
||||
|
||||
**Notes:**
|
||||
- Does not use hardcoded dispatch table
|
||||
- Does not run detection scripts
|
||||
- Reads directly from the repo-side config file
|
||||
|
||||
---
|
||||
|
||||
### Test: @{prompt} placeholder substitution
|
||||
|
||||
**Given:**
|
||||
- `docs/agents/agent-cli.md` contains opencode fixture (args: "run -f @{prompt}")
|
||||
- `command -v mise` succeeds
|
||||
|
||||
**When:** `implement-issue` composes the tmux launch command with prompt file at `/tmp/issue-99-prompt.md`
|
||||
|
||||
**Then:** the generated command contains:
|
||||
|
||||
```
|
||||
opencode run -f /tmp/issue-99-prompt.md
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- `@{prompt}` in args is replaced with the absolute path to the temp prompt file
|
||||
- Works regardless of where `@{prompt}` appears in the args string
|
||||
@@ -1,123 +0,0 @@
|
||||
# HTML Report Format
|
||||
|
||||
The architectural review is rendered as a single self-contained HTML file in the OS temp directory. Tailwind and Mermaid both come from CDNs. Mermaid handles graph-shaped diagrams reliably; hand-built divs and inline SVG handle the more editorial visuals (mass diagrams, cross-sections). Mix the two — don't lean on Mermaid for everything, it'll start to look generic.
|
||||
|
||||
## Scaffold
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Architecture review — {{repo name}}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script type="module">
|
||||
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
||||
mermaid.initialize({ startOnLoad: true, theme: "neutral", securityLevel: "loose" });
|
||||
</script>
|
||||
<style>
|
||||
/* small custom layer for things Tailwind doesn't cover cleanly:
|
||||
dashed seam lines, hand-drawn-feeling arrow heads, etc. */
|
||||
.seam { stroke-dasharray: 4 4; }
|
||||
.leak { stroke: #dc2626; }
|
||||
.deep { background: linear-gradient(135deg, #0f172a, #1e293b); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-stone-50 text-slate-900 font-sans">
|
||||
<main class="max-w-5xl mx-auto px-6 py-12 space-y-12">
|
||||
<header>...</header>
|
||||
<section id="candidates" class="space-y-10">...</section>
|
||||
<section id="top-recommendation">...</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Header
|
||||
|
||||
Repo name, date, and a compact legend: solid box = module, dashed line = seam, red arrow = leakage, thick dark box = deep module. No introduction paragraph — straight into the candidates.
|
||||
|
||||
## Candidate card
|
||||
|
||||
The diagrams carry the weight. Prose is sparse, plain, and uses the glossary terms (from the `/codebase-design` skill) without ceremony.
|
||||
|
||||
Each candidate is one `<article>`:
|
||||
|
||||
- **Title** — short, names the deepening (e.g. "Collapse the Order intake pipeline").
|
||||
- **Badge row** — recommendation strength (`Strong` = emerald, `Worth exploring` = amber, `Speculative` = slate), plus a tag for the dependency category (`in-process`, `local-substitutable`, `ports & adapters`, `mock`).
|
||||
- **Files** — monospaced list, `font-mono text-sm`.
|
||||
- **Before / After diagram** — the centrepiece. Two columns, side by side. See patterns below.
|
||||
- **Problem** — one sentence. What hurts.
|
||||
- **Solution** — one sentence. What changes.
|
||||
- **Wins** — bullets, ≤6 words each. e.g. "Tests hit one interface", "Pricing logic stops leaking", "Delete 4 shallow wrappers".
|
||||
- **ADR callout** (if applicable) — one line in an amber-tinted box.
|
||||
|
||||
No paragraphs of explanation. If the diagram needs a paragraph to be understood, redraw the diagram.
|
||||
|
||||
## Diagram patterns
|
||||
|
||||
Pick the pattern that fits the candidate. Mix them. Don't make every diagram look the same — variety is part of the point.
|
||||
|
||||
### Mermaid graph (the workhorse for dependencies / call flow)
|
||||
|
||||
Use a Mermaid `flowchart` or `graph` when the point is "X calls Y calls Z, and look at the mess." Wrap it in a Tailwind-styled card so it doesn't feel parachuted in. Style with classDef to colour leakage edges red and the deep module dark. Sequence diagrams work well for "before: 6 round-trips; after: 1."
|
||||
|
||||
```html
|
||||
<div class="rounded-lg border border-slate-200 bg-white p-4">
|
||||
<pre class="mermaid">
|
||||
flowchart LR
|
||||
A[OrderHandler] --> B[OrderValidator]
|
||||
B --> C[OrderRepo]
|
||||
C -.leak.-> D[PricingClient]
|
||||
classDef leak stroke:#dc2626,stroke-width:2px;
|
||||
class C,D leak
|
||||
</pre>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Hand-built boxes-and-arrows (when Mermaid's layout fights you)
|
||||
|
||||
Modules as `<div>`s with borders and labels. Arrows as inline SVG `<line>` or `<path>` elements positioned absolutely over a relative container. Reach for this when you want the "after" diagram to feel like one thick-bordered deep module with greyed-out internals — Mermaid won't render that with the right weight.
|
||||
|
||||
### Cross-section (good for layered shallowness)
|
||||
|
||||
Stack horizontal bands (`h-12 border-l-4`) to show layers a call passes through. Before: 6 thin layers each doing nothing. After: 1 thick band labelled with the consolidated responsibility.
|
||||
|
||||
### Mass diagram (good for "interface as wide as implementation")
|
||||
|
||||
Two rectangles per module — one for interface surface area, one for implementation. Before: interface rectangle is nearly as tall as the implementation rectangle (shallow). After: interface rectangle is short, implementation rectangle is tall (deep).
|
||||
|
||||
### Call-graph collapse
|
||||
|
||||
Before: a tree of function calls rendered as nested boxes. After: the same tree collapsed into one box, with the now-internal calls shown faded inside it.
|
||||
|
||||
## Style guidance
|
||||
|
||||
- Lean editorial, not corporate-dashboard. Generous whitespace. Serif optional for headings (`font-serif` works well with stone/slate).
|
||||
- Colour sparingly: one accent (emerald or indigo) plus red for leakage and amber for warnings.
|
||||
- Keep diagrams ~320px tall so before/after sits comfortably side by side without scrolling.
|
||||
- Use `text-xs uppercase tracking-wider` for module labels inside diagrams — they should read as schematic, not as UI.
|
||||
- The only scripts are the Tailwind CDN and the Mermaid ESM import. The report is otherwise static — no app code, no interactivity beyond Mermaid's own rendering.
|
||||
|
||||
## Top recommendation section
|
||||
|
||||
One larger card. Candidate name, one sentence on why, anchor link to its card. That's it.
|
||||
|
||||
## Tone
|
||||
|
||||
Plain English, concise — but the architectural nouns and verbs come straight from the `/codebase-design` skill. Concision is not an excuse to drift.
|
||||
|
||||
**Use exactly:** module, interface, implementation, depth, deep, shallow, seam, adapter, leverage, locality.
|
||||
|
||||
**Never substitute:** component, service, unit (for module) · API, signature (for interface) · boundary (for seam) · layer, wrapper (for module, when you mean module).
|
||||
|
||||
**Phrasings that fit the style:**
|
||||
|
||||
- "Order intake module is shallow — interface nearly matches the implementation."
|
||||
- "Pricing leaks across the seam."
|
||||
- "Deepen: one interface, one place to test."
|
||||
- "Two adapters justify the seam: HTTP in prod, in-memory in tests."
|
||||
|
||||
**Wins bullets** name the gain in glossary terms: *"locality: bugs concentrate in one module"*, *"leverage: one interface, N call sites"*, *"interface shrinks; implementation absorbs the wrappers"*. Don't write *"easier to maintain"* or *"cleaner code"* — those terms aren't in the glossary and don't earn their place.
|
||||
|
||||
No hedging, no throat-clearing, no "it's worth noting that…". If a sentence could be a bullet, make it a bullet. If a bullet could be cut, cut it. If a term isn't in the `/codebase-design` glossary, reach for one that is before inventing a new one.
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
name: improve-codebase-architecture
|
||||
description: Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Improve Codebase Architecture
|
||||
|
||||
Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
|
||||
|
||||
This command is _informed_ by the project's domain model and built on a shared design vocabulary:
|
||||
|
||||
- Run the `/codebase-design` skill for the architecture vocabulary (**module**, **interface**, **depth**, **seam**, **adapter**, **leverage**, **locality**) and its principles (the deletion test, "the interface is the test surface", "one adapter = hypothetical seam, two = real"). Use these terms exactly in every suggestion — don't drift into "component," "service," "API," or "boundary."
|
||||
- The domain language in `CONTEXT.md` gives names to good seams; ADRs in `docs/adr/` record decisions this command should not re-litigate.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Explore
|
||||
|
||||
Read the project's domain glossary (`CONTEXT.md`) and any ADRs in the area you're touching first.
|
||||
|
||||
Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
|
||||
|
||||
- Where does understanding one concept require bouncing between many small modules?
|
||||
- Where are modules **shallow** — interface nearly as complex as the implementation?
|
||||
- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)?
|
||||
- Where do tightly-coupled modules leak across their seams?
|
||||
- Which parts of the codebase are untested, or hard to test through their current interface?
|
||||
|
||||
Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want.
|
||||
|
||||
### 2. Present candidates as an HTML report
|
||||
|
||||
Write a self-contained HTML file to the OS temp directory so nothing lands in the repo. Resolve the temp dir from `$TMPDIR`, falling back to `/tmp` (or `%TEMP%` on Windows), and write to `<tmpdir>/architecture-review-<timestamp>.html` so each run gets a fresh file. Open it for the user — `xdg-open <path>` on Linux, `open <path>` on macOS, `start <path>` on Windows — and tell them the absolute path.
|
||||
|
||||
The report uses **Tailwind via CDN** for layout and styling, and **Mermaid via CDN** for diagrams where a graph/flow/sequence reliably communicates the structure. Mix Mermaid with hand-crafted CSS/SVG visuals — use Mermaid when relationships are graph-shaped (call graphs, dependencies, sequences), and hand-built divs/SVG when you want something more editorial (mass diagrams, cross-sections, collapse animations). Each candidate gets a **before/after visualisation**. Be visual.
|
||||
|
||||
For each candidate, render a card with:
|
||||
|
||||
- **Files** — which files/modules are involved
|
||||
- **Problem** — why the current architecture is causing friction
|
||||
- **Solution** — plain English description of what would change
|
||||
- **Benefits** — explained in terms of locality and leverage, and how tests would improve
|
||||
- **Before / After diagram** — side-by-side, custom-drawn, illustrating the shallowness and the deepening
|
||||
- **Recommendation strength** — one of `Strong`, `Worth exploring`, `Speculative`, rendered as a badge
|
||||
|
||||
End the report with a **Top recommendation** section: which candidate you'd tackle first and why.
|
||||
|
||||
**Use CONTEXT.md vocabulary for the domain, and the `/codebase-design` vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler," and not "the Order service."
|
||||
|
||||
**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly in the card (e.g. a warning callout: _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids.
|
||||
|
||||
See [HTML-REPORT.md](HTML-REPORT.md) for the full HTML scaffold, diagram patterns, and styling guidance.
|
||||
|
||||
Do NOT propose interfaces yet. After the file is written, ask the user: "Which of these would you like to explore?"
|
||||
|
||||
### 3. Grilling loop
|
||||
|
||||
Once the user picks a candidate, run the `/grilling` skill to walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive.
|
||||
|
||||
Side effects happen inline as decisions crystallize — run the `/domain-modeling` skill to keep the domain model current as you go:
|
||||
|
||||
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md`. Create the file lazily if it doesn't exist.
|
||||
- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there.
|
||||
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones.
|
||||
- **Want to explore alternative interfaces for the deepened module?** Run the `/codebase-design` skill and use its design-it-twice parallel sub-agent pattern.
|
||||
@@ -0,0 +1,334 @@
|
||||
---
|
||||
name: lsp-code-analysis
|
||||
description: Semantic code analysis via LSP. Navigate code (definitions, references, implementations), search symbols, preview refactorings, and get file outlines. Use for exploring unfamiliar codebases or performing safe refactoring.
|
||||
license: LICENSE
|
||||
---
|
||||
|
||||
# LSP Code Analysis
|
||||
|
||||
## IMPORTANT: PREREQUISITE
|
||||
|
||||
To use this skill, you **MUST** follow these steps:
|
||||
|
||||
1. **Check for updates**: Run the [update script](scripts/update.sh) to ensure you are using the latest version of the tool.
|
||||
2. **Verify project support**: Run `lsp server start <project_path>` to start the LSP server and confirm the project is supported.
|
||||
|
||||
**IF YOU DO NOT PERFORM THESE STEPS, YOU ARE NOT ALLOWED TO USE THIS SKILL.**
|
||||
|
||||
## Abstract
|
||||
|
||||
This document specifies the operational requirements and best practices for the `lsp-code-analysis` skill. It provides a semantic interface to codebase navigation, analysis and refactoring via the Language Server Protocol (LSP).
|
||||
|
||||
## Overview
|
||||
|
||||
You are provided with `lsp` CLI tool for semantic code navigation and analysis. It SHOULD be preferred over `read` or `grep` for most code understanding tasks.
|
||||
|
||||
Usages:
|
||||
|
||||
- **Semantic navigation**: Jump to definitions, find references, locate implementations - understands code structure, not just text patterns.
|
||||
- **Language-aware**: Distinguishes between variables, functions, classes, types - eliminates false positives from text search.
|
||||
- **Cross-file intelligence**: Trace dependencies, refactor safely across entire codebase - knows what imports what.
|
||||
- **Type-aware**: Get precise type information, signatures, documentation - without reading implementation code.
|
||||
|
||||
### Tool Selection
|
||||
|
||||
**Guideline**: You SHOULD prioritize LSP commands for code navigation and analysis. Agents MAY use `read` or `rg` ONLY when semantic analysis is not applicable (e.g., searching for comments or literal strings).
|
||||
|
||||
| Task | Traditional Tool | Recommended LSP Command |
|
||||
| ------------------- | ---------------- | ----------------------------------------------- |
|
||||
| **Find Definition** | `rg`, `read` | [`definition`](#definition-navigate-to-source)|
|
||||
| **Find Usages** | `rg` | [`reference`](#reference-find-all-usages) |
|
||||
| **Understand File** | `read` | [`outline`](#outline-file-structure) |
|
||||
| **View Docs/Types** | `read` | [`doc`](#doc-get-documentation) |
|
||||
| **Refactor** | `sed` | See [Refactoring Guide](references/refactor.md) |
|
||||
|
||||
## Commands
|
||||
|
||||
All commands support `-h` or `--help`.
|
||||
|
||||
### Locating Symbols
|
||||
|
||||
Most commands use a unified locating syntax via the `--scope` and `--find` options.
|
||||
|
||||
**Arguments**: `<file_path>`
|
||||
|
||||
**Options**:
|
||||
|
||||
- `--scope`: Narrow search to a symbol body or line range.
|
||||
- `--find`: Text pattern to find within the scope.
|
||||
|
||||
**Scope Formats**:
|
||||
|
||||
- `<line>`: Single line number (e.g., `42`).
|
||||
- `<start>,<end>`: Line range (e.g., `10,20`). Use `0` for end to mean till EOF (e.g., `10,0`).
|
||||
- `<symbol_path>`: Symbol path with dots (e.g., `MyClass.my_method`).
|
||||
|
||||
**Find Pattern (`--find`)**:
|
||||
|
||||
The `--find` option narrows the target to a **text pattern within the selected scope**:
|
||||
|
||||
- The scope is determined by `--scope` (line/range/symbol). If no `--scope` is given, the entire file is the scope.
|
||||
- Pattern matching is **whitespace-insensitive**: differences in spaces, tabs, and newlines are ignored.
|
||||
- You MAY include the cursor marker `<|>` inside the pattern to specify the **exact position of interest** within the match (for example, on a variable name, keyword, or operator).
|
||||
- If `--find` is omitted, the command uses the start of the scope (or a tool-specific default) as the navigation target.
|
||||
|
||||
**Cursor Marker (`<|>`)**:
|
||||
|
||||
The `<|>` marker indicates the exact position for symbol resolution. It represents the character immediately to its right. Use it within the find pattern to point to a specific element (e.g., `user.<|>name` to target the `name` property).
|
||||
|
||||
**Examples**:
|
||||
|
||||
- `lsp doc foo.py --find "self.<|>"` - Find `self.` in entire file, position at the character after the dot (typically for completion or member access)
|
||||
- `lsp doc foo.py --scope 42 --find "return <|>result"` - Find `return result` on line 42, position at `r` of `result`
|
||||
- `lsp doc foo.py --scope 10,20 --find "if <|>condition"` - Find `if condition` in lines 10-20, position at `c` of `condition`
|
||||
- `lsp doc foo.py --scope MyClass.my_method --find "self.<|>"` - Find `self.` within `MyClass.my_method`, position after the dot
|
||||
- `lsp doc foo.py --scope MyClass` - Target the `MyClass` symbol directly
|
||||
|
||||
**Guideline for Scope vs. Find**:
|
||||
|
||||
- Use `--scope <symbol_path>` (e.g., `--scope MyClass`, `--scope MyClass.my_method`) to target **classes, functions, or methods**. This is the most robust and preferred way to target symbol.
|
||||
- Use `--find` (often combined with `--scope`) to target variables or specific positions. Use this when the target is not a uniquely named symbol or when you need to pinpoint a specific usage within a code block.
|
||||
|
||||
Agents MAY use `lsp locate <file_path> --scope <scope> --find <find>` to verify if the target exists in the file and view its context before running other commands.
|
||||
|
||||
```bash
|
||||
# Verify location exists
|
||||
lsp locate main.py --scope 42 --find "<|>process_data"
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
Use pagination for large result sets like `reference` or `search`.
|
||||
|
||||
- `--pagination-id <ID>`: (Required) Unique session ID for consistent paging.
|
||||
- `--max-items <N>`: Page size.
|
||||
- `--start-index <N>`: Offset (0-based).
|
||||
|
||||
**Example**:
|
||||
|
||||
```bash
|
||||
# Page 1
|
||||
lsp search "User" --max-items 20 --pagination-id "task_123"
|
||||
|
||||
# Page 2
|
||||
lsp search "User" --max-items 20 --start-index 20 --pagination-id "task_123"
|
||||
```
|
||||
|
||||
**Guideline**: Use pagination with a unique ID for common symbols to fetch results in manageable chunks. Increment `--start-index` using the same ID to browse.
|
||||
|
||||
### Outline: File Structure
|
||||
|
||||
Get hierarchical symbol structure without reading implementation.
|
||||
|
||||
```bash
|
||||
# Get main symbols (classes, functions, methods)
|
||||
lsp outline <file_path>
|
||||
|
||||
# Get all symbols including variables and parameters
|
||||
lsp outline <file_path> --all
|
||||
```
|
||||
|
||||
Agents SHOULD use `outline` before reading files to avoid unnecessary context consumption.
|
||||
|
||||
### Definition: Navigate to Source
|
||||
|
||||
Navigate to where symbols are defined.
|
||||
|
||||
```bash
|
||||
# Jump to where User.get_id is defined
|
||||
lsp definition models.py --scope User.get_id
|
||||
|
||||
# Find where an imported variable comes from
|
||||
lsp definition main.py --scope 42 --find "<|>config"
|
||||
|
||||
# Find declaration (e.g., header files, interface declarations)
|
||||
lsp definition models.py --scope 25 --mode declaration --find "<|>provider"
|
||||
|
||||
# Find the class definition of a variable's type
|
||||
lsp definition models.py --scope 30 --find "<|>user" --mode type_definition
|
||||
```
|
||||
|
||||
### Reference: Find All Usages
|
||||
|
||||
Find where symbols are used or implemented.
|
||||
|
||||
```bash
|
||||
# Find all places where logger is referenced
|
||||
lsp reference main.py --scope MyClass.run --find "<|>logger"
|
||||
|
||||
# Find all concrete implementations of an interface/abstract class
|
||||
lsp reference api.py --scope "IDataProvider" --mode implementations
|
||||
|
||||
# Get more surrounding code context for each reference
|
||||
lsp reference app.py --scope 10 --find "<|>my_var" --context-lines 5
|
||||
|
||||
# Limit results for large codebases
|
||||
lsp reference utils.py --find "<|>helper" --max-items 50 --start-index 0
|
||||
```
|
||||
|
||||
### Doc: Get Documentation
|
||||
|
||||
Get documentation and type information without navigating to source.
|
||||
|
||||
```bash
|
||||
# Get docstring and type info for symbol at line 42
|
||||
lsp doc main.py --scope 42
|
||||
|
||||
# Get API documentation for process_data function
|
||||
lsp doc models.py --scope process_data
|
||||
```
|
||||
|
||||
Agents SHOULD prefer `doc` over `read` when only documentation or type information is needed.
|
||||
|
||||
### Search: Global Symbol Search
|
||||
|
||||
Search for symbols across the workspace when location is unknown.
|
||||
|
||||
```bash
|
||||
# Search by name (defaults to current directory)
|
||||
lsp search "MyClassName"
|
||||
|
||||
# Search in specific project
|
||||
lsp search "UserModel" --project /path/to/project
|
||||
|
||||
# Filter by symbol kind (can specify multiple times)
|
||||
lsp search "init" --kinds function --kinds method
|
||||
|
||||
# Limit and paginate results for large codebases
|
||||
lsp search "Config" --max-items 10
|
||||
lsp search "User" --max-items 20 --start-index 0
|
||||
```
|
||||
|
||||
Agents SHOULD use `--kinds` to filter results and reduce noise.
|
||||
|
||||
### Symbol: Get Complete Symbol Code
|
||||
|
||||
Get the full source code of the symbol containing a location.
|
||||
|
||||
```bash
|
||||
# Get complete code of the function/class at line 15
|
||||
lsp symbol main.py --scope 15
|
||||
|
||||
# Get full UserClass implementation
|
||||
lsp symbol utils.py --scope UserClass
|
||||
|
||||
# Get complete method implementation
|
||||
lsp symbol models.py --scope User.validate
|
||||
```
|
||||
|
||||
Response includes: symbol name, kind (class/function/method), range, and **complete source code**.
|
||||
|
||||
Agents SHOULD use `symbol` to read targeted code blocks instead of using `read` on entire files.
|
||||
|
||||
### Refactoring Operations
|
||||
|
||||
Read [Refactoring Guide](references/refactor.md) for rename, extract, and other safe refactoring operations.
|
||||
|
||||
### Server: Manage Background Servers
|
||||
|
||||
The background manager starts automatically. Manual control is OPTIONAL.
|
||||
|
||||
```bash
|
||||
# List running servers
|
||||
lsp server list
|
||||
|
||||
# Start server for a project
|
||||
lsp server start <path>
|
||||
|
||||
# Stop server for a project
|
||||
lsp server stop <path>
|
||||
|
||||
# Shutdown the background manager
|
||||
lsp server shutdown
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### General Workflows
|
||||
|
||||
#### Understanding Unfamiliar Code
|
||||
|
||||
The RECOMMENDED sequence for exploring new codebases:
|
||||
|
||||
```bash
|
||||
# Step 1: Start with outline - Get file structure without reading implementation
|
||||
lsp outline <file_path>
|
||||
|
||||
# Step 2: Inspect signatures - Use doc to understand API contracts
|
||||
lsp doc <file_path> --scope <symbol_name>
|
||||
|
||||
# Step 3: Navigate dependencies - Follow definition chains
|
||||
lsp definition <file_path> --scope <symbol_name>
|
||||
|
||||
# Step 4: Map usage - Find where code is called with reference
|
||||
lsp reference <file_path> --scope <symbol_name>
|
||||
```
|
||||
|
||||
#### Debugging Unknown Behavior
|
||||
|
||||
```bash
|
||||
# Step 1: Locate symbol definition workspace-wide
|
||||
lsp search "<symbol_name>"
|
||||
|
||||
# Step 2: Verify implementation details
|
||||
lsp definition <file_path> --scope <symbol_name>
|
||||
|
||||
# Step 3: Trace all callers to understand invocation context
|
||||
lsp reference <file_path> --scope <symbol_name>
|
||||
```
|
||||
|
||||
### Finding Interface Implementations
|
||||
|
||||
```bash
|
||||
# Step 1: Locate interface definition
|
||||
lsp search "IUserService" --kinds interface
|
||||
|
||||
# Step 2: Find all implementations
|
||||
lsp reference src/interfaces.py --scope IUserService --mode implementations
|
||||
```
|
||||
|
||||
### Tracing Data Flow
|
||||
|
||||
```bash
|
||||
# Step 1: Find where data is created
|
||||
lsp search UserDTO --kinds class
|
||||
|
||||
# Step 2: Find where it's used
|
||||
lsp reference models.py --scope UserDTO
|
||||
|
||||
# Step 3: Check transformations
|
||||
lsp doc transform.py --scope map_to_dto
|
||||
```
|
||||
|
||||
### Understanding Type Hierarchies
|
||||
|
||||
```bash
|
||||
# Step 1: Get class outline
|
||||
lsp outline models.py
|
||||
|
||||
# Step 2: Find subclasses (references to base)
|
||||
lsp reference models.py --scope BaseModel
|
||||
|
||||
# Step 3: Check type definitions
|
||||
lsp definition models.py --scope BaseModel --mode type_definition
|
||||
```
|
||||
|
||||
### Performance Tips
|
||||
|
||||
```bash
|
||||
# Use outline instead of reading entire files
|
||||
lsp outline large_file.py # Better than: read large_file.py
|
||||
|
||||
# Use symbol paths for nested structures (more precise than line numbers)
|
||||
lsp definition models.py --scope User.Profile.validate
|
||||
|
||||
# Limit results in large codebases
|
||||
lsp search "User" --max-items 20
|
||||
|
||||
# Use doc to understand APIs without navigating to source
|
||||
lsp doc api.py --scope fetch_data # Get docs/types without jumping to definition
|
||||
|
||||
# Verify locate strings if commands fail
|
||||
lsp locate main.py --scope 42 --find "<|>my_var"
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "LSP Code Analysis"
|
||||
short_description: "Analyze code using LSP"
|
||||
policy:
|
||||
allow_implicit_invocation: true
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Project Context Pack"
|
||||
short_description: "Provides context about the project to the agent"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
name: resolving-merge-conflicts
|
||||
description: "Use when you need to resolve an in-progress git merge/rebase conflict."
|
||||
---
|
||||
|
||||
1. **See the current state** of the merge/rebase. Check git history, and the conflicting files.
|
||||
|
||||
2. **Find the primary sources** for each conflict. Understand deeply why each change was made, and what the original intent was. Read the commit messages, check the PRs, check original issues/tickets.
|
||||
|
||||
3. **Resolve each hunk.** Preserve both intents where possible. Where incompatible, pick the one matching the merge's stated goal and note the trade-off. Do **not** invent new behaviour. Always resolve; never `--abort`.
|
||||
|
||||
4. Discover the project's **automated checks** and run them — typically typecheck, then tests, then format. Fix anything the merge broke.
|
||||
|
||||
5. **Finish the merge/rebase.** Stage everything and commit. If rebasing, continue the rebase process until all commits are rebased.
|
||||
@@ -6,13 +6,14 @@ disable-model-invocation: true
|
||||
|
||||
# Setup Engineering Skills
|
||||
|
||||
When the provider-neutral `tracker` package is installed, generated issue-tracker guidance should call it for normal operations and retain provider-specific CLI commands only as adapter/fallback reference. See `docs/agents/tracker.md`.
|
||||
|
||||
Scaffold the per-repo configuration that the engineering skills assume:
|
||||
|
||||
- Issue tracker: where issues live (never assume a default forge)
|
||||
- Triage labels: the strings used for the canonical triage roles, labels are defined in `triage-labels.md`
|
||||
- Domain docs: where `CONTEXT.md` and ADRs live, and the consumer rules for reading them
|
||||
- **ADR wiki** — where ADRs are stored (forge wiki, cloned into `docs/adr/`), and the clone/push workflow
|
||||
- **Agent CLI** — which CLI agent to use for child agents in `/implement-issue`
|
||||
|
||||
This is a prompt-driven skill, not a deterministic script. Explore, present what you found, confirm with the user, then write.
|
||||
|
||||
@@ -22,51 +23,44 @@ This is a prompt-driven skill, not a deterministic script. Explore, present what
|
||||
|
||||
Look at the current repo to understand its starting state. Read whatever exists; don't assume:
|
||||
|
||||
- find out what force is being used for the issue tracker (GitHub, GitLab, or Gitea)
|
||||
- `AGENTS.md` and `CLAUDE.md` at the repo root — does either exist? Is there already an ## Agent skills section in either?
|
||||
* `CONTEXT.md` and `CONTEXT-MAP.md` at the repo root.
|
||||
- `docs/adr/` (check whether it's a wiki clone via `docs/adr/.git/config`) and any `src/*/docs/adr/` directories
|
||||
- `git remote -v` and `.git/config` — is this a GitHub repo? Which one?
|
||||
- `AGENTS.md` and `CLAUDE.md` at the repo root — does either exist? Is there already an `## Agent skills` section in either?
|
||||
- `CONTEXT.md` and `CONTEXT-MAP.md` at the repo root
|
||||
- `docs/adr/` and any `src/*/docs/adr/` directories
|
||||
- `docs/agents/` — does this skill's prior output already exist?
|
||||
- Is the `triage` skill installed? (a `triage` skill folder alongside this one, or `triage` in your available skills.) This decides whether Section B runs at all.
|
||||
- Monorepo signals — a `pnpm-workspace.yaml`, a `workspaces` field in `package.json`, or a populated `packages/*` with its own `src/`. Present only in a genuinely large multi-package repo; their absence means single-context, which is almost every repo.
|
||||
|
||||
### 2. Present findings and ask
|
||||
|
||||
Summarise what's present and what's missing. Then walk the user through the five decisions **one at a time** - present a section, get the user's answer, then move to the next. Don't dump all at once.
|
||||
Summarise what's present and what's missing. Then take the sections in order — one section, one answer, then the next.
|
||||
|
||||
Assume the user does not know what these terms mean. Each section starts with a short explainer (what it is, why these skills need it, what changes if they pick differently). Then show the choices and the default.
|
||||
Lead each section with the recommended answer so the user can accept it in a word. Give a one-line explainer only when the choice genuinely branches; skip the section entirely when exploration already settled it (Section B when `triage` isn't installed, Section C when there's no monorepo).
|
||||
|
||||
Section A - Issue tracker:
|
||||
|
||||
> Explainer: The "issue tracker" is where issues live for this repo. Skills like `to-issues`, `triage`, `to-prd`, and `qa` read from and write to it — they need to know whether to call `gh issue create`, write a markdown file under `.scratch/`, or follow some other workflow you describe. Pick the place you actually track work for this repo.
|
||||
> Explainer: The "issue tracker" is where issues live for this repo. Skills like `to-tickets`, `triage`, `to-spec`, and `qa` read from and write to it — they need to know whether to call `gh issue create`, or follow some other workflow you describe. Pick the place you actually track work for this repo.
|
||||
|
||||
Default posture: these skills were designed for GitHub. If a `git remote` points at GitHub, propose that. If a `git remote` points at GitLab (`gitlab.com` or a self-hosted host), propose GitLab. If a `git remote` point at a Gitea (a self-hosted host with a `gitea` in the url). Otherwise ask the user, offer:
|
||||
|
||||
- **GitHub** — issues live in the repo's GitHub Issues (uses the `gh` CLI)
|
||||
- **GitLab** — issues live in the repo's GitLab Issues (uses the [`glab`](https://gitlab.com/gitlab-org/cli) CLI)
|
||||
* Gitea — issues live in the repo's Gitea Issues (uses the `tea` CLI)
|
||||
- Gitea — issues live in the repo's Gitea Issues (uses the `tea` CLI)
|
||||
- **Other** (Jira, Linear, etc.) — ask the user to describe the workflow in one paragraph; the skill will record it as freeform prose
|
||||
|
||||
If — and only if — the user picked **GitHub**, **GitLab** or **Gitea**, ask one follow-up:
|
||||
Record the choice in `docs/agents/issue-tracker.md`. The GitHub and GitLab templates carry a "PRs as a request surface" flag, defaulted **off** — leave it off and don't raise it; a user who wants external PRs in the triage queue can flip the flag in the file later.
|
||||
|
||||
> Explainer: Open-source repos often receive feature requests as pull requests, not just issues — a PR is an issue with attached code. If you turn this on, `/triage` pulls *external* PRs into the same queue and runs them through the same labels and states as issues (collaborators' in-flight PRs are left alone). Leave it off if PRs aren't a request surface for you.
|
||||
**Section B — Triage label vocabulary.** Skip this section entirely if the `triage` skill isn't installed (exploration told you) — an uninstalled skill needs no labels.
|
||||
|
||||
- **PRs as a request surface** — yes / no (default: no). Record the answer in `docs/agents/issue-tracker.md`. For local-markdown and other trackers, skip this question — there are no PRs.
|
||||
If it is installed, ask exactly one question:
|
||||
|
||||
**Section B — Triage label vocabulary.**
|
||||
> Do you want to keep the default triage labels? (recommended: **yes**)
|
||||
|
||||
> Explainer: When the `triage` skill processes an incoming issue, it moves it through a state machine — needs evaluation, waiting on reporter, ready for an AFK agent to pick up, ready for a human, or won't fix. To do that, it needs to apply labels (or the equivalent in your issue tracker) that match strings *you've actually configured*. If your repo already uses different label names (e.g. `bug:triage` instead of `needs-triage`), map them here so the skill applies the right ones instead of creating duplicates.
|
||||
The defaults canonical roles are listed in `triage-labels.md` each label string equal to its name. On **yes**, write them as-is. Only if the user says no — usually because their tracker already uses other names (e.g. `bug:triage` for `needs-triage`) — collect the overrides so `triage` applies existing labels instead of creating duplicates.
|
||||
|
||||
Look at the `triage-labels.md` file for the labels and each roles they define.
|
||||
**Section C — Domain docs.** Default to **single-context** — one `CONTEXT.md` + `docs/adr/` at the repo root. This fits almost every repo; write it without asking.
|
||||
|
||||
Default: each role's string equals its name. Ask the user if they want to override any. If their issue tracker has no existing labels, the defaults are fine.
|
||||
|
||||
**Section C — Domain docs.**
|
||||
|
||||
> Explainer: Some skills (`improve-codebase-architecture`, `diagnosing-bugs`, `tdd`) read a `CONTEXT.md` file to learn the project's domain languagee, and `adr` for past architectural decisions. They need to know whether the repo has one global context or multiple (e.g. a monorepo with separate frontend/backend contexts) so they look in the right place.
|
||||
|
||||
Confirm the layout:
|
||||
|
||||
- **Single-context** — one `CONTEXT.md` + `adr` at the repo root. Most repos are this.
|
||||
- **Multi-context** — `CONTEXT-MAP.md` at the root pointing to per-context `CONTEXT.md` files (typically a monorepo).
|
||||
Offer **multi-context** — a root `CONTEXT-MAP.md` pointing to per-context `CONTEXT.md` files — only when exploration found monorepo signals. Then confirm which layout they want.
|
||||
|
||||
**Section D — ADR wiki.**
|
||||
|
||||
@@ -75,7 +69,7 @@ Confirm the layout:
|
||||
Derive the wiki URL from the forge detected in Section A:
|
||||
|
||||
| Forge | Wiki URL pattern |
|
||||
|---|---|
|
||||
| --- | --- |
|
||||
| **GitHub** | `https://github.com/<owner>/<repo>.wiki.git` |
|
||||
| **Gitea** | `https://<host>/<owner>/<repo>.wiki.git` |
|
||||
| **GitLab** | `https://gitlab.com/<owner>/<repo>.wiki.git` |
|
||||
@@ -90,43 +84,12 @@ After the forge is confirmed, present the workflow and confirm:
|
||||
|
||||
Record the answers in `docs/agents/adr-wiki.md`.
|
||||
|
||||
**Section E — Agent CLI.**
|
||||
|
||||
> Explainer: The `implement-issue` skill dispatches a child agent in an isolated git worktree. It needs to know which CLI agent to spawn (pi, opencode, goose, codex, or claude) and how to invoke it. This configuration lives in `docs/agents/agent-cli.md` and is set up once per repo so every developer uses the same agent by default.
|
||||
|
||||
Read `agents-seed.md` from this skill directory to get the list of known agents. Present them as a numbered menu:
|
||||
|
||||
```
|
||||
Available agents:
|
||||
1. pi — My primary agent harness. Accepts prompt file via -p flag.
|
||||
2. opencode — OpenCode agent. Accepts prompt file via -f flag.
|
||||
3. goose — Goose agent. Accepts prompt file via -i flag.
|
||||
4. codex — OpenAI Codex. Pipes stdin via cat.
|
||||
5. claude — Anthropic Claude CLI. Pipes stdin via cat.
|
||||
```
|
||||
|
||||
Ask the user to type the agent name (or number):
|
||||
|
||||
> Which CLI agent should `/implement-issue` use for child agents? (default: pi)
|
||||
|
||||
Validate the selected agent's binary is on PATH:
|
||||
|
||||
```bash
|
||||
command -v <selected-binary>
|
||||
```
|
||||
|
||||
If the binary is not found, tell the user:
|
||||
|
||||
> Binary '<binary>' not found on PATH. Please install it or choose a different agent.
|
||||
|
||||
Loop until a valid binary is found or the user quits.
|
||||
|
||||
### 3. Confirm and edit
|
||||
|
||||
Show the user a draft of:
|
||||
|
||||
- The `## Agent skills` block to add to whichever of `CLAUDE.md` / `AGENTS.md` is being edited (see step 4 for selection rules)
|
||||
- The contents of `docs/agents/issue-tracker.md`, `docs/agents/triage-labels.md`, `docs/agents/domain.md`, `docs/agents/adr-wiki.md`, and `docs/agents/agent-cli.md`
|
||||
- The contents of `docs/agents/issue-tracker.md`, `docs/agents/domain.md`, `docs/agents/adr-wiki.md` and `docs/agents/triage-labels.md` (the last only when `triage` is installed)
|
||||
|
||||
Let them edit before writing.
|
||||
|
||||
@@ -149,7 +112,7 @@ The block:
|
||||
|
||||
### Issue tracker
|
||||
|
||||
[one-line summary of where issues are tracked, plus whether external PRs are a triage surface]. See `docs/agents/issue-tracker.md`.
|
||||
[one-line summary of where issues are tracked]. See `docs/agents/issue-tracker.md`.
|
||||
|
||||
### Triage labels
|
||||
|
||||
@@ -163,10 +126,6 @@ The block:
|
||||
|
||||
[one-line summary — forge, wiki URL, auth method]. See `docs/agents/adr-wiki.md`.
|
||||
|
||||
### Agent CLI
|
||||
|
||||
[one-line summary of the configured CLI agent]. See `docs/agents/agent-cli.md`.
|
||||
|
||||
## Project Context Pack
|
||||
|
||||
[Agent memory file that describes the repo's context, codebase, and navigation rules]. See `.agents/project-context.md`.
|
||||
@@ -175,23 +134,16 @@ The block:
|
||||
|
||||
Then write the docs files:
|
||||
|
||||
- [issue-tracker-github.md](./issue-tracker-github.md) — GitHub issue tracker
|
||||
- [issue-tracker-gitlab.md](./issue-tracker-gitlab.md) — GitLab issue tracker
|
||||
- [issue-tracker-gitea.md](./issue-tracker-gitea.md) — Gitea issue tracker
|
||||
- [issue-tracker-github.md](./issue-tracker-github.md) — GitHub issue tracker, including wayfinding operations
|
||||
- [issue-tracker-gitlab.md](./issue-tracker-gitlab.md) — GitLab issue tracker, including wayfinding operations
|
||||
- [issue-tracker-gitea.md](./issue-tracker-gitea.md) — Gitea issue tracker, including wayfinding operations
|
||||
- [triage-labels.md](./triage-labels.md) — label mapping
|
||||
- [domain.md](./domain.md) — domain doc consumer rules + layout
|
||||
- [adr-wiki.md](./adr-wiki.md) — ADR wiki clone and push workflow
|
||||
- `docs/agents/agent-cli.md` — agent CLI config with `selected`, `binary`, and `args` fields
|
||||
|
||||
For Section E, write `docs/agents/agent-cli.md` with the selected agent:
|
||||
For GitHub, GitLab, and Gitea, the generated `docs/agents/issue-tracker.md` must include the full "Wayfinding operations" section from the selected template. Do not omit that section when composing the project file.
|
||||
|
||||
```yaml
|
||||
selected: <agent-name>
|
||||
binary: <binary-name>
|
||||
args: "<args-from-seed>"
|
||||
```
|
||||
|
||||
For "other" issue trackers, write `docs/agents/issue-tracker.md` from scratch using the user's description.
|
||||
For "other" issue trackers, write `docs/agents/issue-tracker.md` from scratch using the user's description, and include an equivalent wayfinding section only if the user described a workable wayfinding workflow for that tracker.
|
||||
|
||||
### 5. Done
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ Architecture Decision Records live on the forge wiki and are cloned into `docs/a
|
||||
<wiki-url>
|
||||
```
|
||||
|
||||
Derived from the forge remote during `/setup-matt-pocock-skills`.
|
||||
Derived from the forge remote during `/setup-skills`.
|
||||
|
||||
## Bootstrap
|
||||
|
||||
On first setup, `/setup-matt-pocock-skills` clones the wiki:
|
||||
On first setup, `/setup-skills` clones the wiki:
|
||||
|
||||
```bash
|
||||
git clone <wiki-url> docs/adr/
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Setup Engineering Skills"
|
||||
short_description: "Setup engineering skills for the agent"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,6 +1,6 @@
|
||||
# Issue tracker: Gitea
|
||||
|
||||
Issues and PRDs for this repo live as Gitea issues. Use the `gitea` CLI for all operations.
|
||||
Prefer the provider-neutral `tracker` command for normal operations; it emits JSON and delegates to `tea`. See `docs/agents/tracker.md`. The `tea` commands below remain adapter and capability reference material.
|
||||
|
||||
## Conventions
|
||||
|
||||
@@ -8,7 +8,7 @@ Issues and PRDs for this repo live as Gitea issues. Use the `gitea` CLI for all
|
||||
- **Read an issue**: `tea issue <number> --comments`. Use `-o json` for machine-readable output.
|
||||
- **List issues**: `tea issue list --state open -o json` with appropriate `--labels` and `--state` filters.
|
||||
- **Comment on an issue**: `tea comment <number> "..."`.
|
||||
- **Apply / remove labels**: `tea issue edit <number> --add-label "..."` / `--remove-label "..."`. Multiple labels can be comma-separated or by repeating the flag.les
|
||||
- **Apply / remove labels**: `tea issue edit <number> --add-label "..."` / `--remove-label "..."`. Multiple labels can be comma-separated or by repeating the flag. Labels need to be created first with `tea label create --name "..." --color "..."`.
|
||||
- **Close**: `tea issue close <number>`. `tea issue close` does not accept a closing comment, so post the explanation first with `tea comment <number> "..."`, then close.
|
||||
|
||||
Infer the repo from git remote -v — `tea` does this automatically when run inside a clone.
|
||||
@@ -17,11 +17,10 @@ Infer the repo from git remote -v — `tea` does this automatically when run ins
|
||||
|
||||
**PRs as a request surface: no.** _(Set to `yes` if this repo treats external PRs as feature requests; `/triage` reads this flag.)_
|
||||
|
||||
When set to `yes`, PRs run through the same labels and states as issues, using the `gh pr` equivalents:
|
||||
When set to `yes`, PRs run through the same labels and states as issues, using the `tea pr` equivalents:
|
||||
|
||||
- **Read a PR**: `tea pr <number> --comments` and `tea api /repos/{owner}/{repo}/pulls/<number>.diff` for the diff.
|
||||
|
||||
- **List external PRs for triage**: `tea pr list --state open -o json ` then keep only PRs whose author is not a project member/owner (a contributor's MR, not a maintainer's in-flight work).
|
||||
- **List external PRs for triage**: `tea pr list --state open -o json` then keep only PRs whose author is not a project member/owner (a contributor's MR, not a maintainer's in-flight work).
|
||||
- **Comment / label / close**: `tea comment <number> "..."`, `tea pr edit --add-label`/`--remove-label`, `tea pr close`.
|
||||
|
||||
Gitea shares one number space across issues and PRs, so a bare `#42` may be either — resolve with `tea pr 42` and fall back to `tea issue 42`.
|
||||
@@ -33,3 +32,14 @@ Create a Gitea issue.
|
||||
## When a skill says "fetch the relevant ticket"
|
||||
|
||||
Run `tea issue <number> --comments`.
|
||||
|
||||
## Wayfinding operations
|
||||
|
||||
Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
||||
|
||||
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `tea issue create --label wayfinder:map`.
|
||||
- **Child ticket**: an issue linked to the map as a GitHub sub-issue (`tea api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #<map>` at the top of the child body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
||||
- **Blocking**: GitHub's **native issue dependencies** — the canonical, UI-visible representation. Add an edge with `tea api --method POST /repos/{owner}/{repo}/issues/<child>/dependencies -F index=<blocker-issue-number> -F repo=<blocker-repo-name> -F owner=<bocker-owner-name>`, where `<blocker-usse-number>` is the blocker's numeric **issue number** (`tea api repos/{owner}/{repo}/issues/<n> --jq ".number. .repository.name, .repository.owner"`, where `.number` is the `<blocker-issue-number>`, `.repository.name` is the `<blocker-repo-name>` and `.repository.owner` is the `<blocker-repo-owner>`. Where dependencies aren't available, fall back to a `Blocked by: #<n>, #<n>` line at the top of the child body. A ticket is unblocked when every blocker is closed.
|
||||
- **Frontier query**: list the map's open dependencies (`tea api /repos/{owner}/{repo}/issues/<n>/dependencies | jq '.[] | select(.state = "open") .number'`, scoped to the map's sub-issues / task list), drop any with an open blocker (`list of dependencies is not empty`, or an open issue in the `Blocked by` line) or an assignee; first in map order wins.
|
||||
- **Claim**: `tea issue edit <n> --add-assignees @me` — the session's first write.
|
||||
- **Resolve**: `tea comments <n> "<answer>"`, then `tea issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Issue tracker: GitHub
|
||||
|
||||
Issues and PRDs for this repo live as GitHub issues. Use the `gh` CLI for all operations.
|
||||
Prefer the provider-neutral `tracker` command for normal operations; it emits JSON and delegates to `gh`. See `docs/agents/tracker.md`. The `gh` commands below remain adapter and capability reference material.
|
||||
|
||||
## Conventions
|
||||
|
||||
@@ -32,3 +32,14 @@ Create a GitHub issue.
|
||||
## When a skill says "fetch the relevant ticket"
|
||||
|
||||
Run `gh issue view <number> --comments`.
|
||||
|
||||
## Wayfinding operations
|
||||
|
||||
Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
||||
|
||||
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `gh issue create --label wayfinder:map`.
|
||||
- **Child ticket**: an issue linked to the map as a GitHub sub-issue (`gh api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #<map>` at the top of the child body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
||||
- **Blocking**: GitHub's **native issue dependencies** — the canonical, UI-visible representation. Add an edge with `gh api --method POST repos/<owner>/<repo>/issues/<child>/dependencies/blocked_by -F issue_id=<blocker-db-id>`, where `<blocker-db-id>` is the blocker's numeric **database id** (`gh api repos/<owner>/<repo>/issues/<n> --jq .id`, _not_ the `#number` or `node_id`). GitHub reports `issue_dependencies_summary.blocked_by` (open blockers only — the live gate). Where dependencies aren't available, fall back to a `Blocked by: #<n>, #<n>` line at the top of the child body. A ticket is unblocked when every blocker is closed.
|
||||
- **Frontier query**: list the map's open children (`gh issue list --state open`, scoped to the map's sub-issues / task list), drop any with an open blocker (`issue_dependencies_summary.blocked_by > 0`, or an open issue in the `Blocked by` line) or an assignee; first in map order wins.
|
||||
- **Claim**: `gh issue edit <n> --add-assignee @me` — the session's first write.
|
||||
- **Resolve**: `gh issue comment <n> --body "<answer>"`, then `gh issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Issue tracker: GitLab
|
||||
|
||||
Issues and PRDs for this repo live as GitLab issues. Use the [`glab`](https://gitlab.com/gitlab-org/cli) CLI for all operations.
|
||||
Prefer the provider-neutral `tracker` command for normal operations; it emits JSON and delegates to `glab`. See `docs/agents/tracker.md`. The `glab` commands below remain adapter and capability reference material.
|
||||
|
||||
## Conventions
|
||||
|
||||
@@ -33,3 +33,14 @@ Create a GitLab issue.
|
||||
## When a skill says "fetch the relevant ticket"
|
||||
|
||||
Run `glab issue view <number> --comments`.
|
||||
|
||||
## Wayfinding operations
|
||||
|
||||
Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
||||
|
||||
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `glab issue create --label wayfinder:map`. (On GitLab tiers with native epics, an epic may hold the map instead; a labelled issue works everywhere.)
|
||||
- **Child ticket**: an issue carrying `Part of #<map>` at the top of its description and labels `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
||||
- **Blocking**: GitLab's **native blocking link** — the canonical, UI-visible representation. Add it with the `/blocked_by #<n>` quick action, posted as a note (`glab issue note <child> --message "/blocked_by #<blocker>"`). Native blocking links are a Premium/Ultimate feature; on the free tier (or where unavailable) fall back to a `Blocked by: #<n>, #<n>` line at the top of the description. A ticket is unblocked when every blocker is closed.
|
||||
- **Frontier query**: `glab issue list -F json` scoped to the map's children, drop any with an open blocker — a native `blocked_by` link to an open issue (`glab api projects/:id/issues/:iid/links`), or an open issue in the `Blocked by` line — or an assignee; first in map order wins.
|
||||
- **Claim**: `glab issue update <n> --assignee @me` — the session's first write.
|
||||
- **Resolve**: `glab issue note <n> --message "<answer>"`, then `glab issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Triage Labels
|
||||
|
||||
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
|
||||
The skills speak in terms of seven canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
|
||||
|
||||
| Label in skills | Label in our tracker | Meaning |
|
||||
| -------------------------- | -------------------- | ---------------------------------------- |
|
||||
@@ -12,6 +12,6 @@ The skills speak in terms of five canonical triage roles. This file maps those r
|
||||
| `in-progress` | `in-progress` | Being actively worked on by a human or agents |
|
||||
| `wontfix` | `wontfix` | Will not be actioned |
|
||||
|
||||
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
|
||||
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table. Only one triage label should be active.
|
||||
|
||||
Edit the right-hand column to match whatever vocabulary you actually use.
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
---
|
||||
name: tdd
|
||||
description: Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
|
||||
---
|
||||
|
||||
# Test-Driven Development
|
||||
|
||||
## Philosophy
|
||||
|
||||
**Core principle**: Tests should verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't.
|
||||
|
||||
**Good tests** are integration-style: they exercise real code paths through public APIs. They describe _what_ the system does, not _how_ it does it. A good test reads like a specification - "user can checkout with valid cart" tells you exactly what capability exists. These tests survive refactors because they don't care about internal structure.
|
||||
|
||||
**Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior.
|
||||
|
||||
See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
|
||||
|
||||
## Anti-Pattern: Horizontal Slices
|
||||
|
||||
**DO NOT write all tests first, then all implementation.** This is "horizontal slicing" - treating RED as "write all tests" and GREEN as "write all code."
|
||||
|
||||
This produces **crap tests**:
|
||||
|
||||
- Tests written in bulk test _imagined_ behavior, not _actual_ behavior
|
||||
- You end up testing the _shape_ of things (data structures, function signatures) rather than user-facing behavior
|
||||
- Tests become insensitive to real changes - they pass when behavior breaks, fail when behavior is fine
|
||||
- You outrun your headlights, committing to test structure before understanding the implementation
|
||||
|
||||
**Correct approach**: Vertical slices via tracer bullets. One test → one implementation → repeat. Each test responds to what you learned from the previous cycle. Because you just wrote the code, you know exactly what behavior matters and how to verify it.
|
||||
|
||||
```
|
||||
WRONG (horizontal):
|
||||
RED: test1, test2, test3, test4, test5
|
||||
GREEN: impl1, impl2, impl3, impl4, impl5
|
||||
|
||||
RIGHT (vertical):
|
||||
RED→GREEN: test1→impl1
|
||||
RED→GREEN: test2→impl2
|
||||
RED→GREEN: test3→impl3
|
||||
...
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Planning
|
||||
|
||||
When exploring the codebase, read `CONTEXT.md` (if it exists) so that test names and interface vocabulary match the project's domain language, and respect ADRs in the area you're touching.
|
||||
|
||||
Before writing any code:
|
||||
|
||||
- [ ] Confirm with user what interface changes are needed
|
||||
- [ ] Confirm with user which behaviors to test (prioritize)
|
||||
- [ ] Identify opportunities for deep modules (small interface, deep implementation) — run the `/codebase-design` skill for the vocabulary and the testability checks
|
||||
- [ ] List the behaviors to test (not implementation steps)
|
||||
- [ ] Get user approval on the plan
|
||||
|
||||
Ask: "What should the public interface look like? Which behaviors are most important to test?"
|
||||
|
||||
**You can't test everything.** Confirm with the user exactly which behaviors matter most. Focus testing effort on critical paths and complex logic, not every possible edge case.
|
||||
|
||||
### 2. Tracer Bullet
|
||||
|
||||
Write ONE test that confirms ONE thing about the system:
|
||||
|
||||
```
|
||||
RED: Write test for first behavior → test fails
|
||||
GREEN: Write minimal code to pass → test passes
|
||||
```
|
||||
|
||||
This is your tracer bullet - proves the path works end-to-end.
|
||||
|
||||
### 3. Incremental Loop
|
||||
|
||||
For each remaining behavior:
|
||||
|
||||
```
|
||||
RED: Write next test → fails
|
||||
GREEN: Minimal code to pass → passes
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- One test at a time
|
||||
- Only enough code to pass current test
|
||||
- Don't anticipate future tests
|
||||
- Keep tests focused on observable behavior
|
||||
|
||||
### 4. Refactor
|
||||
|
||||
After all tests pass, look for [refactor candidates](refactoring.md):
|
||||
|
||||
- [ ] Extract duplication
|
||||
- [ ] Deepen modules (move complexity behind simple interfaces)
|
||||
- [ ] Apply SOLID principles where natural
|
||||
- [ ] Consider what new code reveals about existing code
|
||||
- [ ] Run tests after each refactor step
|
||||
|
||||
**Never refactor while RED.** Get to GREEN first.
|
||||
|
||||
## Checklist Per Cycle
|
||||
|
||||
```
|
||||
[ ] Test describes behavior, not implementation
|
||||
[ ] Test uses public interface only
|
||||
[ ] Test would survive internal refactor
|
||||
[ ] Code is minimal for this test
|
||||
[ ] No speculative features added
|
||||
```
|
||||
@@ -1,59 +0,0 @@
|
||||
# When to Mock
|
||||
|
||||
Mock at **system boundaries** only:
|
||||
|
||||
- External APIs (payment, email, etc.)
|
||||
- Databases (sometimes - prefer test DB)
|
||||
- Time/randomness
|
||||
- File system (sometimes)
|
||||
|
||||
Don't mock:
|
||||
|
||||
- Your own classes/modules
|
||||
- Internal collaborators
|
||||
- Anything you control
|
||||
|
||||
## Designing for Mockability
|
||||
|
||||
At system boundaries, design interfaces that are easy to mock:
|
||||
|
||||
**1. Use dependency injection**
|
||||
|
||||
Pass external dependencies in rather than creating them internally:
|
||||
|
||||
```typescript
|
||||
// Easy to mock
|
||||
function processPayment(order, paymentClient) {
|
||||
return paymentClient.charge(order.total);
|
||||
}
|
||||
|
||||
// Hard to mock
|
||||
function processPayment(order) {
|
||||
const client = new StripeClient(process.env.STRIPE_KEY);
|
||||
return client.charge(order.total);
|
||||
}
|
||||
```
|
||||
|
||||
**2. Prefer SDK-style interfaces over generic fetchers**
|
||||
|
||||
Create specific functions for each external operation instead of one generic function with conditional logic:
|
||||
|
||||
```typescript
|
||||
// GOOD: Each function is independently mockable
|
||||
const api = {
|
||||
getUser: (id) => fetch(`/users/${id}`),
|
||||
getOrders: (userId) => fetch(`/users/${userId}/orders`),
|
||||
createOrder: (data) => fetch('/orders', { method: 'POST', body: data }),
|
||||
};
|
||||
|
||||
// BAD: Mocking requires conditional logic inside the mock
|
||||
const api = {
|
||||
fetch: (endpoint, options) => fetch(endpoint, options),
|
||||
};
|
||||
```
|
||||
|
||||
The SDK approach means:
|
||||
- Each mock returns one specific shape
|
||||
- No conditional logic in test setup
|
||||
- Easier to see which endpoints a test exercises
|
||||
- Type safety per endpoint
|
||||
@@ -1,10 +0,0 @@
|
||||
# Refactor Candidates
|
||||
|
||||
After TDD cycle, look for:
|
||||
|
||||
- **Duplication** → Extract function/class
|
||||
- **Long methods** → Break into private helpers (keep tests on public interface)
|
||||
- **Shallow modules** → Combine or deepen
|
||||
- **Feature envy** → Move logic to where data lives
|
||||
- **Primitive obsession** → Introduce value objects
|
||||
- **Existing code** the new code reveals as problematic
|
||||
@@ -1,61 +0,0 @@
|
||||
# Good and Bad Tests
|
||||
|
||||
## Good Tests
|
||||
|
||||
**Integration-style**: Test through real interfaces, not mocks of internal parts.
|
||||
|
||||
```typescript
|
||||
// GOOD: Tests observable behavior
|
||||
test("user can checkout with valid cart", async () => {
|
||||
const cart = createCart();
|
||||
cart.add(product);
|
||||
const result = await checkout(cart, paymentMethod);
|
||||
expect(result.status).toBe("confirmed");
|
||||
});
|
||||
```
|
||||
|
||||
Characteristics:
|
||||
|
||||
- Tests behavior users/callers care about
|
||||
- Uses public API only
|
||||
- Survives internal refactors
|
||||
- Describes WHAT, not HOW
|
||||
- One logical assertion per test
|
||||
|
||||
## Bad Tests
|
||||
|
||||
**Implementation-detail tests**: Coupled to internal structure.
|
||||
|
||||
```typescript
|
||||
// BAD: Tests implementation details
|
||||
test("checkout calls paymentService.process", async () => {
|
||||
const mockPayment = jest.mock(paymentService);
|
||||
await checkout(cart, payment);
|
||||
expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
|
||||
});
|
||||
```
|
||||
|
||||
Red flags:
|
||||
|
||||
- Mocking internal collaborators
|
||||
- Testing private methods
|
||||
- Asserting on call counts/order
|
||||
- Test breaks when refactoring without behavior change
|
||||
- Test name describes HOW not WHAT
|
||||
- Verifying through external means instead of interface
|
||||
|
||||
```typescript
|
||||
// BAD: Bypasses interface to verify
|
||||
test("createUser saves to database", async () => {
|
||||
await createUser({ name: "Alice" });
|
||||
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
|
||||
expect(row).toBeDefined();
|
||||
});
|
||||
|
||||
// GOOD: Verifies through interface
|
||||
test("createUser makes user retrievable", async () => {
|
||||
const user = await createUser({ name: "Alice" });
|
||||
const retrieved = await getUser(user.id);
|
||||
expect(retrieved.name).toBe("Alice");
|
||||
});
|
||||
```
|
||||
@@ -1,84 +0,0 @@
|
||||
---
|
||||
name: to-issues
|
||||
description: Break a plan, spec, or PRD into independently-grabbable issues on the project issue tracker using tracer-bullet vertical slices.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# To Issues
|
||||
|
||||
Break a plan into independently-grabbable issues using vertical slices (tracer bullets).
|
||||
|
||||
The issue tracker and triage label vocabulary should have been provided to you — run `/setup-matt-pocock-skills` if not.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Gather context
|
||||
|
||||
Work from whatever is already in the conversation context. If the user passes an issue reference (issue number, URL, or path) as an argument, fetch it from the issue tracker and read its full body and comments.
|
||||
|
||||
### 2. Explore the codebase (optional)
|
||||
|
||||
If you have not already explored the codebase, do so to understand the current state of the code. Issue titles and descriptions should use the project's domain glossary vocabulary, and respect ADRs in the area you're touching.
|
||||
|
||||
Look for opportunities to prefactor the code to make the implementation easier. "Make the change easy, then make the easy change."
|
||||
|
||||
### 3. Draft vertical slices
|
||||
|
||||
Break the plan into **tracer bullet** issues. Each issue is a thin vertical slice that cuts through ALL integration layers end-to-end, NOT a horizontal slice of one layer.
|
||||
|
||||
<vertical-slice-rules>
|
||||
|
||||
- Each slice delivers a narrow but COMPLETE path through every layer (schema, API, UI, tests)
|
||||
- A completed slice is demoable or verifiable on its own
|
||||
- Any prefactoring should be done first
|
||||
|
||||
</vertical-slice-rules>
|
||||
|
||||
### 4. Quiz the user
|
||||
|
||||
Present the proposed breakdown as a numbered list. For each slice, show:
|
||||
|
||||
- **Title**: short descriptive name
|
||||
- **Blocked by**: which other slices (if any) must complete first
|
||||
- **User stories covered**: which user stories this addresses (if the source material has them)
|
||||
|
||||
Ask the user:
|
||||
|
||||
- Does the granularity feel right? (too coarse / too fine)
|
||||
- Are the dependency relationships correct?
|
||||
- Should any slices be merged or split further?
|
||||
|
||||
Iterate until the user approves the breakdown.
|
||||
|
||||
### 5. Publish the issues to the issue tracker
|
||||
|
||||
For each approved slice, publish a new issue to the issue tracker. Use the issue body template below. These issues are considered ready for AFK agents, so publish them with the correct triage label unless instructed otherwise.
|
||||
|
||||
Publish issues in dependency order (blockers first) so you can reference real issue identifiers in the "Blocked by" field.
|
||||
|
||||
<issue-template>
|
||||
## Parent
|
||||
|
||||
A reference to the parent issue on the issue tracker (if the source was an existing issue, otherwise omit this section).
|
||||
|
||||
## What to build
|
||||
|
||||
A concise description of this vertical slice. Describe the end-to-end behavior, not layer-by-layer implementation.
|
||||
|
||||
Avoid specific file paths or code snippets — they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it here and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Criterion 1
|
||||
- [ ] Criterion 2
|
||||
- [ ] Criterion 3
|
||||
|
||||
## Blocked by
|
||||
|
||||
- A reference to the blocking ticket (if any)
|
||||
|
||||
Or "None - can start immediately" if no blockers.
|
||||
|
||||
</issue-template>
|
||||
|
||||
Do NOT close or modify any parent issue.
|
||||
@@ -1,75 +0,0 @@
|
||||
---
|
||||
name: to-prd
|
||||
description: Turn the current conversation into a PRD and publish it to the project issue tracker — no interview, just synthesis of what you've already discussed.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
This skill takes the current conversation context and codebase understanding and produces a PRD. Do NOT interview the user — just synthesize what you already know.
|
||||
|
||||
The issue tracker and triage label vocabulary should have been provided to you — run `/setup-matt-pocock-skills` if not.
|
||||
|
||||
## Process
|
||||
|
||||
1. Explore the repo to understand the current state of the codebase, if you haven't already. Use the project's domain glossary vocabulary throughout the PRD, and respect any ADRs in the area you're touching.
|
||||
|
||||
2. Sketch out the seams at which you're going to test the feature. Existing seams should be preferred to new ones. Use the highest seam possible. If new seams are needed, propose them at the highest point you can. The fewer seams across the codebase, the better - the ideal number is one.
|
||||
|
||||
Check with the user that these seams match their expectations.
|
||||
|
||||
3. Write the PRD using the template below, then publish it to the project issue tracker. Apply the `ready-for-agent` triage label - no need for additional triage.
|
||||
|
||||
<prd-template>
|
||||
|
||||
## Problem Statement
|
||||
|
||||
The problem that the user is facing, from the user's perspective.
|
||||
|
||||
## Solution
|
||||
|
||||
The solution to the problem, from the user's perspective.
|
||||
|
||||
## User Stories
|
||||
|
||||
A LONG, numbered list of user stories. Each user story should be in the format of:
|
||||
|
||||
1. As an <actor>, I want a <feature>, so that <benefit>
|
||||
|
||||
<user-story-example>
|
||||
1. As a mobile bank customer, I want to see balance on my accounts, so that I can make better informed decisions about my spending
|
||||
</user-story-example>
|
||||
|
||||
This list of user stories should be extremely extensive and cover all aspects of the feature.
|
||||
|
||||
## Implementation Decisions
|
||||
|
||||
A list of implementation decisions that were made. This can include:
|
||||
|
||||
- The modules that will be built/modified
|
||||
- The interfaces of those modules that will be modified
|
||||
- Technical clarifications from the developer
|
||||
- Architectural decisions
|
||||
- Schema changes
|
||||
- API contracts
|
||||
- Specific interactions
|
||||
|
||||
Do NOT include specific file paths or code snippets. They may end up being outdated very quickly.
|
||||
|
||||
Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it within the relevant decision and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
||||
|
||||
## Testing Decisions
|
||||
|
||||
A list of testing decisions that were made. Include:
|
||||
|
||||
- A description of what makes a good test (only test external behavior, not implementation details)
|
||||
- Which modules will be tested
|
||||
- Prior art for the tests (i.e. similar types of tests in the codebase)
|
||||
|
||||
## Out of Scope
|
||||
|
||||
A description of the things that are out of scope for this PRD.
|
||||
|
||||
## Further Notes
|
||||
|
||||
Any further notes about the feature.
|
||||
|
||||
</prd-template>
|
||||
@@ -1,207 +0,0 @@
|
||||
# Writing Agent Briefs
|
||||
|
||||
An agent brief is a structured comment posted on a GitHub issue or PR when it moves to `ready-for-agent`. It is the authoritative specification that an AFK agent will work from. The original body and discussion are context — the agent brief is the contract.
|
||||
|
||||
The brief states **what the agent should do**, which stretches to both surfaces: for an issue, that's building the change from nothing; for a PR, it's what's left to do *to the existing diff* — finish it, close gaps, address review points. Same principles either way; the PR example below shows the difference.
|
||||
|
||||
## Principles
|
||||
|
||||
### Durability over precision
|
||||
|
||||
The issue may sit in `ready-for-agent` for days or weeks. The codebase will change in the meantime. Write the brief so it stays useful even as files are renamed, moved, or refactored.
|
||||
|
||||
- **Do** describe interfaces, types, and behavioral contracts
|
||||
- **Do** name specific types, function signatures, or config shapes that the agent should look for or modify
|
||||
- **Don't** reference file paths — they go stale
|
||||
- **Don't** reference line numbers
|
||||
- **Don't** assume the current implementation structure will remain the same
|
||||
|
||||
### Behavioral, not procedural
|
||||
|
||||
Describe **what** the system should do, not **how** to implement it. The agent will explore the codebase fresh and make its own implementation decisions.
|
||||
|
||||
- **Good:** "The `SkillConfig` type should accept an optional `schedule` field of type `CronExpression`"
|
||||
- **Bad:** "Open src/types/skill.ts and add a schedule field on line 42"
|
||||
- **Good:** "When a user runs `/triage` with no arguments, they should see a summary of issues needing attention"
|
||||
- **Bad:** "Add a switch statement in the main handler function"
|
||||
|
||||
### Complete acceptance criteria
|
||||
|
||||
The agent needs to know when it's done. Every agent brief must have concrete, testable acceptance criteria. Each criterion should be independently verifiable.
|
||||
|
||||
- **Good:** "Running `gh issue list --label needs-triage` returns issues that have been through initial classification"
|
||||
- **Bad:** "Triage should work correctly"
|
||||
|
||||
### Explicit scope boundaries
|
||||
|
||||
State what is out of scope. This prevents the agent from gold-plating or making assumptions about adjacent features.
|
||||
|
||||
## Template
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Category:** bug / enhancement
|
||||
**Summary:** one-line description of what needs to happen
|
||||
|
||||
**Current behavior:**
|
||||
Describe what happens now. For bugs, this is the broken behavior.
|
||||
For enhancements, this is the status quo the feature builds on.
|
||||
|
||||
**Desired behavior:**
|
||||
Describe what should happen after the agent's work is complete.
|
||||
Be specific about edge cases and error conditions.
|
||||
|
||||
**Key interfaces:**
|
||||
- `TypeName` — what needs to change and why
|
||||
- `functionName()` return type — what it currently returns vs what it should return
|
||||
- Config shape — any new configuration options needed
|
||||
|
||||
**Acceptance criteria:**
|
||||
- [ ] Specific, testable criterion 1
|
||||
- [ ] Specific, testable criterion 2
|
||||
- [ ] Specific, testable criterion 3
|
||||
|
||||
**Out of scope:**
|
||||
- Thing that should NOT be changed or addressed in this issue
|
||||
- Adjacent feature that might seem related but is separate
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Good agent brief (bug)
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Category:** bug
|
||||
**Summary:** Skill description truncation drops mid-word, producing broken output
|
||||
|
||||
**Current behavior:**
|
||||
When a skill description exceeds 1024 characters, it is truncated at exactly
|
||||
1024 characters regardless of word boundaries. This produces descriptions
|
||||
that end mid-word (e.g. "Use when the user wants to confi").
|
||||
|
||||
**Desired behavior:**
|
||||
Truncation should break at the last word boundary before 1024 characters
|
||||
and append "..." to indicate truncation.
|
||||
|
||||
**Key interfaces:**
|
||||
- The `SkillMetadata` type's `description` field — no type change needed,
|
||||
but the validation/processing logic that populates it needs to respect
|
||||
word boundaries
|
||||
- Any function that reads SKILL.md frontmatter and extracts the description
|
||||
|
||||
**Acceptance criteria:**
|
||||
- [ ] Descriptions under 1024 chars are unchanged
|
||||
- [ ] Descriptions over 1024 chars are truncated at the last word boundary
|
||||
before 1024 chars
|
||||
- [ ] Truncated descriptions end with "..."
|
||||
- [ ] The total length including "..." does not exceed 1024 chars
|
||||
|
||||
**Out of scope:**
|
||||
- Changing the 1024 char limit itself
|
||||
- Multi-line description support
|
||||
```
|
||||
|
||||
### Good agent brief (enhancement)
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Category:** enhancement
|
||||
**Summary:** Add `.out-of-scope/` directory support for tracking rejected feature requests
|
||||
|
||||
**Current behavior:**
|
||||
When a feature request is rejected, the issue is closed with a `wontfix` label
|
||||
and a comment. There is no persistent record of the decision or reasoning.
|
||||
Future similar requests require the maintainer to recall or search for the
|
||||
prior discussion.
|
||||
|
||||
**Desired behavior:**
|
||||
Rejected feature requests should be documented in `.out-of-scope/<concept>.md`
|
||||
files that capture the decision, reasoning, and links to all issues that
|
||||
requested the feature. When triaging new issues, these files should be
|
||||
checked for matches.
|
||||
|
||||
**Key interfaces:**
|
||||
- Markdown file format in `.out-of-scope/` — each file should have a
|
||||
`# Concept Name` heading, a `**Decision:**` line, a `**Reason:**` line,
|
||||
and a `**Prior requests:**` list with issue links
|
||||
- The triage workflow should read all `.out-of-scope/*.md` files early
|
||||
and match incoming issues against them by concept similarity
|
||||
|
||||
**Acceptance criteria:**
|
||||
- [ ] Closing a feature as wontfix creates/updates a file in `.out-of-scope/`
|
||||
- [ ] The file includes the decision, reasoning, and link to the closed issue
|
||||
- [ ] If a matching `.out-of-scope/` file already exists, the new issue is
|
||||
appended to its "Prior requests" list rather than creating a duplicate
|
||||
- [ ] During triage, existing `.out-of-scope/` files are checked and surfaced
|
||||
when a new issue matches a prior rejection
|
||||
|
||||
**Out of scope:**
|
||||
- Automated matching (human confirms the match)
|
||||
- Reopening previously rejected features
|
||||
- Bug reports (only enhancement rejections go to `.out-of-scope/`)
|
||||
```
|
||||
|
||||
### Good agent brief (PR)
|
||||
|
||||
For a PR, "Current behavior" describes the state of the diff, and the brief asks the agent to finish or fix it rather than build from scratch.
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Category:** enhancement
|
||||
**Summary:** Finish the contributor's `--json` output flag for `triage list`
|
||||
|
||||
**Current behavior:**
|
||||
The PR adds a `--json` flag that serializes the issue list to JSON. The happy
|
||||
path works and the diff matches the project's command structure. Two gaps
|
||||
remain: errors are still printed as human text (not JSON), and the new flag has
|
||||
no test coverage.
|
||||
|
||||
**Desired behavior:**
|
||||
With `--json`, all output — including errors — is well-formed JSON on stdout,
|
||||
and the command's exit codes are unchanged. The existing human-readable output
|
||||
is untouched when the flag is absent.
|
||||
|
||||
**Key interfaces:**
|
||||
- The command's error path should emit `{ "error": string }` under `--json`
|
||||
instead of the plain-text error
|
||||
- Reuse the existing serializer the PR already added; don't introduce a second
|
||||
|
||||
**Acceptance criteria:**
|
||||
- [ ] `triage list --json` emits valid JSON for both success and error cases
|
||||
- [ ] Exit codes match the non-JSON command
|
||||
- [ ] A test covers the `--json` success output and one error case
|
||||
- [ ] Default (non-JSON) output is byte-for-byte unchanged
|
||||
|
||||
**Out of scope:**
|
||||
- Adding `--json` to any other command
|
||||
- Changing the JSON shape of the success payload the PR already defined
|
||||
```
|
||||
|
||||
### Bad agent brief
|
||||
|
||||
```markdown
|
||||
## Agent Brief
|
||||
|
||||
**Summary:** Fix the triage bug
|
||||
|
||||
**What to do:**
|
||||
The triage thing is broken. Look at the main file and fix it.
|
||||
The function around line 150 has the issue.
|
||||
|
||||
**Files to change:**
|
||||
- src/triage/handler.ts (line 150)
|
||||
- src/types.ts (line 42)
|
||||
```
|
||||
|
||||
This is bad because:
|
||||
- No category
|
||||
- Vague description ("the triage thing is broken")
|
||||
- References file paths and line numbers that will go stale
|
||||
- No acceptance criteria
|
||||
- No scope boundaries
|
||||
- No description of current vs desired behavior
|
||||
@@ -1,105 +0,0 @@
|
||||
# Out-of-Scope Knowledge Base
|
||||
|
||||
The `.out-of-scope/` directory in a repo stores persistent records of rejected feature requests. It serves two purposes:
|
||||
|
||||
1. **Institutional memory** — why a feature was rejected, so the reasoning isn't lost when the issue is closed
|
||||
2. **Deduplication** — when a new issue comes in that matches a prior rejection, the skill can surface the previous decision instead of re-litigating it
|
||||
|
||||
## Directory structure
|
||||
|
||||
```
|
||||
.out-of-scope/
|
||||
├── dark-mode.md
|
||||
├── plugin-system.md
|
||||
└── graphql-api.md
|
||||
```
|
||||
|
||||
One file per **concept**, not per issue. Multiple issues requesting the same thing are grouped under one file.
|
||||
|
||||
## File format
|
||||
|
||||
The file should be written in a relaxed, readable style — more like a short design document than a database entry. Use paragraphs, code samples, and examples to make the reasoning clear and useful to someone encountering it for the first time.
|
||||
|
||||
```markdown
|
||||
# Dark Mode
|
||||
|
||||
This project does not support dark mode or user-facing theming.
|
||||
|
||||
## Why this is out of scope
|
||||
|
||||
The rendering pipeline assumes a single color palette defined in
|
||||
`ThemeConfig`. Supporting multiple themes would require:
|
||||
|
||||
- A theme context provider wrapping the entire component tree
|
||||
- Per-component theme-aware style resolution
|
||||
- A persistence layer for user theme preferences
|
||||
|
||||
This is a significant architectural change that doesn't align with the
|
||||
project's focus on content authoring. Theming is a concern for downstream
|
||||
consumers who embed or redistribute the output.
|
||||
|
||||
```ts
|
||||
// The current ThemeConfig interface is not designed for runtime switching:
|
||||
interface ThemeConfig {
|
||||
colors: ColorPalette; // single palette, resolved at build time
|
||||
fonts: FontStack;
|
||||
}
|
||||
```
|
||||
|
||||
## Prior requests
|
||||
|
||||
- #42 — "Add dark mode support"
|
||||
- #87 — "Night theme for accessibility"
|
||||
- #134 — "Dark theme option"
|
||||
```
|
||||
|
||||
### Naming the file
|
||||
|
||||
Use a short, descriptive kebab-case name for the concept: `dark-mode.md`, `plugin-system.md`, `graphql-api.md`. The name should be recognizable enough that someone browsing the directory understands what was rejected without opening the file.
|
||||
|
||||
### Writing the reason
|
||||
|
||||
The reason should be substantive — not "we don't want this" but why. Good reasons reference:
|
||||
|
||||
- Project scope or philosophy ("This project focuses on X; theming is a downstream concern")
|
||||
- Technical constraints ("Supporting this would require Y, which conflicts with our Z architecture")
|
||||
- Strategic decisions ("We chose to use A instead of B because...")
|
||||
|
||||
The reason should be durable. Avoid referencing temporary circumstances ("we're too busy right now") — those aren't real rejections, they're deferrals.
|
||||
|
||||
## When to check `.out-of-scope/`
|
||||
|
||||
During triage (Step 1: Gather context), read all files in `.out-of-scope/`. When evaluating a new issue:
|
||||
|
||||
- Check if the request matches an existing out-of-scope concept
|
||||
- Matching is by concept similarity, not keyword — "night theme" matches `dark-mode.md`
|
||||
- If there's a match, surface it to the maintainer: "This is similar to `.out-of-scope/dark-mode.md` — we rejected this before because [reason]. Do you still feel the same way?"
|
||||
|
||||
The maintainer may:
|
||||
|
||||
- **Confirm** — the new issue gets added to the existing file's "Prior requests" list, then closed
|
||||
- **Reconsider** — the out-of-scope file gets deleted or updated, and the issue proceeds through normal triage
|
||||
- **Disagree** — the issues are related but distinct, proceed with normal triage
|
||||
|
||||
## When to write to `.out-of-scope/`
|
||||
|
||||
Only when an **enhancement** (not a bug) is *rejected* as `wontfix`. This applies to enhancement PRs exactly as it does to issues — a rejected PR is recorded here so the same request doesn't return as fresh code.
|
||||
|
||||
Do **not** write here when something is closed as `wontfix` because it's **already implemented**. That's a built feature, not a rejected one; recording it would poison the dedup checks with false rejections. Instead, the closing comment points to where the feature already lives.
|
||||
|
||||
The flow:
|
||||
|
||||
1. Maintainer decides a feature request is out of scope
|
||||
2. Check if a matching `.out-of-scope/` file already exists
|
||||
3. If yes: append the new issue to the "Prior requests" list
|
||||
4. If no: create a new file with the concept name, decision, reason, and first prior request
|
||||
5. Post a comment on the issue explaining the decision and mentioning the `.out-of-scope/` file
|
||||
6. Close the issue with the `wontfix` label
|
||||
|
||||
## Updating or removing out-of-scope files
|
||||
|
||||
If the maintainer changes their mind about a previously rejected concept:
|
||||
|
||||
- Delete the `.out-of-scope/` file
|
||||
- The skill does not need to reopen old issues — they're historical records
|
||||
- The new issue that triggered the reconsideration proceeds through normal triage
|
||||
@@ -1,112 +0,0 @@
|
||||
---
|
||||
name: triage
|
||||
description: Move issues and external PRs through a state machine of triage roles — categorise, verify, grill if needed, and write agent-ready briefs.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Triage
|
||||
|
||||
Move issues on the project issue tracker through a small state machine of triage roles.
|
||||
|
||||
If this repo treats external pull requests as a request surface (see the issue-tracker config), triage covers them too: **a PR is an issue with attached code** — same roles, same states, same machine, with a few deltas marked "for a PR" below. Resolve a bare `#42` to an issue or PR per the tracker config.
|
||||
|
||||
Every comment or issue posted to the issue tracker during triage **must** start with this disclaimer:
|
||||
|
||||
```
|
||||
> *This was generated by AI during triage.*
|
||||
```
|
||||
|
||||
## Reference docs
|
||||
|
||||
- [AGENT-BRIEF.md](AGENT-BRIEF.md) — how to write durable agent briefs
|
||||
- [OUT-OF-SCOPE.md](OUT-OF-SCOPE.md) — how the `.out-of-scope/` knowledge base works
|
||||
|
||||
## Roles
|
||||
|
||||
Two **category** roles:
|
||||
|
||||
- `bug` — something is broken
|
||||
- `enhancement` — new feature or improvement
|
||||
|
||||
Five **state** roles:
|
||||
|
||||
- `needs-triage` — maintainer needs to evaluate
|
||||
- `needs-info` — waiting on reporter for more information
|
||||
- `ready-for-agent` — fully specified, ready for an AFK agent
|
||||
- `ready-for-human` — needs human implementation
|
||||
- `wontfix` — will not be actioned
|
||||
|
||||
For a PR, the same states read against the attached code: `ready-for-agent` means a brief is attached and an agent should take the next step on the diff; `ready-for-human` means it's ready for a human to merge.
|
||||
|
||||
Every triaged issue should carry exactly one category role and one state role. If state roles conflict, flag it and ask the maintainer before doing anything else.
|
||||
|
||||
These are canonical role names — the actual label strings used in the issue tracker may differ. The mapping should have been provided to you - run `/setup-matt-pocock-skills` if not.
|
||||
|
||||
State transitions: an unlabeled issue normally goes to `needs-triage` first; from there it moves to `needs-info`, `ready-for-agent`, `ready-for-human`, or `wontfix`. `needs-info` returns to `needs-triage` once the reporter replies. The maintainer can override at any time — flag transitions that look unusual and ask before proceeding.
|
||||
|
||||
## Invocation
|
||||
|
||||
The maintainer invokes `/triage` and describes what they want in natural language. Interpret the request and act. Examples:
|
||||
|
||||
- "Show me anything that needs my attention"
|
||||
- "Let's look at #42" (issue or PR)
|
||||
- "Move #42 to ready-for-agent"
|
||||
- "What's ready for agents to pick up?"
|
||||
|
||||
## Show what needs attention
|
||||
|
||||
Query the issue tracker and present three buckets, oldest first:
|
||||
|
||||
1. **Unlabeled** — never triaged.
|
||||
2. **`needs-triage`** — evaluation in progress.
|
||||
3. **`needs-info` with reporter activity since the last triage notes** — needs re-evaluation.
|
||||
|
||||
When PRs are in scope, include external PRs in these buckets and tag each line `[PR]` or `[issue]`. Discovery surfaces only *external* PRs (the tracker config defines who counts as external) — a collaborator's in-flight PR is not triage work. This filter is discovery-only; an explicitly named PR is always triaged regardless of author.
|
||||
|
||||
Show counts and a one-line summary per item. Let the maintainer pick.
|
||||
|
||||
## Triage a specific issue or PR
|
||||
|
||||
1. **Gather context.** Read the full issue or PR (body, comments, labels, author, dates; for a PR, the diff too). Parse any prior triage notes so you don't re-ask resolved questions. Explore the codebase using the project's domain glossary, respecting ADRs in the area. Run two checks against the codebase: (a) **redundancy** — search for an existing implementation of the requested behavior by domain concept (not just the request's wording), and report where you looked. If found, it's an already-implemented `wontfix` (step 5). (b) **prior rejection** — read `.out-of-scope/*.md` and surface any that resembles this request.
|
||||
|
||||
2. **Recommend.** Tell the maintainer your category and state recommendation with reasoning, plus a brief codebase summary relevant to the request — including whether it's already implemented. Wait for direction.
|
||||
|
||||
3. **Verify the claim.** Before any grilling, check that the claim holds up. For a bug, reproduce it from the reporter's steps. For a PR, confirm the diff does what it claims — check it out, run the relevant tests or commands. Report what happened: confirmed (with code path), failed, or insufficient detail (a strong `needs-info` signal). A confirmed verification makes a much stronger agent brief.
|
||||
|
||||
4. **Grill (if needed).** If the request needs fleshing out, run the `/grilling` and `/domain-modeling` skills together — grill it into shape one question at a time, sharpening domain terms and updating `CONTEXT.md`/ADRs inline as decisions land.
|
||||
|
||||
5. **Apply the outcome:**
|
||||
- `ready-for-agent` — post an agent brief comment ([AGENT-BRIEF.md](AGENT-BRIEF.md)).
|
||||
- `ready-for-human` — same structure as an agent brief, but note why it can't be delegated (judgment calls, external access, design decisions, manual testing).
|
||||
- `needs-info` — post triage notes (template below).
|
||||
- `wontfix` — close, with the comment depending on *why*:
|
||||
- **Already implemented** — the change already exists in the codebase. Point to where it lives; do **not** write to `.out-of-scope/` (that KB is for *rejected* requests, not built ones).
|
||||
- **Rejected (bug)** — polite explanation, then close.
|
||||
- **Rejected (enhancement)** — write to `.out-of-scope/`, link to it from a comment, then close ([OUT-OF-SCOPE.md](OUT-OF-SCOPE.md)).
|
||||
- `needs-triage` — apply the role. Optional comment if there's partial progress.
|
||||
|
||||
## Quick state override
|
||||
|
||||
If the maintainer says "move #42 to ready-for-agent", trust them and apply the role directly. Confirm what you're about to do (role changes, comment, close), then act. Skip grilling. If moving to `ready-for-agent` without a grilling session, ask whether they want to write an agent brief.
|
||||
|
||||
## Needs-info template
|
||||
|
||||
```markdown
|
||||
## Triage Notes
|
||||
|
||||
**What we've established so far:**
|
||||
|
||||
- point 1
|
||||
- point 2
|
||||
|
||||
**What we still need from you (@reporter):**
|
||||
|
||||
- question 1
|
||||
- question 2
|
||||
```
|
||||
|
||||
Capture everything resolved during grilling under "established so far" so the work isn't lost. Questions must be specific and actionable, not "please provide more info".
|
||||
|
||||
## Resuming a previous session
|
||||
|
||||
If prior triage notes exist on the issue or PR, read them, check whether the reporter has answered any outstanding questions, and present an updated picture before continuing. Don't re-ask resolved questions.
|
||||
@@ -0,0 +1,8 @@
|
||||
# In-Progress Skills
|
||||
|
||||
Drafts not yet ready to ship.
|
||||
|
||||
## User-invoked
|
||||
|
||||
- [agent-handoff](agent-handoff/SKILL.md) — Hand the current conversation off to a fresh background agent that picks up the work immediately.
|
||||
- [knowledge-gardener](knowledge-gardener/SKILL.md) — Run vault-aware semantic search, synthesis, note creation, linking, and Zettelkasten workflows for this Obsidian vault.
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: agent-handoff
|
||||
description: Hand the current conversation off to a fresh background agent that picks up the work immediately.
|
||||
argument-hint: "What will the next session be used for?"
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
## Invocation
|
||||
|
||||
```
|
||||
/skill:agent-handoff [focus description]
|
||||
```
|
||||
|
||||
Arguments are optional. If provided, they describe what the next session should focus on and the summary is tailored accordingly.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Assess the conversation
|
||||
|
||||
Review what has been done, what remains, and any user-provided focus. Identify existing artifacts that capture the work so far (PRDs/specs, plans, ADRs, issues/tickets, commits, diffs) so they can be referenced rather than duplicated.
|
||||
|
||||
If the user passed arguments, treat them as the priority or scope for the next session.
|
||||
|
||||
Completion criterion: The conversation is assessed — what's done, what's left, and the user's focus (if any) are identified.
|
||||
|
||||
### 2. Compose the handoff summary
|
||||
|
||||
Write a summary of the current state so a fresh agent can continue the work without reading the full conversation. Organise it with these sections:
|
||||
|
||||
- **Current state** — what was accomplished, branch/commit, what's working
|
||||
- **Next steps** — what needs to be done next, in priority order
|
||||
- **Open questions** — decisions still needed, unknowns, trade-offs
|
||||
- **Suggested skills** — a bullet list of skills the next agent should invoke (e.g. `/tdd`, `/code-review`)
|
||||
- **References** — paths or URLs to existing artifacts (PRDs/specs, plans, ADRs, issues/tickets, commits, diffs). Do **not** duplicate their content — reference them.
|
||||
|
||||
The summary is a **compass**, not a copy: it points the next agent where to go, it does not replay where you've been. Any content already captured in the referenced artifacts does not belong here.
|
||||
|
||||
Redact any sensitive information: API keys, passwords, personally identifiable information.
|
||||
|
||||
Completion criterion: The summary covers all required sections, references existing artifacts without duplicating them, and contains no sensitive information.
|
||||
|
||||
### 3. Launch the background agent
|
||||
|
||||
Use the [`tmux-launch-agent`](../misc/tmux-launch-agent/SKILL.md) skill to fork a fresh agent seeded with the summary:
|
||||
|
||||
```
|
||||
/skill:tmux-launch-agent --name "<descriptive-title>" <handoff-summary>
|
||||
```
|
||||
|
||||
- `--name` is **required** — use a short descriptive title (e.g. `"Fix login bug"`, `"Implement user roles"`). This sets the display name in the job list, session picker, and terminal title.
|
||||
- The handoff summary from step 2 becomes the new agent's initial prompt.
|
||||
|
||||
Completion criterion: `tmux new-window` exits 0 and a new tmux window appears with the handoff summary as the agent's prompt.
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Agent Handoff"
|
||||
short_description: "Handoff the agent to a human operator"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Knowledge Gardener"
|
||||
short_description: "Manage and curate knowledge for the agent"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -0,0 +1,7 @@
|
||||
# Misc Skills
|
||||
|
||||
Kept around but rarely used.
|
||||
|
||||
## User-invoked
|
||||
|
||||
- [tmux-launch-agent](tmux-launch-agent/SKILL.md) — Fork a new agent CLI session into a new tmux window, detected from the current agent.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: tmux-launch-agent
|
||||
description: Fork a new agent CLI session into a new tmux window, detected from the current agent.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
## Agent CLI Seed Data
|
||||
|
||||
Agent config (binary, `args`, `modelflag`) is in [`agents-seed.md`](agents-seed.md). Step 2 reads it by `name`.
|
||||
|
||||
## Dispatch
|
||||
|
||||
1. **Detect agent** — Run `./detect-agent` (sibling). On success, the agent name is known. On failure (exit 1), stop.
|
||||
|
||||
2. **Look up config** — Find the agent's entry in [`agents-seed.md`](agents-seed.md) by `name`. Extract `binary`, `args`, and `modelflag`.
|
||||
|
||||
3. **Parse flags** — Scan user arguments for flags (before prompt text). For each flag, extract its value and remove both the flag and value from the argument list:
|
||||
- `--name <title>` / `-n <title>` — tmux window title
|
||||
- `-c <path>` — working directory (default: project directory)
|
||||
- `--model <name>` / `-m <name>` — model for child session
|
||||
|
||||
Remaining text is the prompt. If `--name` is absent, tmux auto-names the window.
|
||||
|
||||
4. **Build command** — Assemble the inner command:
|
||||
- Start with `<binary>`.
|
||||
- If model specified: append `<modelflag> <model-name>`.
|
||||
- If prompt exists and `args` contains `{prompt}`: write to `/tmp/`, substitute path for `{prompt}`.
|
||||
- If prompt exists and `args` is empty: pipe via `echo`.
|
||||
- If no prompt: launch bare.
|
||||
|
||||
5. **Open window** — Call `./tmux-open <window-name> <start-dir> <inner-command>` where `<inner-command>` is the assembled command from step 4 as separate arguments. On success, a new tmux window appears with the agent session active.
|
||||
+11
-7
@@ -1,6 +1,4 @@
|
||||
# Agent CLI Seed Data
|
||||
|
||||
This file is the source of truth for all supported CLI agents. It is read by `setup-skills` Section E to present the menu and by `implement-issue` for agent dispatch.
|
||||
# TMUX Launch Agent — Agent CLI Seed Data
|
||||
|
||||
## Agents
|
||||
|
||||
@@ -9,28 +7,33 @@ agents:
|
||||
- name: pi
|
||||
binary: pi
|
||||
args: "@{prompt}"
|
||||
description: "My primary agent harness. Accepts prompt file via @{prompt}."
|
||||
modelflag: "--model"
|
||||
description: "My primary agent harness. Accepts prompt file via {prompt}."
|
||||
|
||||
- name: opencode
|
||||
binary: opencode
|
||||
args: ""
|
||||
modelflag: "-m"
|
||||
description: "OpenCode agent. Pipes stdin via cat."
|
||||
note: "Uses stdin piping: args must be empty, prompt via pipe."
|
||||
|
||||
- name: goose
|
||||
binary: goose
|
||||
args: ""
|
||||
modelflag: "--model"
|
||||
description: "Goose agent. Accepts prompt file via -i flag."
|
||||
note: "Uses stdin piping: args must be empty, prompt via pipe."
|
||||
|
||||
- name: codex
|
||||
binary: codex
|
||||
args: "@{prompt}"
|
||||
description: "OpenAI Codex. Accepts prompt file via @{prompt}."
|
||||
modelflag: "-m"
|
||||
description: "OpenAI Codex. Accepts prompt file via {prompt}."
|
||||
|
||||
- name: claude
|
||||
binary: claude
|
||||
args: ""
|
||||
modelflag: "--model"
|
||||
description: "Anthropic Claude CLI. Pipes stdin via cat."
|
||||
note: "Uses stdin piping: args must be empty, prompt via pipe."
|
||||
```
|
||||
@@ -38,9 +41,10 @@ agents:
|
||||
## Field meanings
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| ------- | ------------- |
|
||||
| `name` | Display name used in menus and `--agent` flag |
|
||||
| `binary` | Command name expected on PATH |
|
||||
| `args` | Static arguments appended after the binary. May include `@{prompt}` which is substituted at invocation time with the absolute path to the prompt file. Empty string means stdin piping (prompt is piped via `cat`). |
|
||||
| `args` | Static arguments appended after the binary. May include `{prompt}` which is substituted at invocation time with the absolute path to the prompt file. Empty string means stdin piping (prompt is piped via `cat`). |
|
||||
| `description` | Short human-readable description for the setup menu |
|
||||
| `modelflag` | CLI flag used to select a model (e.g. `--model`, `-m`). Injected into the inner command when `--model <name>` is passed by the user. |
|
||||
| `note` | Optional additional context |
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Tmux Launch Agent"
|
||||
short_description: "Launch an agent in a new tmux session"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
Executable
+353
@@ -0,0 +1,353 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# detect-agent — Detect which AI agent CLI launched this script.
|
||||
#
|
||||
# Checks environment variables and walks the parent process tree to
|
||||
# identify the originating coding agent. Designed for agent-agnostic
|
||||
# tools (skills, scripts, hooks) that need to adjust behaviour based
|
||||
# on which agent invoked them.
|
||||
#
|
||||
# Usage:
|
||||
# detect-agent # print name (e.g. "pi", "opencode")
|
||||
# detect-agent --name # same as default
|
||||
# detect-agent --json # JSON: {"agent":"pi","detected":true}
|
||||
# detect-agent --verbose # human-readable with debug info
|
||||
# detect-agent --list-agents # list all known agent names
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 agent detected
|
||||
# 1 unknown / not detected
|
||||
#
|
||||
# Supported agents (add your own at the top of each definition map):
|
||||
# pi, opencode, aider, claude, codex, cursor, windsurf, continue,
|
||||
# github-copilot, goose
|
||||
#
|
||||
# Detection order:
|
||||
# 1. Environment variable signatures (most reliable)
|
||||
# 2. Walk parent process tree skipping shells
|
||||
# 3. Check /proc/$PPID/{comm,cmdline} as fallback
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
########################################################################
|
||||
# Agent definitions
|
||||
########################################################################
|
||||
|
||||
# Each entry: "<env-var>:<match-value>:<agent-name>"
|
||||
# If match-value is "*", any non-empty value matches.
|
||||
# If match-value is a literal, the env var must equal it.
|
||||
AGENT_ENV_SIGS=(
|
||||
"PI_CODING_AGENT:true:pi"
|
||||
"OPENCODE_CONFIG:*:opencode"
|
||||
"OPENCODE_CONFIG_DIR:*:opencode"
|
||||
"OPENCODE_API_KEY:*:opencode"
|
||||
"CLAUDE_CODE_AGENT_RULE_DISABLED:*:claude"
|
||||
"CLAUDE_CODE_SSE_PORT:*:claude"
|
||||
"AIDER_DARK_MODE:*:aider"
|
||||
"CODEX_API_KEY:*:codex"
|
||||
"CURSOR_AGENT_RULE_DISABLED:*:cursor"
|
||||
"WINDSURF_AGENT_RULE_DISABLED:*:windsurf"
|
||||
"CONTINUE_ON_ERROR:*:continue"
|
||||
)
|
||||
|
||||
# Key = /proc/PID/comm (truncated to 15 chars on Linux).
|
||||
# Value = canonical agent name.
|
||||
declare -A AGENT_COMM
|
||||
AGENT_COMM=(
|
||||
[pi]="pi"
|
||||
[opencode]="opencode"
|
||||
[aider]="aider"
|
||||
[claude]="claude"
|
||||
[codex]="codex"
|
||||
[cursor]="cursor"
|
||||
[windsurf]="windsurf"
|
||||
[continue]="continue"
|
||||
[github-copilot]="github-copilot"
|
||||
[copilot]="github-copilot"
|
||||
[goose]="goose"
|
||||
)
|
||||
|
||||
# For agents running under an interpreter (node, python, etc.).
|
||||
# Key = substring to search in cmdline, Value = agent name.
|
||||
declare -A AGENT_CMDLINE
|
||||
AGENT_CMDLINE=(
|
||||
["opencode"]="opencode"
|
||||
["/pi "]="pi"
|
||||
["/aider"]="aider"
|
||||
["/claude"]="claude"
|
||||
["/codex"]="codex"
|
||||
["cursor"]="cursor"
|
||||
["windsurf"]="windsurf"
|
||||
["continue.dev"]="continue"
|
||||
["github-copilot"]="github-copilot"
|
||||
["goose"]="goose"
|
||||
)
|
||||
|
||||
# Shells to skip when walking ancestors.
|
||||
SHELL_COMMS=(
|
||||
bash zsh fish sh dash ksh mksh posh ash tcsh csh
|
||||
tmux:server tmux screen
|
||||
sudo doas
|
||||
)
|
||||
|
||||
SHELL_CMDLINE_SUBSTRINGS=(
|
||||
"/bash" "/zsh" "/fish" "/sh" "/dash" "/ksh"
|
||||
)
|
||||
|
||||
########################################################################
|
||||
# Helpers
|
||||
########################################################################
|
||||
|
||||
comm_of() {
|
||||
local pid="$1"
|
||||
cat "/proc/${pid}/comm" 2>/dev/null || true
|
||||
}
|
||||
|
||||
cmdline_of() {
|
||||
local pid="$1"
|
||||
tr '\0' ' ' <"/proc/${pid}/cmdline" 2>/dev/null || true
|
||||
}
|
||||
|
||||
is_shell() {
|
||||
local pid="$1"
|
||||
local c; c="$(comm_of "$pid")" || true
|
||||
[[ -z "$c" ]] && return 1
|
||||
|
||||
for s in "${SHELL_COMMS[@]}"; do
|
||||
[[ "$c" == "$s" ]] && return 0
|
||||
done
|
||||
|
||||
local cl; cl="$(cmdline_of "$pid")" || true
|
||||
for s in "${SHELL_CMDLINE_SUBSTRINGS[@]}"; do
|
||||
[[ "$cl" == *"$s"* ]] && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
walk_to_agent_comm() {
|
||||
local pid="$1"
|
||||
local c
|
||||
while [[ "$pid" -gt 1 ]]; do
|
||||
c="$(comm_of "$pid")" || true
|
||||
[[ -z "$c" ]] && break
|
||||
if ! is_shell "$pid"; then
|
||||
echo "$c"
|
||||
return 0
|
||||
fi
|
||||
pid="$(awk '/^PPid:/{print $2}' "/proc/${pid}/status" 2>/dev/null || true)"
|
||||
[[ -z "$pid" ]] && break
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
walk_to_agent_cmdline() {
|
||||
local pid="$1"
|
||||
local cl
|
||||
while [[ "$pid" -gt 1 ]]; do
|
||||
if ! is_shell "$pid"; then
|
||||
cl="$(cmdline_of "$pid")" || true
|
||||
if [[ -n "$cl" ]]; then
|
||||
echo "$cl"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
pid="$(awk '/^PPid:/{print $2}' "/proc/${pid}/status" 2>/dev/null || true)"
|
||||
[[ -z "$pid" ]] && break
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
check_env_sigs() {
|
||||
local var val agent
|
||||
for sig in "${AGENT_ENV_SIGS[@]}"; do
|
||||
IFS=':' read -r var val agent <<< "$sig"
|
||||
if [[ "$val" == "*" ]]; then
|
||||
if [[ -n "${!var:-}" ]]; then
|
||||
echo "$agent"
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
if [[ "${!var:-}" == "$val" ]]; then
|
||||
echo "$agent"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
match_cmdline() {
|
||||
local cl="$1"
|
||||
local needle agent
|
||||
for needle in "${!AGENT_CMDLINE[@]}"; do
|
||||
agent="${AGENT_CMDLINE[$needle]}"
|
||||
if [[ "$cl" == *"$needle"* ]]; then
|
||||
echo "$agent"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
match_comm() {
|
||||
local c="$1"
|
||||
local comm_name
|
||||
for comm_name in "${!AGENT_COMM[@]}"; do
|
||||
if [[ "$c" == "$comm_name" ]]; then
|
||||
echo "${AGENT_COMM[$comm_name]}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# Main detection logic
|
||||
########################################################################
|
||||
|
||||
detect() {
|
||||
local agent=""
|
||||
|
||||
# Phase 1 — environment variables (most reliable)
|
||||
agent="$(check_env_sigs)" || true
|
||||
[[ -n "$agent" ]] && echo "$agent" && return 0
|
||||
|
||||
# Phase 2 — walk ancestors, check comm
|
||||
local ancestor_comm
|
||||
ancestor_comm="$(walk_to_agent_comm "$PPID")" || true
|
||||
if [[ -n "$ancestor_comm" ]]; then
|
||||
agent="$(match_comm "$ancestor_comm")" || true
|
||||
[[ -n "$agent" ]] && echo "$agent" && return 0
|
||||
|
||||
# comm matched but not in our map; try its cmdline
|
||||
local ancestor_pid="$PPID"
|
||||
while [[ "$ancestor_pid" -gt 1 ]]; do
|
||||
local ac; ac="$(comm_of "$ancestor_pid")" || true
|
||||
if [[ "$ac" == "$ancestor_comm" ]]; then
|
||||
local ancestor_cl
|
||||
ancestor_cl="$(cmdline_of "$ancestor_pid")" || true
|
||||
if [[ -n "$ancestor_cl" ]]; then
|
||||
agent="$(match_cmdline "$ancestor_cl")" || true
|
||||
[[ -n "$agent" ]] && echo "$agent" && return 0
|
||||
fi
|
||||
break
|
||||
fi
|
||||
ancestor_pid="$(awk '/^PPid:/{print $2}' "/proc/${ancestor_pid}/status" 2>/dev/null || true)"
|
||||
done
|
||||
fi
|
||||
|
||||
# Phase 3 — walk ancestors, check cmdline (catches node-based agents)
|
||||
local ancestor_cl
|
||||
ancestor_cl="$(walk_to_agent_cmdline "$PPID")" || true
|
||||
if [[ -n "$ancestor_cl" ]]; then
|
||||
agent="$(match_cmdline "$ancestor_cl")" || true
|
||||
[[ -n "$agent" ]] && echo "$agent" && return 0
|
||||
fi
|
||||
|
||||
# Phase 4 — fallback: check immediate PPID directly
|
||||
local ppid_comm; ppid_comm="$(comm_of "$PPID")" || true
|
||||
agent="$(match_comm "$ppid_comm")" || true
|
||||
[[ -n "$agent" ]] && echo "$agent" && return 0
|
||||
|
||||
local ppid_cl; ppid_cl="$(cmdline_of "$PPID")" || true
|
||||
agent="$(match_cmdline "$ppid_cl")" || true
|
||||
[[ -n "$agent" ]] && echo "$agent" && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# Output
|
||||
########################################################################
|
||||
|
||||
mode="${1:---name}"
|
||||
|
||||
case "$mode" in
|
||||
--list-agents)
|
||||
echo "Known agents (by env-var / process-name):"
|
||||
echo ""
|
||||
echo "Environment variable signatures:"
|
||||
for sig in "${AGENT_ENV_SIGS[@]}"; do
|
||||
IFS=':' read -r var val agent <<< "$sig"
|
||||
printf " %-12s %-8s %s=%s\n" "$agent" "(env)" "$var" "$val"
|
||||
done
|
||||
echo ""
|
||||
echo "Process comm signatures:"
|
||||
for comm_name in "${!AGENT_COMM[@]}"; do
|
||||
printf " %-12s %-8s %s\n" "${AGENT_COMM[$comm_name]}" "(comm)" "$comm_name"
|
||||
done
|
||||
echo ""
|
||||
echo "Cmdline substring signatures:"
|
||||
for needle in "${!AGENT_CMDLINE[@]}"; do
|
||||
printf " %-12s %-8s *%s*\n" "${AGENT_CMDLINE[$needle]}" "(cmdline)" "$needle"
|
||||
done
|
||||
;;
|
||||
|
||||
--json)
|
||||
agent="$(detect)" || agent="null"
|
||||
if [[ "$agent" != "null" ]]; then
|
||||
printf '{"agent":"%s","detected":true}\n' "$agent"
|
||||
else
|
||||
printf '{"agent":null,"detected":false}\n'
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
--verbose)
|
||||
echo "=== Agent Detection ==="
|
||||
agent="$(detect)" || true
|
||||
if [[ -n "$agent" ]]; then
|
||||
echo "Detected agent: $agent"
|
||||
else
|
||||
echo "Detected agent: (unknown)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "Relevant environment variables:"
|
||||
for sig in "${AGENT_ENV_SIGS[@]}"; do
|
||||
IFS=':' read -r var val _ <<< "$sig"
|
||||
# Skip entries that aren't real variable names (e.g. patterns with *).
|
||||
[[ "$var" == *\** ]] && continue
|
||||
if [[ -n "${!var:-}" ]]; then
|
||||
echo " $var=${!var}"
|
||||
fi
|
||||
done
|
||||
[[ -z "${PI_CODING_AGENT:-}" ]] && echo " PI_CODING_AGENT=<unset>"
|
||||
echo ""
|
||||
|
||||
echo "Process tree (up from PPID=$PPID):"
|
||||
_dbg_pid="$PPID"
|
||||
_dbg_depth=0
|
||||
while [[ "$_dbg_pid" -gt 1 && "$_dbg_depth" -lt 15 ]]; do
|
||||
_dbg_c="$(comm_of "$_dbg_pid")" || true
|
||||
_dbg_cl="$(cmdline_of "$_dbg_pid")" || true
|
||||
if [[ -n "$_dbg_c" ]]; then
|
||||
printf " [%d] %s" "$_dbg_depth" "$_dbg_c"
|
||||
if [[ -n "$_dbg_cl" ]]; then
|
||||
printf " <- %s" "${_dbg_cl:0:100}"
|
||||
fi
|
||||
if [[ -n "$agent" ]]; then
|
||||
_dbg_mc="$(match_comm "$_dbg_c")" || true
|
||||
if [[ "$_dbg_mc" == "$agent" ]]; then
|
||||
printf " <-- DETECTED"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
_dbg_pid="$(awk '/^PPid:/{print $2}' "/proc/${_dbg_pid}/status" 2>/dev/null || true)"
|
||||
_dbg_depth=$((_dbg_depth + 1))
|
||||
done
|
||||
;;
|
||||
|
||||
--name|*)
|
||||
agent="$(detect)" || agent=""
|
||||
if [[ -n "$agent" ]]; then
|
||||
echo "$agent"
|
||||
else
|
||||
echo "unknown"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
Executable
+115
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# tmux-open — open a new tmux window (or session) and run a command
|
||||
# Usage: tmux-open [-k|--keep] [-h|--help] <window-name> <start-dir> <cmd> [args...]
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: tmux-open [-k|--keep] [-h|--help] <window-name> <start-dir> <cmd> [args...]
|
||||
|
||||
Open a new tmux window in the current session and run a command.
|
||||
If not inside tmux, create a new session named after the window and attach.
|
||||
|
||||
Positional arguments:
|
||||
window-name Name for the new tmux window (and session, if creating one)
|
||||
start-dir Working directory for the new window
|
||||
cmd [args...] Command to run (variadic)
|
||||
|
||||
Options:
|
||||
-k, --keep After the command exits, keep the pane visible (remain-on-exit)
|
||||
so you can inspect output. Default: pane closes when command exits.
|
||||
-h, --help Show this help message
|
||||
|
||||
If `mise` is on PATH, the command runs under `mise x`.
|
||||
|
||||
Errors:
|
||||
- tmux not installed
|
||||
- fewer than 3 positional args
|
||||
- start-dir does not exist
|
||||
- a window with the same name already exists in the current session
|
||||
EOF
|
||||
}
|
||||
|
||||
# --- Flag parsing ---
|
||||
keep_shell=0
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-k|--keep) keep_shell=1; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
--) shift; break ;;
|
||||
-*) echo "error: unknown option: $1" >&2; usage >&2; exit 1 ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- Positional arg validation ---
|
||||
if (( $# < 3 )); then
|
||||
echo "error: expected at least 3 positional arguments (window-name, start-dir, cmd)" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
window_name="$1"; shift
|
||||
start_dir="$1"; shift
|
||||
cmd_args=("$@")
|
||||
|
||||
# --- Pre-flight checks ---
|
||||
if ! command -v tmux >/dev/null 2>&1; then
|
||||
echo "error: tmux is not installed or not on PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$start_dir" ]]; then
|
||||
echo "error: directory does not exist: $start_dir" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( ${#cmd_args[@]} == 0 )); then
|
||||
echo "error: no command specified" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Duplicate window name check (only relevant inside tmux)
|
||||
if [[ -n "${TMUX:-}" ]]; then
|
||||
if tmux list-windows -F '#{window_name}' | grep -Fxq "$window_name"; then
|
||||
echo "error: a window named '$window_name' already exists in the current session" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Detect mise ---
|
||||
has_mise=0
|
||||
if command -v mise >/dev/null 2>&1; then
|
||||
has_mise=1
|
||||
fi
|
||||
|
||||
# --- Build the command for the new window ---
|
||||
# We need to construct a single shell command string for tmux.
|
||||
# The command runs; pane closes on exit (or stays visible with --keep via remain-on-exit).
|
||||
|
||||
# Build the inner command based on mode and mise availability
|
||||
# For argv mode, quote each argument
|
||||
quoted_args=()
|
||||
for arg in "${cmd_args[@]}"; do
|
||||
quoted_args+=("$(printf '%q' "$arg")")
|
||||
done
|
||||
if (( has_mise )); then
|
||||
inner_cmd="mise x -- ${quoted_args[*]}"
|
||||
else
|
||||
inner_cmd="${quoted_args[*]}"
|
||||
fi
|
||||
|
||||
# --- Create window or session ---
|
||||
if [[ -n "${TMUX:-}" ]]; then
|
||||
tmux new-window -n "$window_name" -c "$start_dir" "$inner_cmd"
|
||||
if (( keep_shell )); then
|
||||
tmux set-option remain-on-exit on
|
||||
fi
|
||||
else
|
||||
tmux new-session -d -n "$window_name" -s "$window_name" -c "$start_dir" "$inner_cmd"
|
||||
if (( keep_shell )); then
|
||||
tmux set-option -t "$window_name" remain-on-exit on
|
||||
fi
|
||||
tmux attach-session -t "$window_name"
|
||||
fi
|
||||
@@ -1,6 +1,5 @@
|
||||
# Personal Skills
|
||||
|
||||
_No skills currently live in this bucket._
|
||||
Tied to my own setup, not promoted.
|
||||
|
||||
- `pkm-curation` has been moved to [pkm/pkm-curation](../pkm/pkm-curation/SKILL.md).
|
||||
- `forge-preferences` has been moved to [deprecated/forge-preferences](../deprecated/forge-preferences/SKILL.md).
|
||||
_No skills currently live in this bucket._
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# PKM Skills
|
||||
|
||||
Personal knowledge management.
|
||||
|
||||
## User-invoked
|
||||
|
||||
- [conversation-summary](conversation-summary/SKILL.md) — Summarize the current AI conversation into a new Obsidian markdown note and matching transcript file.
|
||||
- [crit](crit/SKILL.md) — Brainstorm with AI using the CRIT framework to generate and evaluate ideas.
|
||||
- [knowledge-gardener](knowledge-gardener/SKILL.md) — Run vault-aware semantic search, synthesis, note creation, linking, and Zettelkasten workflows for this Obsidian vault.
|
||||
- [pkm-curation](pkm-curation/SKILL.md) — Curate an Obsidian-style personal knowledge vault by classifying notes, normalizing frontmatter, improving structure, extracting atomic notes, and adding meaningful wikilinks.
|
||||
- [research-vault](research-vault/SKILL.md) — Research a topic through a one-question-at-a-time learning conversation and save a linked Obsidian research packet.
|
||||
- [conversation-summary](conversation-summary/SKILL.md) — Save the current conversation as a comprehensive report note in your Obsidian vault, following OKF v0.1 conventions.
|
||||
- [crit](crit/SKILL.md) — Run the CRIT framework — give the AI Context, assign it a Role, let it Interview you one question at a time, then issue the Task.
|
||||
- [research-vault](research-vault/SKILL.md) — Research a topic through a one-question-at-a-time learning conversation, answer directly, share resources when useful, and save a linked OKF-conformant research packet in the Obsidian vault.
|
||||
- [youtube-video-capture](youtube-video-capture/SKILL.md) — Fetch subtitles from a YouTube video, summarize the content, and save both the summary and raw subtitles to the Video bundle in the Obsidian vault.
|
||||
|
||||
## Model-invoked
|
||||
|
||||
_None yet._
|
||||
- [pkm-curation](pkm-curation/SKILL.md) — Curate an Obsidian vault — classify notes, normalize frontmatter, add links, extract atomic notes. Use when curating, batch-processing, reviewing, or doing a serendipity pick.
|
||||
|
||||
@@ -1,224 +1,76 @@
|
||||
---
|
||||
name: conversation-summary
|
||||
description: Summarize the current AI conversation into a new Obsidian markdown note and matching transcript file. Use when the user asks to save, export, log, archive, or summarize the current chat into an Obsidian vault, research note, markdown note, meeting note, decision log, or second-brain workflow.
|
||||
description: Save the current conversation as a comprehensive report note in your Obsidian vault, following OKF v0.1 conventions.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Create exactly one new Obsidian summary note for the current conversation. Also create exactly one transcript note for the same conversation.
|
||||
Save this conversation as two linked files in your Obsidian vault: a report (the narrative) and a transcript (the raw conversation). The files live inside the `AI Conversation Summaries/` bundle and follow OKF v0.1 conventions.
|
||||
|
||||
## Default location
|
||||
## Report
|
||||
|
||||
- Write notes to `obsidian vault` unless the user requests another path.
|
||||
- Ask if you don't know the location of the obsidian vault.
|
||||
- Create the directory if it does not exist.
|
||||
- Never overwrite an existing note.
|
||||
- Never append to an existing note unless the user explicitly asks.
|
||||
Write a thorough, standalone report as a Markdown note. A rich narrative in prose, organized under natural headings that follow the conversation's own shape. Capture everything of substance:
|
||||
|
||||
## Required behavior
|
||||
- What prompted the conversation and what context was shared
|
||||
- What was explored, investigated, or discussed in detail — including code, files, paths, URLs, tools, or skills that came up
|
||||
- What was found, decided, agreed, or concluded
|
||||
- Explanations given and concepts clarified
|
||||
- Open questions or unresolved items
|
||||
|
||||
1. Read the current conversation context only.
|
||||
2. Generate a filesystem-safe title.
|
||||
3. Write one summary note.
|
||||
4. Write one transcript note.
|
||||
5. Verify both files exist.
|
||||
6. Return the exact paths, generated title, and count of action items captured.
|
||||
The report must be self-contained — someone reading it later should understand the full discussion, including the reasoning and all relevant details, without having been there. Err on the side of including too much detail rather than too little.
|
||||
|
||||
## Title rules
|
||||
Include a link to the companion transcript where it fits naturally — a markdown link like `[transcript](2026-05-23_16-35_obsidian-summary-skill-upgrade_A7K2_transcript.md)` in the section where it makes the most sense (often near the end).
|
||||
|
||||
Use this format:
|
||||
## Frontmatter
|
||||
|
||||
`YYYY-MM-DD_HH-mm_<descriptor>_<ID>`
|
||||
Both the report and the transcript use the merged OKF + vault frontmatter schema:
|
||||
|
||||
Rules:
|
||||
- `<descriptor>`: 3-6 word slug based on the main topic.
|
||||
- Allowed characters in the full title: letters, digits, `.`, `_`, `-`.
|
||||
- Replace spaces with `-`.
|
||||
- Remove other punctuation.
|
||||
- Collapse repeated separators.
|
||||
- `<ID>`: 4-character uppercase alphanumeric suffix.
|
||||
- If a filename collision occurs, regenerate only `<ID>` until unique.
|
||||
- If too long for the filesystem, shorten only `<descriptor>`.
|
||||
|
||||
## Obsidian-specific rules
|
||||
|
||||
- Use valid YAML frontmatter.
|
||||
- Keep frontmatter simple and machine-safe.
|
||||
- Use wikilink-friendly filenames.
|
||||
- Include both standard markdown links and Obsidian wikilinks where useful.
|
||||
- Keep headings shallow and scannable.
|
||||
- Use UTF-8 markdown.
|
||||
- Prefer stable tags in frontmatter plus inline hashtag tags at the end.
|
||||
- If the conversation mentions files, URLs, papers, repos, tools, docs, or paths, capture them in a dedicated `References` section.
|
||||
- If the conversation includes explicit choices, approvals, rejections, or resolved tradeoffs, capture them in `Decisions Made`.
|
||||
|
||||
## Summary quality rules
|
||||
|
||||
- Use only facts available in the current conversation.
|
||||
- Do not invent references, decisions, action items, or conclusions.
|
||||
- Do not write a play-by-play transcript in the summary note.
|
||||
- Synthesize the conversation into a compact research/work summary.
|
||||
- If information is missing, say so explicitly.
|
||||
- If the conversation is short, still use the full template with fallback lines.
|
||||
- If the transcript available to you is incomplete, mark it as partial.
|
||||
|
||||
## Automatic tags
|
||||
|
||||
Generate 4-8 tags total.
|
||||
|
||||
Always include:
|
||||
- `ai-summary`
|
||||
- one domain/topic tag based on the conversation
|
||||
- one workflow tag based on the type of work, if clear
|
||||
|
||||
Add tags from these categories when supported by the conversation:
|
||||
- domain: `research`, `coding`, `obsidian`, `automation`, `writing`, `planning`, `debugging`
|
||||
- artifact: `skill`, `note`, `transcript`, `review`, `decision-log`
|
||||
- status: `draft`, `completed`, `follow-up`
|
||||
- technology/tool names in lowercase slug form when clearly central
|
||||
|
||||
Tag rules:
|
||||
- Use lowercase kebab-case only.
|
||||
- Prefer specific tags over generic ones.
|
||||
- Do not add unsupported tags.
|
||||
|
||||
## References capture
|
||||
|
||||
In the `References` section, capture conversation-specific source material that was explicitly mentioned or used, such as:
|
||||
- local file paths
|
||||
- URLs
|
||||
- repository paths
|
||||
- document names
|
||||
- tool names
|
||||
- named skills, scripts, or commands
|
||||
|
||||
Format each reference as one bullet with a short label and what it was used for.
|
||||
|
||||
If none were mentioned, write:
|
||||
- `- No explicit references or source artifacts captured.`
|
||||
|
||||
## Decisions capture
|
||||
|
||||
Capture only explicit decisions. Include items such as:
|
||||
- accepted approach
|
||||
- rejected alternative
|
||||
- agreed file location
|
||||
- approved implementation direction
|
||||
- confirmed formatting preference
|
||||
|
||||
If none were made, write:
|
||||
- `- No explicit decisions captured.`
|
||||
|
||||
## Transcript rules
|
||||
|
||||
- Save transcript in a separate file named `{{title}}_transcript.md`.
|
||||
- Keep chronological order.
|
||||
- Redact likely secrets or credentials.
|
||||
- Add `[Transcript may be partial]` at the top if the available transcript is incomplete.
|
||||
- Do not inline the full transcript inside the summary note.
|
||||
|
||||
## Summary note template
|
||||
|
||||
```md
|
||||
```yaml
|
||||
---
|
||||
id: {{title}}
|
||||
aliases:
|
||||
- {{descriptor}}
|
||||
- {{short human title}}
|
||||
tags:
|
||||
- ai-summary
|
||||
- {{tag1}}
|
||||
- {{tag2}}
|
||||
- {{tag3}}
|
||||
created: {{ISO-8601 timestamp}}
|
||||
source: current-ai-conversation
|
||||
conversation_type: {{research|coding|planning|review|general}}
|
||||
status: {{draft|completed|follow-up}}
|
||||
type: Conversation Report # report: Conversation Report, transcript: Conversation Transcript
|
||||
title: <descriptive title>
|
||||
description: <one-line summary>
|
||||
tags: [conversation, <topic>]
|
||||
timestamp: <ISO 8601 datetime>
|
||||
id: <unique-slug>
|
||||
aliases: []
|
||||
area: <derived from conversation>
|
||||
project: ''
|
||||
---
|
||||
|
||||
# {{short human title}}
|
||||
|
||||
> [!abstract]
|
||||
> **Created:** {{ISO-8601 timestamp}}
|
||||
> **Source:** Current AI conversation
|
||||
> **Main Topic:** {{one sentence, 8-20 words}}
|
||||
|
||||
## Research Question / Objective
|
||||
{{One concise sentence. If none: `Not explicitly provided.`}}
|
||||
|
||||
## Summary
|
||||
{{3-5 sentences synthesizing the main discussion, findings, constraints, and outcome. If none: `No meaningful summary could be derived beyond the limited conversation context.`}}
|
||||
|
||||
## Key Points
|
||||
- {{3-7 concrete bullets}}
|
||||
|
||||
## Decisions Made
|
||||
- {{explicit decision}}
|
||||
|
||||
## Action Items
|
||||
- [ ] {{explicit next step}}
|
||||
|
||||
## Limitations / Unresolved Assumptions
|
||||
- {{limitation or assumption}}
|
||||
|
||||
## Open Questions
|
||||
- {{unresolved question}}
|
||||
|
||||
## References
|
||||
- {{reference label}} — {{why it mattered}}
|
||||
|
||||
## Related Notes
|
||||
- Transcript: [[{{title}}_transcript]]
|
||||
- Markdown link: [{{title}}_transcript.md]({{title}}_transcript.md)
|
||||
|
||||
## Tags
|
||||
#ai-summary {{inline_tags}}
|
||||
```
|
||||
|
||||
## Transcript template
|
||||
## Bundle Awareness
|
||||
|
||||
```md
|
||||
[Transcript may be partial]
|
||||
`AI Conversation Summaries/` is an OKF bundle. After writing each new report/transcript pair:
|
||||
|
||||
# {{short human title}} — Transcript
|
||||
1. Update `AI Conversation Summaries/index.md` — add an entry for the new report and transcript.
|
||||
2. Append an entry to `AI Conversation Summaries/log.md` noting the addition.
|
||||
|
||||
**Summary Note:** [[{{title}}]]
|
||||
**Created:** {{ISO-8601 timestamp}}
|
||||
## Transcript
|
||||
|
||||
{{verbatim conversation transcript with likely secrets redacted}}
|
||||
```
|
||||
Save the raw conversation as a separate Markdown note alongside the report. Keep it chronological with no editorializing. Redact likely credentials, secrets, tokens, and private keys.
|
||||
|
||||
If the transcript is complete, omit the `[Transcript may be partial]` line.
|
||||
## Location
|
||||
|
||||
## Fallback lines
|
||||
Save both files to `AI Conversation Summaries/` under your vault root. Create the directory if needed. Never overwrite existing notes — if a filename collides, append a numeric suffix.
|
||||
|
||||
Use these exact fallbacks when needed:
|
||||
- Decisions Made: `- No explicit decisions captured.`
|
||||
- Action Items: `- [ ] No explicit action items identified.`
|
||||
- Limitations / Unresolved Assumptions: `- No limitations or unresolved assumptions captured.`
|
||||
- Open Questions: `- No open questions identified.`
|
||||
- References: `- No explicit references or source artifacts captured.`
|
||||
## Filenames
|
||||
|
||||
## File paths
|
||||
- Report: `YYYY-MM-DD_HH-mm_<topic-slug>.md`
|
||||
- Transcript: `YYYY-MM-DD_HH-mm_<topic-slug>_transcript.md`
|
||||
|
||||
- Summary: `AI Conversation Summaries/{{title}}.md`
|
||||
- Transcript: `AI Conversation Summaries/{{title}}_transcript.md`
|
||||
## Links
|
||||
|
||||
## Safety rules
|
||||
Use standard markdown links: `[text](relative/path.md)`. Do NOT use `[[wikilinks]]`.
|
||||
|
||||
- Never overwrite existing notes.
|
||||
- Never fabricate transcript lines.
|
||||
- Never fabricate references or decisions.
|
||||
- Redact likely credentials, secrets, tokens, and private keys.
|
||||
- If writing fails, return the full markdown for both files plus intended paths.
|
||||
- If required context is unavailable, state that clearly in the note instead of guessing.
|
||||
Link the report to its transcript and vice versa using filenames:
|
||||
- In report: `[transcript](YYYY-MM-DD_HH-mm_<topic-slug>_transcript.md)`
|
||||
- In transcript: `[report](YYYY-MM-DD_HH-mm_<topic-slug>.md)`
|
||||
|
||||
## Final response format
|
||||
## Safety
|
||||
|
||||
Confirm success with:
|
||||
- summary file path
|
||||
- transcript file path
|
||||
- generated title
|
||||
- number of action items captured
|
||||
- tags generated
|
||||
- number of references captured
|
||||
- number of explicit decisions captured
|
||||
- Use only facts from the conversation. Do not invent references, decisions, or conclusions.
|
||||
- Redact likely credentials, secrets, tokens, and private keys from any inline content.
|
||||
|
||||
## Done
|
||||
|
||||
Confirm both file paths and a one-sentence description of what the report covers. Also confirm that `AI Conversation Summaries/index.md` and `log.md` were updated.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Conversation Summary"
|
||||
short_description: "Summarize the conversation"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
+38
-90
@@ -1,119 +1,67 @@
|
||||
---
|
||||
name: crit
|
||||
description: brainstorm with AI using the CRIT framework to generate and evaluate ideas.
|
||||
description: Run the CRIT framework — give the AI Context, assign it a Role, let it Interview you one question at a time, then issue the Task.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
This skill implements the CRIT (Context-Request-Input-Tone) framework for structured brainstorming and idea evaluation. It transforms abstract discussions into concrete, actionable outcomes through systematic analysis and iterative refinement.
|
||||
CRIT is a four-step prompt framework by Geoff Woods: **Context, Role, Interview,
|
||||
Task**. The sequence front-loads thinking before execution — the AI learns your
|
||||
world, takes a specific lens, interviews you to surface what matters, then acts.
|
||||
|
||||
## Core Workflow
|
||||
The insight: most people skip to Task and get generic output. The Interview
|
||||
step — one question at a time, max three — is where the signal lives.
|
||||
|
||||
### Step 1: Context Analysis
|
||||
Before generating ideas, establish the foundation:
|
||||
## Steps
|
||||
|
||||
1. **Identify the Core Domain**
|
||||
- What field, industry, or subject area?
|
||||
- What are the key constraints (time, resources, technical limitations)?
|
||||
- Who is the target audience and their expertise level?
|
||||
Run these four steps in order when the user invokes `/crit`.
|
||||
|
||||
2. **Assess Current State**
|
||||
- What problems or opportunities exist?
|
||||
- What has been tried before (if applicable)?
|
||||
- What resources are available?
|
||||
### 1. Context — Give the AI your world
|
||||
|
||||
**Completion Criterion**: Clear understanding of domain, constraints, and current state documented.
|
||||
Ask the user: "What should I know about you, your goals, your audience, and any
|
||||
constraints?"
|
||||
|
||||
### Step 2: Request Clarification
|
||||
Structure the brainstorming request:
|
||||
Capture the answer in one paragraph. More detail is better.
|
||||
|
||||
1. **Define the Specific Goal**
|
||||
- What concrete outcome do you want?
|
||||
- What success criteria will be used?
|
||||
- What is the expected timeline?
|
||||
**Completion criterion**: One paragraph covering identity, goal, audience, and
|
||||
constraints — confirmed by the user.
|
||||
|
||||
2. **Gather Input Requirements**
|
||||
- What information is needed to proceed?
|
||||
- What assumptions should be validated?
|
||||
- What data or resources are required?
|
||||
### 2. Role — Assign a viewpoint
|
||||
|
||||
**Completion Criterion**: Specific goal and input requirements clearly defined.
|
||||
Ask the user: "What role should I take?"
|
||||
|
||||
### Step 3: Idea Generation
|
||||
Generate diverse, high-quality ideas:
|
||||
Guide toward a specific lens — "strategy coach who uncovers blind spots,"
|
||||
"editor who cuts fluff," "architect who finds leverage points." Not "be
|
||||
helpful."
|
||||
|
||||
1. **Divergent Thinking Phase**
|
||||
- Generate 5-10 initial concepts without judgment
|
||||
- Apply different perspectives (technical, business, user experience)
|
||||
- Include both obvious and unconventional options
|
||||
**Completion criterion**: A single sentence assigning a named role that implies
|
||||
a specific viewpoint.
|
||||
|
||||
2. **Convergent Analysis Phase**
|
||||
- Evaluate each idea against success criteria
|
||||
- Score ideas on feasibility, impact, and alignment
|
||||
- Identify patterns and synergies between ideas
|
||||
### 3. Interview — One question at a time
|
||||
|
||||
**Completion Criterion**: Minimum 5 distinct ideas generated and evaluated with scores.
|
||||
Instruct yourself: "Ask me no more than three questions, one at a time, to
|
||||
clarify what I'm trying to achieve."
|
||||
|
||||
### Step 4: Iterative Refinement
|
||||
Improve selected ideas:
|
||||
Ask one question. Wait for the answer. Then ask the next. Max three. Do not
|
||||
batch them.
|
||||
|
||||
1. **Select Top Candidates**
|
||||
- Choose 2-3 ideas with highest potential
|
||||
- Detail implementation approach for each
|
||||
- Identify risks and mitigation strategies
|
||||
This step forces the user to slow down and think, and teaches the AI what
|
||||
actually matters.
|
||||
|
||||
2. **Develop Action Plans**
|
||||
- Break down into concrete steps
|
||||
- Assign priorities and dependencies
|
||||
- Define success metrics and checkpoints
|
||||
**Completion criterion**: 1-3 questions asked and answered, one at a time.
|
||||
Stop asking when the user signals readiness or you've asked three.
|
||||
|
||||
**Completion Criterion**: 2-3 refined ideas with detailed action plans.
|
||||
### 4. Task — Issue the assignment
|
||||
|
||||
### Step 5: Tone & Delivery
|
||||
Adapt communication to the audience:
|
||||
Ask the user: "What's the task?"
|
||||
|
||||
1. **Choose Appropriate Role**
|
||||
- Subject Matter Expert for technical depth
|
||||
- Consultant for strategic guidance
|
||||
- Teacher for complex concepts
|
||||
- Collaborator for co-creation
|
||||
- Analyst for multi-perspective evaluation
|
||||
Guide toward a short, clear, slightly uncomfortable prompt that asks the AI to
|
||||
*think*, not just write. Reference the preceding interview.
|
||||
|
||||
2. **Structure Response**
|
||||
- Lead with clear, actionable solutions
|
||||
- Organize information logically and concisely
|
||||
- Include examples or analogies for clarity
|
||||
- Suggest next steps and follow-up questions
|
||||
> "Based on our conversation, give me three non-obvious actions I can take.
|
||||
> Make them surprising but realistic."
|
||||
|
||||
**Completion Criterion**: Response delivered in appropriate tone with clear structure.
|
||||
|
||||
## Quality Guardrails
|
||||
|
||||
- Never generate ideas without clear context and goals
|
||||
- Always evaluate ideas against defined success criteria
|
||||
- Maintain transparency about assumptions and limitations
|
||||
- Encourage iteration and collaboration throughout the process
|
||||
- Ensure all recommendations are practical and actionable
|
||||
|
||||
## Trigger Hints
|
||||
|
||||
Load this skill when the user asks to:
|
||||
- Generate ideas for a specific problem or opportunity
|
||||
- Brainstorm solutions with structured evaluation
|
||||
- Get expert guidance through systematic analysis
|
||||
- Develop actionable plans from abstract concepts
|
||||
- Evaluate multiple approaches to a challenge
|
||||
|
||||
## Output Format
|
||||
|
||||
Return results in this structure:
|
||||
|
||||
1. **Context Analysis**: Documented understanding of domain and constraints
|
||||
2. **Request Clarification**: Defined goals and input requirements
|
||||
3. **Idea Generation**: List of ideas with evaluation scores
|
||||
4. **Refined Solutions**: Detailed action plans for top candidates
|
||||
5. **Next Steps**: Suggested follow-up actions and questions
|
||||
|
||||
Each section should be clearly labeled and include completion criteria verification.
|
||||
Execute the task.
|
||||
|
||||
**Completion criterion**: Task executed and result delivered to the user.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Context Role Interview Task"
|
||||
short_description: "CRIT framework is a structured prompting and interaction methodology"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,65 +1,45 @@
|
||||
---
|
||||
name: pkm-curation
|
||||
description: Curate an Obsidian-style personal knowledge vault by classifying notes, normalizing frontmatter, improving structure, extracting atomic notes, and adding meaningful wikilinks.
|
||||
description: Curate an Obsidian vault — classify notes, normalize frontmatter, add links, extract atomic notes. Use when curating, batch-processing, reviewing, or doing a serendipity pick.
|
||||
---
|
||||
|
||||
# PKM Curation
|
||||
|
||||
Use this skill when working inside a Markdown-first vault that values curation over collection.
|
||||
Use this skill when working inside a Markdown-first vault that follows the [Open Knowledge Format (OKF) v0.1](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md) conventions.
|
||||
|
||||
## Goals
|
||||
|
||||
- Turn raw notes into clear, reusable notes.
|
||||
- Turn raw notes into reusable atomic notes.
|
||||
- Keep new notes consistent with vault conventions.
|
||||
- Strengthen the link graph with meaningful `[[wikilinks]]` syntax `[[Note Title]]`.
|
||||
- Strengthen the link graph with meaningful markdown links.
|
||||
- Extract atomic notes from long or mixed-topic notes.
|
||||
- Avoid unnecessary reorganization and weak links.
|
||||
- Maintain bundle integrity: update index.md and log.md when adding or changing bundle contents.
|
||||
|
||||
## Read This First
|
||||
|
||||
- Read `AGENTS.md` in the current repo scope before editing notes.
|
||||
- Read `references/vault-conventions.md` when normalizing metadata, deciding note types, or choosing folders.
|
||||
- Read `references/vault-conventions.md` when normalizing metadata, deciding note types, or choosing folders. This reference now documents the merged OKF + vault frontmatter schema.
|
||||
- Read `references/agent-integration.md` when running this skill through an agent.
|
||||
- Keep changes small and reviewable.
|
||||
- Keep file operations local to the vault unless the user explicitly asks otherwise.
|
||||
|
||||
## Workflows
|
||||
|
||||
## Search for notes
|
||||
## Search
|
||||
|
||||
```bash
|
||||
# Search by filename
|
||||
fd --type f ".md" "path/to/obsidian-vault" | rg -i "keyword"
|
||||
|
||||
# Search by content
|
||||
rg -l "keyword" "path/to/obsidian-vault" --include "*.md"
|
||||
rg -l "keyword" "path/to/obsidian-vault" -g "*.md"
|
||||
|
||||
# Find backlinks to a note (markdown link form)
|
||||
rg -l "Note Title" "path/to/obsidian-vault" -g "*.md"
|
||||
|
||||
# Find bundle index files
|
||||
fd "index.md" "path/to/obsidian-vault"
|
||||
```
|
||||
|
||||
## Curate existing note
|
||||
1. Inspect the target note or note set.
|
||||
2. Identify the note type: inbox, source, atomic, project, daily, or reference.
|
||||
3. Normalize frontmatter and basic structure.
|
||||
4. Clarify the title if the current one is vague or timestamp-like.
|
||||
5. Summarize or distill the note if it mixes too many ideas.
|
||||
6. Add or suggest meaningful `[[wikilinks]]` to related notes.
|
||||
7. If a note contains multiple durable ideas, extract 1-3 atomic notes.
|
||||
8. Suggest moving the note only if the destination is clearly better.
|
||||
|
||||
## Find related notes
|
||||
|
||||
Search for `[[Note Title]]` across the vault to find backlinks:
|
||||
|
||||
```bash
|
||||
rg -l "\\[\\[Note Title\\]\\]" "path/to/obsidian-vault" --include "*.md"
|
||||
```
|
||||
|
||||
### Find index notes
|
||||
|
||||
```bash
|
||||
fd --type f "Index" "path/to/obsidian-vault"
|
||||
```
|
||||
|
||||
## Operating Rules
|
||||
## Rules
|
||||
|
||||
- Prefer curation over reorganization.
|
||||
- Do not move, rename, or delete many notes at once unless the user asks.
|
||||
@@ -67,60 +47,85 @@ fd --type f "Index" "path/to/obsidian-vault"
|
||||
- Preserve the user's voice unless the user asks for a rewrite.
|
||||
- Keep source material and evergreen ideas separate when possible.
|
||||
- Treat `Inbox/` as temporary capture, not long-term storage.
|
||||
- Preserve all command blocks, code snippets, configuration directives, and
|
||||
step-by-step instructions verbatim. Do not summarize or condense them.
|
||||
- For reference/source notes: add a brief overview at the top, but keep the
|
||||
original commands and details intact below. Completeness > brevity.
|
||||
- Preserve all command blocks, code snippets, configuration directives, and step-by-step instructions verbatim. Do not summarize or condense them.
|
||||
- For reference/source notes: add a brief overview at the top, but keep the original commands and details intact below. Completeness > brevity.
|
||||
- Read every file completely before moving, renaming, or modifying it. Do not rely on head/tail, heading-only scans, or partial reads to judge a file's content.
|
||||
- **Bundle awareness**: When creating or editing notes inside a bundle directory, update the bundle's `index.md` (add or update the entry) and append an entry to `log.md`.
|
||||
|
||||
## Note-Type Heuristics
|
||||
|
||||
### Inbox note
|
||||
Use the type vocabulary from `references/vault-conventions.md`. When inferring a type for a note, use these heuristics:
|
||||
|
||||
### Inbox note → `type: Inbox`
|
||||
Use when the note is raw capture, partial thinking, copied text, or an unprocessed link dump.
|
||||
- Actions: clean obvious structure issues, add frontmatter if missing, classify for later promotion, avoid over-polishing unless requested.
|
||||
|
||||
Actions:
|
||||
- clean obvious structure issues
|
||||
- add frontmatter if missing
|
||||
- classify for later promotion
|
||||
- avoid over-polishing unless requested
|
||||
|
||||
### Source note
|
||||
|
||||
### Source note → `type: Source`
|
||||
Use when the note is based on an article, video, book, paper, transcript, or other external material.
|
||||
- Actions: keep source context intact, summarize key takeaways, extract reusable ideas into separate atomic notes, link to related concepts and projects.
|
||||
|
||||
Actions:
|
||||
- keep source context intact
|
||||
- summarize key takeaways
|
||||
- extract reusable ideas into separate atomic notes
|
||||
- link to related concepts and projects
|
||||
|
||||
### Atomic note
|
||||
|
||||
### Atomic note → `type: Concept`
|
||||
Use when the note captures one durable idea, concept, claim, pattern, or insight.
|
||||
- Actions: ensure one main idea per note, make the title concept-focused, add links to neighboring ideas, keep it concise and self-contained.
|
||||
|
||||
Actions:
|
||||
- ensure one main idea per note
|
||||
- make the title concept-focused
|
||||
- add links to neighboring ideas
|
||||
- keep it concise and self-contained
|
||||
|
||||
### Project note
|
||||
|
||||
### Project note → `type: Project`
|
||||
Use when the note supports active work, planning, resources, decisions, or tasks.
|
||||
- Actions: preserve project context, link tasks to the project note, avoid turning active project logistics into evergreen notes unless there is a reusable insight.
|
||||
|
||||
Actions:
|
||||
- preserve project context
|
||||
- link tasks to the project note
|
||||
- avoid turning active project logistics into evergreen notes unless there is a reusable insight
|
||||
|
||||
### Daily note
|
||||
|
||||
### Daily note → `type: Daily`
|
||||
Use when the note is date-based and captures activity, learning, tasks, or reflection for a single day.
|
||||
- Actions: preserve chronology, link out to durable notes rather than stuffing ideas into the daily note.
|
||||
|
||||
Actions:
|
||||
- preserve chronology
|
||||
- link out to durable notes rather than stuffing ideas into the daily note
|
||||
### Reference note → `type: Reference`
|
||||
Use when the note is lookup material, documentation, specs, or external reference.
|
||||
- Actions: preserve the reference content, add structured overview at top, link to related notes.
|
||||
|
||||
## Frontmatter Normalization
|
||||
|
||||
All notes should have the merged OKF + vault frontmatter schema:
|
||||
|
||||
```yaml
|
||||
---
|
||||
type: <OKF type name> # OKF required
|
||||
title: <display name> # OKF recommended
|
||||
description: <one-line summary> # OKF recommended
|
||||
resource: <canonical URI> # OKF recommended (when applicable)
|
||||
tags: [<tag>, ...] # OKF recommended + vault required
|
||||
timestamp: <ISO 8601 datetime> # OKF recommended
|
||||
id: <unique identifier> # vault required
|
||||
aliases: [<alias>, ...] # vault required
|
||||
area: <area/domain> # vault required
|
||||
project: <project name or ''> # vault required
|
||||
---
|
||||
```
|
||||
|
||||
When normalizing existing frontmatter:
|
||||
- Add `type` using the classification heuristics above.
|
||||
- Ensure `id` is a stable kebab-case slug.
|
||||
- Ensure `timestamp` is ISO 8601 format.
|
||||
- Ensure `project` is a plain string, not a `[[wikilink]]`.
|
||||
- Preserve any additional OKF extension keys.
|
||||
|
||||
## Link Convention
|
||||
|
||||
Use **standard markdown links**: `[text](relative/path.md)`. Do NOT use `[[wikilinks]]`.
|
||||
|
||||
When converting existing wikilinks:
|
||||
- `[[Note Title]]` → `[Note Title](Note%20Title.md)`
|
||||
- `[[Note Title|alias]]` → `[alias](Note%20Title.md)`
|
||||
|
||||
Use vault-relative paths from the linking file to the target.
|
||||
|
||||
## Bundle Awareness
|
||||
|
||||
When working inside a bundle directory:
|
||||
- **After creating a new note**: add an entry to the bundle's `index.md` and append an entry to `log.md`.
|
||||
- **After modifying an existing note**: if the change is significant, update the description in `index.md` and append an entry to `log.md`.
|
||||
- **After deleting or moving a note**: remove or update its entry in `index.md` and append an entry to `log.md`.
|
||||
|
||||
The bundle map is defined in `references/vault-conventions.md`. Key bundle directories include: `Knowledge/`, `Notes/` (with sub-bundles), `Resources/` (with sub-bundles), `Profiles/`, `AI Conversation Summaries/`, `Research/<packet>/`, `Projects/<project>/`.
|
||||
|
||||
Non-bundle directories (`Inbox/`, `Dailies/`, `Templates/`, `Clippings/`) do not get `index.md` or `log.md`.
|
||||
|
||||
## Linking Guidance
|
||||
|
||||
@@ -143,22 +148,24 @@ Extract atomic notes when a note contains:
|
||||
- a reusable method, distinction, or definition
|
||||
- a concept that should be linked from many places
|
||||
|
||||
Keep extracted notes short. One note, one idea.
|
||||
Keep extracted notes short. One note, one idea. When extracting into a bundle directory, update `index.md` and `log.md`.
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Curate one note
|
||||
|
||||
- inspect the note
|
||||
- identify note type
|
||||
- normalize frontmatter
|
||||
- tighten headings and summary
|
||||
- add a few strong links
|
||||
- suggest extracted atomic notes if warranted
|
||||
- locate the target file in the vault
|
||||
- inspect nearby related notes before adding links
|
||||
- patch the note in place
|
||||
- return a short summary of edits and suggested follow-up notes
|
||||
1. Inspect the target note and locate its file in the vault.
|
||||
2. Identify the note type: inbox, source, concept, project, daily, reference, or person.
|
||||
3. Normalize frontmatter and basic structure using the merged schema.
|
||||
4. Clarify the title if vague or timestamp-like.
|
||||
5. Tighten headings and summary; distill if it mixes too many ideas.
|
||||
6. Inspect nearby related notes, then add a few strong markdown links.
|
||||
7. If the note contains multiple durable ideas, extract 1-3 atomic notes.
|
||||
8. Suggest moving only if the destination is clearly better.
|
||||
9. If inside a bundle, update `index.md` and `log.md`.
|
||||
10. Patch the note in place and return a short summary of edits.
|
||||
|
||||
**Completion Criterion**: Note inspected, classified, normalized, linked, and patched, with a summary returned to the user.
|
||||
|
||||
### Curate an inbox batch
|
||||
|
||||
@@ -171,6 +178,8 @@ Keep extracted notes short. One note, one idea.
|
||||
- process them one at a time
|
||||
- stop and summarize after each batch
|
||||
|
||||
**Completion Criterion**: Each note in the batch classified and normalized, with a summary returned after the batch.
|
||||
|
||||
### Review recent notes
|
||||
|
||||
- inspect recently edited notes
|
||||
@@ -180,6 +189,8 @@ Keep extracted notes short. One note, one idea.
|
||||
- search by recent filenames or recent folders when file metadata is available
|
||||
- keep edits conservative and return a review summary
|
||||
|
||||
**Completion Criterion**: Recently edited notes inspected, missing links and mixed concerns flagged, with a review summary returned.
|
||||
|
||||
### Serendipity review
|
||||
|
||||
- choose a note from the vault
|
||||
@@ -189,6 +200,8 @@ Keep extracted notes short. One note, one idea.
|
||||
- pick one note from a user-specified folder or from curated folders only
|
||||
- avoid randomizing across obviously raw capture unless the user asks for that
|
||||
|
||||
**Completion Criterion**: One curated note chosen, briefly summarized, compared to active topic, with only high-confidence connections suggested.
|
||||
|
||||
## Output Style
|
||||
|
||||
When responding to the user:
|
||||
@@ -201,6 +214,7 @@ When responding to the user:
|
||||
- name the file or files touched
|
||||
- separate completed edits from suggested next actions
|
||||
- call out anything that still needs user confirmation
|
||||
- mention any bundle index/log updates made
|
||||
|
||||
## If You Need More Context
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Personal Knowledge Management Curation"
|
||||
short_description: "Curate and manage personal knowledge for the agent"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -5,15 +5,16 @@ aliases:
|
||||
tags:
|
||||
- knowledge-management
|
||||
- reference
|
||||
- okf
|
||||
area: Personal Knowledge Management
|
||||
project:
|
||||
project: ''
|
||||
---
|
||||
|
||||
# Agent Integration
|
||||
|
||||
## Purpose
|
||||
|
||||
This skill should be usable from any agent, and it should also fit chat environments where the agent as tool access.
|
||||
This skill should be usable from any agent, and it should also fit chat environments where the agent has tool access.
|
||||
|
||||
## Recommended Role
|
||||
|
||||
@@ -21,6 +22,14 @@ This skill should be usable from any agent, and it should also fit chat environm
|
||||
- interactive note refinement, serendipity review, and exploratory linking sessions
|
||||
- editor-side entry point that delegates vault actions to agent where possible
|
||||
|
||||
## Bundle Awareness
|
||||
|
||||
The vault follows OKF v0.1 bundle conventions. When running curation tasks:
|
||||
|
||||
- Creating a note inside a bundle directory: add an entry to `index.md` and an event to `log.md`.
|
||||
- Modifying a note inside a bundle: update `index.md` description if the note's purpose changes; append to `log.md` for significant changes.
|
||||
- Moving notes between bundle directories: update both source and destination bundle files.
|
||||
|
||||
## Preferred Behaviors
|
||||
|
||||
- search the vault before proposing links
|
||||
@@ -29,14 +38,16 @@ This skill should be usable from any agent, and it should also fit chat environm
|
||||
- summarize edits in plain Markdown
|
||||
- ask before removing any content or links from a note
|
||||
- ask before moving, renaming, or creating many files
|
||||
- update bundle `index.md` and `log.md` when creating notes inside bundles
|
||||
|
||||
## Good Task Shapes
|
||||
|
||||
- curate a specific note
|
||||
- process a small `Inbox/` batch
|
||||
- curate a specific note (normalize frontmatter, classify type, add markdown links)
|
||||
- process a small `Inbox/` batch (classify and normalize)
|
||||
- review recent notes for missing links
|
||||
- extract atomic notes from one source note
|
||||
- run a serendipity review against a current topic
|
||||
- update or regenerate bundle `index.md` for a directory
|
||||
|
||||
## Avoid
|
||||
|
||||
@@ -44,8 +55,10 @@ This skill should be usable from any agent, and it should also fit chat environm
|
||||
- broad speculative linking passes
|
||||
- converting every long note into atomic notes
|
||||
- changing note titles without stating why
|
||||
- using `[[wikilinks]]` — always use standard markdown `[...](...)` links
|
||||
|
||||
## Portability Guidance
|
||||
|
||||
- keep the workflow in `SKILL.md` tool-agnostic where possible
|
||||
- prefer small deterministic file edits so the skill remains portable to other `SKILL.md`-based agents
|
||||
- always reference `vault-conventions.md` for the current frontmatter schema and type vocabulary
|
||||
|
||||
@@ -6,6 +6,7 @@ tags:
|
||||
- knowledge-management
|
||||
- obsidian
|
||||
- reference
|
||||
- okf
|
||||
- ai
|
||||
area: Personal Knowledge Management
|
||||
project:
|
||||
@@ -13,45 +14,164 @@ project:
|
||||
|
||||
# Vault Conventions
|
||||
|
||||
## Required Frontmatter
|
||||
This vault follows the [Open Knowledge Format (OKF) v0.1](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md) as its structural backbone. All notes, bundles, and links conform to OKF conventions unless noted below.
|
||||
|
||||
All notes should include YAML frontmatter with:
|
||||
---
|
||||
|
||||
## Frontmatter Schema (merged — OKF + vault fields)
|
||||
|
||||
Every non-reserved `.md` file **MUST** have YAML frontmatter with the following fields:
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: unique-id
|
||||
aliases: []
|
||||
tags: []
|
||||
area: Primary area/domain
|
||||
project: [[Project Note]]
|
||||
type: <OKF type name> # OKF required
|
||||
title: <display name> # OKF recommended
|
||||
description: <one-line summary> # OKF recommended
|
||||
resource: <canonical URI> # OKF recommended (when applicable)
|
||||
tags: [<tag>, ...] # OKF recommended + vault required
|
||||
timestamp: <ISO 8601 datetime> # OKF recommended
|
||||
id: <unique identifier> # vault required
|
||||
aliases: [<alias>, ...] # vault required
|
||||
area: <area/domain> # vault required
|
||||
project: <project name or ''> # vault required
|
||||
---
|
||||
```
|
||||
|
||||
Use an empty value for `project:` when there is no relevant project note.
|
||||
### Field Notes
|
||||
|
||||
## Core Principles
|
||||
- **`type`** — Must be one of the OKF type names from the Type Vocabulary table below. This is the **only** required OKF field.
|
||||
- **`title`** — Human-readable display name. If omitted, consumers may derive a title from the filename.
|
||||
- **`description`** — One-line summary. Used by index.md generators, search snippets, and previews.
|
||||
- **`resource`** — A URI identifying the underlying asset the concept describes. Omit for abstract ideas.
|
||||
- **`tags`** — YAML list of short strings for cross-cutting categorization. Always include at least one tag.
|
||||
- **`timestamp`** — ISO 8601 datetime of last meaningful change (e.g. `2026-07-06T12:00:00Z`).
|
||||
- **`id`** — Stable unique identifier (kebab-case slug, never changes).
|
||||
- **`aliases`** — List of alternative titles for search/discovery.
|
||||
- **`area`** — Primary domain or area of knowledge.
|
||||
- **`project`** — Associated project name, or empty string `''` when none.
|
||||
|
||||
- Markdown-first
|
||||
- explicit `[[wikilinks]]`
|
||||
- atomic notes for durable ideas
|
||||
- project, topic, and date-based organization
|
||||
- consistency over novelty
|
||||
**OKF extensions:** Any additional producer-defined keys may be included. Consumers MUST preserve unknown keys when round-tripping.
|
||||
|
||||
---
|
||||
|
||||
## Link Convention
|
||||
|
||||
Use **standard markdown links**: `[text](relative/path.md)`.
|
||||
|
||||
Do NOT use `[[wikilinks]]`. Obsidian renders standard markdown links identically to wikilinks, and markdown links are portable across all markdown renderers.
|
||||
|
||||
### Link Forms
|
||||
|
||||
| Target | Markdown form |
|
||||
|--------|---------------|
|
||||
| Another note in same directory | `[Note Title](Note%20Title.md)` |
|
||||
| Note in subdirectory | `[Note Title](../Subdir/Note%20Title.md)` |
|
||||
| Note with alias | `[alias](Note%20Title.md)` |
|
||||
| Absolute (bundle-relative) | `[Note Title](/path/from/bundle/root.md)` |
|
||||
| External URL | `[text](https://example.org)` |
|
||||
|
||||
### When to Link
|
||||
|
||||
Add links only when they express a real relationship:
|
||||
- concept to broader concept
|
||||
- source note to extracted idea
|
||||
- project note to relevant knowledge note
|
||||
- daily note to work done or ideas learned
|
||||
- sibling concepts that genuinely inform one another
|
||||
|
||||
Do not link on shared words alone.
|
||||
|
||||
---
|
||||
|
||||
## Bundle Definition
|
||||
|
||||
A **bundle** is a subdirectory that follows OKF progressive-disclosure conventions. Every bundle contains two reserved files:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `index.md` | OKF progressive-disclosure listing (no frontmatter, sections with bullet links) |
|
||||
| `log.md` | OKF chronological update history (datestamped entries, newest first) |
|
||||
|
||||
These filenames are **reserved** — they MUST NOT be used for concept documents.
|
||||
|
||||
### Bundle root (vault root)
|
||||
|
||||
`sjb-brain/` (the vault root) is a bundle. It has its own `index.md` and `log.md`.
|
||||
|
||||
### Bundle map
|
||||
|
||||
| Path | bundle? |
|
||||
|------|:-------:|
|
||||
| `sjb-brain/` (root) | ✅ |
|
||||
| `Knowledge/` | ✅ |
|
||||
| `Notes/` + subdirs | ✅ (each subdir is a sub-bundle) |
|
||||
| `Resources/` + subdirs | ✅ (each subdir is a sub-bundle) |
|
||||
| `Profiles/` | ✅ |
|
||||
| `AI Conversation Summaries/` | ✅ |
|
||||
| `Research/<packet>/` | ✅ (each packet is a bundle) |
|
||||
| `Projects/<project>/` | ✅ (each project is a bundle, migrate flat notes to dirs) |
|
||||
| `Inbox/` | ❌ |
|
||||
| `Dailies/` | ❌ |
|
||||
| `Templates/` | ❌ |
|
||||
| `Clippings/` | ❌ |
|
||||
| `Tasks/`, `tools/`, `wts-services/` | ⏳ deferred |
|
||||
|
||||
Non-bundle directories are treated as flat collections. They do not get `index.md` or `log.md`.
|
||||
|
||||
---
|
||||
|
||||
## Type Vocabulary
|
||||
|
||||
The following OKF type names are used in this vault. Every note MUST have exactly one `type` from this table.
|
||||
|
||||
| type | Purpose |
|
||||
|------|---------|
|
||||
| `Inbox` | Raw capture, unprocessed |
|
||||
| `Source` | Based on external material |
|
||||
| `Concept` | One durable evergreen idea |
|
||||
| `Project` | Active work, planning |
|
||||
| `Daily` | Day-specific activity |
|
||||
| `Reference` | Lookup material, specs |
|
||||
| `Conversation Report` | Narrative summary of conversation |
|
||||
| `Conversation Transcript` | Raw conversation transcript |
|
||||
| `Research Synthesis` | Compiled answer for a research topic |
|
||||
| `Research Claim Index` | Collection of evidence-backed claims |
|
||||
| `Research Source List` | Index of sources consulted |
|
||||
| `Research Glossary` | Term definitions for a topic |
|
||||
| `Research Questions` | Open and resolved questions |
|
||||
| `Research Log` | Chronological research session log |
|
||||
| `MOC` | Map of content / curated index |
|
||||
| `Template` | Template note for note creation |
|
||||
| `Person` | A person you want notes about |
|
||||
| `Tool` | A tool, app, or service |
|
||||
|
||||
---
|
||||
|
||||
## Folder Roles
|
||||
|
||||
- `Inbox/`: raw capture and unprocessed notes
|
||||
- `Dailies/`: day-specific notes and reflection
|
||||
- `Templates/`: note templates
|
||||
- `Knowledge/`: preferred home for evergreen atomic notes if the folder exists or is created
|
||||
- project/topic folders: active work and structured reference material
|
||||
- `Inbox/`: raw capture and unprocessed notes (not a bundle)
|
||||
- `Dailies/`: day-specific notes and reflection (not a bundle)
|
||||
- `Templates/`: note templates (not a bundle)
|
||||
- `Clippings/`: clipped web articles (not a bundle)
|
||||
- `Knowledge/`: preferred home for evergreen atomic notes (bundle)
|
||||
- `Notes/`: technical and domain notes, with subdirectories for each topic (bundle with sub-bundles)
|
||||
- `Resources/`: reference and resource notes, with subdirectories for each domain (bundle with sub-bundles)
|
||||
- `Profiles/`: notes about people (bundle)
|
||||
- `AI Conversation Summaries/`: saved conversation summaries and transcripts (bundle)
|
||||
- `Research/<packet>/`: research packets, each a self-contained bundle
|
||||
- `Projects/<project>/`: active project notes, each project a sub-bundle
|
||||
|
||||
---
|
||||
|
||||
## Task Conventions
|
||||
|
||||
- Use Markdown task items: `- [ ] Task description [[Project Name]]`
|
||||
- Use Markdown task items: `- [ ] Task description [Project Name](Projects/Project%20Name.md)`
|
||||
- Add status and priority tags where useful
|
||||
- Use `due:: YYYY-MM-DD` for due dates
|
||||
- Add `start::` and `end::` only when explicitly requested or when tracking active work
|
||||
|
||||
---
|
||||
|
||||
## Curation Heuristics
|
||||
|
||||
- A saved thing is not yet a knowledge note.
|
||||
@@ -59,3 +179,16 @@ Use an empty value for `project:` when there is no relevant project note.
|
||||
- A link should reflect a real conceptual or project relationship.
|
||||
- A long note may remain long if it is reference material; only extract notes when reuse is likely.
|
||||
- Prefer gradual improvement over mass refactoring.
|
||||
- When creating or editing notes inside a bundle directory, maintain the bundle's `index.md` (add/update entries).
|
||||
- When a bundle's contents change significantly, append an entry to the bundle's `log.md`.
|
||||
|
||||
---
|
||||
|
||||
## Core Principles
|
||||
|
||||
- Markdown-first
|
||||
- OKF-conformant bundles with `index.md` and `log.md`
|
||||
- Standard markdown `[...](...)` links (not wikilinks)
|
||||
- Atomic notes for durable ideas
|
||||
- Project, topic, and date-based organization
|
||||
- Consistency over novelty
|
||||
|
||||
@@ -1,16 +1,95 @@
|
||||
---
|
||||
name: research-vault
|
||||
description: Research a topic through a one-question-at-a-time learning conversation, answer directly, share resources when useful, and save a linked Obsidian research packet.
|
||||
description: Research a topic through a one-question-at-a-time learning conversation, answer directly, share resources when useful, and save a linked OKF-conformant research packet in the Obsidian vault.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Research Vault
|
||||
|
||||
Use when the user wants to learn or research a topic conversationally and save the outcome in an Obsidian vault.
|
||||
Use when the user wants to learn or research a topic conversationally and save the outcome in an Obsidian vault using the [Open Knowledge Format (OKF) v0.1](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md).
|
||||
|
||||
## Packet
|
||||
|
||||
Create one vault folder per research run, normally `Research/<YYYY-MM-DD> <Topic>/`, containing all notes from the run: `Index.md`, `Sources.md`, `Synthesis.md`, `Claims.md`, `Questions.md`, `Conversation.md`, `Glossaries.md`, `Log.md`, and any atomic notes. Treat the packet as a Karpathy-style compiled wiki for the topic: raw sources stay immutable, packet notes are the maintained synthesis layer, and this skill is the schema. Load `references/research-packet-template.md` before writing packet files or when exact structure matters.
|
||||
Create one vault folder per research run, normally `Research/<YYYY-MM-DD> <Topic>/`, containing an OKF bundle with `index.md` (progressive-disclosure listing, no frontmatter), `log.md` (chronological), and packet pages. Load `references/research-packet-template.md` before writing packet files or when exact structure matters.
|
||||
|
||||
### Bundle structure
|
||||
|
||||
```
|
||||
Research/<YYYY-MM-DD> <Topic>/
|
||||
├── index.md # OKF progressive-disclosure, no frontmatter
|
||||
├── log.md # OKF chronological, datestamped entries (newest first)
|
||||
├── Sources.md # Research Source List
|
||||
├── Synthesis.md # Research Synthesis
|
||||
├── Claims.md # Research Claim Index
|
||||
├── Questions.md # Research Questions
|
||||
├── Conversation.md # Conversation Report
|
||||
├── Glossaries.md # Research Glossary
|
||||
└── <atomic-note>.md # Concept / atomic notes
|
||||
```
|
||||
|
||||
### Reserved filenames
|
||||
|
||||
`index.md` and `log.md` are reserved — they MUST follow OKF format and MUST NOT be used for concept documents.
|
||||
|
||||
### Frontmatter schema (merged — OKF + vault fields)
|
||||
|
||||
Every concept document (non-reserved `.md` file) MUST have:
|
||||
|
||||
```yaml
|
||||
---
|
||||
type: <OKF type name> # OKF required
|
||||
title: <display name> # OKF recommended
|
||||
description: <one-line summary> # OKF recommended
|
||||
resource: <canonical URI> # OKF recommended (when applicable)
|
||||
tags: [<tag>, ...] # OKF recommended + vault required
|
||||
timestamp: <ISO 8601 datetime> # OKF recommended
|
||||
id: <unique identifier> # vault required
|
||||
aliases: [<alias>, ...] # vault required
|
||||
area: <area/domain> # vault required
|
||||
project: <project name or ''> # vault required
|
||||
---
|
||||
```
|
||||
|
||||
Use the type vocabulary from `references/vault-conventions.md`.
|
||||
|
||||
### `index.md` (OKF progressive-disclosure)
|
||||
|
||||
No frontmatter. Sections with bullet links and descriptions:
|
||||
|
||||
```markdown
|
||||
# <Topic>
|
||||
|
||||
## Purpose
|
||||
- Learning goal:
|
||||
- Scope:
|
||||
- Success criteria:
|
||||
|
||||
## Packet Map
|
||||
- [Sources](Sources.md) — compiled source list
|
||||
- [Synthesis](Synthesis.md) — synthesized answer
|
||||
- [Claims](Claims.md) — evidence-backed claims
|
||||
- [Questions](Questions.md) — open and resolved questions
|
||||
- [Conversation](Conversation.md) — raw conversation record
|
||||
- [Glossaries](Glossaries.md) — term definitions
|
||||
- [Log](Log.md) — session log
|
||||
|
||||
## Compiled Pages
|
||||
| Page | Type | Purpose | Status |
|
||||
|---|---|---|---|
|
||||
| | | | draft \| reviewed \| promoted |
|
||||
```
|
||||
|
||||
### `log.md` (OKF chronological)
|
||||
|
||||
Datestamped entries, newest first:
|
||||
|
||||
```markdown
|
||||
# Log
|
||||
|
||||
## YYYY-MM-DD
|
||||
* **Event type**: Description of what happened.
|
||||
* **Files changed**: ...
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -18,12 +97,12 @@ Create one vault folder per research run, normally `Research/<YYYY-MM-DD> <Topic
|
||||
2. **Frame the goal.** Ask one question at a time until you can restate the topic, learning objective, scope, and success criteria. Discover the user's goal, prior knowledge, intended use, depth, constraints, output preference, and trusted/distrusted sources only as needed.
|
||||
3. **Create or defer the folder.** Create the packet folder once the topic has a stable working title; if still vague, keep conversing. Before writing durable notes, every artifact must have a path inside the packet folder.
|
||||
4. **Map context.** Search the vault for the topic, synonyms, projects, and neighboring concepts. Read enough relevant notes to make meaningful links, not keyword matches.
|
||||
5. **Ingest sources into claims.** For each source, preserve its path in `Sources.md`, extract evidence-backed claims into `Claims.md`, update affected concept/summary pages, and append an event to `Log.md`. Completion: every non-trivial factual claim has source, evidence, confidence, and at least one related link or explicit note that no link exists yet.
|
||||
5. **Ingest sources into claims.** For each source, preserve its path in `Sources.md`, extract evidence-backed claims into `Claims.md`, update affected concept/summary pages, and append an event to `log.md`. Completion: every non-trivial factual claim has source, evidence, confidence, and at least one related link or explicit note that no link exists yet.
|
||||
6. **Research and teach.** Answer the user directly from the compiled packet when possible, explain findings in small chunks, cite claims, preserve uncertainty, share resources when they improve learning or evidence quality, and correct misunderstandings before moving on.
|
||||
7. **File good answers.** When a query produces a durable synthesis, decision, comparison, or explanation, add it back into `Synthesis.md`, `Claims.md`, or an atomic note instead of leaving it only in chat. Append the query and filed destination to `Log.md`.
|
||||
7. **File good answers.** When a query produces a durable synthesis, decision, comparison, or explanation, add it back into `Synthesis.md`, `Claims.md`, or an atomic note instead of leaving it only in chat. Append the query and filed destination to `log.md`.
|
||||
8. **Check understanding and completeness.** Ask one diagnostic or completeness question per turn. Answer follow-ups with evidence, examples, counterarguments, implementation details, or resources as needed. Capture unresolved gaps in `Questions.md`.
|
||||
9. **Write incrementally.** For long sessions, update `Conversation.md`, `Questions.md`, `Glossaries.md`, `Sources.md`, and `Log.md` as the work progresses. By the end, no research, teaching insight, user question, glossary term, source, or durable knowledge should exist only in chat.
|
||||
10. **Lint the packet.** Before final report or promotion, check for unsupported claims, stale or contradictory claims, missing source links, orphan atomic notes, glossary terms without links, unanswered questions, and missing backlinks from `Index.md`. Record notable lint findings or fixes in `Log.md`.
|
||||
9. **Write incrementally.** For long sessions, update `Conversation.md`, `Questions.md`, `Glossaries.md`, `Sources.md`, and `log.md` as the work progresses. By the end, no research, teaching insight, user question, glossary term, source, or durable knowledge should exist only in chat.
|
||||
10. **Lint the packet.** Before final report or promotion, check for unsupported claims, stale or contradictory claims, missing source links, orphan atomic notes, glossary terms without links, unanswered questions, and missing backlinks from `index.md`. Record notable lint findings or fixes in `log.md`.
|
||||
11. **Promote reusable notes.** If a packet note becomes broadly useful beyond the run, propose promotion to the main vault, copy or move only with user approval, leave a provenance link in the packet, and update relevant indexes/MOCs.
|
||||
12. **Report.** Summarize the packet path, files changed, links or backlink suggestions, top takeaways, what the user now understands, remaining questions, lint status, and promotion candidates.
|
||||
|
||||
@@ -35,7 +114,7 @@ Create one vault folder per research run, normally `Research/<YYYY-MM-DD> <Topic
|
||||
- Use a teach-back loop for complex topics: plain-language explanation → concrete example → one check question or restatement prompt → correction → continue only when the user is satisfied or uncertainty is captured.
|
||||
- Ask checks before research, after initial framing, after major findings, and before finalizing notes.
|
||||
- When the user corrects you, record the correction in `Conversation.md`, update affected packet notes, and prefer the corrected framing unless later evidence contradicts it.
|
||||
- Record important answers and useful resources in `Conversation.md`; reflect scope-changing details in `Index.md`, `Sources.md`, or `Synthesis.md`.
|
||||
- Record important answers and useful resources in `Conversation.md`; reflect scope-changing details in `index.md`, `Sources.md`, or `Synthesis.md`.
|
||||
|
||||
## Capture Rules
|
||||
|
||||
@@ -49,18 +128,20 @@ Create one vault folder per research run, normally `Research/<YYYY-MM-DD> <Topic
|
||||
- Keep citations close to claims; mark confidence when evidence is incomplete or contested.
|
||||
- Preserve useful quotes verbatim with attribution.
|
||||
- Distinguish source claims from interpretation; record failed searches or missing evidence when they affect the conclusion.
|
||||
- Create atomic notes in the packet folder for durable concepts; link each from `Index.md` and back to its source or synthesis section.
|
||||
- Create atomic notes in the packet folder for durable concepts; link each from `index.md` and back to its source or synthesis section.
|
||||
|
||||
## Retrieval Checks
|
||||
|
||||
Run these checks before treating the packet as complete:
|
||||
|
||||
- Could a future agent answer the user's main question from `Index.md`, `Synthesis.md`, and `Claims.md` without rereading raw sources?
|
||||
- Does `Index.md` point to every durable packet page with enough context to choose the right page?
|
||||
- Could a future agent answer the user's main question from `index.md`, `Synthesis.md`, and `Claims.md` without rereading raw sources?
|
||||
- Does `index.md` point to every durable packet page with enough context to choose the right page?
|
||||
- Are important terms findable in `Glossaries.md` and linked from the pages that use them?
|
||||
- Are contradictions, uncertainty, and missing evidence visible near the relevant claims?
|
||||
- Does `Log.md` show the ingest/query/lint history well enough to reconstruct what changed and why?
|
||||
- Does `log.md` show the ingest/query/lint history well enough to reconstruct what changed and why?
|
||||
|
||||
## Linking Rules
|
||||
|
||||
Use Obsidian wikilinks: `[[Note Title]]` or `[[path/to/Note|alias]]`. Add links only when they explain context: broader concepts, projects, areas, MOCs, sources, authors, methods, tools, domains, supporting/refining/contradicting claims, or active problems. Do not link on shared words alone.
|
||||
Use standard markdown links: `[text](relative/path.md)`. Do NOT use `[[wikilinks]]`. Add links only when they explain context: broader concepts, projects, areas, MOCs, sources, authors, methods, tools, domains, supporting/refining/contradicting claims, or active problems. Do not link on shared words alone.
|
||||
|
||||
Use bundle-relative links for intra-packet references (`[Sources](Sources.md)`) and vault-relative or absolute paths for cross-bundle references.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Research Vault"
|
||||
short_description: "Store and retrieve research notes and documents"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,10 +1,27 @@
|
||||
# Research Packet Template
|
||||
# Research Packet Template (OKF v0.1)
|
||||
|
||||
Use inside `Research/<YYYY-MM-DD> <Topic>/` unless the vault has a clearer convention.
|
||||
|
||||
## Required files
|
||||
The packet is an OKF bundle. See [vault-conventions.md](../../pkm-curation/references/vault-conventions.md) for the full frontmatter schema and type vocabulary.
|
||||
|
||||
### `Index.md`
|
||||
## Bundle structure
|
||||
|
||||
```
|
||||
Research/<YYYY-MM-DD> <Topic>/
|
||||
├── index.md # OKF progressive-disclosure, no frontmatter
|
||||
├── log.md # OKF chronological, datestamped entries (newest first)
|
||||
├── Sources.md # Research Source List
|
||||
├── Synthesis.md # Research Synthesis
|
||||
├── Claims.md # Research Claim Index
|
||||
├── Questions.md # Research Questions
|
||||
├── Conversation.md # Conversation Report
|
||||
├── Glossaries.md # Research Glossary
|
||||
└── <atomic-note>.md # Concept
|
||||
```
|
||||
|
||||
### `index.md`
|
||||
|
||||
No frontmatter. Progressive-disclosure listing:
|
||||
|
||||
```markdown
|
||||
# <Topic>
|
||||
@@ -18,19 +35,19 @@ Use inside `Research/<YYYY-MM-DD> <Topic>/` unless the vault has a clearer conve
|
||||
-
|
||||
|
||||
## Packet Map
|
||||
- [[Sources]]
|
||||
- [[Synthesis]]
|
||||
- [[Claims]]
|
||||
- [[Questions]]
|
||||
- [[Conversation]]
|
||||
- [[Glossaries]]
|
||||
- [[Log]]
|
||||
- [Sources](Sources.md) — compiled source list
|
||||
- [Synthesis](Synthesis.md) — synthesized answer
|
||||
- [Claims](Claims.md) — evidence-backed claims
|
||||
- [Questions](Questions.md) — open and resolved questions
|
||||
- [Conversation](Conversation.md) — raw conversation record
|
||||
- [Glossaries](Glossaries.md) — term definitions
|
||||
- [Log](Log.md) — session log
|
||||
|
||||
## Related Vault Notes
|
||||
-
|
||||
|
||||
## Compiled Pages
|
||||
| Page | Type | One-line purpose | Source basis | Status |
|
||||
| Page | Type | Purpose | Source basis | Status |
|
||||
|---|---|---|---|---|
|
||||
| | source summary \| concept \| claim cluster \| synthesis \| promoted | | | draft \| reviewed \| promoted |
|
||||
|
||||
@@ -38,9 +55,33 @@ Use inside `Research/<YYYY-MM-DD> <Topic>/` unless the vault has a clearer conve
|
||||
-
|
||||
```
|
||||
|
||||
### `log.md`
|
||||
|
||||
Datestamped entries, newest first. No frontmatter required (concept notes in the bundle DO need frontmatter, but log.md follows OKF §7 format).
|
||||
|
||||
```markdown
|
||||
# Log
|
||||
|
||||
## YYYY-MM-DD
|
||||
* **Event**: Description of what changed.
|
||||
* **Files changed**: ...
|
||||
```
|
||||
|
||||
### `Sources.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: Research Source List
|
||||
title: <Topic> - Sources
|
||||
description: Compiled source list for <Topic>.
|
||||
tags: [research, sources]
|
||||
timestamp: <ISO 8601>
|
||||
id: <topic-slug>-sources
|
||||
aliases: []
|
||||
area: Research
|
||||
project: ''
|
||||
---
|
||||
|
||||
# Sources
|
||||
|
||||
| Source | Type | Why useful | Reliability notes | Accessed |
|
||||
@@ -60,6 +101,18 @@ Include source links, books, papers, docs, videos, examples, search terms, or re
|
||||
### `Synthesis.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: Research Synthesis
|
||||
title: <Topic> - Synthesis
|
||||
description: Compiled answer for <Topic> research.
|
||||
tags: [research, synthesis]
|
||||
timestamp: <ISO 8601>
|
||||
id: <topic-slug>-synthesis
|
||||
aliases: []
|
||||
area: Research
|
||||
project: ''
|
||||
---
|
||||
|
||||
# Synthesis
|
||||
|
||||
## Short Answer
|
||||
@@ -79,6 +132,18 @@ Include source links, books, papers, docs, videos, examples, search terms, or re
|
||||
### `Claims.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: Research Claim Index
|
||||
title: <Topic> - Claims
|
||||
description: Evidence-backed claims for <Topic> research.
|
||||
tags: [research, claims]
|
||||
timestamp: <ISO 8601>
|
||||
id: <topic-slug>-claims
|
||||
aliases: []
|
||||
area: Research
|
||||
project: ''
|
||||
---
|
||||
|
||||
# Claims
|
||||
|
||||
## Claim: <single answerable claim>
|
||||
@@ -96,6 +161,18 @@ Include source links, books, papers, docs, videos, examples, search terms, or re
|
||||
### `Questions.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: Research Questions
|
||||
title: <Topic> - Questions
|
||||
description: Open and resolved questions for <Topic> research.
|
||||
tags: [research, questions]
|
||||
timestamp: <ISO 8601>
|
||||
id: <topic-slug>-questions
|
||||
aliases: []
|
||||
area: Research
|
||||
project: ''
|
||||
---
|
||||
|
||||
# Questions
|
||||
|
||||
## User Questions
|
||||
@@ -114,6 +191,18 @@ Include source links, books, papers, docs, videos, examples, search terms, or re
|
||||
### `Conversation.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: Conversation Report
|
||||
title: <Topic> - Conversation
|
||||
description: Narrative summary of the research conversation for <Topic>.
|
||||
tags: [research, conversation]
|
||||
timestamp: <ISO 8601>
|
||||
id: <topic-slug>-conversation
|
||||
aliases: []
|
||||
area: Research
|
||||
project: ''
|
||||
---
|
||||
|
||||
# Conversation
|
||||
|
||||
## Learning Goal
|
||||
@@ -132,25 +221,21 @@ Include source links, books, papers, docs, videos, examples, search terms, or re
|
||||
|
||||
Record important answers, corrections, and scope decisions as the session progresses.
|
||||
|
||||
### `Log.md`
|
||||
|
||||
```markdown
|
||||
# Log
|
||||
|
||||
Append one entry for each ingest, query filed back into the packet, lint pass, promotion, or major correction.
|
||||
|
||||
## YYYY-MM-DD HH:MM — <ingest | query | lint | promotion | correction>
|
||||
- Input:
|
||||
- Files read:
|
||||
- Files changed:
|
||||
- Claims added or revised:
|
||||
- Links added:
|
||||
- Open issues:
|
||||
```
|
||||
|
||||
### `Glossaries.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: Research Glossary
|
||||
title: <Topic> - Glossary
|
||||
description: Term definitions for <Topic> research.
|
||||
tags: [research, glossary]
|
||||
timestamp: <ISO 8601>
|
||||
id: <topic-slug>-glossary
|
||||
aliases: []
|
||||
area: Research
|
||||
project: ''
|
||||
---
|
||||
|
||||
# Glossaries
|
||||
|
||||
## <Term or Acronym>
|
||||
@@ -167,12 +252,17 @@ Add every acronym, abbreviation, domain-specific phrase, specialized term, jargo
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: Concept
|
||||
title: <Concept Name>
|
||||
description: <One-line summary of the concept>.
|
||||
tags: [research, atomic-note, <topic-tag>]
|
||||
timestamp: <ISO 8601>
|
||||
id: <stable-id>
|
||||
aliases: []
|
||||
tags: [research, atomic-note]
|
||||
area: <area>
|
||||
project: [[<packet topic>]]
|
||||
project: <packet topic>
|
||||
---
|
||||
|
||||
# <Concept>
|
||||
|
||||
## Idea
|
||||
@@ -182,7 +272,7 @@ project: [[<packet topic>]]
|
||||
<Explain retrieval, decision, or learning value.>
|
||||
|
||||
## Evidence or source
|
||||
- Claim: [[Claims#Claim <anchor or short title>]]
|
||||
- Claim: [Claims](Claims.md)
|
||||
- Source:
|
||||
|
||||
## Links
|
||||
@@ -191,5 +281,9 @@ project: [[<packet topic>]]
|
||||
- Contrasts:
|
||||
|
||||
## Promotion status
|
||||
- Packet-local | candidate for main vault | promoted to [[path/to/promoted note]]
|
||||
- Packet-local | candidate for main vault | promoted to [path/to/promoted note](path/to/promoted.md)
|
||||
```
|
||||
|
||||
## Linking
|
||||
|
||||
Use standard markdown links `[text](relative/path.md)` throughout. Do NOT use `[[wikilinks]]`. Intra-packet links are bundle-relative (e.g., `[Claims](Claims.md)`). Cross-bundle links use vault-relative paths.
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
name: youtube-video-capture
|
||||
description: Fetch subtitles from a YouTube video, summarize the content, and save both the summary and raw subtitles to the Video bundle in the Obsidian vault. Use when the user wants to capture a YouTube video, mentions "summarize this video", "capture this talk", or pastes a YouTube URL wanting it saved to vault.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Capture a YouTube video into the Obsidian vault. Extract subtitles, produce a summary, and save both to the `Video/` bundle as OKF-conformant notes.
|
||||
|
||||
## Dependencies
|
||||
|
||||
`yt-dlp` must be installed. Check with `which yt-dlp`. If missing, tell the user to install it (`pip install yt-dlp` or `brew install yt-dlp`) and stop.
|
||||
|
||||
## Extract subtitles
|
||||
|
||||
Run `yt-dlp` to download auto-generated English subtitles as SRT:
|
||||
|
||||
```bash
|
||||
yt-dlp --write-auto-subs --sub-lang en-orig --convert-subs srt --skip-download -o "/tmp/yt-subs-%(id)s.%(ext)s" "<url>"
|
||||
```
|
||||
|
||||
If no subtitles are available, tell the user and stop.
|
||||
|
||||
Read the SRT file. Strip timestamps and sequence numbers to produce clean, contiguous text for summarization.
|
||||
|
||||
## Summarize
|
||||
|
||||
Produce a prose summary from the full subtitle text. Synthesize the content — do not regurgitate the transcript. Then extract **key takeaways** as a bulleted list.
|
||||
|
||||
## Write to vault
|
||||
|
||||
The vault root is `/home/sjb/Documents/sjb-brain/`. The `Video/` bundle is an OKF bundle. Create the directory if it doesn't exist, with an `index.md` and `log.md` following the pattern of other bundles in the vault.
|
||||
|
||||
### Filenames
|
||||
|
||||
- Summary: `YYYY-MM-DD_HH-mm_<kebab-case-title>.md`
|
||||
- Subtitle: `YYYY-MM-DD_HH-mm_<kebab-case-title>.srt`
|
||||
|
||||
Use the video title, kebab-cased.
|
||||
|
||||
### Summary note
|
||||
|
||||
Write a markdown note with OKF v0.1 frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: <kebab-case-title>
|
||||
type: Video Summary
|
||||
aliases: []
|
||||
tags: [video, youtube, <content-derived-tags>]
|
||||
area: <derived-from-content-or-empty>
|
||||
project: ''
|
||||
---
|
||||
```
|
||||
|
||||
Body:
|
||||
|
||||
- Video title as the note's `#` heading
|
||||
- Link to the original YouTube URL
|
||||
- The prose summary
|
||||
- `## Key Takeaways` with bulleted list
|
||||
- `## Subtitle` with a markdown link to the `.srt` file
|
||||
|
||||
Use standard markdown links, never `[[wikilinks]]`.
|
||||
|
||||
### Subtitle file
|
||||
|
||||
Copy the SRT file into `Video/` alongside the summary. No frontmatter.
|
||||
|
||||
### Bundle maintenance
|
||||
|
||||
After writing the pair:
|
||||
|
||||
1. Update `Video/index.md` — add the new summary under a `## Flat Notes` or appropriate heading, with a one-line description.
|
||||
2. Append an entry to `Video/log.md` noting the addition with a timestamp.
|
||||
|
||||
### Safety
|
||||
|
||||
- If a filename collides, append a numeric suffix.
|
||||
- Redact likely credentials, secrets, or tokens from any inline content.
|
||||
- If the video has no subtitles, stop — do not attempt to transcribe.
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Youtube Video Capture"
|
||||
short_description: "Capture a youtube video"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,11 +1,5 @@
|
||||
# Productivity Skills
|
||||
|
||||
## User-invoked
|
||||
Daily non-code workflow tools.
|
||||
|
||||
- [grill-me](grill-me/SKILL.md) — A relentless interview to sharpen a plan or design.
|
||||
- [handoff](handoff/SKILL.md) — Compact the current conversation into a handoff document for another agent to pick up.
|
||||
- [writing-great-skills](writing-great-skills/SKILL.md) — Reference for writing and editing skills well — the vocabulary and principles that make a skill predictable.
|
||||
|
||||
## Model-invoked
|
||||
|
||||
- [grilling](grilling/SKILL.md) — Interview the user relentlessly about a plan or design.
|
||||
_No skills currently live in this bucket._
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
name: grill-me
|
||||
description: A relentless interview to sharpen a plan or design.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Run a `/grilling` session.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: grilling
|
||||
description: Interview the user relentlessly about a plan or design. Use when the user wants to stress-test a plan before building, or uses any 'grill' trigger phrases.
|
||||
---
|
||||
|
||||
Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
|
||||
|
||||
Ask the questions one at a time, waiting for feedback on each question before continuing. Asking multiple questions at once is bewildering.
|
||||
|
||||
If a question can be answered by exploring the codebase, explore the codebase instead.
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
name: handoff
|
||||
description: Compact the current conversation into a handoff document for another agent to pick up.
|
||||
argument-hint: "What will the next session be used for?"
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace.
|
||||
|
||||
Include a "suggested skills" section in the document, which suggests skills that the agent should invoke.
|
||||
|
||||
Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.
|
||||
|
||||
Redact any sensitive information, such as API keys, passwords, or personally identifiable information.
|
||||
|
||||
If the user passed arguments, treat them as a description of what the next session will focus on and tailor the doc accordingly.
|
||||
@@ -1,82 +0,0 @@
|
||||
---
|
||||
name: writing-great-skills
|
||||
description: Reference for writing and editing skills well — the vocabulary and principles that make a skill predictable.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
A skill exists to wrangle determinism out of a stochastic system. **Predictability** — the agent taking the same _process_ every run, not producing the same output — is the root virtue; every lever below serves it.
|
||||
|
||||
**Bold terms** are defined in [`GLOSSARY.md`](GLOSSARY.md); look them up there for the full meaning.
|
||||
|
||||
## Invocation
|
||||
|
||||
Two choices, trading different costs:
|
||||
|
||||
- A **model-invoked** skill keeps a **description**, so the agent can fire it autonomously _and_ other skills can reach it (you can still type its name too). It contributes to **context load** — the description sits in the window every turn. Mechanics: omit `disable-model-invocation`, and write a model-facing description with rich trigger phrasing ("Use when the user wants…, mentions…").
|
||||
- A **user-invoked** skill strips the description from the agent's reach: only you, typing its name, can invoke it — and no other skill can. Zero context load, but it spends **cognitive load**: _you_ are the index that must remember it exists. Mechanics: set `disable-model-invocation: true`; the `description` becomes human-facing — a one-line summary, trigger lists stripped.
|
||||
|
||||
Pick model-invocation only when the agent must reach the skill on its own, or another skill must. If it only ever fires by hand, make it user-invoked and pay no context load.
|
||||
|
||||
When user-invoked skills multiply past what you can remember, that piled-up cognitive load is cured by a **router skill**: one user-invoked skill that names the others and when to reach for each.
|
||||
|
||||
## Writing the description
|
||||
|
||||
A model-invoked **description** does two jobs — state what the skill is, and list the **branches** that should trigger it. Every word increases **context load**, so a description earns even harder pruning than the body:
|
||||
|
||||
- **Front-load the skill's leading word** — the description is where it does its invocation work.
|
||||
- **One trigger per branch.** Synonyms that rename a single branch are **duplication** — "build features using TDD … asks for test-first development" is one branch written twice. Collapse them; keep only genuinely distinct branches.
|
||||
- **Cut identity that's already in the body.** Keep the description to triggers, plus any "when another skill needs…" reach clause.
|
||||
|
||||
## Information hierarchy
|
||||
|
||||
A skill is built from two content types — **steps** and **reference** — that mix freely: a skill can be all steps, all reference, or both. The core decision is which to use and where each sits on the **information hierarchy**, a ladder ranked by how immediately the agent needs the material:
|
||||
|
||||
1. **In-skill step** — an ordered action in `SKILL.md`, the primary tier: what the agent does, in order. Each step ends on a **completion criterion**, the condition that tells the agent the work is done. Make it _checkable_ (can the agent tell done from not-done?) and, where it matters, _exhaustive_ ("every modified model accounted for", not "produce a change list") — a vague criterion invites **premature completion**.
|
||||
2. **In-skill reference** — a definition, rule, or fact in `SKILL.md`, consulted on demand. Often a legitimately flat peer-set (every rule of a review on one rung) — a fine arrangement, not a smell. _This skill is all reference._
|
||||
3. **External reference** — reference pushed out of `SKILL.md` into a separate file, reached by a **context pointer**, loaded only when the pointer fires. (Spans _disclosed_ reference — a sibling file like `GLOSSARY.md`, still part of the skill — through fully **external reference** that lives outside the skill system and any skill can point at.)
|
||||
|
||||
A demanding completion criterion drives thorough **legwork** — the digging the agent does within the work — whether the skill has steps or not, since "every rule applied" binds flat reference just as "every step done" binds a sequence.
|
||||
|
||||
Push too little down and the top bloats; push too much and you hide material the agent actually needs. That tension is the whole decision.
|
||||
|
||||
**Progressive disclosure** is the move down the ladder — out of `SKILL.md` into a linked file — so the top stays legible. Mechanics: a linked `.md` file in the skill folder, named for what it holds (this skill discloses its full definitions to `GLOSSARY.md`). Some skills are used in more than one way, and each distinct way is a **branch** — different runs taking different paths through the skill. Branching is the cleanest disclosure test: inline what every branch needs, and push behind a pointer what only some branches reach. A **context pointer**'s _wording_, not its target, decides when and how reliably the agent reaches the material.
|
||||
|
||||
Where the ladder decides _how far down_ a piece sits, **co-location** decides _what sits beside it_ once there: keep a concept's definition, rules, and caveats under one heading rather than scattered, so reading one part brings its neighbours with it.
|
||||
|
||||
## When to split
|
||||
|
||||
**Granularity** is how finely you divide skills, and each cut spends one of the two loads, so split only when the cut earns it. Two cuts:
|
||||
|
||||
- **By invocation** — split off a **model-invoked** skill when you have a distinct **leading word** that should trigger it on its own, or another skill must reach it. You pay **context load** for the new always-loaded **description**, so that independent reach has to be worth it.
|
||||
- **By sequence** — split a run of **steps** when the steps still ahead (a step's **post-completion steps**) tempt the agent to rush the one in front of it (**premature completion**). Keeping them out of view encourages the agent to do more **legwork** on the current task.
|
||||
|
||||
## Pruning
|
||||
|
||||
Keep each meaning in a **single source of truth**: one authoritative place, so changing the behaviour is a one-place edit.
|
||||
|
||||
Check every line for **relevance**: does it still bear on what the skill does?
|
||||
|
||||
Then hunt **no-ops** sentence by sentence, not just line by line: run the no-op test on each sentence in isolation, and when one fails, delete the whole sentence rather than trim words from it. Be aggressive — most prose that fails should go, not be rewritten.
|
||||
|
||||
## Leading words
|
||||
|
||||
A **leading word** is a compact concept already living in the model's pretraining that the agent thinks with while running the skill (e.g. _lesson_, _fog of war_, _tracer bullets_). Repeated throughout the text (though not necessarily - a strong leading word might only be needed once), it accumulates a distributed definition and anchors a whole region of behaviour in the fewest tokens, by recruiting priors the model already holds.
|
||||
|
||||
It serves predictability twice. In the body it anchors _execution_: the agent reaches for the same behaviour every time the word appears. In the description it anchors _invocation_: when the same word lives in your prompts, docs, and code, the agent links that shared language to the skill and fires it more reliably.
|
||||
|
||||
Hunt for opportunities to refactor skills to use leading words. A triad spelled out at three sites (**duplication**), a description spending a sentence to gesture at one idea — each is a passage begging to **collapse** into a single token. Examples include:
|
||||
|
||||
- "fast, deterministic, low-overhead" -> _tight_ — one quality restated across a phase — into a single pretrained word (a _tight_ loop).
|
||||
- "a loop you believe in" -> _red_ — converts a fuzzy gate into a binary observable state (the loop goes _red_ on the bug, or it doesn't).
|
||||
|
||||
You win twice over: fewer tokens, _and_ a sharper hook for the agent to hang its thinking on. Assume every skill is carrying restatements that leading words retire — go find them.
|
||||
|
||||
## Failure modes
|
||||
|
||||
Use these to diagnose issues the user may be having with the skill.
|
||||
|
||||
- **Premature completion** — ending a step before it's genuinely done, attention slipping to _being done_. Defence, in order: sharpen the completion criterion first (cheap, local); only if it is irreducibly fuzzy _and_ you observe the rush, hide the post-completion steps by splitting (the sequence cut).
|
||||
- **Duplication** — the same meaning in more than one place. Costs maintenance and tokens, and inflates a meaning's prominence on the ladder past its real rank.
|
||||
- **Sediment** — stale layers that settle because adding feels safe and removing feels risky. The default fate of any skill without a pruning discipline.
|
||||
- **Sprawl** — a skill simply too long, even when every line is live and unique. Hurts readability and maintainability and wastes tokens. The cure is the ladder: disclose **reference** behind pointers, and split by **branch** or sequence so each path carries only what it needs.
|
||||
- **No-op** — a line the model already obeys by default, so you pay load to say nothing. The test: does it change behaviour versus the default? A weak leading word (_be thorough_ when the agent is already thorough-ish) is a no-op; the fix is a stronger word (_relentless_), not a different technique.
|
||||
@@ -1,181 +0,0 @@
|
||||
# Glossary — Building Great Skills
|
||||
|
||||
The domain model for what makes a skill great. A skill exists to wrangle determinism out of a stochastic system; every term below is a lever on that goal. This is the disclosed reference for [`writing-great-skills`](SKILL.md).
|
||||
|
||||
**Bold terms** in any definition are themselves defined in this glossary; find them by their heading.
|
||||
|
||||
## Language
|
||||
|
||||
### Predictability
|
||||
|
||||
The degree to which a skill makes the agent behave the same *way* on every run — the same process, not the same output (a brainstorming skill should *predictably* diverge; its tokens vary, its behaviour doesn't). The root virtue every other term serves — cost and maintainability are symptoms of it, not rivals.
|
||||
|
||||
_Avoid_: consistency, reliability, robustness, output-determinism
|
||||
|
||||
### Model-Invoked
|
||||
|
||||
A skill that keeps its **description** field, so the agent can see it and fire it autonomously — and the human can still type its name, so model-invocation always *includes* user reach. There is no model-only state: a description only ever *adds* agent discovery, never removes the human's. Pays a permanent **context load** on every turn in exchange for that discoverability. Reachable by other skills, because the description that makes it agent-discoverable makes it invocable. A model-invoked skill whose content is all **reference** is also one home for shared reference: another skill can invoke it, so reference needed by several skills lives in one place. Pick model-invocation only when the agent must reach the skill on its own; if it never fires except by hand, drop the description and pay no context load.
|
||||
|
||||
_Avoid_: ability, tool, capability
|
||||
|
||||
### User-Invoked
|
||||
|
||||
A skill with its **description** stripped — invisible to the agent and reachable only by the human typing its name (user-*only*, where **model-invoked** is user-*and-agent*). Trades agent-discoverability for zero **context load**. Because it has no description, nothing but the human can reach it: no other skill can fire it.
|
||||
|
||||
_Avoid_: procedure, workflow, command
|
||||
|
||||
### Description
|
||||
|
||||
The skill's machine-readable trigger, and the one **context pointer** a **model-invoked** skill is forced to keep loaded at all times. Its mere presence *is* the invocation axis: keep it and the skill is model-invoked (and reachable by other skills); delete it and the skill is **user-invoked**, reachable only by the human. The source of a model-invoked skill's **context load**.
|
||||
|
||||
_Avoid_: frontmatter, summary
|
||||
|
||||
### Context Pointer
|
||||
|
||||
A reference held in the agent's context that names some out-of-context material and encodes the condition for reaching it. The **description** is the top-level context pointer (context window → skill); pointers to disclosed files are the same object one level down. Its wording, not the target, decides *when* the agent reaches — and *how reliably*. A must-have target behind a weakly worded pointer is a variance bug: fix the wording first, and inline the material only if sharpening fails.
|
||||
|
||||
_Avoid_: link, reference, import
|
||||
|
||||
### Context Load
|
||||
|
||||
The cost a **model-invoked** skill imposes on the agent's context window — its **description**, always loaded, spending both tokens and attention. What **user-invoked** skills escape by having no description, and the brake on splitting into more model-invoked skills.
|
||||
|
||||
_Avoid_: token cost, context bloat
|
||||
|
||||
### Cognitive Load
|
||||
|
||||
The cost a **user-invoked** skill imposes on the human — what they must hold in their head: which skills exist and when to reach for each (the human is the index). What **model-invocation** removes by being agent-discoverable, and the brake on splitting into more user-invoked skills. Not a cost to minimise: it is the price of human agency, the reason some skills stay user-invoked. Spend it where human judgement matters; remove it where it does not.
|
||||
|
||||
_Avoid_: human index, burden, overhead
|
||||
|
||||
### Granularity
|
||||
|
||||
How finely you divide skills. Finer division spends one of the two loads: more **model-invoked** skills spend **context load** (more descriptions crowding the window and competing for attention); more **user-invoked** skills spend **cognitive load** (more for the human to remember and reach for). Two cuts guide the division. By **invocation**, split off a model-invoked skill where you have a distinct **leading word** to trigger it — a trigger word you actually use in your prompts. By **sequence**, split a run of **steps** where a step's **post-completion steps** need hiding, since isolating it in its own context clears what follows. Beware the reverse: merging sequences exposes each step's post-completion steps to what follows, inviting premature completion.
|
||||
|
||||
_Avoid_: chunking, modularity
|
||||
|
||||
### Router Skill
|
||||
|
||||
A **user-invoked** skill whose job is to point at your other user-invoked skills — naming each and when to reach for it — so the human has one skill to remember instead of many. It can only hint, never fire them: user-invoked skills have no **description**, so nothing but the human can reach them. The cure for **cognitive load** when user-invoked skills multiply.
|
||||
|
||||
_Avoid_: dispatcher, menu, registry, index, router procedure
|
||||
|
||||
### Information Hierarchy
|
||||
|
||||
A skill's content ranked by how immediately the agent needs it — a single ladder, produced by two cuts: in-file or behind a pointer, and step or reference. The rungs:
|
||||
|
||||
- **Steps** — in-file, primary
|
||||
- **Reference**, in-file — secondary
|
||||
- **Reference**, disclosed — behind a **context pointer**
|
||||
|
||||
A skill with no **steps** uses just the bottom two rungs — often a legitimately flat peer-set (e.g. every rule of a review on one rung), which is a fine arrangement, not a smell. The hierarchy is independent of invocation: a skill can be model- or user-invoked whether it is all steps, all reference, or both. When a skill has steps, in-file reference that should be disclosed buries them and turns attending to them into a coin-flip — a variance lever, not just a legibility one. Keep the top of the ladder legible; push down it whatever you can.
|
||||
|
||||
_Avoid_: structure, organization, layout
|
||||
|
||||
### Co-location
|
||||
|
||||
Keeping the material an agent needs at once in one place — a concept's definition, rules, and caveats under a single heading, not scattered across the file — so reading one part brings its neighbours with it. The within-file companion to the **Information Hierarchy**: the hierarchy ranks *how far down* a piece sits; co-location decides *what sits beside it* once there. There is no formula for the right format of a body of **reference**; the test is that a skill should read like documentation written for the agent, and grouped material reads that way where scattered material does not. Distinct from **Duplication**: that repeats one meaning in two places, where scattering fragments a single meaning across many.
|
||||
|
||||
_Avoid_: grouping, clustering, cohesion
|
||||
|
||||
### Branch
|
||||
|
||||
A distinct way a skill can be invoked — a case the skill handles — so different runs take different paths through it. A skill with many steps may carry many branches; a linear one has none.
|
||||
|
||||
_Avoid_: path, case, fork
|
||||
|
||||
### Progressive Disclosure
|
||||
|
||||
Moving **reference** down the ladder — out of SKILL.md and behind a **context pointer** — so the top stays legible. Not primarily a token optimisation; it is how the **information hierarchy** is protected. Licensed by **branching**: disclose what only some branches need, inline what every path needs, and if a pointer fires unreliably on must-have material, sharpen its wording, and pull it back inline only if that fails.
|
||||
|
||||
_Avoid_: lazy loading, chunking
|
||||
|
||||
### Steps
|
||||
|
||||
The ordered actions the agent performs — when a skill has them, the primary tier of its content, and the part that earns its place in SKILL.md. Not every skill has steps: a skill can be all steps (`tdd`), all **reference** (a review), or both, independent of invocation. Every step ends on a **completion criterion**, clear or vague.
|
||||
|
||||
_Avoid_: workflow, instructions, choreography
|
||||
|
||||
### Completion Criterion
|
||||
|
||||
The condition that tells the agent a unit of work is done — the target it judges against. Two properties make it a lever, not just a quality. Its **clarity** (can the agent tell done from not-done?) resists **premature completion** — a vague bound ("understanding reached") lets the agent declare done and slip to the next step; this axis needs *steps* to bite, since premature completion is a between-steps failure. Its **demand** (how much it requires) sets **legwork** — "every modified model accounted for" forces thorough work where "produce a change list" does not — and this axis is *not* step-bound: it can bind a body of flat reference too, which is how a skill with no steps still carries an exhaustiveness bar ("every rule applied"). The strongest criteria are both checkable and exhaustive.
|
||||
|
||||
_Avoid_: done condition, exit condition, stopping rule
|
||||
|
||||
### Post-Completion Steps
|
||||
|
||||
The **steps** that follow the current step. Visible, they pull the agent forward into **premature completion** — the more it sees, the stronger the tug; the defence is to hide them by splitting the sequence of steps into two.
|
||||
|
||||
_Avoid_: horizon, fog of war, lookahead
|
||||
|
||||
### Legwork
|
||||
|
||||
The work an agent does behind the scenes within a single step — reading files, exploring the codebase, making changes, digging up what it needs rather than offloading to the user. It lives below the step structure: never written as its own step, latent in the wording, controlled by the agent rather than the skill. The within-step counterpart to **post-completion steps**' across-step pull. Raised by a **leading word** (_comprehensive_, _thorough_) or a **completion criterion** that demands the work be exhaustive — including the demand axis applied to flat reference, which is what drives a skill of flat reference to cover all its rungs. Goes thin either when that demand is missing or when **premature completion** cuts the step short.
|
||||
|
||||
_Avoid_: scope, effort, diligence, coverage
|
||||
|
||||
### Reference
|
||||
|
||||
Material the agent refers to on demand — definitions, facts, parameters, examples, conditional instructions. When a skill has **steps** it is secondary to them; when a skill has none it is the entire content; or it lives outside any skill entirely — see **External Reference**. Reached via **context pointers**, and the prime candidate for **progressive disclosure**.
|
||||
|
||||
_Avoid_: supporting material, docs, background
|
||||
|
||||
### External Reference
|
||||
|
||||
**Reference** that lives outside the skill system — a plain file, no **description**, no **steps**, not invocable — that any skill can point at. The home for shared reference that needn't fire on its own, and the only shared home two **user-invoked** skills can use, since neither has a description and so neither can fire the other.
|
||||
|
||||
_Avoid_: doc, resource, knowledge base
|
||||
|
||||
### Leading Word
|
||||
|
||||
A compact concept — also called a *Leitwort* — already living in the model's pretraining, that the agent thinks with while running the skill. It encodes a behavioural principle in the fewest possible tokens by invoking priors the model already holds (e.g. _lesson_, _proximal zone of development_, _fog of war_, _tracer bullets_). Repeated as a token, never as a sentence, it accumulates a distributed definition across the skill and anchors a whole region of behaviour. Coining your own works if you define it clearly, but a made-up word recruits no priors — you pay in definition tokens what a pretrained word gives free. Reach for an existing word first.
|
||||
|
||||
A leading word serves **predictability** twice. In the body it anchors **execution** — the agent reaches for the same behaviour every time the concept appears, and inside flat reference it focuses attention on a class of thing to look for, recruiting the right checks each run. In the **description** it anchors **invocation** — and not only within the skill: when the same word lives in your prompts, your docs, and your codebase, the agent links that shared language to the skill and fires it more reliably. Word a description with the leading words you actually use when you want the skill.
|
||||
|
||||
_Avoid_: keyword, term, motif
|
||||
|
||||
### Single Source of Truth
|
||||
|
||||
The desired state where each meaning lives in exactly one authoritative place, so a change to the skill's behaviour is a change in one place. **Duplication** is its violation.
|
||||
|
||||
_Avoid_: home, canonical location
|
||||
|
||||
### Relevance
|
||||
|
||||
Whether a line still bears on what the skill does — the lens for what to keep. A line loses relevance either by never bearing on the task (mere exposition, or a **branch** that should be disclosed) or by going stale: drifting out of date as the behaviour or world it describes changes. Shorter skills are easier to keep relevant, because each line is cheaper to check. Distinct from **no-op**: relevance asks whether a line bears on the task, not whether it changes behaviour.
|
||||
|
||||
_Avoid_: load-bearing, staleness, freshness
|
||||
|
||||
## Failure Modes
|
||||
|
||||
### Premature Completion
|
||||
|
||||
Ending the current step before it is genuinely done, because the agent's attention slips to being done rather than to the work. A between-steps failure: it needs **steps** to occur — a skill with no steps that quits early isn't premature completion but thin **legwork** under an unmet demand. A tug-of-war between two forces: visible **post-completion steps** (the pull forward) and the **completion criterion**'s clarity (the resistance — a sharp, checkable bar holds; a vague one gives way). Fuzziness is the necessary condition: a sharp bound resists the pull no matter how many later steps are visible, so a step that never rushes needs no defending. Two levers hold a step that does, but reach for them in order: **sharpen the bound first** — it is local and cheap. Only when the criterion is irreducibly fuzzy *and* you actually observe the rush do you **hide the later steps** — and hiding only works across a real context boundary (a user-invoked hand-off or a subagent dispatch; an inline model-invoked call leaves the later steps in context and clears nothing). One cause of thin legwork, but distinct from it: legwork can be thin even when a step runs to full completion.
|
||||
|
||||
_Avoid_: premature closure, the rush, rushing, shortcutting
|
||||
|
||||
### Duplication
|
||||
|
||||
The same meaning given more than one **single source of truth**. It costs maintenance (change one place, you must change the others), costs tokens, and inflates prominence — repeating a meaning weights it on the ladder past its real rank. The accidental inverse of a **leading word**, which raises attention on purpose by repeating a token, never the meaning.
|
||||
|
||||
_Avoid_: repetition, redundancy
|
||||
|
||||
### Sediment
|
||||
|
||||
Layers of old content that settle in a skill and are never cleared, because adding feels safe and removing feels risky — so stale and irrelevant lines accumulate and you must core down through them to find what is still live. The default fate of any skill without a pruning discipline; the slow erosion of **relevance**, as opposed to **duplication**'s repeated meaning.
|
||||
|
||||
_Avoid_: accretion, bloat, cruft, rot
|
||||
|
||||
### Sprawl
|
||||
|
||||
A skill that is simply too long — too many lines in SKILL.md — independent of whether they are stale or repeated. Even an all-live, all-unique skill can sprawl. It costs readability (the agent wades through more before it can act, and attention thins across the excess), maintainability (every extra line is one more to keep **relevant**), and tokens. The cure is the **information hierarchy**: push **reference** down behind **context pointers**, and split by **branch** or sequence so each path carries only what it needs. Distinct from **sediment** (length from stale accumulation) and **duplication** (length from repeated meaning) — sprawl is length itself, whatever its cause.
|
||||
|
||||
_Avoid_: bloat, length, size, verbosity
|
||||
|
||||
### No-Op
|
||||
|
||||
An instruction that changes nothing because the model already does it by default — you pay load to tell the agent what it would do anyway. The test: does a line change behaviour versus the default? A line can be perfectly **relevant** and still be a no-op. The same priors that make a **leading word** free make a no-op worthless.
|
||||
|
||||
A leading word is a *technique*; No-Op is a *verdict* on a line — and they cross. A leading word too weak to beat the default is a no-op (_be thorough_ when the agent is already thorough-ish), and the fix is a stronger word that passes the verdict (_relentless_), not a different technique. So the No-Op test — does it change behaviour versus the default? — is also how you grade whether a leading word is earning its repetitions. This is model-relative, not reader-relative: two people disagreeing over whether a line is a no-op disagree about the default, and settle it by running the skill, not by debate.
|
||||
|
||||
_Avoid_: redundant instruction, restating the obvious, belaboring
|
||||
@@ -1,23 +0,0 @@
|
||||
# Agent CLI Configuration
|
||||
|
||||
The CLI agent used by `/implement-issue` for spawning child agents in isolated git worktrees.
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
selected: pi
|
||||
binary: pi
|
||||
args: "@{prompt}"
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `selected` | Display name of the selected agent |
|
||||
| `binary` | Command name expected on PATH |
|
||||
| `args` | Static arguments. `@{prompt}` is substituted with the prompt file path at invocation time. Empty means stdin piping via `cat`. |
|
||||
|
||||
## Changing the agent
|
||||
|
||||
Run `/setup-skills` Section E to reconfigure. The full list of supported agents is in `common/engineering/setup-skills/agents-seed.md`.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Issue tracker: Gitea
|
||||
# Issue tracker: provider-neutral tracker over Gitea
|
||||
|
||||
Issues and PRDs for this repo live as Gitea issues. Use the `tea` CLI for all operations.
|
||||
Use `tracker` (see `docs/agents/tracker.md`) for normal issue and pull-request automation. It delegates to `tea` and emits one JSON envelope. The Gitea command details below remain capability and fallback reference material; do not issue exploratory raw commands when a tracker operation exists.
|
||||
|
||||
## Conventions
|
||||
|
||||
@@ -15,12 +15,12 @@ Infer the repo from git remote -v — `tea` does this automatically when run ins
|
||||
|
||||
## Pull requests as a triage surface
|
||||
|
||||
**PRs as a request surface: no.**
|
||||
**PRs as a request surface: no.** _(Set to `yes` if this repo treats external PRs as feature requests; `/triage` reads this flag.)_
|
||||
|
||||
When set to `yes`, PRs run through the same labels and states as issues, using the `tea pr` equivalents:
|
||||
|
||||
- **Read a PR**: `tea pr <number> --comments` and `tea api /repos/{owner}/{repo}/pulls/<number>.diff` for the diff.
|
||||
- **List external PRs for triage**: `tea pr list --state open -o json` then keep only PRs whose author is not a project member/owner.
|
||||
- **List external PRs for triage**: `tea pr list --state open -o json` then keep only PRs whose author is not a project member/owner (a contributor's MR, not a maintainer's in-flight work).
|
||||
- **Comment / label / close**: `tea comment <number> "..."`, `tea pr edit --add-label`/`--remove-label`, `tea pr close`.
|
||||
|
||||
Gitea shares one number space across issues and PRs, so a bare `#42` may be either — resolve with `tea pr 42` and fall back to `tea issue 42`.
|
||||
@@ -32,3 +32,14 @@ Create a Gitea issue.
|
||||
## When a skill says "fetch the relevant ticket"
|
||||
|
||||
Run `tea issue <number> --comments`.
|
||||
|
||||
## Wayfinding operations
|
||||
|
||||
Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
||||
|
||||
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `tea issue create --label wayfinder:map`.
|
||||
- **Child ticket**: an issue linked to the map as a GitHub sub-issue (`tea api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #<map>` at the top of the child body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
||||
- **Blocking**: GitHub's **native issue dependencies** — the canonical, UI-visible representation. Add an edge with `tea api --method POST /repos/{owner}/{repo}/issues/<child>/dependencies -F index=<blocker-issue-number> -F repo=<blocker-repo-name> -F owner=<bocker-owner-name>`, where `<blocker-issue-number>` is the blocker's numeric **issue number** (`tea api repos/{owner}/{repo}/issues/<n> --jq ".number. .repository.name, .repository.owner"`, where `.number` is the `<blocker-issue-number>`, `.repository.name` is the `<blocker-repo-name>` and `.repository.owner` is the `<blocker-repo-owner>`. Where dependencies aren't available, fall back to a `Blocked by: #<n>, #<n>` line at the top of the child body. A ticket is unblocked when every blocker is closed.
|
||||
- **Frontier query**: list the map's open dependencies (`tea api /repos/{owner}/{repo}/issues/<n>/dependencies | jq '.[] | select(.state == "open") | .number'`, scoped to the map's sub-issues / task list), drop any with an open blocker (`list of dependencies is not empty`, or an open issue in the `Blocked by` line) or an assignee; first in map order wins.
|
||||
- **Claim**: `tea issue edit <n> --add-assignees @me` — the session's first write.
|
||||
- **Resolve**: `tea comment <n> "<answer>"`, then `tea issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Provider-neutral tracker automation
|
||||
|
||||
Use `tracker` for normal issue and pull-request automation. It owns the high-level operation, prerequisite checks, normalization, bounded retry policy, and JSON protocol; it delegates credentials and provider commands to `gh`, `glab`, or `tea`.
|
||||
|
||||
```sh
|
||||
tracker --provider gitea issue get 16
|
||||
TRACKER_PROVIDER=gitlab tracker issue list --state open --label ready-for-agent
|
||||
tracker pr get 42 --diff
|
||||
```
|
||||
|
||||
Provider selection is explicit CLI flag, then `TRACKER_PROVIDER`, then the `origin` remote. Always choose `issue` or `pr` explicitly for reads and writes. Use `resolve-reference` only for an intentionally ambiguous bare number.
|
||||
|
||||
Parse `ok` and `error.code`; do not parse provider output or issue exploratory retries. The library (`from tracker import Tracker`) returns the same envelope as the CLI. `RecordingRunner` provides the fake subprocess seam for tests.
|
||||
|
||||
Provider-specific capability and fallback references remain in `docs/agents/issue-tracker.md` and the setup templates. They are not the normal execution path. Wayfinding relationships may use native provider APIs where available and task-list/body or note fallbacks otherwise; the result's `details` identifies the relationship mode.
|
||||
@@ -1,15 +1,15 @@
|
||||
# Triage Labels
|
||||
|
||||
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
|
||||
The skills speak in terms of seven canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
|
||||
|
||||
| Label in skills | Label in our tracker | Meaning |
|
||||
| -------------------------- | -------------------- | ---------------------------------------- |
|
||||
| ----------------- | -------------------- | ---------------------------------------- |
|
||||
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
|
||||
| `needs-info` | `needs-info` | Waiting on reporter for more information |
|
||||
| `needs-review` | `needs-review` | Waiting for reviewed by a human |
|
||||
| `needs-review` | `needs-review` | Waiting for review by a human |
|
||||
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
|
||||
| `ready-for-human` | `ready-for-human` | Requires human implementation |
|
||||
| `in-progress` | `in-progress` | Being actively worked on by a human or agents |
|
||||
| `wontfix` | `wontfix` | Will not be actioned |
|
||||
|
||||
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
|
||||
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table. Only one triage label should be active.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "tracker-automation"
|
||||
version = "0.1.0"
|
||||
description = "Provider-neutral tracker automation CLI and library"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
[project.scripts]
|
||||
tracker = "tracker.cli:main"
|
||||
tracker-automation = "tracker.cli:main"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["tracker*", "tracker_automation*"]
|
||||
|
||||
[tool.pyright]
|
||||
include = ["tracker", "tests"]
|
||||
extraPaths = ["."]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py310"
|
||||
line-length = 100
|
||||
@@ -0,0 +1,125 @@
|
||||
import unittest
|
||||
|
||||
from tracker import CompletedCommand, RecordingRunner, RetryPolicy, Tracker # type: ignore[reportMissingImports]
|
||||
|
||||
|
||||
class TrackerOperationTests(unittest.TestCase):
|
||||
def test_add_label_ensures_missing_label_before_assignment(self):
|
||||
runner = RecordingRunner(
|
||||
[
|
||||
CompletedCommand("[]"),
|
||||
CompletedCommand('{"name":"ready","color":"ededed"}'),
|
||||
CompletedCommand('{"number":7,"labels":[{"name":"ready"}]}'),
|
||||
]
|
||||
)
|
||||
result = Tracker(provider="github", runner=runner).add_label("issue", 7, "ready")
|
||||
|
||||
self.assertTrue(result["ok"])
|
||||
self.assertEqual([call[0][:3] for call in runner.calls], [["gh", "label", "list"], ["gh", "label", "create"], ["gh", "issue", "edit"]])
|
||||
self.assertEqual(result["details"]["ensured"]["created"], True)
|
||||
|
||||
def test_existing_label_is_not_recreated(self):
|
||||
runner = RecordingRunner(
|
||||
[
|
||||
CompletedCommand('[{"name":"ready","color":"ff0000"}]'),
|
||||
CompletedCommand('{"number":7,"labels":[{"name":"ready"}]}'),
|
||||
]
|
||||
)
|
||||
result = Tracker(provider="gitea", runner=runner).add_label("issue", 7, "ready")
|
||||
|
||||
self.assertTrue(result["ok"])
|
||||
self.assertEqual(len(runner.calls), 2)
|
||||
self.assertEqual(result["details"]["ensured"]["created"], False)
|
||||
|
||||
def test_transient_failure_is_retried_and_normalized(self):
|
||||
runner = RecordingRunner(
|
||||
[
|
||||
CompletedCommand("", "connection reset", 1),
|
||||
CompletedCommand('{"number":4,"iid":4,"title":"Fix","description":"body","state":"opened","labels":[]}'),
|
||||
]
|
||||
)
|
||||
result = Tracker(provider="gitlab", runner=runner, retry=RetryPolicy(attempts=2)).get_issue(4)
|
||||
|
||||
self.assertTrue(result["ok"])
|
||||
self.assertEqual(len(runner.calls), 2)
|
||||
self.assertEqual(result["result"]["number"], 4)
|
||||
self.assertEqual(result["result"]["body"], "body")
|
||||
|
||||
def test_external_pr_filter_normalizes_github_associations(self):
|
||||
runner = RecordingRunner([
|
||||
CompletedCommand('[{"number":1,"title":"inside","authorAssociation":"MEMBER"},{"number":2,"title":"outside","authorAssociation":"NONE"}]')
|
||||
])
|
||||
result = Tracker(provider="github", runner=runner).list_external_prs()
|
||||
|
||||
self.assertTrue(result["ok"])
|
||||
self.assertEqual([item["number"] for item in result["result"]], [2])
|
||||
|
||||
def test_external_filter_reports_missing_membership_metadata(self):
|
||||
runner = RecordingRunner([CompletedCommand('[{"number":1,"title":"unknown"}]')])
|
||||
result = Tracker(provider="gitea", runner=runner).list_external_prs()
|
||||
|
||||
self.assertFalse(result["ok"])
|
||||
self.assertEqual(result["error"]["code"], "unsupported_capability")
|
||||
|
||||
def test_pr_get_can_include_diff(self):
|
||||
runner = RecordingRunner([
|
||||
CompletedCommand('{"number":3,"title":"Change","state":"open"}'),
|
||||
CompletedCommand("diff --git a/a b/a"),
|
||||
])
|
||||
result = Tracker(provider="gitlab", runner=runner).get_pr(3, diff=True)
|
||||
|
||||
self.assertTrue(result["ok"])
|
||||
self.assertEqual(result["result"]["diff"], "diff --git a/a b/a")
|
||||
|
||||
def test_child_creation_links_native_and_updates_map_order(self):
|
||||
runner = RecordingRunner([
|
||||
CompletedCommand("[]"),
|
||||
CompletedCommand("{}"),
|
||||
CompletedCommand('{"number":7,"title":"Research"}'),
|
||||
CompletedCommand("{}"),
|
||||
CompletedCommand('{"number":9,"body":"Notes"}'),
|
||||
CompletedCommand("{}"),
|
||||
])
|
||||
result = Tracker(provider="github", runner=runner).create_child(9, "Research", wayfinder_type="research")
|
||||
|
||||
self.assertTrue(result["ok"])
|
||||
self.assertEqual(result["result"]["number"], 7)
|
||||
self.assertEqual(result["details"]["relationship"], "native")
|
||||
self.assertTrue(result["details"]["map_updated"])
|
||||
self.assertTrue(any("sub_issues" in part for part in runner.calls[3][0]))
|
||||
|
||||
def test_resolve_reports_completed_steps_after_partial_failure(self):
|
||||
runner = RecordingRunner([
|
||||
CompletedCommand("{}"),
|
||||
CompletedCommand("", "connection reset", 1),
|
||||
])
|
||||
result = Tracker(provider="gitea", runner=runner, retry=RetryPolicy(attempts=1)).resolve("issue", 7, "answer")
|
||||
|
||||
self.assertFalse(result["ok"])
|
||||
self.assertEqual(result["error"]["code"], "partial_failure")
|
||||
self.assertEqual(result["error"]["details"]["completed"], ["comment"])
|
||||
|
||||
def test_frontier_uses_map_task_order_and_filters_closed_or_claimed(self):
|
||||
runner = RecordingRunner([
|
||||
CompletedCommand('{"number":9,"body":"- [ ] #2 second\\n- [ ] #1 first","state":"open"}'),
|
||||
CompletedCommand('{"number":2,"title":"second","state":"open","assignees":[]}'),
|
||||
CompletedCommand('{"number":1,"title":"first","state":"closed","assignees":[]}'),
|
||||
])
|
||||
result = Tracker(provider="github", runner=runner).frontier(9)
|
||||
|
||||
self.assertTrue(result["ok"])
|
||||
self.assertEqual([item["number"] for item in result["result"]], [2])
|
||||
self.assertTrue(result["details"]["deterministic"])
|
||||
|
||||
def test_authentication_failure_is_not_retryable(self):
|
||||
runner = RecordingRunner([CompletedCommand("", "not logged in", 1)])
|
||||
result = Tracker(provider="github", runner=runner).get_issue(4)
|
||||
|
||||
self.assertFalse(result["ok"])
|
||||
self.assertEqual(result["error"]["code"], "auth_required")
|
||||
self.assertFalse(result["error"]["retryable"])
|
||||
self.assertEqual(len(runner.calls), 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,37 @@
|
||||
import unittest
|
||||
|
||||
from tracker import TrackerError, resolve_provider # type: ignore[reportMissingImports]
|
||||
|
||||
|
||||
class ProviderResolutionTests(unittest.TestCase):
|
||||
def test_cli_provider_wins_over_environment_and_remote(self):
|
||||
self.assertEqual(
|
||||
resolve_provider(
|
||||
explicit="github",
|
||||
env={"TRACKER_PROVIDER": "gitlab"},
|
||||
remote="https://gitea.example.com/team/repo.git",
|
||||
),
|
||||
"github",
|
||||
)
|
||||
|
||||
def test_environment_provider_wins_over_remote(self):
|
||||
self.assertEqual(
|
||||
resolve_provider(
|
||||
env={"TRACKER_PROVIDER": "gitlab"},
|
||||
remote="git@github.com:team/repo.git",
|
||||
),
|
||||
"gitlab",
|
||||
)
|
||||
|
||||
def test_remote_provider_is_detected(self):
|
||||
self.assertEqual(resolve_provider(remote="git@gitea.example.com:team/repo.git"), "gitea")
|
||||
|
||||
def test_unsupported_remote_is_structured_error(self):
|
||||
with self.assertRaises(TrackerError) as context:
|
||||
resolve_provider(remote="https://example.com/team/repo.git")
|
||||
self.assertEqual(context.exception.code, "provider_detection_failed")
|
||||
self.assertFalse(context.exception.retryable)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,30 @@
|
||||
# Tracker automation
|
||||
|
||||
`tracker` is the provider-neutral execution seam for issue-tracker skills. It emits one JSON envelope on stdout and delegates authentication and repository work to `gh`, `glab`, or `tea`.
|
||||
|
||||
## Use
|
||||
|
||||
```sh
|
||||
python -m tracker --provider gitea issue get 16
|
||||
TRACKER_PROVIDER=github tracker issue list --state open --label ready-for-agent
|
||||
tracker pr get 42 --diff
|
||||
```
|
||||
|
||||
Provider precedence is `--provider`, `TRACKER_PROVIDER`, then the `origin` Git remote. Explicit resource commands (`issue` and `pr`) avoid shared-number ambiguity. Use `resolve-reference` only when intentional resolution of a bare number is required.
|
||||
|
||||
Stable failures are returned as:
|
||||
|
||||
```json
|
||||
{"ok":false,"provider":"gitea","operation":"issue.get","error":{"code":"auth_required","message":"...","retryable":false,"provider":"gitea","operation":"issue.get","details":{}}}
|
||||
```
|
||||
|
||||
The Python API is the same seam as the CLI:
|
||||
|
||||
```python
|
||||
from tracker import Tracker
|
||||
|
||||
tracker = Tracker(provider="gitea")
|
||||
result = tracker.add_label("issue", 16, "needs-review")
|
||||
```
|
||||
|
||||
Inject `RecordingRunner` or another object with `run(argv, **kwargs)` for deterministic contract tests. Provider-specific capability gaps are explicit in `error.code` or `details`; credentials are never accepted or stored by this package.
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Provider-neutral issue tracker automation library."""
|
||||
|
||||
# pi-lens-ignore: reportMissingImports
|
||||
from .detection import resolve_provider # type: ignore[reportMissingImports]
|
||||
# pi-lens-ignore: reportMissingImports
|
||||
from .errors import TrackerError # type: ignore[reportMissingImports]
|
||||
from .models import CompletedCommand, Envelope, ResourceRef, RetryPolicy # type: ignore[reportMissingImports]
|
||||
from .runner import RecordingRunner, SubprocessRunner # type: ignore[reportMissingImports]
|
||||
from .service import Tracker # type: ignore[reportMissingImports]
|
||||
|
||||
__all__ = [
|
||||
"CompletedCommand",
|
||||
"Envelope",
|
||||
"RecordingRunner",
|
||||
"ResourceRef",
|
||||
"RetryPolicy",
|
||||
"SubprocessRunner",
|
||||
"Tracker",
|
||||
"TrackerError",
|
||||
"resolve_provider",
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
from .cli import main # type: ignore[reportMissingImports]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,328 @@
|
||||
import json
|
||||
from .models import normalize_resource # type: ignore[reportMissingImports]
|
||||
|
||||
|
||||
JSON_FIELDS = "number,title,body,state,labels,comments,author,assignees,url,createdAt,updatedAt"
|
||||
|
||||
|
||||
class Adapter:
|
||||
provider = ""
|
||||
executable = ""
|
||||
|
||||
def __init__(self, repo=None):
|
||||
self.repo = repo
|
||||
|
||||
def _repo_args(self):
|
||||
return ["--repo", self.repo] if self.repo else []
|
||||
|
||||
def command(self, operation, **kwargs):
|
||||
method = getattr(self, f"command_{operation.replace('.', '_')}")
|
||||
return method(**kwargs)
|
||||
|
||||
def normalize(self, value, kind):
|
||||
return normalize_resource(value, kind=kind, provider=self.provider)
|
||||
|
||||
def json_value(self, stdout):
|
||||
text = (stdout or "").strip()
|
||||
if not text:
|
||||
return {}
|
||||
try:
|
||||
return json.loads(text)
|
||||
except (TypeError, ValueError):
|
||||
return {"output": text}
|
||||
|
||||
|
||||
class GitHubAdapter(Adapter):
|
||||
provider = "github"
|
||||
executable = "gh"
|
||||
|
||||
def command_issue_create(self, title, body, labels, assignees):
|
||||
args = ["gh", "issue", "create", "--title", title, "--body", body]
|
||||
for label in labels:
|
||||
args += ["--label", label]
|
||||
for user in assignees:
|
||||
args += ["--assignee", user]
|
||||
return args + self._repo_args()
|
||||
|
||||
def command_pr_create(self, title, body, head, base, labels, assignees):
|
||||
args = ["gh", "pr", "create", "--title", title, "--body", body]
|
||||
if head:
|
||||
args += ["--head", head]
|
||||
if base:
|
||||
args += ["--base", base]
|
||||
for label in labels:
|
||||
args += ["--label", label]
|
||||
for user in assignees:
|
||||
args += ["--assignee", user]
|
||||
return args + self._repo_args()
|
||||
|
||||
def _view(self, kind, number, comments=True):
|
||||
command = "pr" if kind == "pr" else "issue"
|
||||
args = ["gh", command, "view", str(number)]
|
||||
if comments:
|
||||
args.append("--comments")
|
||||
return args + ["--json", JSON_FIELDS] + self._repo_args()
|
||||
|
||||
def command_issue_get(self, number, comments=True):
|
||||
return self._view("issue", number, comments)
|
||||
|
||||
def command_pr_get(self, number, comments=True):
|
||||
return self._view("pr", number, comments)
|
||||
|
||||
def command_issue_list(self, state, labels, limit):
|
||||
args = ["gh", "issue", "list", "--state", state, "--limit", str(limit)]
|
||||
for label in labels:
|
||||
args += ["--label", label]
|
||||
return args + ["--json", JSON_FIELDS] + self._repo_args()
|
||||
|
||||
def command_pr_list(self, state, limit):
|
||||
return ["gh", "pr", "list", "--state", state, "--limit", str(limit), "--json", JSON_FIELDS] + self._repo_args()
|
||||
|
||||
def _edit(self, kind, number, title=None, body=None, add_label=None, remove_label=None, assignee=None, unassign=None):
|
||||
command = "pr" if kind == "pr" else "issue"
|
||||
args = ["gh", command, "edit", str(number)]
|
||||
if title is not None:
|
||||
args += ["--title", title]
|
||||
if body is not None:
|
||||
args += ["--body", body]
|
||||
if add_label:
|
||||
args += ["--add-label", add_label]
|
||||
if remove_label:
|
||||
args += ["--remove-label", remove_label]
|
||||
if assignee:
|
||||
args += ["--add-assignee", assignee]
|
||||
if unassign:
|
||||
args += ["--remove-assignee", unassign]
|
||||
return args + self._repo_args()
|
||||
|
||||
def command_issue_edit(self, **kwargs):
|
||||
return self._edit("issue", **kwargs)
|
||||
|
||||
def command_pr_edit(self, **kwargs):
|
||||
return self._edit("pr", **kwargs)
|
||||
|
||||
def command_issue_comment(self, number, body):
|
||||
return ["gh", "issue", "comment", str(number), "--body", body] + self._repo_args()
|
||||
|
||||
def command_pr_comment(self, number, body):
|
||||
return ["gh", "pr", "comment", str(number), "--body", body] + self._repo_args()
|
||||
|
||||
def command_issue_close(self, number):
|
||||
return ["gh", "issue", "close", str(number)] + self._repo_args()
|
||||
|
||||
def command_pr_close(self, number):
|
||||
return ["gh", "pr", "close", str(number)] + self._repo_args()
|
||||
|
||||
def command_pr_diff(self, number):
|
||||
return ["gh", "pr", "diff", str(number)] + self._repo_args()
|
||||
|
||||
def command_label_list(self):
|
||||
return ["gh", "label", "list", "--limit", "1000", "--json", "name,color,description"] + self._repo_args()
|
||||
|
||||
def command_label_create(self, name, color, description):
|
||||
args = ["gh", "label", "create", name, "--color", color]
|
||||
if description:
|
||||
args += ["--description", description]
|
||||
return args + self._repo_args()
|
||||
|
||||
def command_issue_resolve(self, number):
|
||||
return self.command_issue_get(number)
|
||||
|
||||
|
||||
class GitLabAdapter(Adapter):
|
||||
provider = "gitlab"
|
||||
executable = "glab"
|
||||
|
||||
def _format(self):
|
||||
return ["-F", "json"]
|
||||
|
||||
def _surface(self, kind):
|
||||
return "mr" if kind == "pr" else "issue"
|
||||
|
||||
def command_issue_create(self, title, body, labels, assignees):
|
||||
args = ["glab", "issue", "create", "--title", title, "--description", body]
|
||||
if labels:
|
||||
args += ["--label", ",".join(labels)]
|
||||
if assignees:
|
||||
args += ["--assignee", ",".join(assignees)]
|
||||
return args + self._repo_args()
|
||||
|
||||
def command_pr_create(self, title, body, head, base, labels, assignees):
|
||||
args = ["glab", "mr", "create", "--title", title, "--description", body]
|
||||
if head:
|
||||
args += ["--source-branch", head]
|
||||
if base:
|
||||
args += ["--target-branch", base]
|
||||
if labels:
|
||||
args += ["--label", ",".join(labels)]
|
||||
if assignees:
|
||||
args += ["--assignee", ",".join(assignees)]
|
||||
return args + self._repo_args()
|
||||
|
||||
def command_issue_get(self, number, comments=True):
|
||||
args = ["glab", "issue", "view", str(number)]
|
||||
if comments:
|
||||
args.append("--comments")
|
||||
return args + self._format() + self._repo_args()
|
||||
|
||||
def command_pr_get(self, number, comments=True):
|
||||
args = ["glab", "mr", "view", str(number)]
|
||||
if comments:
|
||||
args.append("--comments")
|
||||
return args + self._format() + self._repo_args()
|
||||
|
||||
def command_issue_list(self, state, labels, limit):
|
||||
args = ["glab", "issue", "list", "--state", state, "--per-page", str(limit)]
|
||||
if labels:
|
||||
args += ["--label", ",".join(labels)]
|
||||
return args + self._format() + self._repo_args()
|
||||
|
||||
def command_pr_list(self, state, limit):
|
||||
return ["glab", "mr", "list", "--state", state, "--per-page", str(limit)] + self._format() + self._repo_args()
|
||||
|
||||
def _edit(self, kind, number, title=None, body=None, add_label=None, remove_label=None, assignee=None, unassign=None):
|
||||
args = ["glab", self._surface(kind), "update", str(number)]
|
||||
if title is not None:
|
||||
args += ["--title", title]
|
||||
if body is not None:
|
||||
args += ["--description", body]
|
||||
if add_label:
|
||||
args += ["--label", add_label]
|
||||
if remove_label:
|
||||
args += ["--unlabel", remove_label]
|
||||
if assignee:
|
||||
args += ["--assignee", assignee]
|
||||
if unassign:
|
||||
args += ["--unassign", unassign]
|
||||
return args + self._repo_args()
|
||||
|
||||
def command_issue_edit(self, **kwargs):
|
||||
return self._edit("issue", **kwargs)
|
||||
|
||||
def command_pr_edit(self, **kwargs):
|
||||
return self._edit("pr", **kwargs)
|
||||
|
||||
def command_issue_comment(self, number, body):
|
||||
return ["glab", "issue", "note", str(number), "--message", body] + self._repo_args()
|
||||
|
||||
def command_pr_comment(self, number, body):
|
||||
return ["glab", "mr", "note", str(number), "--message", body] + self._repo_args()
|
||||
|
||||
def command_issue_close(self, number):
|
||||
return ["glab", "issue", "close", str(number)] + self._repo_args()
|
||||
|
||||
def command_pr_close(self, number):
|
||||
return ["glab", "mr", "close", str(number)] + self._repo_args()
|
||||
|
||||
def command_pr_diff(self, number):
|
||||
return ["glab", "mr", "diff", str(number)] + self._repo_args()
|
||||
|
||||
def command_label_list(self):
|
||||
return ["glab", "label", "list"] + self._format() + self._repo_args()
|
||||
|
||||
def command_label_create(self, name, color, description):
|
||||
args = ["glab", "label", "create", name, "--color", color]
|
||||
if description:
|
||||
args += ["--description", description]
|
||||
return args + self._repo_args()
|
||||
|
||||
|
||||
class GiteaAdapter(Adapter):
|
||||
provider = "gitea"
|
||||
executable = "tea"
|
||||
|
||||
def _format(self):
|
||||
return ["-o", "json"]
|
||||
|
||||
def _surface(self, kind):
|
||||
return "pr" if kind == "pr" else "issue"
|
||||
|
||||
def command_issue_create(self, title, body, labels, assignees):
|
||||
args = ["tea", "issue", "create", "--title", title, "--description", body]
|
||||
if labels:
|
||||
args += ["--labels", ",".join(labels)]
|
||||
if assignees:
|
||||
args += ["--assignees", ",".join(assignees)]
|
||||
return args + self._repo_args()
|
||||
|
||||
def command_pr_create(self, title, body, head, base, labels, assignees):
|
||||
args = ["tea", "pr", "create", "--title", title, "--description", body]
|
||||
if head:
|
||||
args += ["--head", head]
|
||||
if base:
|
||||
args += ["--base", base]
|
||||
if labels:
|
||||
args += ["--labels", ",".join(labels)]
|
||||
if assignees:
|
||||
args += ["--assignees", ",".join(assignees)]
|
||||
return args + self._repo_args()
|
||||
|
||||
def _view(self, kind, number, comments=True):
|
||||
args = ["tea", self._surface(kind), str(number)]
|
||||
if comments:
|
||||
args.append("--comments")
|
||||
return args + self._format() + self._repo_args()
|
||||
|
||||
def command_issue_get(self, number, comments=True):
|
||||
return self._view("issue", number, comments)
|
||||
|
||||
def command_pr_get(self, number, comments=True):
|
||||
return self._view("pr", number, comments)
|
||||
|
||||
def command_issue_list(self, state, labels, limit):
|
||||
args = ["tea", "issue", "list", "--state", state, "--limit", str(limit)]
|
||||
if labels:
|
||||
args += ["--labels", ",".join(labels)]
|
||||
return args + self._format() + self._repo_args()
|
||||
|
||||
def command_pr_list(self, state, limit):
|
||||
return ["tea", "pr", "list", "--state", state, "--limit", str(limit)] + self._format() + self._repo_args()
|
||||
|
||||
def _edit(self, kind, number, title=None, body=None, add_label=None, remove_label=None, assignee=None, unassign=None):
|
||||
args = ["tea", self._surface(kind), "edit", str(number)]
|
||||
if title is not None:
|
||||
args += ["--title", title]
|
||||
if body is not None:
|
||||
args += ["--description", body]
|
||||
if add_label:
|
||||
args += ["--add-label", add_label]
|
||||
if remove_label:
|
||||
args += ["--remove-label", remove_label]
|
||||
if assignee:
|
||||
args += ["--add-assignee", assignee]
|
||||
if unassign:
|
||||
args += ["--remove-assignee", unassign]
|
||||
return args + self._repo_args()
|
||||
|
||||
def command_issue_edit(self, **kwargs):
|
||||
return self._edit("issue", **kwargs)
|
||||
|
||||
def command_pr_edit(self, **kwargs):
|
||||
return self._edit("pr", **kwargs)
|
||||
|
||||
def command_issue_comment(self, number, body):
|
||||
return ["tea", "comment", str(number), body] + self._repo_args()
|
||||
|
||||
def command_pr_comment(self, number, body):
|
||||
return ["tea", "comment", str(number), body] + self._repo_args()
|
||||
|
||||
def command_issue_close(self, number):
|
||||
return ["tea", "issue", "close", str(number)] + self._repo_args()
|
||||
|
||||
def command_pr_close(self, number):
|
||||
return ["tea", "pr", "close", str(number)] + self._repo_args()
|
||||
|
||||
def command_pr_diff(self, number):
|
||||
return ["tea", "api", f"/repos/{{owner}}/{{repo}}/pulls/{number}.diff"] + self._repo_args()
|
||||
|
||||
def command_label_list(self):
|
||||
return ["tea", "label", "list"] + self._format() + self._repo_args()
|
||||
|
||||
def command_label_create(self, name, color, description):
|
||||
args = ["tea", "label", "create", "--name", name, "--color", color]
|
||||
if description:
|
||||
args += ["--description", description]
|
||||
return args + self._repo_args()
|
||||
|
||||
|
||||
ADAPTERS = {"github": GitHubAdapter, "gitlab": GitLabAdapter, "gitea": GiteaAdapter}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
from .errors import TrackerError # type: ignore[reportMissingImports]
|
||||
from .models import Envelope, RetryPolicy # type: ignore[reportMissingImports]
|
||||
from .service import Tracker # type: ignore[reportMissingImports]
|
||||
|
||||
|
||||
class JsonArgumentParser(argparse.ArgumentParser):
|
||||
def error(self, message):
|
||||
raise TrackerError("invalid_input", message)
|
||||
|
||||
|
||||
def _parse_attempts(value):
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError) as error:
|
||||
raise TrackerError("invalid_input", f"invalid retry attempt count: {value}") from error
|
||||
|
||||
|
||||
def _take_global_options(argv):
|
||||
remaining = []
|
||||
provider = repo = None
|
||||
attempts = 3
|
||||
index = 0
|
||||
while index < len(argv):
|
||||
item = argv[index]
|
||||
if item == "--provider" and index + 1 < len(argv):
|
||||
provider = argv[index + 1]
|
||||
index += 2
|
||||
elif item.startswith("--provider="):
|
||||
provider = item.split("=", 1)[1]
|
||||
index += 1
|
||||
elif item == "--repo" and index + 1 < len(argv):
|
||||
repo = argv[index + 1]
|
||||
index += 2
|
||||
elif item.startswith("--repo="):
|
||||
repo = item.split("=", 1)[1]
|
||||
index += 1
|
||||
elif item == "--retry-attempts" and index + 1 < len(argv):
|
||||
attempts = _parse_attempts(argv[index + 1])
|
||||
index += 2
|
||||
elif item.startswith("--retry-attempts="):
|
||||
attempts = _parse_attempts(item.split("=", 1)[1])
|
||||
index += 1
|
||||
else:
|
||||
remaining.append(item)
|
||||
index += 1
|
||||
return remaining, provider, repo, attempts
|
||||
|
||||
|
||||
def _add_resource_commands(subparsers, kind):
|
||||
resource = subparsers.add_parser(kind)
|
||||
commands = resource.add_subparsers(dest="action", required=True)
|
||||
|
||||
create = commands.add_parser("create")
|
||||
create.add_argument("--title", required=True)
|
||||
create.add_argument("--body", default="")
|
||||
create.add_argument("--label", action="append", default=[])
|
||||
create.add_argument("--assignee", action="append", default=[])
|
||||
if kind == "pr":
|
||||
create.add_argument("--head")
|
||||
create.add_argument("--base")
|
||||
|
||||
get = commands.add_parser("get")
|
||||
get.add_argument("number", type=int)
|
||||
get.add_argument("--no-comments", action="store_true")
|
||||
if kind == "pr":
|
||||
get.add_argument("--diff", action="store_true")
|
||||
|
||||
listing = commands.add_parser("list")
|
||||
listing.add_argument("--state", default="open")
|
||||
listing.add_argument("--label", action="append", default=[])
|
||||
listing.add_argument("--limit", type=int, default=100)
|
||||
if kind == "pr":
|
||||
listing.add_argument("--external-only", action="store_true")
|
||||
|
||||
comment = commands.add_parser("comment")
|
||||
comment.add_argument("number", type=int)
|
||||
comment.add_argument("--body", required=True)
|
||||
|
||||
edit = commands.add_parser("edit")
|
||||
edit.add_argument("number", type=int)
|
||||
edit.add_argument("--title")
|
||||
edit.add_argument("--body")
|
||||
|
||||
assign = commands.add_parser("assign")
|
||||
assign.add_argument("number", type=int)
|
||||
assign.add_argument("--user", required=True)
|
||||
|
||||
close = commands.add_parser("close")
|
||||
close.add_argument("number", type=int)
|
||||
close.add_argument("--explanation")
|
||||
|
||||
if kind == "pr":
|
||||
diff = commands.add_parser("diff")
|
||||
diff.add_argument("number", type=int)
|
||||
|
||||
return resource
|
||||
|
||||
|
||||
def build_parser():
|
||||
parser = JsonArgumentParser(prog="tracker")
|
||||
commands = parser.add_subparsers(dest="resource", required=True)
|
||||
_add_resource_commands(commands, "issue")
|
||||
_add_resource_commands(commands, "pr")
|
||||
|
||||
labels = commands.add_parser("label")
|
||||
label_commands = labels.add_subparsers(dest="action", required=True)
|
||||
ensure = label_commands.add_parser("ensure")
|
||||
ensure.add_argument("name")
|
||||
ensure.add_argument("--color", default="ededed")
|
||||
ensure.add_argument("--description")
|
||||
add = label_commands.add_parser("add")
|
||||
add.add_argument("kind", choices=("issue", "pr"))
|
||||
add.add_argument("number", type=int)
|
||||
add.add_argument("name")
|
||||
remove = label_commands.add_parser("remove")
|
||||
remove.add_argument("kind", choices=("issue", "pr"))
|
||||
remove.add_argument("number", type=int)
|
||||
remove.add_argument("name")
|
||||
|
||||
map_parser = commands.add_parser("map")
|
||||
map_commands = map_parser.add_subparsers(dest="action", required=True)
|
||||
map_create = map_commands.add_parser("create")
|
||||
map_create.add_argument("--title", required=True)
|
||||
map_create.add_argument("--body", default="")
|
||||
map_create.add_argument("--label", action="append", default=[])
|
||||
|
||||
child = commands.add_parser("child")
|
||||
child_commands = child.add_subparsers(dest="action", required=True)
|
||||
child_create = child_commands.add_parser("create")
|
||||
child_create.add_argument("map_number", type=int)
|
||||
child_create.add_argument("--title", required=True)
|
||||
child_create.add_argument("--type", dest="wayfinder_type", choices=("research", "prototype", "grilling", "task"), default="task")
|
||||
child_create.add_argument("--body", default="")
|
||||
child_create.add_argument("--label", action="append", default=[])
|
||||
|
||||
dependency = commands.add_parser("dependency")
|
||||
dependency_commands = dependency.add_subparsers(dest="action", required=True)
|
||||
dependency_add = dependency_commands.add_parser("add")
|
||||
dependency_add.add_argument("child", type=int)
|
||||
dependency_add.add_argument("blocker", type=int)
|
||||
|
||||
frontier = commands.add_parser("frontier")
|
||||
frontier.add_argument("map_number", type=int)
|
||||
|
||||
claim = commands.add_parser("claim")
|
||||
claim.add_argument("kind", choices=("issue", "pr"))
|
||||
claim.add_argument("number", type=int)
|
||||
claim.add_argument("--user")
|
||||
|
||||
resolve = commands.add_parser("resolve")
|
||||
resolve.add_argument("kind", choices=("issue", "pr"))
|
||||
resolve.add_argument("number", type=int)
|
||||
resolve.add_argument("--answer", required=True)
|
||||
resolve.add_argument("--map", dest="map_number", type=int)
|
||||
|
||||
reference = commands.add_parser("resolve-reference")
|
||||
reference.add_argument("number", type=int)
|
||||
return parser
|
||||
|
||||
|
||||
def _dispatch(tracker, args):
|
||||
resource = args.resource
|
||||
if resource in ("issue", "pr"):
|
||||
if args.action == "create":
|
||||
method = tracker.create_issue if resource == "issue" else tracker.create_pr
|
||||
kwargs = {"body": args.body, "labels": args.label, "assignees": args.assignee}
|
||||
if resource == "pr":
|
||||
kwargs.update(head=args.head, base=args.base)
|
||||
return method(args.title, **kwargs)
|
||||
if args.action == "get":
|
||||
if resource == "issue":
|
||||
return tracker.get_issue(args.number, comments=not args.no_comments)
|
||||
return tracker.get_pr(args.number, comments=not args.no_comments, diff=args.diff)
|
||||
if args.action == "list":
|
||||
if resource == "issue":
|
||||
return tracker.list_issues(state=args.state, labels=args.label, limit=args.limit)
|
||||
return tracker.list_prs(state=args.state, limit=args.limit, external_only=args.external_only)
|
||||
if args.action == "comment":
|
||||
return tracker.comment(resource, args.number, args.body)
|
||||
if args.action == "edit":
|
||||
return (tracker.edit_issue if resource == "issue" else tracker.edit_pr)(args.number, title=args.title, body=args.body)
|
||||
if args.action == "assign":
|
||||
return tracker.assign(resource, args.number, args.user)
|
||||
if args.action == "close":
|
||||
return tracker.close(resource, args.number, explanation=args.explanation)
|
||||
if args.action == "diff":
|
||||
return tracker.diff(args.number)
|
||||
if resource == "label":
|
||||
if args.action == "ensure":
|
||||
return tracker.ensure_label(args.name, color=args.color, description=args.description)
|
||||
if args.action == "add":
|
||||
return tracker.add_label(args.kind, args.number, args.name)
|
||||
return tracker.remove_label(args.kind, args.number, args.name)
|
||||
if resource == "map":
|
||||
return tracker.create_map(args.title, body=args.body, labels=args.label)
|
||||
if resource == "child":
|
||||
return tracker.create_child(args.map_number, args.title, wayfinder_type=args.wayfinder_type, body=args.body, labels=args.label)
|
||||
if resource == "dependency":
|
||||
return tracker.add_dependency(args.child, args.blocker)
|
||||
if resource == "frontier":
|
||||
return tracker.frontier(args.map_number)
|
||||
if resource == "claim":
|
||||
return tracker.claim(args.kind, args.number, user=args.user)
|
||||
if resource == "resolve":
|
||||
return tracker.resolve(args.kind, args.number, args.answer, map_number=args.map_number)
|
||||
return tracker.resolve_reference(args.number)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
argv = list(sys.argv[1:] if argv is None else argv)
|
||||
try:
|
||||
command_argv, provider, repo, attempts = _take_global_options(argv)
|
||||
args = build_parser().parse_args(command_argv)
|
||||
tracker = Tracker(provider=provider, repo=repo, retry=RetryPolicy(attempts=attempts))
|
||||
envelope = _dispatch(tracker, args)
|
||||
except TrackerError as error:
|
||||
operation = error.operation
|
||||
if operation is None:
|
||||
operation = "cli"
|
||||
error.operation = operation
|
||||
envelope = Envelope(False, getattr(error, "provider", None), operation, error=error.to_dict()).to_dict()
|
||||
except (ValueError, TypeError, OSError) as error:
|
||||
failure = TrackerError("invalid_input", str(error), operation="cli")
|
||||
envelope = Envelope(False, None, "cli", error=failure.to_dict()).to_dict()
|
||||
print(json.dumps(envelope, sort_keys=True))
|
||||
return 0 if envelope.get("ok") else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,53 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
from .errors import TrackerError # type: ignore[reportMissingImports]
|
||||
|
||||
SUPPORTED_PROVIDERS = ("github", "gitlab", "gitea")
|
||||
|
||||
|
||||
def _validate_provider(value):
|
||||
provider = (value or "").strip().lower()
|
||||
if provider not in SUPPORTED_PROVIDERS:
|
||||
raise TrackerError(
|
||||
"invalid_provider",
|
||||
f"unsupported provider: {value}",
|
||||
details={"supported": list(SUPPORTED_PROVIDERS)},
|
||||
)
|
||||
return provider
|
||||
|
||||
|
||||
def _providers_from_remote(remote):
|
||||
host_match = re.search(r"(?:https?://|ssh://|git@)([^/:]+)", remote or "")
|
||||
host = host_match.group(1).lower() if host_match else ""
|
||||
found = []
|
||||
if host == "github.com" or "github" in host:
|
||||
found.append("github")
|
||||
if host == "gitlab.com" or "gitlab" in host:
|
||||
found.append("gitlab")
|
||||
if "gitea" in host:
|
||||
found.append("gitea")
|
||||
return found
|
||||
|
||||
|
||||
def resolve_provider(explicit=None, *, env=None, remote=None):
|
||||
"""Resolve provider using CLI, environment, then remote precedence."""
|
||||
if explicit is not None:
|
||||
return _validate_provider(explicit)
|
||||
values = os.environ if env is None else env
|
||||
configured = values.get("TRACKER_PROVIDER")
|
||||
if configured:
|
||||
return _validate_provider(configured)
|
||||
matches = _providers_from_remote(remote or "")
|
||||
if len(matches) == 1:
|
||||
return matches[0]
|
||||
if not remote:
|
||||
raise TrackerError(
|
||||
"provider_detection_failed",
|
||||
"provider was not specified and no Git remote was available",
|
||||
)
|
||||
if len(matches) > 1:
|
||||
message = "Git remote matches multiple supported providers"
|
||||
else:
|
||||
message = f"unsupported or unrecognised Git remote: {remote}"
|
||||
raise TrackerError("provider_detection_failed", message, details={"remote": remote})
|
||||
@@ -0,0 +1,21 @@
|
||||
class TrackerError(Exception):
|
||||
"""A stable, user-facing tracker failure."""
|
||||
|
||||
def __init__(self, code, message, *, retryable=False, provider=None, operation=None, details=None):
|
||||
super().__init__(message)
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.retryable = retryable
|
||||
self.provider = provider
|
||||
self.operation = operation
|
||||
self.details = details or {}
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"code": self.code,
|
||||
"message": self.message,
|
||||
"retryable": self.retryable,
|
||||
"provider": self.provider,
|
||||
"operation": self.operation,
|
||||
"details": self.details,
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user