diff --git a/agent-lib.lock.json b/agent-lib.lock.json index bae51cd..1de563d 100644 --- a/agent-lib.lock.json +++ b/agent-lib.lock.json @@ -4,7 +4,7 @@ "mattpocock": { "url": "https://github.com/mattpocock/skills.git", "ref": "main", - "rev": "d81f3a183412e71a5b1e84ca21bc1a35eea03a60", + "rev": "b0618bc436ad893b3c5e84e55fba86586d34a404", "discovery": { "skills_dir": "skills" }, @@ -18,6 +18,7 @@ "inventory": { "skills": [ "ask-matt", + "chief-of-staff", "code-review", "codebase-design", "diagnosing-bugs", @@ -55,6 +56,87 @@ ] } }, + "officecli": { + "url": "https://github.com/iOfficeAI/OfficeCLI.git", + "ref": "main", + "rev": "8d079c777ed6bea35b922c3d893758d3c2081137", + "selection": { + "mode": "all", + "exclude": [ + "morph-ppt", + "morph-ppt-3d" + ], + "exclude_types": [ + "agents", + "commands", + "mcp" + ] + }, + "inventory": { + "skills": [ + "officecli", + "officecli-academic-paper", + "officecli-data-dashboard", + "officecli-docx", + "officecli-financial-model", + "officecli-pitch-deck", + "officecli-pptx", + "officecli-word-form", + "officecli-xlsx" + ] + } + }, + "okf": { + "url": "https://github.com/scaccogatto/okf-skills.git", + "ref": "main", + "rev": "8e3187875e66051bb52f91a5ed27342e2c3208da", + "selection": { + "mode": "all" + }, + "inventory": { + "agents": [ + "bundle-weaver", + "event-analyzer" + ], + "skills": [ + "backfill", + "okf", + "validate", + "visualize" + ] + }, + "warnings": [ + "agents/bundle-weaver: malformed frontmatter line (missing colon): Fold event analyses into an OKF knowledge bundle in deterministic sequence. This is the", + "agents/bundle-weaver: malformed frontmatter line (missing colon): concepts over time, enforce anti-degeneration rules, and guarantee no events are lost.", + "agents/bundle-weaver: malformed frontmatter line (missing colon): only agent that writes `.okf/`, executing the reduce phase of backfill replay to evolve", + "agents/event-analyzer: malformed frontmatter line (missing colon): # Retier by forking this file; the backfill skill resolves the agent by name.", + "agents/event-analyzer: malformed frontmatter line (missing colon): # on the condition that the two explicit instructions below (flag source, evidence-only claims) stay.", + "agents/event-analyzer: malformed frontmatter line (missing colon): Analyze a single repository history event (git commit or session turn) to extract domain", + "agents/event-analyzer: malformed frontmatter line (missing colon): concepts and semantic content. Use in parallel during the map phase of OKF backfill replay", + "agents/event-analyzer: malformed frontmatter line (missing colon): to materialize decision rationale from raw commit diffs and session outcomes.", + "skills/backfill: malformed frontmatter line (missing colon): Reconstruct an OKF bundle by event-sourcing a repository's history (git log", + "skills/backfill: malformed frontmatter line (missing colon): and Claude session transcripts). Use when creating an `.okf/` bundle for an", + "skills/backfill: malformed frontmatter line (missing colon): existing repository that predates this skill, or when resuming an interrupted", + "skills/backfill: malformed frontmatter line (missing colon): knowledge bundle\", \"event-source the history\".", + "skills/okf: malformed frontmatter line (missing colon): \"capture this as a concept\", or any work in a repo that has an OKF bundle.", + "skills/okf: malformed frontmatter line (missing colon): Author, maintain, and consume Open Knowledge Format (OKF) knowledge bundles —", + "skills/okf: malformed frontmatter line (missing colon): capturing project knowledge (services, APIs, schemas, metrics, runbooks,", + "skills/okf: malformed frontmatter line (missing colon): decisions) into an OKF bundle, when updating one after code or docs change, or", + "skills/okf: malformed frontmatter line (missing colon): portable markdown + YAML frontmatter that both humans and agents read. Use when", + "skills/okf: malformed frontmatter line (missing colon): when a repository contains an `.okf/` (or other OKF) bundle that should inform", + "skills/validate: malformed frontmatter line (missing colon): Check that an Open Knowledge Format (OKF) bundle is conformant with the v0.2", + "skills/validate: malformed frontmatter line (missing colon): committing changes to one. Runs a deterministic Python checker — not an", + "skills/validate: malformed frontmatter line (missing colon): eyeball pass. Also migrates a v0.1 bundle to v0.2 in place with `--migrate`.", + "skills/validate: malformed frontmatter line (missing colon): spec (§11). Use when asked to validate, lint, or check an OKF bundle, or before", + "skills/visualize: malformed frontmatter line (missing colon): Render an Open Knowledge Format (OKF) bundle as a single self-contained,", + "skills/visualize: malformed frontmatter line (missing colon): Use when asked to visualize, graph, preview, or explore an OKF bundle.", + "skills/visualize: malformed frontmatter line (missing colon): a wiki-style detail panel with rendered markdown, v0.2 trust/lifecycle/provenance", + "skills/visualize: malformed frontmatter line (missing colon): filter and search.", + "skills/visualize: malformed frontmatter line (missing colon): interactive HTML graph (viz.html) — concepts as nodes coloured by type and", + "skills/visualize: malformed frontmatter line (missing colon): metadata, and \"Links to\" / \"Cited by\" backlinks, layout switching, per-type", + "skills/visualize: malformed frontmatter line (missing colon): sized by body length, markdown links and bundle-internal `sources` as edges," + ] + }, "superpowers": { "url": "https://github.com/obra/superpowers.git", "ref": "main", diff --git a/external/mattpocock/skills/ask-matt/SKILL.md b/external/mattpocock/skills/ask-matt/SKILL.md index ce33ac6..4bfeeb6 100644 --- a/external/mattpocock/skills/ask-matt/SKILL.md +++ b/external/mattpocock/skills/ask-matt/SKILL.md @@ -8,6 +8,8 @@ disable-model-invocation: true You don't remember every skill, so ask. +Before stating what a skill does or recommending a step be skipped, read that skill's SKILL.md: the summaries here are for orientation only. + A **flow** is a path through the skills. Most paths run along one **main flow**, and two **on-ramps** merge onto it. Everything else is standalone, or a vocabulary layer that runs underneath. ## The main flow: idea → ship @@ -45,7 +47,7 @@ A starting situation that generates work, then merges onto the main flow. Triage is only for issues **you didn't create**: bug reports, incoming feature requests, anything that arrives raw. Tickets that `/to-tickets` produced are already agent-ready, so **don't triage them**. -- **Something's broken** → **`/diagnosing-bugs`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression that crept in between two known-good states. It refuses to theorise until it has a **tight feedback loop** (one command that already goes red on *this* bug), then fixes with a regression test. Its post-mortem hands off to **`/improve-codebase-architecture`** when the real finding is that there's no good seam to lock the bug down. +- **Something's broken** → **`/diagnosing-bugs`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression that crept in between two known-good states. It refuses to theorise until it has a **tight feedback loop** (one command that already goes red on *this* bug), then fixes with a regression test. Once the fix is in, run **`/retro`** in the same session to ask what would have prevented the bug; where the real finding is that there's no good seam to lock it down, that's a job for **`/improve-codebase-architecture`**. - **A huge, foggy effort: a greenfield project or a huge feature build, too big for one session** → **`/wayfinder`**, the most cognitively demanding flow here. When the way from here to the destination isn't visible yet, it charts a **shared map** of **decision tickets** on the issue tracker and resolves them one at a time, producing **decisions, not deliverables**, until the fog is pushed back and the way is clear. Where **`/grill-with-docs`** sharpens an idea you can hold in one session, wayfinder is for the idea you can't, and it's slower and denser, so save it for exactly that, never a well-scoped feature. diff --git a/external/mattpocock/skills/code-review/SKILL.md b/external/mattpocock/skills/code-review/SKILL.md index e28d7ac..373a4f2 100644 --- a/external/mattpocock/skills/code-review/SKILL.md +++ b/external/mattpocock/skills/code-review/SKILL.md @@ -10,7 +10,7 @@ Two-axis review of the diff between `HEAD` and a fixed point the user supplies: Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings. -The issue tracker should have been provided to you. If `docs/agents/issue-tracker.md` is missing, tell the user to run `/setup-matt-pocock-skills`. +The issue tracker should have been provided to you. If not, tell the user to run `/setup-matt-pocock-skills`. ## Process @@ -26,14 +26,14 @@ Before going further, confirm the fixed point resolves (`git rev-parse ``` +Word each question so "yes" accepts your recommended answer. + Each round the user answers reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one. Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it; don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report; ask the rest of the frontier now. The _decisions_ are the user's: put each to them and wait. diff --git a/external/mattpocock/skills/handoff/SKILL.md b/external/mattpocock/skills/handoff/SKILL.md index 2eb98a5..a224edc 100644 --- a/external/mattpocock/skills/handoff/SKILL.md +++ b/external/mattpocock/skills/handoff/SKILL.md @@ -5,7 +5,7 @@ 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. +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 (`$TMPDIR`, else `/tmp`; `%TEMP%` on Windows) - not the current workspace. Include a "suggested skills" section in the document, naming which skills the next agent should call the Skill tool for. diff --git a/external/mattpocock/skills/implement/SKILL.md b/external/mattpocock/skills/implement/SKILL.md index 7a0b11f..bd49e47 100644 --- a/external/mattpocock/skills/implement/SKILL.md +++ b/external/mattpocock/skills/implement/SKILL.md @@ -6,10 +6,12 @@ disable-model-invocation: true Implement the work described by the user in the spec or tickets. -Use /tdd where possible, at pre-agreed seams. +If the user passes a ticket reference, fetch it from the issue tracker and state its title before starting. If the reference is ambiguous, ask. + +Call the Skill tool with "tdd" where possible, at pre-agreed seams. Run typechecking regularly, single test files regularly, and the full test suite once at the end. -Once done, use /code-review to review the work. +Once done, call the Skill tool with "code-review" to review the work. Commit your work to the current branch. diff --git a/external/mattpocock/skills/setup-matt-pocock-skills/SKILL.md b/external/mattpocock/skills/setup-matt-pocock-skills/SKILL.md index c0d850b..293c25b 100644 --- a/external/mattpocock/skills/setup-matt-pocock-skills/SKILL.md +++ b/external/mattpocock/skills/setup-matt-pocock-skills/SKILL.md @@ -101,6 +101,8 @@ The block: Include the `### Triage labels` sub-block, and write `docs/agents/triage-labels.md`, only when `triage` is installed and Section B ran. When it isn't, both are omitted. +When Section B ran on GitHub or GitLab, create each configured label the tracker lacks (`gh label create` / `glab label create`). + Then write the docs files using the seed templates in this skill folder as a starting point: - [issue-tracker-github.md](./issue-tracker-github.md): GitHub issue tracker diff --git a/external/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-github.md b/external/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-github.md index b258aeb..a63510f 100644 --- a/external/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-github.md +++ b/external/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-github.md @@ -5,8 +5,9 @@ Issues and specs for this repo live as GitHub issues. Use the `gh` CLI for all o ## Conventions - **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies. -- **Read an issue**: `gh issue view --comments`, filtering comments by `jq` and also fetching labels. +- **Read an issue**: `gh issue view --json number,title,body,labels,comments`. - **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters. +- **Make an issue a sub-issue of a parent**: `gh issue create --parent ...`, or `gh issue edit --add-sub-issue ` afterwards (`gh` 2.94+). Older `gh`: `gh api --method POST repos///issues//sub_issues -F sub_issue_id=` (database id, as in **Blocking** below). Without sub-issues, put `Part of #` at the top of the child body. - **Comment on an issue**: `gh issue comment --body "..."` - **Apply / remove labels**: `gh issue edit --add-label "..."` / `--remove-label "..."` - **Close**: `gh issue close --comment "..."` @@ -20,7 +21,7 @@ Infer the repo from `git remote -v`; `gh` does this automatically when run insid When set to `yes`, PRs run through the same labels and states as issues, using the `gh pr` equivalents: - **Read a PR**: `gh pr view --comments` and `gh pr diff ` for the diff. -- **List external PRs for triage**: `gh pr list --state open --json number,title,body,labels,author,authorAssociation,comments` then keep only `authorAssociation` of `CONTRIBUTOR`, `FIRST_TIME_CONTRIBUTOR`, or `NONE` (drop `OWNER`/`MEMBER`/`COLLABORATOR`). +- **List external PRs for triage**: `gh api --paginate 'repos/{owner}/{repo}/pulls?state=open' --jq '.[] | select(.author_association | IN("OWNER","MEMBER","COLLABORATOR") | not) | {number, title, author: .user.login, author_association, labels: [.labels[].name]}'`. - **Comment / label / close**: `gh pr comment`, `gh pr edit --add-label`/`--remove-label`, `gh pr close`. GitHub shares one number space across issues and PRs, so a bare `#42` may be either: resolve with `gh pr view 42` and fall back to `gh issue view 42`. @@ -31,14 +32,14 @@ Create a GitHub issue. ## When a skill says "fetch the relevant ticket" -Run `gh issue view --comments`. +Read it as in **Read an issue** above. ## 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 #` at the top of the child body. Labels: `wayfinder:` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev. +- **Child ticket**: an issue linked to the map as a GitHub sub-issue (see **Make an issue a sub-issue of a parent**). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #` at the top of the child body. Labels: `wayfinder:` (`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///issues//dependencies/blocked_by -F issue_id=`, where `` is the blocker's numeric **database id** (`gh api repos///issues/ --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: #, #` 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 --add-assignee @me`, the session's first write. diff --git a/external/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md b/external/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md index 251035a..81499dc 100644 --- a/external/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +++ b/external/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md @@ -6,7 +6,7 @@ Issues and specs for this repo live as GitLab issues. Use the [`glab`](https://g - **Create an issue**: `glab issue create --title "..." --description "..."`. Use a heredoc for multi-line descriptions. Pass `--description -` to open an editor. - **Read an issue**: `glab issue view --comments`. Use `-F json` for machine-readable output. -- **List issues**: `glab issue list -F json` with appropriate `--label` filters. +- **List issues**: `glab issue list -O json` with appropriate `--label` filters. - **Comment on an issue**: `glab issue note --message "..."`. GitLab calls comments "notes". - **Apply / remove labels**: `glab issue update --label "..."` / `--unlabel "..."`. Multiple labels can be comma-separated or by repeating the flag. - **Close**: `glab issue close `. `glab issue close` does not accept a closing comment, so post the explanation first with `glab issue note --message "..."`, then close. @@ -41,6 +41,6 @@ Used by `/wayfinder`. The **map** is a single issue with **child** issues as tic - **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 #` at the top of its description and labels `wayfinder:` (`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 #` quick action, posted as a note (`glab issue note --message "/blocked_by #"`). Native blocking links are a Premium/Ultimate feature; on the free tier (or where unavailable) fall back to a `Blocked by: #, #` 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. +- **Frontier query**: `glab issue list -O 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//links`), or an open issue in the `Blocked by` line, or an assignee; first in map order wins. - **Claim**: `glab issue update --assignee @me`, the session's first write. - **Resolve**: `glab issue note --message ""`, then `glab issue close `, then append a context pointer (gist + link) to the map's Decisions-so-far. diff --git a/external/mattpocock/skills/setup-matt-pocock-skills/triage-labels.md b/external/mattpocock/skills/setup-matt-pocock-skills/triage-labels.md index b716855..5276464 100644 --- a/external/mattpocock/skills/setup-matt-pocock-skills/triage-labels.md +++ b/external/mattpocock/skills/setup-matt-pocock-skills/triage-labels.md @@ -12,4 +12,4 @@ The skills speak in terms of five canonical triage roles. This file maps those r 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. +Edit the "Label in our tracker" column to match whatever vocabulary you actually use. diff --git a/external/mattpocock/skills/tdd/SKILL.md b/external/mattpocock/skills/tdd/SKILL.md index 250871c..01eadaa 100644 --- a/external/mattpocock/skills/tdd/SKILL.md +++ b/external/mattpocock/skills/tdd/SKILL.md @@ -21,7 +21,7 @@ A **seam** is the public boundary you test at: the interface where you observe b **Test only at pre-agreed seams.** Before writing any test, write down the seams under test and confirm them with the user. No test is written at an unconfirmed seam. You can't test everything, so agreeing the seams up front is how testing effort lands on the critical paths and complex logic instead of every edge case. -Ask: "What's the public interface, and which seams should we test?" +Ask: "What's the public interface, and which seams should we test?" Give each proposed seam a one-line note on what it catches and what it misses. When the shape of that interface is itself in question (how deep the module is, where the seam belongs, what the interface should expose), call the Skill tool with "codebase-design" for the vocabulary. It is the shared source of the module, interface, depth, seam, adapter, leverage and locality terms, and it is a reference to consult, not a session to run. diff --git a/external/mattpocock/skills/teach/SKILL.md b/external/mattpocock/skills/teach/SKILL.md index c679eec..493fa3b 100644 --- a/external/mattpocock/skills/teach/SKILL.md +++ b/external/mattpocock/skills/teach/SKILL.md @@ -9,7 +9,7 @@ The user has asked you to teach them something. This is a stateful request - the ## Teaching Workspace -Treat the current directory as a teaching workspace. The state of their learning is captured in this directory in several files: +Treat the current directory as a teaching workspace. Workspace paths (`./lessons/` and the rest) resolve from the directory `/teach` was run in; only the `*-FORMAT.md` links resolve from this skill's folder. The state of their learning is captured in this directory in several files: - `MISSION.md`: A document capturing the _reason_ the user is interested in the topic. This should be used to ground all teaching. Use the format in [MISSION-FORMAT.md](./MISSION-FORMAT.md). - `./reference/*.html`: A directory of reference materials. These are the compressed learnings from the lessons - cheat sheets, reference algorithms, syntax, yoga poses, glossaries. They are the raw units of learning. They should be beautiful documents which print out well, and are designed for quick reference. @@ -107,7 +107,7 @@ For skill acquisition, difficulty is the tool. Effortful retrieval is what build Each of these should be based on a **feedback loop**, where the user receives feedback on their performance. This feedback loop should be as tight as possible, giving feedback immediately - and ideally automatically. -For quizzes, each answer should be exactly the same number of words (and characters, if possible). Don't give the user any clues about the answer through formatting. +For quizzes, each answer should be exactly the same number of words (and characters, if possible). Vary which position holds the correct answer across questions. Don't give the user any clues about the answer through formatting or order. ## Acquiring Wisdom diff --git a/external/mattpocock/skills/to-tickets/SKILL.md b/external/mattpocock/skills/to-tickets/SKILL.md index e868c83..9b77a01 100644 --- a/external/mattpocock/skills/to-tickets/SKILL.md +++ b/external/mattpocock/skills/to-tickets/SKILL.md @@ -60,7 +60,7 @@ Iterate until the user approves the breakdown. Publish the approved tickets. **How** depends on the tracker `/setup-matt-pocock-skills` configured; the tickets are the same either way, only the shape of the blocking edges changes: - **Local files** → write one file per ticket under `.scratch//issues/-.md`, numbered from `01` in dependency order (blockers first). Each file's "Blocked by" lists the numbers/titles it depends on. Use the per-ticket file template below: one ticket per file, never a single combined file. -- **A real issue tracker (GitHub, Linear, …)** → publish one issue per ticket in dependency order (blockers first) so each ticket's blocking edges can reference real identifiers. Use the platform's native blocking / sub-issue relationship where it has one; otherwise set each ticket's "Blocked by" to the blocking issues. Apply the `ready-for-agent` triage label unless instructed otherwise; the tickets are agent-grabbable by construction. +- **A real issue tracker (GitHub, Linear, …)** → publish one issue per ticket in dependency order (blockers first) so each ticket's blocking edges can reference real identifiers. Use the platform's native blocking relationship where it has one; otherwise set each ticket's "Blocked by" to the blocking issues. If the source was an existing issue, make each ticket its sub-issue (tracker doc's operation). Apply the `ready-for-agent` triage label unless instructed otherwise; the tickets are agent-grabbable by construction. Work the **frontier**: any ticket whose blockers are all done. For a purely linear chain that means top to bottom. @@ -98,7 +98,7 @@ The end-to-end behaviour this ticket makes work, from the user's perspective, no ## Blocked by -- A reference to each blocking ticket, or "None (can start immediately)". +- A reference to each blocking ticket, or "None (can start immediately)". Omit this section when blockers were set as native edges. diff --git a/external/mattpocock/skills/wayfinder/SKILL.md b/external/mattpocock/skills/wayfinder/SKILL.md index 812805b..e28cf30 100644 --- a/external/mattpocock/skills/wayfinder/SKILL.md +++ b/external/mattpocock/skills/wayfinder/SKILL.md @@ -62,7 +62,7 @@ Each ticket is a **child issue** of the map; the tracker's issue id is its ident ``` -Each ticket carries a `wayfinder:` label, one of `research`, `prototype`, `grilling`, `task` (see [Ticket Types](#ticket-types)). +Each ticket carries a `wayfinder:` label, one of `research`, `prototype`, `grilling`, `task` (see [Ticket Types](#ticket-types)). `wayfinder:` labels are the only labels a map and its tickets carry, never a triage label like `ready-for-agent`: they are decisions, not implementation work. A session **claims** a ticket by assigning it to the dev driving the map, **first**, before any work, so concurrent sessions skip it. That assignee _is_ the claim: an open, unassigned ticket is unclaimed. @@ -111,8 +111,8 @@ User invokes with a loose idea. 1. **Name the destination.** Call the Skill tool twice, for "grilling" and "domain-modeling", to pin down what this map is finding its way to: the spec, decision, or change. The destination fixes the scope, so it's settled first. 2. **Map the frontier.** Grill again, **breadth-first** this time: fan out across the whole space rather than deep on any one thread, surfacing the open decisions and the first steps takeable now. **If this surfaces no fog** (the way to the destination is already clear, the whole journey small enough for one session), you don't need a map. Stop and ask the user how they'd like to proceed. 3. **Create the map** (label `wayfinder:map`): Destination and Notes filled in, Decisions-so-far empty, the fog sketched into **Not yet specified**. -4. **Create the tickets you can specify now** as child issues of the map, then wire blocking edges in a **second pass** (issues need ids before they can reference each other). Wiring sorts them into the frontier and the blocked; everything you can't yet specify stays in the fog: the **Not yet specified** section. -5. **Fire the research subagents.** For each `research` ticket you just created, spin up a subagent that calls the Skill tool with "research" to resolve it in parallel, capturing its findings on a throwaway `research/` branch with a context pointer from the ticket. +4. **Create the tickets you can specify now** as child issues of the map, then wire blocking edges in a **second pass** (issues need ids before they can reference each other). Write cross-references in that pass too, with real ids: a placeholder `#` auto-links to an unrelated issue. Wiring sorts them into the frontier and the blocked; everything you can't yet specify stays in the fog: the **Not yet specified** section. +5. **Fire the research subagents.** For each `research` ticket you just created, spin up a subagent that calls the Skill tool with "research" to resolve it in parallel, capturing its findings on a throwaway `research/` branch with a context pointer from the ticket. Push the branch but open no PR: it is never merged. 6. Stop: charting is one session's work; it hand-resolves nothing. ### Work through the map @@ -121,7 +121,7 @@ User invokes with a map (URL or number). A ticket is **optional**: without one, 1. Load the **map**: the low-res view, not every ticket body. 2. Choose the ticket. If the user named one, use it. Otherwise take the first frontier ticket in order. **Claim it**: assign it to yourself before any work. -3. Resolve it. **Zoom as needed**: fetch the full body of any related or closed ticket on demand; call the Skill tool for whichever skills the `## Notes` block names. If in doubt, call the Skill tool twice, for "grilling" and "domain-modeling". +3. Resolve it as the type its `wayfinder:` label names (see [Ticket Types](#ticket-types)). Read the label, not just the body: the body never states the type. **Zoom as needed**: fetch the full body of any related or closed ticket on demand; call the Skill tool for whichever skills the `## Notes` block names. If in doubt, call the Skill tool twice, for "grilling" and "domain-modeling". 4. Record the resolution: post the answer as a **resolution comment**, **close** the issue, and **append a context pointer** to the map's Decisions-so-far. 5. Add newly-surfaced tickets (create-then-wire); graduate any fog the answer has made specifiable, clearing each graduated patch from **Not yet specified** so it lives only as its new ticket. If the answer reveals that a ticket (this one or another) sits beyond the destination, **rule it out of scope** rather than resolving it on the route. If the decision invalidates other parts of the map, update or delete those tickets. diff --git a/external/mattpocock/skills/wizard/template.sh b/external/mattpocock/skills/wizard/template.sh index 1b3cdf4..2d0de38 100644 --- a/external/mattpocock/skills/wizard/template.sh +++ b/external/mattpocock/skills/wizard/template.sh @@ -70,7 +70,7 @@ open_url() { elif command -v explorer.exe >/dev/null 2>&1; then explorer.exe "$url" elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$url" elif command -v open >/dev/null 2>&1; then open "$url" - else warn "couldn't open a browser; visit it manually: $url"; fi + else false; fi } >/dev/null 2>&1 || warn "couldn't open a browser, so visit it manually: $url" } @@ -88,52 +88,57 @@ confirm() { [[ "$reply" =~ ^[Yy] ]] } -# _existing KEY: current value of KEY in ENV_FILE, if any. +# _existing KEY: current value of KEY in ENV_FILE, if any, with write_env's quoting undone. _existing() { [[ -f "$ENV_FILE" ]] || return 1 - local line; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1 - printf '%s' "${line#*=}" + local line value sq="'\\''"; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1 + value="${line#*=}" + if [[ "$value" == \'*\' ]]; then value="${value:1:${#value}-2}"; value="${value//"$sq"/\'}"; fi + printf '%s' "$value" } # ask KEY "Prompt" reads a value into $KEY. Offers the existing .env value as -# a default on re-runs (Enter keeps it). Visible input (non-secret). +# a default on re-runs (Enter keeps it). Visible input (non-secret), edited +# with Readline. Fails at EOF with no input. ask() { - local key="$1" prompt="$2" current input + local key="$1" prompt="$2" current input s=$'\001' e=$'\002' p rc=0 current=$(_existing "$key" || true) - if [[ -n "$current" ]]; then - printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET" - else - printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET" - fi - read -r input || true + # Readline needs the colour codes wrapped in \001..\002 to place the cursor. + p=" $s$BOLD$e$prompt$s$RESET$e " + [[ -n "$current" ]] && p+="$s$DIM$e[Enter keeps current]$s$RESET$e " + read -e -r -p "$p" input || rc=1 + if (( rc )) && [[ -z "$input" ]]; then return 1; fi [[ -z "$input" && -n "$current" ]] && input="$current" printf -v "$key" '%s' "$input" } # ask_secret KEY "Prompt" is like ask, but input is hidden. ask_secret() { - local key="$1" prompt="$2" current input + local key="$1" prompt="$2" current input rc=0 current=$(_existing "$key" || true) if [[ -n "$current" ]]; then printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET" else printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET" fi - read -rs input || true + read -rs input || rc=1 printf '\n' + if (( rc )) && [[ -z "$input" ]]; then return 1; fi [[ -z "$input" && -n "$current" ]] && input="$current" printf -v "$key" '%s' "$input" } -# write_env KEY VALUE upserts KEY=VALUE into ENV_FILE (creates it; replaces -# any existing line). Idempotent. +# write_env KEY VALUE upserts KEY='VALUE' into ENV_FILE (replaces any existing +# line). Idempotent. Creates ENV_FILE at mode 0600; writes through a symlink +# and keeps an existing file's mode. write_env() { - local key="$1" value="$2" tmp - touch "$ENV_FILE" + local key="$1" value="$2" tmp sq="'\\''" + [[ -e "$ENV_FILE" ]] || (umask 077 && : > "$ENV_FILE") tmp=$(mktemp) grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true - printf '%s=%s\n' "$key" "$value" >> "$tmp" - mv "$tmp" "$ENV_FILE" + printf "%s='%s'\n" "$key" "${value//\'/$sq}" >> "$tmp" + cat "$tmp" > "$ENV_FILE" + rm -f "$tmp" WRITTEN_ENV+=("$key") printf ' %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE" }