@noir-ai/skills 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/builtin/noir-backend/SKILL.md +12 -0
- package/builtin/noir-brainstorm/SKILL.md +17 -0
- package/builtin/noir-branch/SKILL.md +12 -0
- package/builtin/noir-checkpoint/SKILL.md +18 -0
- package/builtin/noir-clarify/SKILL.md +17 -0
- package/builtin/noir-commit/SKILL.md +12 -0
- package/builtin/noir-context/SKILL.md +18 -0
- package/builtin/noir-debug/SKILL.md +38 -0
- package/builtin/noir-doctor/SKILL.md +12 -0
- package/builtin/noir-document/SKILL.md +17 -0
- package/builtin/noir-execute/SKILL.md +17 -0
- package/builtin/noir-explore/SKILL.md +16 -0
- package/builtin/noir-frontend/SKILL.md +12 -0
- package/builtin/noir-intake/SKILL.md +17 -0
- package/builtin/noir-parallel/SKILL.md +50 -0
- package/builtin/noir-plan/SKILL.md +20 -0
- package/builtin/noir-pr/SKILL.md +12 -0
- package/builtin/noir-readme/SKILL.md +12 -0
- package/builtin/noir-recall/SKILL.md +18 -0
- package/builtin/noir-remember/SKILL.md +23 -0
- package/builtin/noir-review/SKILL.md +28 -0
- package/builtin/noir-security/SKILL.md +12 -0
- package/builtin/noir-skill-author/SKILL.md +12 -0
- package/builtin/noir-spec/SKILL.md +22 -0
- package/builtin/noir-subagent/SKILL.md +47 -0
- package/builtin/noir-sync/SKILL.md +18 -0
- package/builtin/noir-tdd/SKILL.md +49 -0
- package/builtin/noir-test/SKILL.md +12 -0
- package/builtin/noir-verify/SKILL.md +17 -0
- package/builtin/noir-worktree/SKILL.md +12 -0
- package/builtin/noir-wrap/SKILL.md +12 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +131 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 agaaaptr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @noir-ai/skills
|
|
2
|
+
|
|
3
|
+
The builtin `noir-*` skill pack (31 skills across SDD lifecycle, power, session, git, and domain categories) plus a copy-and-validate compiler. `noir init` / `noir sync` emit the pack to the host's skill directory idempotently. There is no plugin or marketplace — only native `noir-` builtins.
|
|
4
|
+
|
|
5
|
+
Part of the **[Noir](https://github.com/agaaaptr/noir#readme)** toolkit — the discipline, context, and memory layer for any agentic CLI.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @noir-ai/skills
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
> Most users install the CLI instead, which emits the builtin skills via `noir init` / `noir sync`:
|
|
14
|
+
>
|
|
15
|
+
> ```bash
|
|
16
|
+
> npm install -g @noir-ai/cli
|
|
17
|
+
> ```
|
|
18
|
+
|
|
19
|
+
## License
|
|
20
|
+
|
|
21
|
+
MIT © agaaaptr
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-backend
|
|
3
|
+
description: Use when building APIs, database schemas, or server logic — for robust, scalable backend patterns.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-backend
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** you are building an API, a database schema, or server-side logic and want patterns that age well.
|
|
11
|
+
|
|
12
|
+
**For now:** model the data and the errors before the endpoints, validate at the boundary, and keep side effects out of pure logic.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-brainstorm
|
|
3
|
+
description: Use before any creative work — creating features, building components, or adding functionality — to explore intent, requirements, and design before implementation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Explore the user's intent, requirements, and design space before writing any code or plan. The goal is a shared, written understanding — not a shortcut to the first plausible build.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Restate the goal.** In one sentence, say what the user is trying to achieve and why.
|
|
10
|
+
2. **Surface requirements.** Ask clarifying questions (one batch) about scope, constraints, users, and non-goals. Prefer a structured prompt when the answers are constrained choices.
|
|
11
|
+
3. **Explore options.** Offer 2–3 distinct approaches with explicit trade-offs; do not collapse to a single path prematurely.
|
|
12
|
+
4. **Record decisions.** Capture the chosen direction and any open questions as a spec stub under `.noir/specs/`. `noir-spec` deepens it; the SDD engine records the brainstorm checkpoint observably.
|
|
13
|
+
5. **Hand off.** Point to `noir-spec`, then `noir-plan`.
|
|
14
|
+
|
|
15
|
+
## Notes
|
|
16
|
+
- Discipline is observable, not rhetorical — the SDD engine's gates record that brainstorming happened. This skill is the playbook.
|
|
17
|
+
- For a trivial one-liner, say so and skip; brainstorming is for genuinely creative or ambiguous work.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-branch
|
|
3
|
+
description: Use when implementation is complete and tests pass — to decide how to integrate (merge, PR, or cleanup).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-branch
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** implementation is complete, tests pass, and you need to decide how to land the work.
|
|
11
|
+
|
|
12
|
+
**For now:** verify green tests and a clean tree, then choose one of merge to main, open a PR, or discard the branch — confirm the choice with the user before acting.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-checkpoint
|
|
3
|
+
description: Use mid-session — to save in-flight state before a context-risky moment or interruption, so work survives.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Save in-flight state so work survives a context-risky moment (compaction, handoff, or interruption). Persists the task's phase, progress, and next step into `.noir/` and records the checkpoint observably via `noir.checkpoint`.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Find the task.** Locate the active task stub under `.noir/tasks/<id>.md` (the S4 engine's persisted state). If none, ask the user which task to checkpoint — or note "no active task — nothing to checkpoint" and stop.
|
|
10
|
+
2. **Capture state.** Update `.noir/tasks/<id>.md`: set the current phase, a status line (e.g. `checkpointed @ phase N`), an `updated:` timestamp, and fill *done so far* + *next steps* + any blockers. Append a one-line history entry so a fresh session can pick up cleanly.
|
|
11
|
+
3. **Record the checkpoint.** Call `noir.checkpoint` so the SDD engine records the save observably; the task stub is the source of truth, the checkpoint is the durable signal that survives context loss.
|
|
12
|
+
4. **Memory (best-effort).** Save the checkpoint to Noir memory (phase + key decisions + next step) if the host's memory tooling is available. Skip cleanly if not — `.noir/tasks/<id>.md` remains the durable record either way.
|
|
13
|
+
5. **Uncommitted work.** Note any uncommitted changes (`git status --porcelain`) in the checkpoint and advise the user — commit or stash before truly leaving, or the on-disk state and the working tree will diverge.
|
|
14
|
+
6. **Report.** State the task id, the phase, and the next step. Point to the SDD lifecycle to resume — do not invent a slash command.
|
|
15
|
+
|
|
16
|
+
## Fallbacks
|
|
17
|
+
- No active task → say so; nothing to checkpoint.
|
|
18
|
+
- `noir.checkpoint` or Noir memory unavailable → skip silently; the task stub is the source of truth and the engine degrades read-only rather than failing.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-clarify
|
|
3
|
+
description: Use when an idea or spec has ambiguities — to surface and resolve open questions before committing to an approach.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Resolve every open question and assumption into a confirmed shared understanding before any planning or code. This is the gate that prevents the rest of the lifecycle from building on a guess.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Collect the ambiguities.** Read the task stub under `.noir/tasks/` (or the draft spec under `.noir/specs/`). List every assumption you would otherwise carry forward as an explicit open question. Add the ones the intake phase flagged.
|
|
10
|
+
2. **Resolve in a focused batch.** Put the questions to the user in one round, grouped by theme — not a wall of one-by-one prompts. Record each Q&A pair verbatim in the task stub.
|
|
11
|
+
3. **State the shared understanding.** Print a concise *My understanding* summary: the task, the chosen approach, the files/modules it touches, and the conventions it honors. Keep it short enough that the user can actually review it.
|
|
12
|
+
4. **Wait for confirmation.** Do not proceed past this gate until the user confirms. The SDD engine records the clarify checkpoint observably via `noir.checkpoint` — confirmation is recorded, not asserted by you.
|
|
13
|
+
5. **Route forward.** With the gate cleared, hand to `noir-spec` to formalize, or directly to `noir-plan` if a spec already exists. On later doubt mid-build, return here rather than improvising.
|
|
14
|
+
|
|
15
|
+
## Notes
|
|
16
|
+
- One confirmed answer is worth ten inferred ones. If a question cannot be resolved, leave it open and say so — do not pick a default and proceed.
|
|
17
|
+
- The gate is procedural, not rhetorical: state the understanding, get confirmation, let the engine record it.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-commit
|
|
3
|
+
description: Use when creating a git commit — to scope changes logically and write a conventional-commit message.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-commit
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** you are about to commit and want changes scoped to one logical concern with a conventional-commit message (`type(scope): subject`).
|
|
11
|
+
|
|
12
|
+
**For now:** stage one logical change at a time (`git add <paths>`), write a concise conventional-commit message, and verify with `git status` before committing. Never bundle unrelated changes.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-context
|
|
3
|
+
description: Use when a question spans more files than fit in context — to seed the repo into a hybrid index once, then query it for windowed snippets instead of reading whole files.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Ground a question in the whole repo without pulling every file into context. Seed the index once with `context_index`, then query it with `context_search` and reason over the returned windowed snippets. Snippets are extracted around each match and never truncated, so the surrounding line context is intact without a whole-file read.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Seed the index.** Call `context_index { paths: ["src", "docs"] }` with the trees you need grounded, or omit `paths` to index the project root. Indexing is incremental by SHA-256 content-hash — unchanged files are skipped, so re-call it freely after edits. It must run once before `context_search` returns anything useful.
|
|
10
|
+
2. **Query, do not dump.** Call `context_search { query, budgetTokens }` with a natural-language or identifier query (e.g. `"ContextEngine"` or `"where are errors thrown"`). Set `budgetTokens` to what you can spare (default 4096); optionally pass `source` to scope both legs to one bucket such as `"docs"` or `"codebase"`. You get ranked hits — path, a windowed snippet, a score — fused from BM25 and vector similarity, not raw file dumps.
|
|
11
|
+
3. **Reason over the snippets.** Read the returned windows and decide; open a file by path only when a snippet implicates a change. Everything else stays a citation.
|
|
12
|
+
4. **Re-seed when stale.** After non-trivial edits, re-run `context_index` so the index reflects current content. The content-hash skip keeps it cheap; staleness is the only way a snippet drifts from the code on disk.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
- **Degraded is honest, not broken.** With no embedder configured (or the local model failed to load), `context_search` still works in BM25-only mode and returns `degraded: true, mode: "bm25-only"` — lexical recall with no semantic leg. A read-only store (daemon down) still serves `context_search` and `context_status`; only `context_index` is fenced off, returning a clear `ok: false, degraded: true` envelope rather than failing mid-write.
|
|
16
|
+
- **Check health first when unsure.** `context_status` reports `docCount`, `vecCount`, `indexedFiles`, the active embedder (`kind` / `model` / `dim`), and the persistent `degraded` flag — read it once to know whether the index is populated and whether to expect vectors.
|
|
17
|
+
- **Tools absent?** The three tools are registered only when the daemon was started with context enabled (the `ctx.context` service). If they are missing, run `noir init` and start the daemon before reaching for this skill.
|
|
18
|
+
- Keep raw search output out of context where the tool already did the windowing; reach for whole-file reads only for the few files you will actually edit (see `noir-explore`). This skill grounds a query in indexed content — it does not review or refactor what it finds; hand to `noir-review` for judgment.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-debug
|
|
3
|
+
description: Use when encountering any bug, test failure, or unexpected behavior — before proposing a fix.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Find root cause before attempting any fix. Symptom patches waste time and create new bugs; the path is investigate → hypothesize → test → fix, in that order. Skipping ahead is the most expensive move in debugging.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
|
|
10
|
+
### 1. Reproduce and read
|
|
11
|
+
- **Read the error completely.** Stack traces, line numbers, file paths, error codes — do not skim past the message; it usually narrows the search immediately.
|
|
12
|
+
- **Reproduce reliably.** Find the minimal, repeatable trigger. If you cannot reproduce it, gather more data; do not guess at a fix from a single trace.
|
|
13
|
+
- **Check recent changes.** `git diff` and recent commits often point straight at the cause. Note new dependencies, config edits, and environmental differences.
|
|
14
|
+
|
|
15
|
+
### 2. Gather evidence at component boundaries
|
|
16
|
+
When the system has multiple layers (CLI → adapter → daemon → store, or API → service → database), instrument each boundary before proposing fixes: log what enters and what exits, verify config propagation, check state at each layer. Run once, read the output, then investigate the specific layer the evidence implicates.
|
|
17
|
+
|
|
18
|
+
### 3. Trace data flow to the source
|
|
19
|
+
For a bad value deep in a call stack, trace backward: where does it originate, who passed it down, who created it. Fix at the source, not at the symptom. Resist the urge to patch where the symptom surfaces.
|
|
20
|
+
|
|
21
|
+
### 4. Find the pattern
|
|
22
|
+
Locate similar working code in the same codebase and list every difference from the broken path — however small. When applying a known pattern, read the reference implementation completely; partial understanding is a common cause of the bug you are now debugging.
|
|
23
|
+
|
|
24
|
+
### 5. Form one hypothesis and test minimally
|
|
25
|
+
State the hypothesis explicitly ("I think X is the root cause because Y"). Make the smallest possible change that would prove or disprove it — one variable at a time, never a bundle. If the hypothesis survives the minimal test, proceed; if not, form a new hypothesis with the new information. Do not stack fixes on top of a failed one.
|
|
26
|
+
|
|
27
|
+
### 6. Fix at the root, with a regression test
|
|
28
|
+
- Write the simplest failing test that reproduces the bug (see `noir-tdd`).
|
|
29
|
+
- Implement the single fix at the root cause.
|
|
30
|
+
- Verify the test goes green and that no other test regressed.
|
|
31
|
+
- One change at a time; no "while I'm here" refactors bundled into the fix.
|
|
32
|
+
|
|
33
|
+
### 7. Know when to stop fixing
|
|
34
|
+
If three or more fixes have failed, each revealing a new problem in a different place, the issue is likely architectural, not a missing patch. Stop, name the pattern that is not holding, and surface the architectural question (with evidence) instead of attempting fix number four.
|
|
35
|
+
|
|
36
|
+
## Notes
|
|
37
|
+
- Discipline is observable, not rhetorical: the SDD engine records the debug gate via `noir.checkpoint` with the failing test and the eventual fix attached.
|
|
38
|
+
- Most "no root cause found" cases are incomplete investigations. If the issue is genuinely environmental or timing-dependent after a complete pass, document what you investigated, implement appropriate handling (retry, timeout, error message), and add monitoring — but say so plainly rather than implying the root cause is unknowable.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-doctor
|
|
3
|
+
description: Use when diagnosing environment or project health — deps, config, runtime, toolchain.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-doctor
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** something is off in the environment or project and you want a structured health check.
|
|
11
|
+
|
|
12
|
+
**For now:** check installed deps and versions against manifests, inspect config files, confirm the runtime and toolchain resolve, and report concrete mismatches — the `noir doctor` CLI command is the entry point.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-document
|
|
3
|
+
description: Use when closing a work session — to update docs, CHANGELOG, decisions, and memory before wrapping up.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Close a work session by bringing durable docs, decisions, and memory in line with what actually shipped, then committing per scope. The terminal phase of the SDD lifecycle.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Survey what actually changed.** Run `git status` and `git diff --stat`. List the files touched and the contracts, modules, or behaviors affected. The diff is the source of truth for what docs need updating — not memory of what was intended.
|
|
10
|
+
2. **Update durable docs to match.** Write decisions to `.noir/decisions/`, update handoff notes and API/contract docs where they drift, and add a CHANGELOG entry if the project keeps one. State the change in user-observable terms.
|
|
11
|
+
3. **Curate ephemeral notes.** Promote anything durable from scratch notes into permanent docs; delete the rest. Confirm the promote/delete list with the user before acting — do not unilaterally delete.
|
|
12
|
+
4. **Commit per scope; confirm push.** One conventional commit per logical change; never bundle unrelated work. Confirm with the user before pushing.
|
|
13
|
+
5. **Record and report.** Capture the session outcome via `noir.checkpoint` (the SDD engine's document gate — terminal). Report: test result (with evidence from `noir-verify`), docs updated, commits, push status, and any unfinished work.
|
|
14
|
+
|
|
15
|
+
## Notes
|
|
16
|
+
- Documentation tracks the diff, not the plan. If the diff and the plan disagree, document what shipped and flag the gap.
|
|
17
|
+
- Memory and decisions are written for the next session, not for this one — keep them concise and factual.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-execute
|
|
3
|
+
description: Use when executing a written implementation plan, task by task — driving the SDD execute phase.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Execute an approved implementation plan one task at a time, with a review gate between tasks. The plan is the contract; this skill drives it.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Load the plan.** Read the plan file under `.noir/plans/`. If none, stop and point to `noir-plan`.
|
|
10
|
+
2. **One task at a time.** Work the tasks in order. For each task: read its files/interfaces, write the failing test, implement minimally, run the test, commit when green.
|
|
11
|
+
3. **Review between tasks.** After each task, run typecheck + lint + tests and review the diff before moving on. The SDD engine records the execute-phase checkpoint observably (`noir.checkpoint`).
|
|
12
|
+
4. **Stay in scope.** Commit per logical change with a conventional-commit message; never bundle unrelated changes. If you discover missing scope, return to `noir-clarify` / `noir-plan` rather than improvising.
|
|
13
|
+
5. **Hand off to verify.** When all tasks are done, point to `noir-verify` before claiming completion.
|
|
14
|
+
|
|
15
|
+
## Notes
|
|
16
|
+
- Discipline is observable, not rhetorical: the SDD engine's gates record that each task was reviewed. This skill is the playbook.
|
|
17
|
+
- On doubt mid-execution, return to the plan or clarify — do not guess.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-explore
|
|
3
|
+
description: Use when answering means sweeping many files, directories, or naming conventions — to fan out read-only search and return the conclusion, not the file dumps.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Locate code across the codebase without reading every file into context. Prefer a read-only search fan-out that returns locations and a short conclusion; read whole files only for the few you actually need to edit.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Frame the question.** State precisely what you are looking for — a symbol, a pattern, a naming convention, or where something is defined. A fuzzy query yields a fuzzy sweep.
|
|
10
|
+
2. **Fan out.** Use `grep` / `glob`, or dispatch a read-only search subagent across the likely locations. State the search breadth up front (medium for a focused module, very thorough for cross-package naming or convention questions).
|
|
11
|
+
3. **Return the conclusion.** Summarize where things live with `file:line` references and the one-line gist of each match — not the raw matched lines. Call out conventions discovered (e.g. "errors are thrown in `src/errors/`, not at call sites").
|
|
12
|
+
4. **Read targeted.** Open only the specific files or line ranges you need to act on. Everything else stays a citation.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
- Keep raw search output out of context where possible — process it in a sandbox or subagent and surface only the derived answer. Large `grep` dumps cost context for the rest of the session.
|
|
16
|
+
- This skill locates code — it does not review, audit, or refactor it. Hand to `noir-review` for judgment on what you found.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-frontend
|
|
3
|
+
description: Use when building or reshaping UI — for distinctive visual design, typography, and component patterns.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-frontend
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** you are building or reshaping a UI and want intentional, distinctive design rather than templated defaults.
|
|
11
|
+
|
|
12
|
+
**For now:** choose typography and a restrained palette deliberately, compose from primitives over chasing components, and let hierarchy and spacing carry the design.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-intake
|
|
3
|
+
description: Use when starting a new feature or task from a raw idea, ticket, or issue — before any design or code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Capture a raw request as a grounded task stub before any design or implementation. The job here is to record what is actually known and flag what is not — not to solve, design, or paraphrase ambiguity away.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Capture the raw input.** Take the idea, ticket, or issue text as-is. Quote the originating words; do not silently summarize. If the source is a paste, keep the paste.
|
|
10
|
+
2. **Ground in project context.** Read `CLAUDE.md` / `AGENTS.md`, recent commits, and any neighbor spec under `.noir/specs/`. Note which existing module or package this request most likely touches — as a hypothesis, not a decision.
|
|
11
|
+
3. **Draft the task stub.** Write `.noir/tasks/<id>.md` with: problem statement (in the user's words), rough acceptance criteria (observable, not vague), likely touch points, and an explicit *Open questions* list. Every blank becomes an open question — never an assumption.
|
|
12
|
+
4. **Start the SDD task.** Call `noir.workflow_status` to confirm the task is at the `intake` phase. The engine records the intake checkpoint observably via `noir.checkpoint`; you do not need to assert it.
|
|
13
|
+
5. **Hand off.** Point to `noir-clarify` to resolve the open questions. Do not design, plan, or code in this phase.
|
|
14
|
+
|
|
15
|
+
## Notes
|
|
16
|
+
- Intake is a recording step, not a solution step. If you cannot tell whether something is a requirement, write it as an open question.
|
|
17
|
+
- Do not invent acceptance criteria to fill blanks — surface them. `noir-clarify` exists to close them.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-parallel
|
|
3
|
+
description: Use when facing two or more independent tasks with no shared state or ordering — to work them concurrently.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Dispatch one focused subagent per independent problem and let them run concurrently. Each subagent gets precisely crafted scope and context — never the controller's session history — so it stays narrow and you preserve your own context for coordination. Sequential investigation of independent problems wastes wall-clock; parallel dispatch collapses that to the slowest agent.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
|
|
10
|
+
### 1. Confirm the tasks are genuinely independent
|
|
11
|
+
Before dispatching, verify:
|
|
12
|
+
- each problem can be understood without context from the others;
|
|
13
|
+
- fixing one cannot fix or break another;
|
|
14
|
+
- the agents will not edit the same files or contend on the same resource (ports, locks, the working tree).
|
|
15
|
+
|
|
16
|
+
If the failures are related, or you do not yet know what is broken, do not parallelize — investigate together first. Shared state and exploratory debugging disqualify this skill.
|
|
17
|
+
|
|
18
|
+
### 2. Group by independent domain
|
|
19
|
+
Cluster the work by what is actually broken or what is actually being built: one test file or subsystem per agent. "Fix all the tests" is too broad; "fix the 3 failing tests in `agent-tool-abort.test.ts`" is a domain.
|
|
20
|
+
|
|
21
|
+
### 3. Craft each task
|
|
22
|
+
Each dispatch carries:
|
|
23
|
+
- **Scope** — the exact file(s) or subsystem; name them.
|
|
24
|
+
- **Goal** — what "done" looks like (tests green, function added with signature X).
|
|
25
|
+
- **Context** — the error messages, test names, or interface contract the agent needs. Do not make it re-derive what you already know.
|
|
26
|
+
- **Constraints** — what it must not touch ("production code", "other test files"), and any anti-patterns to refuse ("do not just raise the timeout — find the real issue").
|
|
27
|
+
- **Output** — what to return (a summary of root cause and the change made, plus the test evidence).
|
|
28
|
+
|
|
29
|
+
### 4. Dispatch in parallel
|
|
30
|
+
Issue every subagent dispatch in the same response — multiple dispatches in one response run concurrently, one per response runs sequentially. This is the move that buys the time saving.
|
|
31
|
+
|
|
32
|
+
### 5. Review and integrate
|
|
33
|
+
When the agents return:
|
|
34
|
+
- read each summary;
|
|
35
|
+
- check the diffs for conflicts — did two agents edit the same code?;
|
|
36
|
+
- run the full suite (not just each agent's scoped tests) to verify the fixes compose;
|
|
37
|
+
- spot-check the changes — a subagent can make a systematic error that its own scoped tests do not catch.
|
|
38
|
+
|
|
39
|
+
If two agents touched overlapping code, reconcile manually and re-run before claiming green.
|
|
40
|
+
|
|
41
|
+
## When not to use
|
|
42
|
+
- The failures are related — fixing one might fix or break another.
|
|
43
|
+
- Understanding requires seeing the whole system at once.
|
|
44
|
+
- The work is exploratory — you do not yet know what is broken.
|
|
45
|
+
- Agents would contend (editing the same files, binding the same port, taking the same lock).
|
|
46
|
+
|
|
47
|
+
## Notes
|
|
48
|
+
- Parallel dispatch trades coordination cost for wall-clock. For two small tasks, the overhead of crafting two briefs may not be worth it — sequential is fine. The win compounds at three or more independent domains.
|
|
49
|
+
- Each agent's report is its deliverable; the controller's job at the end is integration and the full-suite check, not re-doing the investigation.
|
|
50
|
+
- Discipline is observable, not rhetorical: the SDD engine records the parallel dispatch and its integrated result via `noir.checkpoint` (the SDD execute gate).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-plan
|
|
3
|
+
description: Use when you have an approved spec and need a step-by-step implementation plan — before touching code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Write a step-by-step implementation plan from the approved spec, sized so each task is one reviewable commit. The plan is what `noir-execute` drives; make it complete enough that execution needs no new design decisions.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Read the spec.** Open the approved spec under `.noir/specs/`. Every requirement, acceptance criterion, and non-goal must map to at least one task — if a requirement has no task, add one before you consider the plan done.
|
|
10
|
+
2. **Lay out tasks in execution order.** For each task, write:
|
|
11
|
+
- **Files** — exact paths to create and modify (with line ranges where relevant).
|
|
12
|
+
- **Interfaces** — what the task consumes from earlier tasks (exact signatures) and what it produces for later ones (exact names and types). This block is how a neighbor task learns the contract.
|
|
13
|
+
- **Test** — the failing test that proves the task, and the command that runs it.
|
|
14
|
+
3. **Right-size each task.** A task is one reviewable commit. Split anything that bundles unrelated changes; merge anything that is just a sub-step of a single change. Prefer bite-sized tasks that can fail and pass independently.
|
|
15
|
+
4. **Self-review the plan.** Check spec coverage (point to a task per requirement), placeholder scan (no TBD / TODO / vague "implement the thing"), and type consistency across tasks (a function called `clearLayers()` in Task 3 must still be `clearLayers()` in Task 7). Fix inline.
|
|
16
|
+
5. **Write and hand off.** Save to `.noir/plans/<topic>.md`. The SDD engine records the plan checkpoint observably via `noir.checkpoint`. Point to `noir-execute`.
|
|
17
|
+
|
|
18
|
+
## Notes
|
|
19
|
+
- Assume the implementer is a skilled developer with zero context for this codebase. Document the paths, names, and test commands explicitly — no placeholders, no "you know what I mean".
|
|
20
|
+
- If you cannot complete a task without a new design decision, the spec is incomplete — route back to `noir-spec` rather than deciding in the plan.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-pr
|
|
3
|
+
description: Use when committing, pushing, and opening a pull request in one flow.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-pr
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** the work is commit-ready and you want to push it and open a PR end-to-end.
|
|
11
|
+
|
|
12
|
+
**For now:** confirm the commit is sound, confirm the push target with the user, push the branch, then open a PR with `gh pr create` — title and body describing what changed and why.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-readme
|
|
3
|
+
description: Use when generating or updating a README or docs from the codebase.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-readme
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** the README is missing, stale, or out of sync with what the code actually does.
|
|
11
|
+
|
|
12
|
+
**For now:** read the manifests, entry points, and existing docs, then write a README that reflects the real setup and usage — install, run, test, and the one or two commands that matter — without inventing features.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-recall
|
|
3
|
+
description: Use when starting a task, before re-deriving something that may already be known — to recall a prior decision, pattern, bug, or fact from Noir's cross-session memory.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Recall what past sessions already learned before re-deriving it. Noir's memory is a local, project-scoped store of observations (pattern / preference / architecture / bug / workflow / fact / decision / lesson) you saved in earlier sessions. Querying it first avoids repeat work and keeps decisions stable across sessions.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Query with intent.** Call `memory_recall { query, limit }` with a natural-language or identifier query (e.g. `"auth token storage"`, `"why sqlite-vec over pgvector"`, `"ContextEngine embed"`). The hybrid path fuses BM25 + vector similarity via RRF and returns ranked observations scoped to memory only — context-index rows never leak in.
|
|
10
|
+
2. **Use the instant path for a quick check.** When you only need "does anything mention X" and want no embedding cost, call `memory_search { query }` — BM25-only, faster, still scoped to memory.
|
|
11
|
+
3. **Read the full content, not the snippet.** Every returned observation carries its complete `content` (never truncated). Reason over the full text plus its `concepts`, `files`, `type`, and `ts`; cite the observation rather than paraphrasing it loosely.
|
|
12
|
+
4. **Scope when useful.** Pass `type` to filter to one kind (e.g. `"decision"`) or `session_id` to limit to one session, when the query is broad.
|
|
13
|
+
5. **Act, then stop.** If recall surfaces a relevant decision or pattern, follow it; if it contradicts the current plan, surface the conflict before proceeding. If nothing relevant returns, say so plainly and proceed — absence is not a hidden result.
|
|
14
|
+
|
|
15
|
+
## Notes
|
|
16
|
+
- **Degraded is honest, not broken.** With no embedder configured (or the local model failed to load), `memory_recall` still works in BM25-only mode. A read-only store (daemon down) still serves `memory_recall` and `memory_search`; only writes are fenced off.
|
|
17
|
+
- **Hydrate, never truncate.** Recall hydrates each hit from the authoritative store row, so what you read is the full original text — the BM25 snippet is only the preview that ranked it.
|
|
18
|
+
- **Tools absent?** The memory tools are registered only when the daemon was started with memory enabled (the `ctx.memory` service). If they are missing, run `noir init` and start the daemon. To forget something recalled here, see `noir-remember`'s forget path.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-remember
|
|
3
|
+
description: Use when an insight, decision, pattern, or bug worth keeping surfaces, or the user says "remember this" / "save this" — to persist it to Noir's cross-session memory.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Persist a durable observation to Noir's cross-session memory so a future session can recall it. Saving is always local and free — no network call, no LLM call. The observation is written to the local store and recalled later via `noir-recall`.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Decide it is worth keeping.** Save observations that will matter beyond this session: a decision and its rationale, a confirmed pattern or convention, a hard-won bug fix, a project architecture fact, a workflow that worked. Do not save transient status, scratch reasoning, or anything already obvious from the code.
|
|
10
|
+
2. **Call `memory_save`.** `memory_save { content, type?, concepts?, files?, importance?, session_id? }`. Only `content` is required; the rest sharpen recall:
|
|
11
|
+
- `content` — the full insight in prose. Write it to be readable cold, with the why, not just the what. It is never truncated.
|
|
12
|
+
- `type` — `pattern` | `preference` | `architecture` | `bug` | `workflow` | `fact` | `decision` (unknown values are accepted). Pick the closest fit; `lesson` is reserved for consolidation output.
|
|
13
|
+
- `concepts` — short tags that a future query would use (e.g. `["auth", "sqlite-vec"]`).
|
|
14
|
+
- `files` — repo-relative paths the insight concerns.
|
|
15
|
+
- `importance` — 0..1 (default 0.5); raise it for load-bearing decisions.
|
|
16
|
+
3. **Confirm the round-trip.** After a non-trivial save, a quick `memory_recall { query }` with a phrase from the content confirms it landed and is retrievable.
|
|
17
|
+
4. **Forget when wrong.** If something saved is outdated or wrong, remove it with `memory_forget { ids: [...] }` using the id returned from `memory_save`. The row and its search indexes are purged.
|
|
18
|
+
|
|
19
|
+
## Notes
|
|
20
|
+
- **Local + free, by design.** Saving performs only a local store write plus a local embedding (when an embedder is configured). It never makes a paid call; the only LLM surface in memory is consolidation, which is separately provider-gated and off by default.
|
|
21
|
+
- **Append-only; edit by forgetting.** Memory does not mutate rows in place. To correct an observation, forget the old one and save the new one — the audit trail stays clean.
|
|
22
|
+
- **Read-only store.** If the daemon is down the store opens read-only and `memory_save` refuses cleanly rather than failing mid-write; start the daemon (`noir daemon start`) and retry.
|
|
23
|
+
- **Tools absent?** The memory tools register only when the daemon was started with memory enabled. If they are missing, run `noir init` and start the daemon.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-review
|
|
3
|
+
description: Use when completing a task or before merging — to verify the work meets its requirements.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Run a real review on completed work before claiming it done. This skill covers both halves: requesting a review with precisely crafted context, and receiving review feedback with technical rigor rather than performative agreement. The reviewer gets the work product, never your session's reasoning history.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
|
|
10
|
+
### Requesting a review
|
|
11
|
+
1. **Scope the diff.** Capture `BASE_SHA` (the commit the work started from — never `HEAD~1`, which silently truncates a multi-commit task) and `HEAD_SHA`. Write a one-line description of what was built and a pointer to the plan or requirements it must satisfy.
|
|
12
|
+
2. **Dispatch a reviewer with crafted context, not session history.** Hand the reviewer: the description, the plan-or-requirements pointer, the BASE/HEAD shas, and the diff as a file (e.g. `git diff <BASE> <HEAD> -U10 > .noir/reviews/<task>-diff.patch`). A fresh reviewer seeing only the work product stays focused and preserves your context for continued work.
|
|
13
|
+
3. **Triage findings by severity.** Fix Critical issues immediately, fix Important issues before moving on, and record Minor issues for the final whole-branch pass. Do not batch them all into one fix run.
|
|
14
|
+
4. **Push back when the reviewer is wrong.** If a finding is technically incorrect for this codebase, lacks context, or violates an accepted decision, respond with technical reasoning — working tests, code that proves the existing behavior, or the spec line that mandates the current shape. Do not implement a suggestion just because a reviewer raised it.
|
|
15
|
+
|
|
16
|
+
### Receiving feedback
|
|
17
|
+
5. **Read, then verify — do not react.** Restate the requirement in your own words (or ask for clarification) before implementing. Check each item against the codebase: is it technically correct here, does it break existing behavior, does it conflict with a prior decision?
|
|
18
|
+
6. **Clarify unclear items as a batch.** If part of a multi-item review is unclear, stop and ask about every unclear item before implementing any of them — items are often related, and partial understanding produces wrong implementation.
|
|
19
|
+
7. **YAGNI-check "implement it properly" suggestions.** Before adding a suggested abstraction or endpoint, grep the codebase for actual usage. If nothing calls it, propose removal before proposing the proper version.
|
|
20
|
+
8. **Acknowledge correct feedback quietly.** State the fix ("Fixed: extracted the magic number to `PROGRESS_INTERVAL`"). No performative agreement, no thanks — the code itself is the acknowledgment.
|
|
21
|
+
|
|
22
|
+
### Close the loop
|
|
23
|
+
9. **Record the review.** Capture the verdict, the findings acted on, and any deferred Minor items via `noir.checkpoint` (the SDD verify gate). For the final whole-branch review, point `MERGE_BASE = git merge-base main HEAD` so the reviewer sees the entire branch.
|
|
24
|
+
|
|
25
|
+
## Notes
|
|
26
|
+
- External feedback is a suggestion to evaluate, not an order to follow. Verify, question, then implement.
|
|
27
|
+
- The reviewer and the implementer both report to the work's requirements. If a suggestion violates the spec or adds unused surface area, the answer is evidence-based pushback, not compliance.
|
|
28
|
+
- Discipline is observable, not rhetorical: the SDD engine records that the verify gate ran, with the diff and verdict attached.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-security
|
|
3
|
+
description: Use when reviewing changes for security vulnerabilities — injection, auth, SSRF, data exposure.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-security
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** you are reviewing a change and want to check it for the common, exploitable classes before merging.
|
|
11
|
+
|
|
12
|
+
**For now:** scan for injection (SQL/command/template), broken auth and authz, SSRF in any outbound call, and sensitive data in logs or responses — flag concretely, do not rubber-stamp.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-skill-author
|
|
3
|
+
description: Use when creating new skills or editing existing ones — TDD for process docs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-skill-author
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** you are authoring or revising a skill and want it loadable, valid, and genuinely useful when invoked.
|
|
11
|
+
|
|
12
|
+
**For now:** frontmatter is `name: noir-<kebab>` plus a real WHEN `description`, a short body that tells the model when and how to act, and a reference file only if the skill needs one — validate before shipping.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-spec
|
|
3
|
+
description: Use when turning a brainstormed idea into a formal spec (what / why / acceptance / non-goals) — before planning or code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Turn the confirmed intake into a written spec that a planner (and later an implementer) can work against without re-litigating decisions. The spec is the contract for everything downstream.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Read the resolved intake.** Pull the task stub and any clarification Q&A from `.noir/tasks/`. If anything is still ambiguous, route back to `noir-clarify` instead of guessing here.
|
|
10
|
+
2. **Draft the spec sections.** Write, one section each and no filler:
|
|
11
|
+
- **Problem** — why this work exists, in user terms.
|
|
12
|
+
- **Scope** — what is being built; the surface area.
|
|
13
|
+
- **Acceptance criteria** — observable, each one a check an implementer can run.
|
|
14
|
+
- **Non-goals** — what is explicitly out of scope, to protect the plan.
|
|
15
|
+
- **Open questions** — anything genuinely unresolved, named not hidden.
|
|
16
|
+
3. **Where approaches are live, pick one.** Name the alternatives, state the trade-off in one line, and choose with a reason. Record the rejected options briefly — the planner needs to know the decision is settled.
|
|
17
|
+
4. **Self-review the draft.** Scan for placeholders (TBD, TODO, vague nouns), internal contradictions (does the scope match the acceptance criteria?), scope-fit (is this one plan, or should it decompose?), and ambiguity (could any line be read two ways?). Fix inline.
|
|
18
|
+
5. **Write and hand off.** Save to `.noir/specs/<topic>.md`. The SDD engine records the spec checkpoint observably via `noir.checkpoint`. Point to `noir-plan` for the implementation plan.
|
|
19
|
+
|
|
20
|
+
## Notes
|
|
21
|
+
- A spec is done when an implementer can plan against it without coming back for clarification. If you would need to ask, the spec is not done.
|
|
22
|
+
- Rejected approaches are part of the spec — write them down so they do not come back as new ideas.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-subagent
|
|
3
|
+
description: Use when executing an implementation plan with independent tasks — to drive a fresh subagent per task with review between.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Execute an implementation plan by dispatching a fresh implementer subagent per task, a task review (spec compliance + code quality) after each, and a broad whole-branch review at the end. The controller curates exactly what each subagent needs; subagents never inherit the controller's session history, and artifacts move as files rather than pasted text. This keeps each subagent focused and preserves the controller's context for coordination.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
|
|
10
|
+
### Pre-flight
|
|
11
|
+
1. **Read the plan once.** Note the global constraints (exact values, formats, cross-task relationships) and any conflicts between tasks. If you find tasks that contradict each other or the plan's own constraints, surface the whole batch to the user as one question before dispatching Task 1 — not one interrupt per conflict mid-plan.
|
|
12
|
+
2. **Open a progress ledger.** Create `.noir/sdd/progress.md`. Each task gets one line when its review comes back clean: `Task N: complete (commits <base7>..<head7>, review clean)`. After any compaction or resume, trust the ledger and `git log` over your recollection — controllers that lost their place have re-dispatched entire completed sequences.
|
|
13
|
+
|
|
14
|
+
### Per task
|
|
15
|
+
3. **Brief the implementer.** Extract the task's full text to `.noir/sdd/task-N-brief.md` (the single source of requirements — exact values, signatures, test cases live there). Compose the dispatch with: one line on where this task fits, the brief path ("read this first — it is your requirements"), interfaces and decisions from earlier tasks the brief cannot know, your resolution of any ambiguity you noticed, and the report path `.noir/sdd/task-N-report.md`. Name the model explicitly — omitted models inherit the session default and silently defeat the cost/performance choice below.
|
|
16
|
+
4. **Handle implementer status.**
|
|
17
|
+
- **DONE** — generate the review package and dispatch the task reviewer.
|
|
18
|
+
- **DONE_WITH_CONCERNS** — read the concerns; address correctness/scope concerns before review, note observations and proceed.
|
|
19
|
+
- **NEEDS_CONTEXT** — provide the missing context, re-dispatch.
|
|
20
|
+
- **BLOCKED** — change something before retrying: more context, a more capable model, a smaller task, or escalate to the user if the plan itself is wrong.
|
|
21
|
+
5. **Review the task.** Run `git diff <BASE> <HEAD>` for the task's commit range into a file (BASE = the commit you recorded before dispatching the implementer, never `HEAD~1` — multi-commit tasks get truncated) and pass the path to the reviewer along with the brief and the report. The reviewer returns two verdicts: spec compliance and code quality. Both are required.
|
|
22
|
+
6. **Adjudicate findings.** Dispatch fix subagents for Critical and Important findings (one fixer carrying the complete list, not one per finding — per-finding fixers each rebuild context and re-run suites). Re-review until clean. Resolve "cannot verify from diff" items yourself before marking the task complete — you hold the plan and cross-task context the reviewer lacks. If a finding conflicts with what the plan's text mandates, present the finding and the plan text to the user and ask which governs; do not dismiss it and do not silently fix past the plan.
|
|
23
|
+
7. **Mark complete.** Append the ledger line, move to the next task. Do not pause for check-ins between tasks — execute the plan through, stopping only on BLOCKED status you cannot resolve, genuine ambiguity, or all tasks complete.
|
|
24
|
+
|
|
25
|
+
### Final
|
|
26
|
+
8. **Dispatch the whole-branch review.** Package the branch diff with `MERGE_BASE = git merge-base main HEAD` and dispatch the final reviewer on the most capable available model — this is the judgment step, not the session default. If it returns findings, dispatch one fix subagent carrying the complete list.
|
|
27
|
+
9. **Record and route.** Capture the per-task reviews and the final verdict via `noir.checkpoint` (the SDD execute/verify gates). Hand off to `noir-document`.
|
|
28
|
+
|
|
29
|
+
## Constructing reviewer prompts
|
|
30
|
+
- Do not add open-ended directives ("check all uses", "run race tests if useful") without a concrete, task-specific reason.
|
|
31
|
+
- Do not ask a reviewer to re-run tests the implementer already ran on the same code — the implementer's report carries the evidence.
|
|
32
|
+
- Do not pre-judge findings for the reviewer. If your prompt contains "do not flag", "don't treat X as a defect", "at most Minor", or "the plan chose" — stop. You are pre-judging, usually to spare yourself a review loop. Let the reviewer raise it and adjudicate in the loop.
|
|
33
|
+
- The global-constraints block you hand the reviewer is its attention lens: copy the binding requirements verbatim from the plan (exact values, formats, stated relationships). Process rules (YAGNI, test hygiene) already live in the reviewer template.
|
|
34
|
+
- Every fix dispatch carries the implementer contract: re-run the tests covering the change and report the result (test files, command, output). Re-dispatch the reviewer only once all three are present.
|
|
35
|
+
|
|
36
|
+
## Model selection
|
|
37
|
+
Use the least powerful model that handles each role.
|
|
38
|
+
- Mechanical, well-specified implementation (complete spec, 1–2 files, or plan text that already contains the code): cheap/fast tier.
|
|
39
|
+
- Integration and judgment (multi-file coordination, pattern matching): standard tier.
|
|
40
|
+
- Architecture, design, and the final whole-branch review: most capable tier. Always specify the model explicitly; an omitted model inherits the session default.
|
|
41
|
+
|
|
42
|
+
Cheapest models often take 2–3× the turns on multi-step work — use a mid-tier model as the floor for reviewers and for implementers working from prose. Turn count beats token price.
|
|
43
|
+
|
|
44
|
+
## Notes
|
|
45
|
+
- One dispatch describes one task, not the session's history. Do not paste accumulated prior-task summaries into later dispatches — a fresh subagent needs its task, its interfaces, and the global constraints. Nothing else.
|
|
46
|
+
- Never start implementation on `main`/`master`; never run multiple implementation subagents in parallel (they conflict on the working tree); never let a subagent self-review replace the actual task review — both are needed.
|
|
47
|
+
- Discipline is observable, not rhetorical: the SDD engine records each per-task review and the final whole-branch review, with the diff packages and verdicts attached.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-sync
|
|
3
|
+
description: Use at the start of a session — to load project context (anchors, git state, setup) and recall prior memory before doing anything else.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Load project context at session start. Read-only — do not edit project files. Discipline comes from the SDD engine's observable gates, not from this skill.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Read anchors.** Read `CLAUDE.md` / `AGENTS.md` (always-on context) to ground the session.
|
|
10
|
+
2. **Project setup.** Detect stack + the test/run command from manifests and `CLAUDE.md`; note project type. Informational — flag anything non-conventional, do not mutate.
|
|
11
|
+
3. **Git state.** `git rev-parse --abbrev-ref HEAD`; `git status --porcelain`; `git log --oneline -5`.
|
|
12
|
+
4. **Noir state.** Read `.noir/NOIR.md`; check `.noir/tasks/` for an in-flight task (the S4 engine's persisted state). If one exists, surface its phase via `noir.workflow_status` and offer to resume it through the SDD lifecycle — do not auto-resume.
|
|
13
|
+
5. **Recall memory.** Query Noir memory (or the host's recall tooling, if any); surface 2–4 top facts relevant to the current task. If empty, say so — `.noir/` artifacts are the durable fallback.
|
|
14
|
+
6. **Print a brief** of essentials only (project type, stack, key commands, in-flight task) — no large dumps. Then await the user; do not auto-start work.
|
|
15
|
+
|
|
16
|
+
## Fallbacks
|
|
17
|
+
- No memory found → say so; `.noir/NOIR.md` + `.noir/tasks/` are always the durable record.
|
|
18
|
+
- Not initialized (no `.noir/`) → tell the user to run `noir init` before continuing.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-tdd
|
|
3
|
+
description: Use when implementing any feature or bugfix — to write the failing test before the implementation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Write the test first, watch it fail for the right reason, then write the minimal code that makes it pass. The cycle is red → green → refactor, in that order. The proof that a test actually tests something is that you saw it fail before the feature existed.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
|
|
10
|
+
### RED — write one failing test
|
|
11
|
+
- One behavior per test. If the name contains "and", split it.
|
|
12
|
+
- A clear name that describes behavior (`retries failed operations 3 times`), not an index (`test1`).
|
|
13
|
+
- Real code paths, not mocks of the code under test — mock only external boundaries you do not own.
|
|
14
|
+
- Test the public behavior, not the implementation. A test that asserts on internal call shape breaks on every refactor.
|
|
15
|
+
|
|
16
|
+
### Verify RED — watch it fail
|
|
17
|
+
Run the test before writing any implementation. Confirm:
|
|
18
|
+
- it fails (does not error — a syntax or import error is not a red);
|
|
19
|
+
- the failure message is the one you expected;
|
|
20
|
+
- it fails because the feature is missing, not because of a typo in the test.
|
|
21
|
+
|
|
22
|
+
A test that passes immediately is testing existing behavior. Fix the test, not the cycle.
|
|
23
|
+
|
|
24
|
+
### GREEN — write the minimal code
|
|
25
|
+
Write the simplest code that turns the test green. No speculative options object, no "while I'm here" cleanup, no extra branches the test does not exercise. YAGNI is the rule in this step — the next test will ask for the next feature.
|
|
26
|
+
|
|
27
|
+
### Verify GREEN — watch it pass
|
|
28
|
+
Run the full suite, not just the new test:
|
|
29
|
+
- the new test passes;
|
|
30
|
+
- no other test regressed;
|
|
31
|
+
- the output is pristine — no warnings, no deprecation notices, no swallowed errors.
|
|
32
|
+
|
|
33
|
+
If another test broke, the implementation is not done. Fix it now, do not commit and move on.
|
|
34
|
+
|
|
35
|
+
### REFACTOR — clean up, stay green
|
|
36
|
+
Only after green: remove duplication, improve names, extract helpers. Run the suite after each refactor step. No new behavior lands during refactor — that requires a new RED.
|
|
37
|
+
|
|
38
|
+
### Repeat
|
|
39
|
+
Pick the next behavior, write its failing test, cycle again.
|
|
40
|
+
|
|
41
|
+
## Why order matters
|
|
42
|
+
Tests written after the implementation pass immediately, and a passing test proves nothing — it might test the wrong thing, it might assert on the implementation rather than the requirement, and you never saw it catch a bug. Test-first forces edge-case discovery before the implementation makes the edge case look easy.
|
|
43
|
+
|
|
44
|
+
A bug found in the wild is treated the same way: write the failing test that reproduces it, watch it fail, fix at the root, watch it pass. The test stays in the suite as the regression guard.
|
|
45
|
+
|
|
46
|
+
## Notes
|
|
47
|
+
- Discipline is observable, not rhetorical: the SDD engine records the execute-phase gate, and a regression test counts only after a red→green cycle was observed — a test that passed once is not evidence of a fix.
|
|
48
|
+
- If the test is hard to write, the design is hard to use. Listen to the test before fighting it; the friction usually names a real coupling problem.
|
|
49
|
+
- When you genuinely must explore an API before designing it, throw the exploration away and start the implementation from a fresh failing test. "Keep it as reference" is how tests-after slips in.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-test
|
|
3
|
+
description: Use when writing tests — for test design, coverage, and edge cases (not just running them).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-test
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** you are writing tests and want them to actually catch bugs — design and coverage, not just running a suite.
|
|
11
|
+
|
|
12
|
+
**For now:** name each test for a behavior, cover the happy path plus the null/empty/boundary/error cases, and assert on observable outcomes rather than internals.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-verify
|
|
3
|
+
description: Use when about to claim work is complete or fixed — to run verification and gather evidence before asserting success.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Run the commands that prove the claim, read their output, and report the actual status with evidence. No completion or fix claim is made until fresh output backs it.
|
|
7
|
+
|
|
8
|
+
## Procedure
|
|
9
|
+
1. **Name the claim and its proof.** For each thing you are about to assert ("tests pass", "lint clean", "build succeeds", "bug fixed", "requirements met"), name the exact command that would prove it. A claim with no proof command is not yet a claim.
|
|
10
|
+
2. **Run each command fresh.** Full invocation in this turn — not a previous run, not a cached result, not a partial check. Capture stdout, exit code, and failure counts.
|
|
11
|
+
3. **Read the output before asserting.** If the output confirms the claim, state the claim with the evidence. If it contradicts the claim, state the actual status with evidence and stop — do not soften, do not promise a follow-up.
|
|
12
|
+
4. **Refuse common substitutes for evidence.** "Should work now", "I'm confident", "the linter passed so the build is fine", "the agent reported success", and "partial check is enough" are not evidence. Partial output proves nothing about the whole.
|
|
13
|
+
5. **Record and route.** Capture the verification result via `noir.checkpoint` (the SDD engine's verify gate). Only then point to `noir-document`.
|
|
14
|
+
|
|
15
|
+
## Notes
|
|
16
|
+
- The rule is procedural, not rhetorical: name the proof, run it, read it, then claim. The engine records that the verify gate ran with output attached.
|
|
17
|
+
- A regression test counts only after a red→green cycle is observed. A test that passes once is not evidence of a fix.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-worktree
|
|
3
|
+
description: Use when starting feature work that needs isolation from the current workspace.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-worktree
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** you are starting work that should not disturb the current workspace (long-running branch, parallel feature, risky change).
|
|
11
|
+
|
|
12
|
+
**For now:** create an isolated worktree on a fresh branch, do the work there, and merge or discard when done — keep the main workspace clean.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noir-wrap
|
|
3
|
+
description: Use when closing a session cleanly — run tests, curate docs, confirm commits, save memory.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# noir-wrap
|
|
7
|
+
|
|
8
|
+
> **Stub:** this skill ships as a valid, loadable placeholder in S5; its full playbook is deepened in a later slice.
|
|
9
|
+
|
|
10
|
+
**When to use:** you are ending a session and want to leave the work in a clean, recoverable state.
|
|
11
|
+
|
|
12
|
+
**For now:** run the project's test command, curate or delete ephemeral notes, confirm commits are made (and pushed or intentionally local), and save durable memory before closing.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
interface SkillFrontmatter {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
references?: string[];
|
|
5
|
+
[k: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
interface BuiltinReference {
|
|
8
|
+
name: string;
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
interface BuiltinSkill {
|
|
12
|
+
name: string;
|
|
13
|
+
dir: string;
|
|
14
|
+
skillMd: string;
|
|
15
|
+
frontmatter: SkillFrontmatter;
|
|
16
|
+
references: BuiltinReference[];
|
|
17
|
+
}
|
|
18
|
+
interface ValidationResult {
|
|
19
|
+
ok: boolean;
|
|
20
|
+
errors: string[];
|
|
21
|
+
}
|
|
22
|
+
interface EmittedFile {
|
|
23
|
+
path: string[];
|
|
24
|
+
content: string;
|
|
25
|
+
}
|
|
26
|
+
interface CompiledSkill {
|
|
27
|
+
name: string;
|
|
28
|
+
files: EmittedFile[];
|
|
29
|
+
}
|
|
30
|
+
interface EmitSummary {
|
|
31
|
+
dir: string;
|
|
32
|
+
emitted: string[];
|
|
33
|
+
references: number;
|
|
34
|
+
}
|
|
35
|
+
type CompileTarget = 'claude';
|
|
36
|
+
|
|
37
|
+
declare const BUILTIN_DIR: string;
|
|
38
|
+
declare function discoverBuiltin(builtinDir?: string): BuiltinSkill[];
|
|
39
|
+
|
|
40
|
+
declare function parseFrontmatter(md: string): SkillFrontmatter;
|
|
41
|
+
declare function bodyOf(md: string): string;
|
|
42
|
+
/** A WHEN description leads with its trigger. Requiring a leading cue — rather
|
|
43
|
+
* than a loose "contains when/before/after anywhere" — avoids false positives
|
|
44
|
+
* ("A tool that decides when to run tests") and accepts valid leads ("Upon…"). */
|
|
45
|
+
declare function looksLikeWhenDescription(desc: string): boolean;
|
|
46
|
+
declare function validateSkill(skill: BuiltinSkill): ValidationResult;
|
|
47
|
+
declare function compileSkill(skill: BuiltinSkill, target?: CompileTarget): CompiledSkill;
|
|
48
|
+
declare function emitSkillsToDir(targetDir: string, opts?: {
|
|
49
|
+
builtinDir?: string;
|
|
50
|
+
}): Promise<EmitSummary>;
|
|
51
|
+
|
|
52
|
+
declare const FORBIDDEN_RESIDUE: readonly string[];
|
|
53
|
+
|
|
54
|
+
export { BUILTIN_DIR, type BuiltinReference, type BuiltinSkill, type CompileTarget, type CompiledSkill, type EmitSummary, type EmittedFile, FORBIDDEN_RESIDUE, type SkillFrontmatter, type ValidationResult, bodyOf, compileSkill, discoverBuiltin, emitSkillsToDir, looksLikeWhenDescription, parseFrontmatter, validateSkill };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// src/compiler.ts
|
|
2
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
3
|
+
import { basename, dirname as dirname2, join as join2 } from "path";
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
5
|
+
|
|
6
|
+
// src/discover.ts
|
|
7
|
+
import { readdirSync, readFileSync } from "fs";
|
|
8
|
+
import { dirname, join } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
var HERE = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
var PKG_ROOT = dirname(HERE);
|
|
12
|
+
var BUILTIN_DIR = join(PKG_ROOT, "builtin");
|
|
13
|
+
function discoverBuiltin(builtinDir = BUILTIN_DIR) {
|
|
14
|
+
const dirs = readdirSync(builtinDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("noir-")).map((e) => e.name).sort();
|
|
15
|
+
return dirs.map((name) => {
|
|
16
|
+
const dir = join(builtinDir, name);
|
|
17
|
+
const skillMd = readFileSync(join(dir, "SKILL.md"), "utf8");
|
|
18
|
+
const frontmatter = parseFrontmatter(skillMd);
|
|
19
|
+
const refsDir = join(dir, "references");
|
|
20
|
+
let references = [];
|
|
21
|
+
try {
|
|
22
|
+
references = readdirSync(refsDir).filter((f) => f.endsWith(".md")).sort().map((f) => ({ name: f, content: readFileSync(join(refsDir, f), "utf8") }));
|
|
23
|
+
} catch {
|
|
24
|
+
references = [];
|
|
25
|
+
}
|
|
26
|
+
return { name, dir, skillMd, frontmatter, references };
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/compiler.ts
|
|
31
|
+
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
32
|
+
var NAME_RE = /^noir-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
33
|
+
var WHEN_START = /^(use|using|used|whenever|when|before|after|while|starting|encountering|completing|creating|about to|upon|during|to|for|on)\b/i;
|
|
34
|
+
var MAX_DESC = 1024;
|
|
35
|
+
function parseFrontmatter(md) {
|
|
36
|
+
const m = md.match(FRONTMATTER_RE);
|
|
37
|
+
if (!m) throw new Error("Skill missing YAML frontmatter (expected --- ... ---)");
|
|
38
|
+
const yaml = m[1];
|
|
39
|
+
if (yaml === void 0) throw new Error("Skill missing YAML frontmatter (expected --- ... ---)");
|
|
40
|
+
const fm = parseYaml(yaml);
|
|
41
|
+
if (typeof fm?.name !== "string" || typeof fm?.description !== "string") {
|
|
42
|
+
throw new Error("Skill frontmatter requires string `name` + `description`");
|
|
43
|
+
}
|
|
44
|
+
return fm;
|
|
45
|
+
}
|
|
46
|
+
function bodyOf(md) {
|
|
47
|
+
return md.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
48
|
+
}
|
|
49
|
+
function looksLikeWhenDescription(desc) {
|
|
50
|
+
return WHEN_START.test(desc.trim());
|
|
51
|
+
}
|
|
52
|
+
function validateSkill(skill) {
|
|
53
|
+
const errors = [];
|
|
54
|
+
const { name, description } = skill.frontmatter;
|
|
55
|
+
if (!name) errors.push("missing `name`");
|
|
56
|
+
else if (!NAME_RE.test(name)) errors.push(`name "${name}" must match noir-<kebab>`);
|
|
57
|
+
if (basename(skill.dir) !== name) {
|
|
58
|
+
errors.push(`dir "${basename(skill.dir)}" must equal name "${name}"`);
|
|
59
|
+
}
|
|
60
|
+
if (!description?.trim()) errors.push("missing `description`");
|
|
61
|
+
else if (description.length > MAX_DESC) errors.push(`description exceeds ${MAX_DESC} chars`);
|
|
62
|
+
else if (!looksLikeWhenDescription(description)) {
|
|
63
|
+
errors.push('description must state WHEN to trigger (e.g. "Use when\u2026"), not WHAT it does');
|
|
64
|
+
}
|
|
65
|
+
for (const r of skill.references) {
|
|
66
|
+
if (!/^[a-z0-9-]+\.md$/i.test(r.name)) errors.push(`reference "${r.name}" must be <kebab>.md`);
|
|
67
|
+
if (!r.content.trim()) errors.push(`reference "${r.name}" is empty`);
|
|
68
|
+
}
|
|
69
|
+
return { ok: errors.length === 0, errors };
|
|
70
|
+
}
|
|
71
|
+
function compileSkill(skill, target = "claude") {
|
|
72
|
+
const res = validateSkill(skill);
|
|
73
|
+
if (!res.ok) throw new Error(`Cannot compile ${skill.name}: ${res.errors.join("; ")}`);
|
|
74
|
+
if (target !== "claude") throw new Error(`Unsupported compile target: ${target}`);
|
|
75
|
+
const files = [
|
|
76
|
+
{ path: ["SKILL.md"], content: skill.skillMd },
|
|
77
|
+
...skill.references.map((r) => ({ path: ["references", r.name], content: r.content }))
|
|
78
|
+
];
|
|
79
|
+
return { name: skill.name, files };
|
|
80
|
+
}
|
|
81
|
+
async function emitSkillsToDir(targetDir, opts = {}) {
|
|
82
|
+
const skills = discoverBuiltin(opts.builtinDir);
|
|
83
|
+
for (const s of skills) {
|
|
84
|
+
const res = validateSkill(s);
|
|
85
|
+
if (!res.ok) throw new Error(`Invalid builtin skill ${s.name}: ${res.errors.join("; ")}`);
|
|
86
|
+
}
|
|
87
|
+
await mkdir(targetDir, { recursive: true });
|
|
88
|
+
let references = 0;
|
|
89
|
+
for (const s of skills) {
|
|
90
|
+
const compiled = compileSkill(s, "claude");
|
|
91
|
+
for (const f of compiled.files) {
|
|
92
|
+
const dest = join2(targetDir, s.name, ...f.path);
|
|
93
|
+
await mkdir(dirname2(dest), { recursive: true });
|
|
94
|
+
await writeFile(dest, f.content, "utf8");
|
|
95
|
+
if (f.path[0] !== "SKILL.md") references++;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { dir: targetDir, emitted: skills.map((s) => s.name), references };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/residue.ts
|
|
102
|
+
var FORBIDDEN_RESIDUE = [
|
|
103
|
+
"workflow/<task",
|
|
104
|
+
// predecessor state file
|
|
105
|
+
"noir-workflow.mode",
|
|
106
|
+
// predecessor mode flag
|
|
107
|
+
"noir-workflow",
|
|
108
|
+
// predecessor plugin name (as a plugin/path reference)
|
|
109
|
+
"@uiigateway",
|
|
110
|
+
// predecessor Angular specifics
|
|
111
|
+
"ClickUp",
|
|
112
|
+
"clickup",
|
|
113
|
+
"<EXTREMELY-IMPORTANT",
|
|
114
|
+
// Superpowers rhetoric
|
|
115
|
+
"SUBAGENT-STOP",
|
|
116
|
+
// Superpowers rhetoric
|
|
117
|
+
"plugins/noir-workflow"
|
|
118
|
+
// predecessor path
|
|
119
|
+
];
|
|
120
|
+
export {
|
|
121
|
+
BUILTIN_DIR,
|
|
122
|
+
FORBIDDEN_RESIDUE,
|
|
123
|
+
bodyOf,
|
|
124
|
+
compileSkill,
|
|
125
|
+
discoverBuiltin,
|
|
126
|
+
emitSkillsToDir,
|
|
127
|
+
looksLikeWhenDescription,
|
|
128
|
+
parseFrontmatter,
|
|
129
|
+
validateSkill
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compiler.ts","../src/discover.ts","../src/residue.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { discoverBuiltin } from './discover.js';\nimport type {\n BuiltinSkill,\n CompiledSkill,\n CompileTarget,\n EmitSummary,\n SkillFrontmatter,\n ValidationResult,\n} from './types.js';\n\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/;\nconst NAME_RE = /^noir-[a-z0-9]+(?:-[a-z0-9]+)*$/;\nconst WHEN_START =\n /^(use|using|used|whenever|when|before|after|while|starting|encountering|completing|creating|about to|upon|during|to|for|on)\\b/i;\nconst MAX_DESC = 1024;\n\nexport function parseFrontmatter(md: string): SkillFrontmatter {\n const m = md.match(FRONTMATTER_RE);\n if (!m) throw new Error('Skill missing YAML frontmatter (expected --- ... ---)');\n // Group 1 always exists when the regex matches; guard past noUncheckedIndexedAccess.\n const yaml = m[1];\n if (yaml === undefined) throw new Error('Skill missing YAML frontmatter (expected --- ... ---)');\n const fm = parseYaml(yaml) as SkillFrontmatter;\n if (typeof fm?.name !== 'string' || typeof fm?.description !== 'string') {\n throw new Error('Skill frontmatter requires string `name` + `description`');\n }\n return fm;\n}\n\nexport function bodyOf(md: string): string {\n return md.replace(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?/, '');\n}\n\n/** A WHEN description leads with its trigger. Requiring a leading cue — rather\n * than a loose \"contains when/before/after anywhere\" — avoids false positives\n * (\"A tool that decides when to run tests\") and accepts valid leads (\"Upon…\"). */\nexport function looksLikeWhenDescription(desc: string): boolean {\n return WHEN_START.test(desc.trim());\n}\n\nexport function validateSkill(skill: BuiltinSkill): ValidationResult {\n const errors: string[] = [];\n const { name, description } = skill.frontmatter;\n if (!name) errors.push('missing `name`');\n else if (!NAME_RE.test(name)) errors.push(`name \"${name}\" must match noir-<kebab>`);\n if (basename(skill.dir) !== name) {\n errors.push(`dir \"${basename(skill.dir)}\" must equal name \"${name}\"`);\n }\n if (!description?.trim()) errors.push('missing `description`');\n else if (description.length > MAX_DESC) errors.push(`description exceeds ${MAX_DESC} chars`);\n else if (!looksLikeWhenDescription(description)) {\n errors.push('description must state WHEN to trigger (e.g. \"Use when…\"), not WHAT it does');\n }\n for (const r of skill.references) {\n if (!/^[a-z0-9-]+\\.md$/i.test(r.name)) errors.push(`reference \"${r.name}\" must be <kebab>.md`);\n if (!r.content.trim()) errors.push(`reference \"${r.name}\" is empty`);\n }\n return { ok: errors.length === 0, errors };\n}\n\nexport function compileSkill(skill: BuiltinSkill, target: CompileTarget = 'claude'): CompiledSkill {\n const res = validateSkill(skill);\n if (!res.ok) throw new Error(`Cannot compile ${skill.name}: ${res.errors.join('; ')}`);\n if (target !== 'claude') throw new Error(`Unsupported compile target: ${target}`);\n // Claude (v1) target = canonical format copied verbatim (DS-4). Multi-host transform is S10.\n const files = [\n { path: ['SKILL.md'], content: skill.skillMd },\n ...skill.references.map((r) => ({ path: ['references', r.name], content: r.content })),\n ];\n return { name: skill.name, files };\n}\n\nexport async function emitSkillsToDir(\n targetDir: string,\n opts: { builtinDir?: string } = {},\n): Promise<EmitSummary> {\n const skills = discoverBuiltin(opts.builtinDir);\n // Validate the whole pack before writing anything (fail-fast, atomic-ish).\n for (const s of skills) {\n const res = validateSkill(s);\n if (!res.ok) throw new Error(`Invalid builtin skill ${s.name}: ${res.errors.join('; ')}`);\n }\n await mkdir(targetDir, { recursive: true });\n let references = 0;\n for (const s of skills) {\n const compiled = compileSkill(s, 'claude');\n for (const f of compiled.files) {\n const dest = join(targetDir, s.name, ...f.path);\n await mkdir(dirname(dest), { recursive: true });\n await writeFile(dest, f.content, 'utf8');\n if (f.path[0] !== 'SKILL.md') references++;\n }\n }\n return { dir: targetDir, emitted: skills.map((s) => s.name), references };\n}\n\n// Convenience for callers/tests that already hold raw markdown (unused by emit path;\n// kept so adapters/tests can validate a single in-memory skill without a dir).\nexport { discoverBuiltin };\n","import { readdirSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseFrontmatter } from './compiler.js';\nimport type { BuiltinSkill } from './types.js';\n\n// Package root = ONE level up from THIS module's directory. Works in every layout\n// because `builtin/` is always a sibling of the dir this code runs from:\n// - vitest: .../packages/skills/src/discover.ts -> HERE = src -> PKG_ROOT = package root\n// - built: .../packages/skills/dist/index.js -> HERE = dist -> PKG_ROOT = package root\n// (tsup bundles everything into dist/index.js; `dirname` still yields `dist`.)\nconst HERE = dirname(fileURLToPath(import.meta.url)); // .../src OR .../dist\nconst PKG_ROOT = dirname(HERE); // .../packages/skills\nexport const BUILTIN_DIR = join(PKG_ROOT, 'builtin');\n\nexport function discoverBuiltin(builtinDir: string = BUILTIN_DIR): BuiltinSkill[] {\n const dirs = readdirSync(builtinDir, { withFileTypes: true })\n .filter((e) => e.isDirectory() && e.name.startsWith('noir-'))\n .map((e) => e.name)\n .sort();\n return dirs.map((name) => {\n const dir = join(builtinDir, name);\n const skillMd = readFileSync(join(dir, 'SKILL.md'), 'utf8');\n const frontmatter = parseFrontmatter(skillMd);\n const refsDir = join(dir, 'references');\n let references: BuiltinSkill['references'] = [];\n try {\n references = readdirSync(refsDir)\n .filter((f) => f.endsWith('.md'))\n .sort()\n .map((f) => ({ name: f, content: readFileSync(join(refsDir, f), 'utf8') }));\n } catch {\n references = []; // no references/ dir is fine\n }\n return { name, dir, skillMd, frontmatter, references };\n });\n}\n","// Tokens that must NOT survive a port (predecessor plugin internals + Superpowers\n// rhetoric). Shared by the compiler's lint helper and the hygiene tests (T2–T5).\nexport const FORBIDDEN_RESIDUE: readonly string[] = [\n 'workflow/<task', // predecessor state file\n 'noir-workflow.mode', // predecessor mode flag\n 'noir-workflow', // predecessor plugin name (as a plugin/path reference)\n '@uiigateway', // predecessor Angular specifics\n 'ClickUp',\n 'clickup',\n '<EXTREMELY-IMPORTANT', // Superpowers rhetoric\n 'SUBAGENT-STOP', // Superpowers rhetoric\n 'plugins/noir-workflow', // predecessor path\n];\n"],"mappings":";AAAA,SAAS,OAAO,iBAAiB;AACjC,SAAS,UAAU,WAAAA,UAAS,QAAAC,aAAY;AACxC,SAAS,SAAS,iBAAiB;;;ACFnC,SAAS,aAAa,oBAAoB;AAC1C,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAS9B,IAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AACnD,IAAM,WAAW,QAAQ,IAAI;AACtB,IAAM,cAAc,KAAK,UAAU,SAAS;AAE5C,SAAS,gBAAgB,aAAqB,aAA6B;AAChF,QAAM,OAAO,YAAY,YAAY,EAAE,eAAe,KAAK,CAAC,EACzD,OAAO,CAAC,MAAM,EAAE,YAAY,KAAK,EAAE,KAAK,WAAW,OAAO,CAAC,EAC3D,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK;AACR,SAAO,KAAK,IAAI,CAAC,SAAS;AACxB,UAAM,MAAM,KAAK,YAAY,IAAI;AACjC,UAAM,UAAU,aAAa,KAAK,KAAK,UAAU,GAAG,MAAM;AAC1D,UAAM,cAAc,iBAAiB,OAAO;AAC5C,UAAM,UAAU,KAAK,KAAK,YAAY;AACtC,QAAI,aAAyC,CAAC;AAC9C,QAAI;AACF,mBAAa,YAAY,OAAO,EAC7B,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,EAC/B,KAAK,EACL,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,aAAa,KAAK,SAAS,CAAC,GAAG,MAAM,EAAE,EAAE;AAAA,IAC9E,QAAQ;AACN,mBAAa,CAAC;AAAA,IAChB;AACA,WAAO,EAAE,MAAM,KAAK,SAAS,aAAa,WAAW;AAAA,EACvD,CAAC;AACH;;;ADvBA,IAAM,iBAAiB;AACvB,IAAM,UAAU;AAChB,IAAM,aACJ;AACF,IAAM,WAAW;AAEV,SAAS,iBAAiB,IAA8B;AAC7D,QAAM,IAAI,GAAG,MAAM,cAAc;AACjC,MAAI,CAAC,EAAG,OAAM,IAAI,MAAM,uDAAuD;AAE/E,QAAM,OAAO,EAAE,CAAC;AAChB,MAAI,SAAS,OAAW,OAAM,IAAI,MAAM,uDAAuD;AAC/F,QAAM,KAAK,UAAU,IAAI;AACzB,MAAI,OAAO,IAAI,SAAS,YAAY,OAAO,IAAI,gBAAgB,UAAU;AACvE,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AACA,SAAO;AACT;AAEO,SAAS,OAAO,IAAoB;AACzC,SAAO,GAAG,QAAQ,mCAAmC,EAAE;AACzD;AAKO,SAAS,yBAAyB,MAAuB;AAC9D,SAAO,WAAW,KAAK,KAAK,KAAK,CAAC;AACpC;AAEO,SAAS,cAAc,OAAuC;AACnE,QAAM,SAAmB,CAAC;AAC1B,QAAM,EAAE,MAAM,YAAY,IAAI,MAAM;AACpC,MAAI,CAAC,KAAM,QAAO,KAAK,gBAAgB;AAAA,WAC9B,CAAC,QAAQ,KAAK,IAAI,EAAG,QAAO,KAAK,SAAS,IAAI,2BAA2B;AAClF,MAAI,SAAS,MAAM,GAAG,MAAM,MAAM;AAChC,WAAO,KAAK,QAAQ,SAAS,MAAM,GAAG,CAAC,sBAAsB,IAAI,GAAG;AAAA,EACtE;AACA,MAAI,CAAC,aAAa,KAAK,EAAG,QAAO,KAAK,uBAAuB;AAAA,WACpD,YAAY,SAAS,SAAU,QAAO,KAAK,uBAAuB,QAAQ,QAAQ;AAAA,WAClF,CAAC,yBAAyB,WAAW,GAAG;AAC/C,WAAO,KAAK,kFAA6E;AAAA,EAC3F;AACA,aAAW,KAAK,MAAM,YAAY;AAChC,QAAI,CAAC,oBAAoB,KAAK,EAAE,IAAI,EAAG,QAAO,KAAK,cAAc,EAAE,IAAI,sBAAsB;AAC7F,QAAI,CAAC,EAAE,QAAQ,KAAK,EAAG,QAAO,KAAK,cAAc,EAAE,IAAI,YAAY;AAAA,EACrE;AACA,SAAO,EAAE,IAAI,OAAO,WAAW,GAAG,OAAO;AAC3C;AAEO,SAAS,aAAa,OAAqB,SAAwB,UAAyB;AACjG,QAAM,MAAM,cAAc,KAAK;AAC/B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,kBAAkB,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,IAAI,CAAC,EAAE;AACrF,MAAI,WAAW,SAAU,OAAM,IAAI,MAAM,+BAA+B,MAAM,EAAE;AAEhF,QAAM,QAAQ;AAAA,IACZ,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,MAAM,QAAQ;AAAA,IAC7C,GAAG,MAAM,WAAW,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,IAAI,GAAG,SAAS,EAAE,QAAQ,EAAE;AAAA,EACvF;AACA,SAAO,EAAE,MAAM,MAAM,MAAM,MAAM;AACnC;AAEA,eAAsB,gBACpB,WACA,OAAgC,CAAC,GACX;AACtB,QAAM,SAAS,gBAAgB,KAAK,UAAU;AAE9C,aAAW,KAAK,QAAQ;AACtB,UAAM,MAAM,cAAc,CAAC;AAC3B,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,yBAAyB,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,EAC1F;AACA,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC1C,MAAI,aAAa;AACjB,aAAW,KAAK,QAAQ;AACtB,UAAM,WAAW,aAAa,GAAG,QAAQ;AACzC,eAAW,KAAK,SAAS,OAAO;AAC9B,YAAM,OAAOC,MAAK,WAAW,EAAE,MAAM,GAAG,EAAE,IAAI;AAC9C,YAAM,MAAMC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,YAAM,UAAU,MAAM,EAAE,SAAS,MAAM;AACvC,UAAI,EAAE,KAAK,CAAC,MAAM,WAAY;AAAA,IAChC;AAAA,EACF;AACA,SAAO,EAAE,KAAK,WAAW,SAAS,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,WAAW;AAC1E;;;AE/FO,IAAM,oBAAuC;AAAA,EAClD;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;","names":["dirname","join","join","dirname"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@noir-ai/skills",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"description": "Noir skills — the builtin noir-* skill pack (31 skills) and a copy-and-validate compiler.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "agaaaptr",
|
|
7
|
+
"homepage": "https://github.com/agaaaptr/noir#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/agaaaptr/noir.git",
|
|
11
|
+
"directory": "packages/skills"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/agaaaptr/noir/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"noir",
|
|
18
|
+
"skills",
|
|
19
|
+
"compiler",
|
|
20
|
+
"sdd",
|
|
21
|
+
"spec-driven",
|
|
22
|
+
"agent",
|
|
23
|
+
"pack"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"provenance": true
|
|
31
|
+
},
|
|
32
|
+
"type": "module",
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"builtin",
|
|
44
|
+
"README.md"
|
|
45
|
+
],
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"yaml": "^2.5.0",
|
|
48
|
+
"@noir-ai/core": "1.0.0-beta.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^26.1.1"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"typecheck": "tsc --noEmit"
|
|
56
|
+
}
|
|
57
|
+
}
|