cclaw-cli 0.8.0 → 0.9.0
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/dist/content/examples.d.ts +16 -0
- package/dist/content/examples.js +212 -4
- package/dist/content/harness-tool-refs.d.ts +20 -0
- package/dist/content/harness-tool-refs.js +240 -0
- package/dist/content/meta-skill.js +72 -4
- package/dist/content/skills.js +63 -41
- package/dist/content/stage-schema.js +29 -3
- package/dist/content/templates.js +13 -3
- package/dist/doctor.js +77 -0
- package/dist/install.js +19 -0
- package/package.json +1 -1
|
@@ -1,3 +1,19 @@
|
|
|
1
1
|
import type { FlowStage } from "../types.js";
|
|
2
2
|
export declare function stageGoodBadExamples(stage: FlowStage): string;
|
|
3
|
+
export declare const STAGE_EXAMPLES_REFERENCE_DIR = "references/stages";
|
|
4
|
+
export declare function stageExamplesReferencePath(stage: FlowStage): string;
|
|
5
|
+
/**
|
|
6
|
+
* Returns the full example artifact body as a standalone reference markdown
|
|
7
|
+
* file. Materialized under .cclaw/references/stages/<stage>-examples.md so
|
|
8
|
+
* the always-rendered skill body can link instead of inlining.
|
|
9
|
+
*/
|
|
10
|
+
export declare function stageExamplesReferenceMarkdown(stage: FlowStage): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Returns the short inline pointer rendered directly inside the stage skill.
|
|
13
|
+
* Replaces the previous always-inline ~50-100 line fenced block and
|
|
14
|
+
* delivers true progressive disclosure: the full example lives in a
|
|
15
|
+
* reference file loaded on demand.
|
|
16
|
+
*/
|
|
3
17
|
export declare function stageExamples(stage: FlowStage): string;
|
|
18
|
+
export type ExampleDomain = "web" | "cli" | "library" | "data-pipeline";
|
|
19
|
+
export declare function stageDomainExamples(stage: FlowStage): string;
|
package/dist/content/examples.js
CHANGED
|
@@ -495,14 +495,29 @@ export function stageGoodBadExamples(stage) {
|
|
|
495
495
|
""
|
|
496
496
|
].join("\n");
|
|
497
497
|
}
|
|
498
|
-
export
|
|
498
|
+
export const STAGE_EXAMPLES_REFERENCE_DIR = "references/stages";
|
|
499
|
+
export function stageExamplesReferencePath(stage) {
|
|
500
|
+
return `.cclaw/${STAGE_EXAMPLES_REFERENCE_DIR}/${stage}-examples.md`;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Returns the full example artifact body as a standalone reference markdown
|
|
504
|
+
* file. Materialized under .cclaw/references/stages/<stage>-examples.md so
|
|
505
|
+
* the always-rendered skill body can link instead of inlining.
|
|
506
|
+
*/
|
|
507
|
+
export function stageExamplesReferenceMarkdown(stage) {
|
|
499
508
|
const examples = STAGE_EXAMPLES[stage];
|
|
500
509
|
if (!examples)
|
|
501
|
-
return
|
|
510
|
+
return null;
|
|
502
511
|
return [
|
|
503
|
-
|
|
512
|
+
`---`,
|
|
513
|
+
`stage: ${stage}`,
|
|
514
|
+
`name: ${stage}-stage-examples`,
|
|
515
|
+
`description: "Full sample artifact for the ${stage} stage. Loaded only when an agent explicitly needs a complete example; the stage skill links here rather than inlining."`,
|
|
516
|
+
`---`,
|
|
504
517
|
"",
|
|
505
|
-
|
|
518
|
+
`# ${stage} stage — full artifact sample`,
|
|
519
|
+
"",
|
|
520
|
+
`This file is linked from \`.cclaw/skills/<${stage}-stage>/SKILL.md\` under **Examples → See also**. The sample uses H2 headings that mirror the artifact a cclaw session must produce, so the markdown is wrapped in a fence to avoid collapsing into the outline.`,
|
|
506
521
|
"",
|
|
507
522
|
"```markdown",
|
|
508
523
|
examples,
|
|
@@ -510,3 +525,196 @@ export function stageExamples(stage) {
|
|
|
510
525
|
""
|
|
511
526
|
].join("\n");
|
|
512
527
|
}
|
|
528
|
+
/**
|
|
529
|
+
* Returns the short inline pointer rendered directly inside the stage skill.
|
|
530
|
+
* Replaces the previous always-inline ~50-100 line fenced block and
|
|
531
|
+
* delivers true progressive disclosure: the full example lives in a
|
|
532
|
+
* reference file loaded on demand.
|
|
533
|
+
*/
|
|
534
|
+
export function stageExamples(stage) {
|
|
535
|
+
const examples = STAGE_EXAMPLES[stage];
|
|
536
|
+
if (!examples)
|
|
537
|
+
return "";
|
|
538
|
+
return [
|
|
539
|
+
"## Examples",
|
|
540
|
+
"",
|
|
541
|
+
`Full artifact sample for this stage lives at \`${stageExamplesReferencePath(stage)}\`. Open it when you need a complete reference; do NOT paste the example into the artifact verbatim — it is a shape guide, not a template.`,
|
|
542
|
+
"",
|
|
543
|
+
"Summary of what the reference covers:",
|
|
544
|
+
...exampleSummaryBullets(stage),
|
|
545
|
+
""
|
|
546
|
+
].join("\n");
|
|
547
|
+
}
|
|
548
|
+
function exampleSummaryBullets(stage) {
|
|
549
|
+
const headings = STAGE_EXAMPLE_SECTION_HEADINGS[stage] ?? [];
|
|
550
|
+
if (headings.length === 0)
|
|
551
|
+
return ["- Full artifact structure."];
|
|
552
|
+
return headings.map((heading) => `- ${heading}`);
|
|
553
|
+
}
|
|
554
|
+
// Kept in sync with STAGE_EXAMPLES above so the inline summary matches the
|
|
555
|
+
// reference file without duplicating the heavy text. Update whenever the
|
|
556
|
+
// sample in STAGE_EXAMPLES gains or loses a top-level section.
|
|
557
|
+
const STAGE_EXAMPLE_SECTION_HEADINGS = {
|
|
558
|
+
brainstorm: [
|
|
559
|
+
"Problem framing (problem, success, constraints)",
|
|
560
|
+
"Candidate approaches with trade-offs",
|
|
561
|
+
"Recommended direction + open questions",
|
|
562
|
+
"Clarification log and decision record"
|
|
563
|
+
],
|
|
564
|
+
scope: [
|
|
565
|
+
"In-scope / out-of-scope / deferred lists with concrete capabilities",
|
|
566
|
+
"Requirements table with stable R# IDs",
|
|
567
|
+
"Boundary stress-tests and non-negotiables",
|
|
568
|
+
"Decision record for premise challenges"
|
|
569
|
+
],
|
|
570
|
+
design: [
|
|
571
|
+
"Blast-radius file list",
|
|
572
|
+
"Mandatory architecture diagram (Mermaid)",
|
|
573
|
+
"Failure-mode table with detection + mitigation",
|
|
574
|
+
"Test strategy + performance budget",
|
|
575
|
+
"Completion dashboard + unresolved decisions"
|
|
576
|
+
],
|
|
577
|
+
spec: [
|
|
578
|
+
"Acceptance-criteria table (observable, measurable, falsifiable)",
|
|
579
|
+
"Requirement-ref column tying each AC back to an R# from scope",
|
|
580
|
+
"Verification-approach column",
|
|
581
|
+
"Approval block"
|
|
582
|
+
],
|
|
583
|
+
plan: [
|
|
584
|
+
"Dependency graph + dependency waves",
|
|
585
|
+
"Task list with effort + minutes estimate per task",
|
|
586
|
+
"Acceptance mapping (every AC → task IDs)",
|
|
587
|
+
"No-Placeholder scan row + WAIT_FOR_CONFIRM marker"
|
|
588
|
+
],
|
|
589
|
+
tdd: [
|
|
590
|
+
"RED evidence per slice (failing test output)",
|
|
591
|
+
"Acceptance mapping per slice",
|
|
592
|
+
"GREEN evidence (full-suite pass)",
|
|
593
|
+
"REFACTOR notes with behavior-preservation confirmation",
|
|
594
|
+
"Test-pyramid shape + prove-it reproduction when applicable"
|
|
595
|
+
],
|
|
596
|
+
review: [
|
|
597
|
+
"Spec-compliance findings (Layer 1)",
|
|
598
|
+
"Code-quality findings (Layer 2)",
|
|
599
|
+
"Severity, evidence, and status per finding",
|
|
600
|
+
"Go / no-go verdict"
|
|
601
|
+
],
|
|
602
|
+
ship: [
|
|
603
|
+
"Release checklist (version, changelog, tag, artifacts)",
|
|
604
|
+
"Rollback plan with trigger, steps, verification",
|
|
605
|
+
"Runbook (how to verify the release post-deploy)",
|
|
606
|
+
"Sign-off block"
|
|
607
|
+
]
|
|
608
|
+
};
|
|
609
|
+
const DOMAIN_LABELS = {
|
|
610
|
+
web: "Web app (full-stack)",
|
|
611
|
+
cli: "CLI tool",
|
|
612
|
+
library: "Library / SDK",
|
|
613
|
+
"data-pipeline": "Data pipeline / ETL"
|
|
614
|
+
};
|
|
615
|
+
const STAGE_DOMAIN_SAMPLES = {
|
|
616
|
+
spec: [
|
|
617
|
+
{
|
|
618
|
+
domain: "web",
|
|
619
|
+
label: "AC",
|
|
620
|
+
body: "AC-W1: Given a signed-in admin viewing `/dashboard/orders`, when an order's status changes server-side, the row updates within 2s without a full navigation (assert via `pnpm playwright test orders-live.spec.ts`)."
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
domain: "cli",
|
|
624
|
+
label: "AC",
|
|
625
|
+
body: "AC-C1: Given `cclaw init --claude` run in an empty directory, exit code is `0`, `.cclaw/config.yaml` is created with `harnesses: [claude]`, and stderr contains no warnings (asserted by `tests/integration/init-sync-doctor.test.ts`)."
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
domain: "library",
|
|
629
|
+
label: "AC",
|
|
630
|
+
body: "AC-L1: `validateHookDocument(obj)` returns `{ ok: true }` for every fixture under `tests/fixtures/valid-hooks/` and `{ ok: false, errors: [...] }` with at least one message for every fixture under `tests/fixtures/invalid-hooks/`."
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
domain: "data-pipeline",
|
|
634
|
+
label: "AC",
|
|
635
|
+
body: "AC-D1: For any `orders.csv` input, the pipeline emits exactly one row per `(order_id, event_ts)` pair to `warehouse.fact_orders`; running the job twice on the same input is idempotent (row count unchanged, verified by `dbt test --select fact_orders`)."
|
|
636
|
+
}
|
|
637
|
+
],
|
|
638
|
+
plan: [
|
|
639
|
+
{
|
|
640
|
+
domain: "web",
|
|
641
|
+
label: "Task",
|
|
642
|
+
body: "T-W-3 `[~4m]`: Wire SSE endpoint `/api/orders/stream` into `useOrderFeed` hook. AC-W1. Verify: `pnpm playwright test orders-live.spec.ts`. Depends on: T-W-2."
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
domain: "cli",
|
|
646
|
+
label: "Task",
|
|
647
|
+
body: "T-C-2 `[~3m]`: Add `--dry-run` flag to `cclaw archive` that prints the would-be-archived run IDs to stdout and exits 0. AC-C3. Verify: `node dist/cli.js archive --dry-run` + `tests/unit/cli-parse.test.ts`."
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
domain: "library",
|
|
651
|
+
label: "Task",
|
|
652
|
+
body: "T-L-1 `[~5m]`: Expose `validateHookDocument` from the package root and re-export its types. AC-L1. Verify: `pnpm build && node -e \"console.log(require('./dist').validateHookDocument)\"`."
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
domain: "data-pipeline",
|
|
656
|
+
label: "Task",
|
|
657
|
+
body: "T-D-2 `[~5m]`: Add dedup step keyed on `(order_id, event_ts)` between `raw.orders` and `fact_orders`. AC-D1. Verify: `dbt run --select fact_orders+ && dbt test --select fact_orders`."
|
|
658
|
+
}
|
|
659
|
+
],
|
|
660
|
+
tdd: [
|
|
661
|
+
{
|
|
662
|
+
domain: "web",
|
|
663
|
+
label: "RED→GREEN→REFACTOR",
|
|
664
|
+
body: "RED: `pnpm playwright test orders-live.spec.ts` → timeout waiting for row update. GREEN: wired SSE event → row rerenders via `useOrderFeed`. REFACTOR: extracted `applyOrderEvent(row, event)` pure helper; 87/87 tests still pass."
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
domain: "cli",
|
|
668
|
+
label: "RED→GREEN→REFACTOR",
|
|
669
|
+
body: "RED: `tests/unit/cli-parse.test.ts` expects `--dry-run` flag → `unknown option` error. GREEN: added to the Zod parser; 19/19 pass. REFACTOR: hoisted the dry-run formatter into `src/cli/format.ts` shared with `status`."
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
domain: "library",
|
|
673
|
+
label: "RED→GREEN→REFACTOR",
|
|
674
|
+
body: "RED: `tests/unit/hook-schema.test.ts` imports `validateHookDocument` from package root → `export not found`. GREEN: added re-export + types. REFACTOR: renamed internal `__validate` to `validateHookDocument` so the export name matches the source."
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
domain: "data-pipeline",
|
|
678
|
+
label: "RED→GREEN→REFACTOR",
|
|
679
|
+
body: "RED: `dbt test --select fact_orders` → `unique test on (order_id, event_ts)` fails on re-run. GREEN: added `row_number()` dedup in the staging model. REFACTOR: extracted the dedup CTE into `int_orders_deduped` for reuse by `fact_returns`."
|
|
680
|
+
}
|
|
681
|
+
],
|
|
682
|
+
ship: [
|
|
683
|
+
{
|
|
684
|
+
domain: "web",
|
|
685
|
+
label: "Rollback",
|
|
686
|
+
body: "Trigger: error rate on `/api/orders/stream` > 2% for 5 minutes, or p95 latency > 1.5s for 10 minutes. Steps: `vercel rollback <deployment>`; run `2026_04_14_revert_orders_stream.sql` before traffic returns. Verify: error rate returns to baseline within 10 minutes on the `orders-live` dashboard."
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
domain: "cli",
|
|
690
|
+
label: "Rollback",
|
|
691
|
+
body: "Trigger: `cclaw init --claude` exits non-zero on a fresh tmp dir, OR `cclaw doctor` regresses (FAIL count increases) on the smoke matrix. Steps: `npm unpublish cclaw-cli@<version>` (within the 72h window) or `npm deprecate cclaw-cli@<version> '<reason>'`; publish the previous patch. Verify: `npx cclaw-cli@latest --version` prints the previous version."
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
domain: "library",
|
|
695
|
+
label: "Rollback",
|
|
696
|
+
body: "Trigger: any consumer reports `validateHookDocument` no longer exported, OR the CI `dual-package-check` job fails. Steps: `npm deprecate cclaw-cli@<version> 'broken package export — use <prev>'`; publish the previous minor with a patch bump; emit changelog `## Rollback` entry. Verify: a smoke consumer project `pnpm add cclaw-cli@latest` imports cleanly."
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
domain: "data-pipeline",
|
|
700
|
+
label: "Rollback",
|
|
701
|
+
body: "Trigger: `dbt test --select fact_orders` fails on production run, OR downstream dashboard MAU count drops >10% week-over-week. Steps: disable the new model via `dbt_project.yml` + `dbt run --select state:modified` with the previous git SHA; rerun backfill `dagster asset materialize fact_orders --partition <yesterday>`. Verify: `fact_orders` row count within ±1% of the previous week's baseline."
|
|
702
|
+
}
|
|
703
|
+
]
|
|
704
|
+
};
|
|
705
|
+
export function stageDomainExamples(stage) {
|
|
706
|
+
const samples = STAGE_DOMAIN_SAMPLES[stage];
|
|
707
|
+
if (!samples || samples.length === 0)
|
|
708
|
+
return "";
|
|
709
|
+
const lines = [
|
|
710
|
+
"## Living Examples by Domain",
|
|
711
|
+
"",
|
|
712
|
+
"Use the row matching your project shape to calibrate voice, specificity, and command choice. The rows are deliberately terse — copy the **shape**, not the text.",
|
|
713
|
+
""
|
|
714
|
+
];
|
|
715
|
+
for (const sample of samples) {
|
|
716
|
+
lines.push(`**${DOMAIN_LABELS[sample.domain]} — ${sample.label}:** ${sample.body}`);
|
|
717
|
+
lines.push("");
|
|
718
|
+
}
|
|
719
|
+
return lines.join("\n");
|
|
720
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-harness tool-mapping reference files.
|
|
3
|
+
*
|
|
4
|
+
* Addresses A.1#4: the four supported harnesses (claude, cursor, opencode, codex)
|
|
5
|
+
* expose different primitive names for the same capabilities (ask-user,
|
|
6
|
+
* delegate/Task, web fetch, file edit, code execution, ...). cclaw's stage skills
|
|
7
|
+
* need to pick the right name at runtime without bloating every stage with per-harness
|
|
8
|
+
* if/else ladders.
|
|
9
|
+
*
|
|
10
|
+
* Each file below is short (one table per capability), authoritative, and materialised
|
|
11
|
+
* at `.cclaw/references/harness-tools/<harness>.md`. Stage skills and the meta-skill
|
|
12
|
+
* cite the folder instead of duplicating the mappings inline.
|
|
13
|
+
*
|
|
14
|
+
* When a new harness is added (or an existing one renames a tool), update the
|
|
15
|
+
* corresponding entry here — do NOT scatter tool names across skill text.
|
|
16
|
+
*/
|
|
17
|
+
import type { HarnessId } from "../types.js";
|
|
18
|
+
export declare const HARNESS_TOOL_REFS_DIR = "references/harness-tools";
|
|
19
|
+
export declare function harnessToolRefMarkdown(harness: HarnessId): string;
|
|
20
|
+
export declare const HARNESS_TOOL_REFS_INDEX_MD = "---\nname: Harness tool maps\ndescription: \"Index file. One reference per supported harness \u2014 cite the per-harness file instead of hardcoding tool names in stage skills.\"\n---\n\n# Harness Tool Maps\n\ncclaw supports four harnesses; each exposes different primitive names for the same capabilities. Stage skills and utility skills cite the file matching the currently active harness and fall back to plain-text equivalents for capabilities that the harness lacks.\n\n| Harness | File | Notes |\n|---|---|---|\n| Claude Code | `.cclaw/references/harness-tools/claude.md` | Richest tool surface (AskUserQuestion, Task, WebFetch, WebSearch, MCP, \u2026). |\n| Cursor | `.cclaw/references/harness-tools/cursor.md` | Near-parity with Claude; uses `AskQuestion` instead of `AskUserQuestion`. |\n| OpenCode | `.cclaw/references/harness-tools/opencode.md` | No native ask-user / dispatch; more plain-text fallbacks. |\n| Codex | `.cclaw/references/harness-tools/codex.md` | No native ask-user / dispatch; shell + file I/O only by default. |\n\nWhen a new harness is added or an existing one renames a tool, update the corresponding file (and this index) \u2014 do NOT scatter tool names across skill text.\n";
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-harness tool-mapping reference files.
|
|
3
|
+
*
|
|
4
|
+
* Addresses A.1#4: the four supported harnesses (claude, cursor, opencode, codex)
|
|
5
|
+
* expose different primitive names for the same capabilities (ask-user,
|
|
6
|
+
* delegate/Task, web fetch, file edit, code execution, ...). cclaw's stage skills
|
|
7
|
+
* need to pick the right name at runtime without bloating every stage with per-harness
|
|
8
|
+
* if/else ladders.
|
|
9
|
+
*
|
|
10
|
+
* Each file below is short (one table per capability), authoritative, and materialised
|
|
11
|
+
* at `.cclaw/references/harness-tools/<harness>.md`. Stage skills and the meta-skill
|
|
12
|
+
* cite the folder instead of duplicating the mappings inline.
|
|
13
|
+
*
|
|
14
|
+
* When a new harness is added (or an existing one renames a tool), update the
|
|
15
|
+
* corresponding entry here — do NOT scatter tool names across skill text.
|
|
16
|
+
*/
|
|
17
|
+
export const HARNESS_TOOL_REFS_DIR = "references/harness-tools";
|
|
18
|
+
const CLAUDE_TOOLS_MD = `---
|
|
19
|
+
harness: claude
|
|
20
|
+
name: Claude Code tool map
|
|
21
|
+
description: "Canonical mapping of cclaw capability names → Claude Code tool names. Cited by stage skills; do not duplicate in per-stage text."
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Claude Code — Tool Map
|
|
25
|
+
|
|
26
|
+
Use this file as the single source of truth for which Claude Code tool to call when a cclaw skill references a generic capability.
|
|
27
|
+
|
|
28
|
+
## Core capabilities
|
|
29
|
+
|
|
30
|
+
| cclaw capability | Claude Code tool | Notes |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| Ask user a structured question | \`AskUserQuestion\` | Max 4 options; lettered labels ≤12 chars. Fall back to plain-text lettered list on schema error. |
|
|
33
|
+
| Dispatch a subagent (read-only or write) | \`Task\` with \`subagent_type\` | \`explore\` = read-only; \`generalPurpose\` = read-write. Background via \`run_in_background: true\`. |
|
|
34
|
+
| Read file | \`Read\` | Prefer this over \`cat\` / \`head\` / \`tail\`. |
|
|
35
|
+
| Edit file | \`StrReplace\` (exact match) or \`Write\` (overwrite) | Always \`Read\` before editing; avoid \`sed\`/\`awk\` unless asked. |
|
|
36
|
+
| Create file | \`Write\` | Reject if the task can be solved by editing an existing file. |
|
|
37
|
+
| Search file contents | \`Grep\` (ripgrep-backed) | Use \`output_mode: files_with_matches\` for file lists. |
|
|
38
|
+
| Find files by name / glob | \`Glob\` | Pattern matches mtime-sorted. |
|
|
39
|
+
| Shell command | \`Shell\` | Background long-running jobs with \`block_until_ms: 0\`; poll with \`Await\`. |
|
|
40
|
+
| Fetch URL | \`WebFetch\` | Returns markdown. No auth, no binaries. |
|
|
41
|
+
| Web search | \`WebSearch\` | Use for docs, real-time info, version lookups. |
|
|
42
|
+
| Semantic code search | \`SemanticSearch\` | One directory per call; whole-repo via \`[]\`. |
|
|
43
|
+
| Todo tracking | \`TodoWrite\` | Use \`merge: true\` to update; keep one task \`in_progress\`. |
|
|
44
|
+
| Ask tool (multi-question) | \`AskQuestion\` (Cursor-only, unavailable in Claude) | NOT available in Claude — use \`AskUserQuestion\` instead. |
|
|
45
|
+
| MCP tool call | \`CallMcpTool\` | Always read the tool's schema descriptor first. |
|
|
46
|
+
|
|
47
|
+
## Decision-protocol mapping
|
|
48
|
+
|
|
49
|
+
When a stage skill says "ask the user a structured question", in Claude Code that means:
|
|
50
|
+
|
|
51
|
+
\`\`\`
|
|
52
|
+
AskUserQuestion({
|
|
53
|
+
questions: [{
|
|
54
|
+
id: "...",
|
|
55
|
+
prompt: "One-sentence decision, plain English",
|
|
56
|
+
options: [
|
|
57
|
+
{ id: "a", label: "Short label" }, // ≤12 chars
|
|
58
|
+
{ id: "b", label: "Alt label" },
|
|
59
|
+
{ id: "c", label: "Recommended" }
|
|
60
|
+
]
|
|
61
|
+
}]
|
|
62
|
+
})
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
One question per call. Never batch.
|
|
66
|
+
|
|
67
|
+
## Escalation / fall-back
|
|
68
|
+
|
|
69
|
+
If a tool returns a schema error twice in a row (see the meta-skill's Error / Retry Budget), switch to plain-text equivalents:
|
|
70
|
+
|
|
71
|
+
- \`AskUserQuestion\` → write a numbered list in the response, wait for reply.
|
|
72
|
+
- \`Task\` (dispatch) → inline the work in the current turn.
|
|
73
|
+
- \`WebFetch\` → ask the user for the URL's content.
|
|
74
|
+
`;
|
|
75
|
+
const CURSOR_TOOLS_MD = `---
|
|
76
|
+
harness: cursor
|
|
77
|
+
name: Cursor tool map
|
|
78
|
+
description: "Canonical mapping of cclaw capability names → Cursor agent tool names. Cited by stage skills; do not duplicate in per-stage text."
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
# Cursor — Tool Map
|
|
82
|
+
|
|
83
|
+
Use this file as the single source of truth for which Cursor agent tool to call when a cclaw skill references a generic capability.
|
|
84
|
+
|
|
85
|
+
## Core capabilities
|
|
86
|
+
|
|
87
|
+
| cclaw capability | Cursor tool | Notes |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| Ask user a structured question | \`AskQuestion\` | \`questions\` is an array; each question has \`id\`, \`prompt\`, \`options\`, optional \`allow_multiple\`. |
|
|
90
|
+
| Dispatch a subagent | \`Task\` with \`subagent_type\` | Available types: \`generalPurpose\`, \`explore\` (readonly), \`shell\`, \`browser-use\`, \`best-of-n-runner\`. |
|
|
91
|
+
| Read file | \`Read\` | Line-numbered output; avoid \`cat\` / \`head\` / \`tail\`. |
|
|
92
|
+
| Edit file | \`StrReplace\` | Unique \`old_string\` required; use \`replace_all: true\` for bulk renames. |
|
|
93
|
+
| Create file | \`Write\` | Prefer editing existing files. |
|
|
94
|
+
| Search file contents | \`Grep\` (ripgrep-backed) | Output modes: \`content\`, \`files_with_matches\`, \`count\`. |
|
|
95
|
+
| Find files by name / glob | \`Glob\` | Auto-prepends \`**/\` when pattern does not start with it. |
|
|
96
|
+
| Shell command | \`Shell\` | Long-running jobs go to background via \`block_until_ms: 0\`; poll with \`Await\`. |
|
|
97
|
+
| Fetch URL | \`WebFetch\` | Markdown output. |
|
|
98
|
+
| Web search | \`WebSearch\` | Use for real-time info, framework docs, news. |
|
|
99
|
+
| Semantic code search | \`SemanticSearch\` | Prefer for exploratory "how does X work?" queries. |
|
|
100
|
+
| Todo tracking | \`TodoWrite\` | Supports \`merge: true\` for partial updates. |
|
|
101
|
+
| Generate image | \`GenerateImage\` | Only on explicit user request. |
|
|
102
|
+
| Ask structured questions (Claude-style) | \`AskUserQuestion\` | NOT available in Cursor — use \`AskQuestion\`. |
|
|
103
|
+
| MCP tool call | \`CallMcpTool\` | Cursor exposes MCP tools via this wrapper; read the descriptor first. |
|
|
104
|
+
| Jupyter notebook edit | \`EditNotebook\` | Use for \`.ipynb\` only; cell-granular edits. |
|
|
105
|
+
| Mode switching | \`SwitchMode\` | Propose plan/agent mode changes when task character shifts. |
|
|
106
|
+
|
|
107
|
+
## Decision-protocol mapping
|
|
108
|
+
|
|
109
|
+
In Cursor, structured asks look like:
|
|
110
|
+
|
|
111
|
+
\`\`\`
|
|
112
|
+
AskQuestion({
|
|
113
|
+
questions: [{
|
|
114
|
+
id: "...",
|
|
115
|
+
prompt: "One-sentence decision",
|
|
116
|
+
options: [
|
|
117
|
+
{ id: "a", label: "Option A" },
|
|
118
|
+
{ id: "b", label: "Option B" }
|
|
119
|
+
]
|
|
120
|
+
}]
|
|
121
|
+
})
|
|
122
|
+
\`\`\`
|
|
123
|
+
|
|
124
|
+
## Escalation / fall-back
|
|
125
|
+
|
|
126
|
+
On repeated tool errors, fall back to plain-text equivalents just like Claude — see the meta-skill's Error / Retry Budget.
|
|
127
|
+
`;
|
|
128
|
+
const OPENCODE_TOOLS_MD = `---
|
|
129
|
+
harness: opencode
|
|
130
|
+
name: OpenCode tool map
|
|
131
|
+
description: "Canonical mapping of cclaw capability names → OpenCode primitives. Cited by stage skills; do not duplicate in per-stage text."
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
# OpenCode — Tool Map
|
|
135
|
+
|
|
136
|
+
OpenCode exposes a leaner tool surface than Claude Code / Cursor. When a cclaw skill describes a capability that OpenCode lacks, fall back to the plain-text equivalent listed below.
|
|
137
|
+
|
|
138
|
+
## Core capabilities
|
|
139
|
+
|
|
140
|
+
| cclaw capability | OpenCode primitive | Notes |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| Ask user a structured question | **Not available as a tool.** | Emit a plain-text numbered list: \`A) ... B) ... C) (recommended) ...\`. Wait for the user's letter. |
|
|
143
|
+
| Dispatch a subagent | **Not available as a tool.** | Inline the work in the current turn, or split across multiple turns with the user driving. |
|
|
144
|
+
| Read file | file-read primitive | Same role as \`Read\`. |
|
|
145
|
+
| Edit file | file-edit primitive | Same role as \`StrReplace\`; confirm diff before writing. |
|
|
146
|
+
| Create file | file-write primitive | Prefer editing existing files. |
|
|
147
|
+
| Search file contents | \`rg\` via shell | Cite \`rg\` output verbatim as evidence when a skill requires a grep result. |
|
|
148
|
+
| Find files by name / glob | \`fd\` or \`find\` via shell | Capture the command + output. |
|
|
149
|
+
| Shell command | shell primitive | Long-running jobs require explicit background + polling — check the OpenCode docs for \`&\` semantics. |
|
|
150
|
+
| Fetch URL | \`curl\` via shell | No markdown conversion; extract manually. |
|
|
151
|
+
| Web search | **Not available.** | Ask the user to paste docs or provide a URL, then fetch via shell. |
|
|
152
|
+
| Todo tracking | **Not available as a tool.** | Maintain a \`### TODO\` block inline in your response; keep one item in progress. |
|
|
153
|
+
| MCP tool call | Depends on runtime config. | If MCP is enabled, use the documented invocation; otherwise treat as unavailable. |
|
|
154
|
+
|
|
155
|
+
## Decision-protocol mapping
|
|
156
|
+
|
|
157
|
+
\`\`\`
|
|
158
|
+
Decision: <one sentence>.
|
|
159
|
+
|
|
160
|
+
A) <label> — <trade-off>
|
|
161
|
+
B) <label> — <trade-off>
|
|
162
|
+
C) <label> — <trade-off> (recommended, because <one-line reason>)
|
|
163
|
+
|
|
164
|
+
Please reply with the letter.
|
|
165
|
+
\`\`\`
|
|
166
|
+
|
|
167
|
+
## Escalation / fall-back
|
|
168
|
+
|
|
169
|
+
Because OpenCode lacks native ask-user and dispatch tools, more of cclaw's protocols degrade to plain text. This is expected — the flow gates and artifacts are identical; only the delivery channel changes.
|
|
170
|
+
`;
|
|
171
|
+
const CODEX_TOOLS_MD = `---
|
|
172
|
+
harness: codex
|
|
173
|
+
name: Codex tool map
|
|
174
|
+
description: "Canonical mapping of cclaw capability names → Codex CLI primitives. Cited by stage skills; do not duplicate in per-stage text."
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
# Codex — Tool Map
|
|
178
|
+
|
|
179
|
+
Codex (OpenAI Codex CLI) exposes roughly the same core surface as OpenCode: file I/O, shell, no native ask-user, no dispatch. Fall back to plain text for anything else.
|
|
180
|
+
|
|
181
|
+
## Core capabilities
|
|
182
|
+
|
|
183
|
+
| cclaw capability | Codex primitive | Notes |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| Ask user a structured question | **Not available as a tool.** | Emit a plain-text lettered list; wait for the user's reply. |
|
|
186
|
+
| Dispatch a subagent | **Not available as a tool.** | Inline the work; split turns if needed. |
|
|
187
|
+
| Read file | \`read\` / \`open\` primitive | Same role as \`Read\`. |
|
|
188
|
+
| Edit file | \`edit\` / \`patch\` primitive | Same role as \`StrReplace\`. |
|
|
189
|
+
| Create file | \`write\` primitive | Prefer editing existing files. |
|
|
190
|
+
| Search file contents | \`rg\` via shell | Capture command + output verbatim. |
|
|
191
|
+
| Find files by name / glob | \`fd\` / \`find\` / \`ls\` via shell | Capture command + output. |
|
|
192
|
+
| Shell command | shell primitive | Codex CLI may restrict some binaries by default — check the effective permissions. |
|
|
193
|
+
| Fetch URL | \`curl\` via shell | Extract markdown manually. |
|
|
194
|
+
| Web search | **Not available.** | Ask user for docs / URL. |
|
|
195
|
+
| Todo tracking | **Not available as a tool.** | Keep an inline \`### TODO\` section; update it as you progress. |
|
|
196
|
+
| MCP tool call | Depends on runtime config. | If MCP is wired, cite the descriptor; otherwise treat as unavailable. |
|
|
197
|
+
|
|
198
|
+
## Decision-protocol mapping
|
|
199
|
+
|
|
200
|
+
\`\`\`
|
|
201
|
+
Decision: <one sentence>.
|
|
202
|
+
|
|
203
|
+
A) <label> — <trade-off>
|
|
204
|
+
B) <label> — <trade-off> (recommended, because <reason>)
|
|
205
|
+
C) <label> — <trade-off>
|
|
206
|
+
|
|
207
|
+
Please reply with the letter.
|
|
208
|
+
\`\`\`
|
|
209
|
+
|
|
210
|
+
## Escalation / fall-back
|
|
211
|
+
|
|
212
|
+
Treat missing tools as "plain-text required", not "skip the step". The gate still has to pass; only the channel changes.
|
|
213
|
+
`;
|
|
214
|
+
const HARNESS_TOOL_REFS = {
|
|
215
|
+
claude: CLAUDE_TOOLS_MD,
|
|
216
|
+
cursor: CURSOR_TOOLS_MD,
|
|
217
|
+
opencode: OPENCODE_TOOLS_MD,
|
|
218
|
+
codex: CODEX_TOOLS_MD
|
|
219
|
+
};
|
|
220
|
+
export function harnessToolRefMarkdown(harness) {
|
|
221
|
+
return HARNESS_TOOL_REFS[harness];
|
|
222
|
+
}
|
|
223
|
+
export const HARNESS_TOOL_REFS_INDEX_MD = `---
|
|
224
|
+
name: Harness tool maps
|
|
225
|
+
description: "Index file. One reference per supported harness — cite the per-harness file instead of hardcoding tool names in stage skills."
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
# Harness Tool Maps
|
|
229
|
+
|
|
230
|
+
cclaw supports four harnesses; each exposes different primitive names for the same capabilities. Stage skills and utility skills cite the file matching the currently active harness and fall back to plain-text equivalents for capabilities that the harness lacks.
|
|
231
|
+
|
|
232
|
+
| Harness | File | Notes |
|
|
233
|
+
|---|---|---|
|
|
234
|
+
| Claude Code | \`.cclaw/${HARNESS_TOOL_REFS_DIR}/claude.md\` | Richest tool surface (AskUserQuestion, Task, WebFetch, WebSearch, MCP, …). |
|
|
235
|
+
| Cursor | \`.cclaw/${HARNESS_TOOL_REFS_DIR}/cursor.md\` | Near-parity with Claude; uses \`AskQuestion\` instead of \`AskUserQuestion\`. |
|
|
236
|
+
| OpenCode | \`.cclaw/${HARNESS_TOOL_REFS_DIR}/opencode.md\` | No native ask-user / dispatch; more plain-text fallbacks. |
|
|
237
|
+
| Codex | \`.cclaw/${HARNESS_TOOL_REFS_DIR}/codex.md\` | No native ask-user / dispatch; shell + file I/O only by default. |
|
|
238
|
+
|
|
239
|
+
When a new harness is added or an existing one renames a tool, update the corresponding file (and this index) — do NOT scatter tool names across skill text.
|
|
240
|
+
`;
|
|
@@ -209,10 +209,7 @@ When a stage requires user input (approval, choice, direction):
|
|
|
209
209
|
1. **State the decision** in one sentence.
|
|
210
210
|
2. **Present options** as labeled choices (A, B, C...), one-line each, with trade-off / consequence.
|
|
211
211
|
3. **Mark one option \`(recommended)\`** with a one-line reason. Do NOT use numeric "Completeness" rubrics — pick the option that best closes the decision with the smallest blast radius, lowest irreversible risk, and clearest evidence.
|
|
212
|
-
4. **Use the harness ask-user tool when available
|
|
213
|
-
- Claude Code: \`AskUserQuestion\`
|
|
214
|
-
- Cursor: \`AskQuestion\` (options array)
|
|
215
|
-
- Codex/OpenCode: numbered list in plain text (no native ask tool).
|
|
212
|
+
4. **Use the harness ask-user tool when available.** For the exact tool name and fallback, consult \`.cclaw/references/harness-tools/<harness>.md\` (one file per supported harness — claude, cursor, opencode, codex). Summary: Claude Code → \`AskUserQuestion\`; Cursor → \`AskQuestion\`; OpenCode / Codex → plain-text lettered list.
|
|
216
213
|
5. **Wait for response.** Do not proceed until the user picks.
|
|
217
214
|
6. **Commit to the choice.** Once decided, do not re-argue.
|
|
218
215
|
|
|
@@ -236,6 +233,43 @@ When a stage requires user input (approval, choice, direction):
|
|
|
236
233
|
|
|
237
234
|
If the same approach fails three times in a row (same verification command, same review finding, same tool invocation), STOP and escalate: summarize what you tried, what evidence you have, what hypothesis you are now testing, and ask the user how to proceed. Do not invent a new angle silently on the fourth attempt.
|
|
238
235
|
|
|
236
|
+
### Shared Stage Completion Protocol
|
|
237
|
+
|
|
238
|
+
Every stage skill ends with a completion block parameterized by four values: \`next\` (next stage or \`done\`), \`gates\` (gate IDs to mark passed), \`artifact\` (file under \`.cclaw/artifacts/\`), and \`mandatory\` (agents required by delegation enforcement). Stage skills print their **Completion Parameters** and then defer to this procedure — do NOT re-print the full procedure per stage.
|
|
239
|
+
|
|
240
|
+
When all required gates are satisfied and the artifact is written, execute **in this exact order**:
|
|
241
|
+
|
|
242
|
+
0. **Delegation pre-flight** (BLOCKING, only when \`mandatory\` is non-empty).
|
|
243
|
+
- For each agent in \`mandatory\`: confirm it was dispatched (via Task/delegate) and completed, OR record an explicit waiver with reason in \`.cclaw/state/delegation-log.json\`.
|
|
244
|
+
- Write a JSON entry per agent: \`{ "stage": "<stage>", "agent": "<name>", "mode": "mandatory", "status": "completed"|"waived", "waiverReason": "<if waived>", "ts": "<ISO timestamp>" }\`.
|
|
245
|
+
- If the harness does not support delegation, record status \`"waived"\` with reason \`"harness_limitation"\`.
|
|
246
|
+
- **Do NOT proceed to step 1 until every mandatory agent has an entry in the delegation log.**
|
|
247
|
+
1. **Update \`.cclaw/state/flow-state.json\`:**
|
|
248
|
+
- Set \`currentStage\` to \`next\` (or leave unchanged when \`next === "done"\`).
|
|
249
|
+
- Add the current stage to \`completedStages\`.
|
|
250
|
+
- Move every gate ID in \`gates\` into \`stageGateCatalog.<stage>.passed\`.
|
|
251
|
+
- Clear \`stageGateCatalog.<stage>.blocked\`.
|
|
252
|
+
- For each passed gate, add an entry to \`guardEvidence\`: \`"<gate_id>": "<artifact path or excerpt proving the gate>"\`. Do NOT leave \`guardEvidence\` empty.
|
|
253
|
+
2. **Persist artifact** at \`.cclaw/artifacts/<artifact>\`. Do NOT manually copy into \`.cclaw/runs/\`; archival is handled by \`cclaw archive\`.
|
|
254
|
+
3. **Doctor pre-flight** — run \`npx cclaw doctor\` (or the installed cclaw binary). If any check fails, resolve the issue (missing delegation entry, artifact section, gate evidence) and re-run until all checks pass. Do NOT proceed while doctor reports failures.
|
|
255
|
+
4. **Tell the user** (verbatim when \`next\` is a stage; use the flow-complete variant when \`next === "done"\`):
|
|
256
|
+
> **Stage \`<stage>\` complete.** Next: **<next>** — <one-line next-stage description>.
|
|
257
|
+
>
|
|
258
|
+
> Run \`/cc-next\` to continue.
|
|
259
|
+
|
|
260
|
+
Flow-complete variant:
|
|
261
|
+
> **Flow complete.** All stages finished. The project is ready for release.
|
|
262
|
+
|
|
263
|
+
5. **STOP.** Do not load the next stage skill yourself. The user will run \`/cc-next\` when ready (same session or new session).
|
|
264
|
+
|
|
265
|
+
### Shared Resume Protocol
|
|
266
|
+
|
|
267
|
+
When resuming a stage in a NEW session (artifact exists but gates are not all passed in \`flow-state.json\`):
|
|
268
|
+
|
|
269
|
+
1. Read the existing artifact and mark every gate whose evidence is already present in the artifact.
|
|
270
|
+
2. For each unverified gate, ask the user to confirm ONE gate at a time. Do NOT batch multiple gate confirmations in a single message.
|
|
271
|
+
3. Update \`guardEvidence\` for each confirmed gate before proceeding to the next unverified gate.
|
|
272
|
+
|
|
239
273
|
## </EXTREMELY-IMPORTANT>
|
|
240
274
|
|
|
241
275
|
## Invocation Preamble (per turn, non-trivial tasks)
|
|
@@ -255,6 +289,40 @@ The preamble exists to prevent silent drift from the user's ask. If the preamble
|
|
|
255
289
|
|
|
256
290
|
Do not re-emit the preamble on every subsequent tool call — once per user turn is sufficient. If the user message changes the goal mid-execution, emit a fresh preamble before acting on the new direction.
|
|
257
291
|
|
|
292
|
+
## Engineering Ethos
|
|
293
|
+
|
|
294
|
+
Three guardrails apply to every stage, every turn. Internalise them — they trump speed, cleverness, and novelty:
|
|
295
|
+
|
|
296
|
+
### Search Before Building
|
|
297
|
+
|
|
298
|
+
Before writing new code, a new skill, a new abstraction, or a new artifact section, spend 60–120 seconds checking whether the thing already exists. Order of search:
|
|
299
|
+
|
|
300
|
+
1. **Project artifacts** — \`.cclaw/artifacts/**\`, \`docs/**\`, root-level \`README.md\` / \`SPEC.md\` / \`DESIGN.md\`.
|
|
301
|
+
2. **Project knowledge** — \`.cclaw/knowledge.jsonl\` (lessons with matching \`domain\` / \`trigger\`).
|
|
302
|
+
3. **Codebase** — \`rg\` / \`Grep\` for the symbol, function, test, or comment that describes what you're about to add.
|
|
303
|
+
4. **Framework/library primitives** — prefer a stdlib or framework-native affordance over a handwritten helper.
|
|
304
|
+
5. **Existing skill or stage rule** — \`.cclaw/skills/**/SKILL.md\` and \`.cclaw/commands/**/*.md\`.
|
|
305
|
+
|
|
306
|
+
Only after the first four turn up nothing do you build. Every duplicate helper, redefined type, parallel-but-incompatible artifact section, or re-discovered lesson is a tax on the next five sessions. Record the negative search result (what you looked for, where, and why nothing fit) in the turn's preamble or the stage artifact so future agents don't repeat the hunt.
|
|
307
|
+
|
|
308
|
+
### Boil the Lake (scoped minimum-sweep rule)
|
|
309
|
+
|
|
310
|
+
"Boil the lake" normally means wasteful, exhaustive work. **cclaw inverts the phrase**: within the current stage, you are expected to sweep *the defined surface exhaustively* — not to stop at the first plausible answer.
|
|
311
|
+
|
|
312
|
+
- In \`brainstorm\` / \`scope\` — enumerate every viable approach in the defined option space; name the ones you rejected and why.
|
|
313
|
+
- In \`design\` — trace every data-flow and failure edge across the chosen component boundary, not just the happy path.
|
|
314
|
+
- In \`spec\` — list every acceptance criterion for the in-scope surface; "and similar" / "etc." is banned.
|
|
315
|
+
- In \`tdd\` — exercise every branch / error path / boundary of the slice under test, not only the canonical case.
|
|
316
|
+
- In \`review\` — audit every file touched in the diff, not just the files named in the spec.
|
|
317
|
+
|
|
318
|
+
The sweep is bounded by the stage's declared surface. Expanding the surface is a Decision Protocol question, not a silent enlargement.
|
|
319
|
+
|
|
320
|
+
### Do Less, Prove More
|
|
321
|
+
|
|
322
|
+
When in doubt between adding code / scope / artifact sections and cutting them, cut. The flow already forces you to justify each stage's output — volume is never a proxy for quality. One acceptance criterion with captured evidence beats five without; one labeled architecture diagram beats three generic boxes-and-arrows; one REFACTOR note explaining a concrete trade-off beats a paragraph of filler.
|
|
323
|
+
|
|
324
|
+
If a rule, template section, or agent feels ornamental, flag it in \`Operational Self-Improvement\` and propose removal — cclaw's invariant is that every section must pay its tokens back by preventing a specific failure mode.
|
|
325
|
+
|
|
258
326
|
## Operational Self-Improvement (auto-learn)
|
|
259
327
|
|
|
260
328
|
cclaw treats **lived friction** as first-class knowledge. When you observe one of the triggers below during a session, append a single JSONL line to \`.cclaw/knowledge.jsonl\` via \`/cc-learn add\` (or queue it for the next \`/cc-learn\` call) — do NOT let the signal evaporate when the session ends.
|
package/dist/content/skills.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
-
import { stageExamples, stageGoodBadExamples } from "./examples.js";
|
|
2
|
+
import { stageDomainExamples, stageExamples, stageGoodBadExamples } from "./examples.js";
|
|
3
3
|
import { selfImprovementBlock } from "./learnings.js";
|
|
4
4
|
import { stageAutoSubagentDispatch, stageSchema } from "./stage-schema.js";
|
|
5
5
|
function rationalizationTable(stage) {
|
|
@@ -155,60 +155,81 @@ function waveExecutionModeBlock(stage) {
|
|
|
155
155
|
|
|
156
156
|
After plan approval (**WAIT_FOR_CONFIRM** / \`plan_wait_for_confirm\` satisfied), process **all tasks in the current dependency wave** sequentially: **RED → GREEN → REFACTOR** per task, recording evidence per slice. **Stop** only on **BLOCKED**, a test failure that **requires user input**, or **wave completion** (every task in the wave has the required RED / GREEN / REFACTOR evidence per the plan artifact).
|
|
157
157
|
|
|
158
|
+
### Walkthrough — Wave 1 with 3 tasks
|
|
159
|
+
|
|
160
|
+
The example below is **illustrative only** — do not copy the command names blindly, match them to your stack.
|
|
161
|
+
|
|
162
|
+
Assume Wave 1 from the plan artifact contains three tasks:
|
|
163
|
+
|
|
164
|
+
| Task ID | Description | AC | Verification |
|
|
165
|
+
|---|---|---|---|
|
|
166
|
+
| T-1 \`[~3m]\` | Add \`User.emailNormalized\` column | AC-1 | \`npm test -- users/schema\` |
|
|
167
|
+
| T-2 \`[~4m]\` | Normalize on write in \`UserRepo.save\` | AC-1 | \`npm test -- users/repo\` |
|
|
168
|
+
| T-3 \`[~3m]\` | Reject duplicates in \`UserService.signup\` | AC-2 | \`npm test -- users/service\` |
|
|
169
|
+
|
|
170
|
+
**Execution transcript** (one slice at a time, evidence captured per step):
|
|
171
|
+
|
|
172
|
+
**T-1 — RED**
|
|
173
|
+
|
|
174
|
+
> Run: \`npm test -- users/schema\` → **FAIL** (missing column: \`emailNormalized\`). Captured the failure stack as RED evidence. No production code touched yet.
|
|
175
|
+
|
|
176
|
+
**T-1 — GREEN**
|
|
177
|
+
|
|
178
|
+
> Added the column in the schema module. Re-ran \`npm test -- users/schema\` → **PASS**. Ran the full suite \`npm test\` → **PASS**. Captured both outputs as GREEN evidence.
|
|
179
|
+
|
|
180
|
+
**T-1 — REFACTOR**
|
|
181
|
+
|
|
182
|
+
> Extracted the column definition into a shared \`NormalizedEmail\` type used by T-2/T-3. Re-ran \`npm test\` → **PASS**. Captured REFACTOR note: "Extracted NormalizedEmail type to keep T-2/T-3 DRY; zero behavior change, all tests still green."
|
|
183
|
+
|
|
184
|
+
**T-2 — RED / GREEN / REFACTOR**: same shape — write the repo test that expects normalised writes, watch it fail (RED), implement normalisation inside \`UserRepo.save\` only (GREEN), then refactor the normaliser out of the repo into a helper shared with T-3 (REFACTOR).
|
|
185
|
+
|
|
186
|
+
**T-3 — RED / GREEN / REFACTOR**: write the service-level duplicate test that expects a rejection, watch it fail (RED), add the duplicate check in \`UserService.signup\` (GREEN), refactor the error message into a named constant (REFACTOR).
|
|
187
|
+
|
|
188
|
+
**Wave gate check**
|
|
189
|
+
|
|
190
|
+
After T-3 REFACTOR, before declaring Wave 1 done:
|
|
191
|
+
|
|
192
|
+
1. Run the **full suite** (\`npm test\`) one final time → **PASS** captured as wave-exit evidence.
|
|
193
|
+
2. Verify the TDD artifact contains RED, GREEN, and REFACTOR evidence for T-1, T-2, **and** T-3. No partial waves.
|
|
194
|
+
3. Only now mark Wave 1 complete. Wave 2 cannot start until this step.
|
|
195
|
+
|
|
196
|
+
**When to stop mid-wave (do NOT push through)**
|
|
197
|
+
|
|
198
|
+
- A RED test fails for a reason you did not predict (e.g. an unrelated flaky test) → **pause**, diagnose, log an operational-self-improvement entry, and decide with the user before proceeding.
|
|
199
|
+
- A GREEN step would require touching code outside the task's acceptance criterion → **pause**, the task is scoped wrong; adjust the plan or open a follow-up task.
|
|
200
|
+
- The same RED failure reappears after a GREEN change → **escalate** per the 3-attempts rule; do not keep patching.
|
|
201
|
+
|
|
158
202
|
`;
|
|
159
203
|
}
|
|
160
204
|
function stageCompletionProtocol(schema) {
|
|
161
205
|
const stage = schema.stage;
|
|
162
206
|
const gateIds = schema.requiredGates.map((g) => g.id);
|
|
163
207
|
const gateList = gateIds.map((id) => `\`${id}\``).join(", ");
|
|
164
|
-
const nextStage = schema.next === "done" ?
|
|
208
|
+
const nextStage = schema.next === "done" ? "done" : schema.next;
|
|
165
209
|
const mandatory = schema.mandatoryDelegations;
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
?
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
- Move all gate IDs for this stage (${gateList}) into \`stageGateCatalog.${stage}.passed\`
|
|
174
|
-
- Clear \`stageGateCatalog.${stage}.blocked\``;
|
|
175
|
-
const delegationBlock = mandatory.length > 0
|
|
176
|
-
? `0. **Delegation pre-flight** (BLOCKING):
|
|
177
|
-
- Mandatory agents for this stage: ${mandatory.map((a) => `\`${a}\``).join(", ")}.
|
|
178
|
-
- For each mandatory agent: confirm it was dispatched (via Task/delegate) and completed, OR record an explicit waiver with reason in \`${delegationLogRel}\`.
|
|
179
|
-
- Write a JSON entry per agent: \`{ "stage": "${stage}", "agent": "<name>", "mode": "mandatory", "status": "completed"|"waived", "waiverReason": "<if waived>", "ts": "<ISO timestamp>" }\`.
|
|
180
|
-
- If the harness does not support delegation, record status \`"waived"\` with reason \`"harness_limitation"\`.
|
|
181
|
-
- **Do NOT proceed to step 1 until every mandatory agent has an entry in the delegation log.**
|
|
182
|
-
`
|
|
183
|
-
: "";
|
|
184
|
-
let nextAction;
|
|
185
|
-
if (nextStage) {
|
|
186
|
-
const nextSchema = stageSchema(nextStage);
|
|
187
|
-
const nextDescription = nextSchema.skillDescription.charAt(0).toLowerCase() + nextSchema.skillDescription.slice(1);
|
|
188
|
-
nextAction = `4. Tell the user:\n\n > **Stage \`${stage}\` complete.** Next: **${nextStage}** — ${nextDescription}\n >\n > Run \`/cc-next\` to continue.`;
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
nextAction = `4. Tell the user:\n\n > **Flow complete.** All stages finished. The project is ready for release.`;
|
|
192
|
-
}
|
|
210
|
+
const mandatoryList = mandatory.length > 0 ? mandatory.map((a) => `\`${a}\``).join(", ") : "none";
|
|
211
|
+
const nextDescription = schema.next === "done"
|
|
212
|
+
? "flow complete — release cut and handoff signed off"
|
|
213
|
+
: (() => {
|
|
214
|
+
const nextSchema = stageSchema(schema.next);
|
|
215
|
+
return nextSchema.skillDescription.charAt(0).toLowerCase() + nextSchema.skillDescription.slice(1);
|
|
216
|
+
})();
|
|
193
217
|
return `## Stage Completion Protocol
|
|
194
218
|
|
|
195
|
-
|
|
219
|
+
Apply the **Shared Stage Completion Protocol** from \`.cclaw/skills/using-cclaw/SKILL.md\` with these parameters — do NOT re-derive the generic steps here.
|
|
196
220
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
${
|
|
221
|
+
**Completion Parameters**
|
|
222
|
+
- \`stage\` — \`${stage}\`
|
|
223
|
+
- \`next\` — \`${nextStage}\` (${nextDescription})
|
|
224
|
+
- \`gates\` — ${gateList}
|
|
225
|
+
- \`artifact\` — \`${RUNTIME_ROOT}/artifacts/${schema.artifactFile}\`
|
|
226
|
+
- \`mandatory\` — ${mandatoryList}
|
|
203
227
|
|
|
204
|
-
|
|
228
|
+
When all required gates are satisfied and the artifact is written, execute the shared procedure (delegation pre-flight → flow-state update → artifact persistence → \`npx cclaw doctor\` → user handoff → STOP) using the parameters above. If any check fails, resolve the issue and re-run before proceeding.
|
|
205
229
|
|
|
206
230
|
## Resume Protocol
|
|
207
231
|
|
|
208
|
-
When resuming
|
|
209
|
-
1. Read the existing artifact and check which gates can be verified from artifact evidence.
|
|
210
|
-
2. For each unverified gate, ask the user to confirm ONE gate at a time. Do NOT batch multiple gate confirmations in a single message.
|
|
211
|
-
3. Update \`guardEvidence\` for each confirmed gate before proceeding.
|
|
232
|
+
When resuming this stage in a NEW session (artifact exists but not all of ${gateList} are passed), follow the **Shared Resume Protocol** in \`.cclaw/skills/using-cclaw/SKILL.md\` — confirm one gate at a time, update \`guardEvidence\` for each, never batch confirmations.
|
|
212
233
|
`;
|
|
213
234
|
}
|
|
214
235
|
function stageTransitionAutoAdvanceBlock(schema) {
|
|
@@ -364,6 +385,7 @@ You MUST complete these steps in order:
|
|
|
364
385
|
${checklistItems}
|
|
365
386
|
|
|
366
387
|
${stageGoodBadExamples(stage)}
|
|
388
|
+
${stageDomainExamples(stage)}
|
|
367
389
|
${stageExamples(stage)}
|
|
368
390
|
${namedAntiPatternBlock(stage)}
|
|
369
391
|
${cognitivePatternsList(stage)}
|
|
@@ -865,6 +865,8 @@ const PLAN = {
|
|
|
865
865
|
cognitivePatterns: [
|
|
866
866
|
{ name: "Vertical Slice Thinking", description: "Each task delivers one thin end-to-end slice of value. Horizontal layers (all models, then all controllers) create integration risk. Vertical slices (one feature through all layers) reduce it." },
|
|
867
867
|
{ name: "Two-Minute Smell Test", description: "If a competent engineer cannot understand and start a task in two minutes, the task is too large or too vague. Break it down further." },
|
|
868
|
+
{ name: "Five-Minute Budget (hard)", description: "Every plan step MUST fit a 2-to-5-minute execution budget on a competent implementer. If a step plausibly takes longer, it is two steps pretending to be one — split it. Measure by 'keyboard minutes on this slice', not by wall clock. Write the estimated minutes next to each task (e.g. `[~3m]`); when a TDD slice later consumes >2× the estimate, log an operational-self-improvement entry so future plans calibrate better." },
|
|
869
|
+
{ name: "No Placeholders", description: "Plan text must be copy-pasteable. Forbidden tokens anywhere in the artifact: `TODO`, `TBD`, `FIXME`, `<fill-in>`, `<your-*-here>`, `xxx`, `...` (as ellipsis for omitted content — real commands use real args). Every acceptance-criterion link, file path, test command, and verification command must be concrete and runnable as written. A placeholder is a deferred decision masquerading as a plan; decide it now or remove the task." },
|
|
868
870
|
{ name: "Make the Change Easy, Then Make the Easy Change", description: "Refactor first, implement second. Never structural + behavioral changes simultaneously. Sequence tasks accordingly." },
|
|
869
871
|
{ name: "Diagnose Before Fix", description: "Before decomposing work, understand the current state of the codebase. Read existing code, tests, and conventions. Tasks should reference what exists, not assume a blank slate." },
|
|
870
872
|
{ name: "Scrap Signals", description: "If a task description is vague, the acceptance criterion is missing, or the verification command is a placeholder — it is scrap. Either rewrite it or remove it. Half-specified tasks waste more time than no tasks." },
|
|
@@ -892,6 +894,16 @@ const PLAN = {
|
|
|
892
894
|
"Are there hidden dependencies between tasks in different waves?"
|
|
893
895
|
],
|
|
894
896
|
stopGate: true
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
title: "Five-Minute Budget + No-Placeholders Audit",
|
|
900
|
+
evaluationPoints: [
|
|
901
|
+
"Does every task carry an explicit minutes estimate (e.g. `[~3m]`) and does every estimate fit the 2-to-5-minute budget? Estimates >5 minutes must be split.",
|
|
902
|
+
"Are all file paths, test commands, and verification commands copy-pasteable as written — no `TODO`, `TBD`, `FIXME`, `<fill-in>`, `<your-*-here>`, `xxx`, or ellipsis standing in for omitted args?",
|
|
903
|
+
"Does every acceptance-criterion reference resolve to a real R# / AC-### in the spec (not a blank link)?",
|
|
904
|
+
"If an estimate is genuinely uncertain (first-time integration, unfamiliar library), is the uncertainty named explicitly and scheduled as a spike task in wave 0, rather than hidden behind a large estimate?"
|
|
905
|
+
],
|
|
906
|
+
stopGate: true
|
|
895
907
|
}
|
|
896
908
|
],
|
|
897
909
|
completionStatus: ["DONE", "DONE_WITH_CONCERNS", "BLOCKED"],
|
|
@@ -903,11 +915,12 @@ const PLAN = {
|
|
|
903
915
|
artifactValidation: [
|
|
904
916
|
{ section: "Dependency Graph", required: true, validationRule: "Ordering and parallel opportunities explicit. No circular dependencies." },
|
|
905
917
|
{ section: "Dependency Waves", required: true, validationRule: "Every task belongs to a wave. Each wave has an exit gate and dependency statement." },
|
|
906
|
-
{ section: "Task List", required: true, validationRule: "Each task
|
|
918
|
+
{ section: "Task List", required: true, validationRule: "Each task row includes ID, description, acceptance criterion, verification command, and effort estimate (S/M/L). Every task must also carry a minutes estimate within the 2-5 minute budget." },
|
|
907
919
|
{ section: "Acceptance Mapping", required: true, validationRule: "Every spec criterion is covered by at least one task." },
|
|
908
920
|
{ section: "Risk Assessment", required: false, validationRule: "If present: per-task or per-wave risk identification with likelihood, impact, and mitigation strategy." },
|
|
909
921
|
{ section: "Boundary Map", required: false, validationRule: "If present: per-wave or per-task interface contracts listing what each task produces (exports) and consumes (imports) from other tasks." },
|
|
910
|
-
{ section: "WAIT_FOR_CONFIRM", required: true, validationRule: "Explicit marker present. Status: pending until user approves." }
|
|
922
|
+
{ section: "WAIT_FOR_CONFIRM", required: true, validationRule: "Explicit marker present. Status: pending until user approves." },
|
|
923
|
+
{ section: "No-Placeholder Scan", required: false, validationRule: "If present: confirmation that a text scan for `TODO`, `TBD`, `FIXME`, `<fill-in>`, `<your-*-here>`, `xxx`, or bare ellipses has zero hits in the task list. A placeholder is a deferred decision masquerading as a plan." }
|
|
911
924
|
],
|
|
912
925
|
namedAntiPattern: {
|
|
913
926
|
title: "Task Details Can Be Finalized During Coding",
|
|
@@ -1041,7 +1054,9 @@ const TDD = {
|
|
|
1041
1054
|
{ name: "Characterization First", description: "Before changing existing behavior, write characterization tests that capture current behavior as-is. These tests document what the system does today — even if that behavior is wrong. Only after the characterization suite is green do you add the new RED test for the desired change. This prevents accidental behavior destruction during refactoring." },
|
|
1042
1055
|
{ name: "Test Pyramid Shape", description: "Healthy test suites look like a pyramid: many small fast tests at the base, fewer medium integration tests in the middle, few large end-to-end tests at the top. Each layer catches a different class of bug; none of them substitutes for another. If your suite is top-heavy (mostly E2E) it is slow and flaky; if it is base-only it misses integration contracts. During TDD, default to the smallest layer that can prove the behavior." },
|
|
1043
1056
|
{ name: "Prove-It Pattern (bug fixes)", description: "For any reported regression or hotfix, the FIRST test is a reproduction — it must fail without your fix, pass with your fix, and fail again if the fix is reverted. This is the only way to prove you fixed the reported bug and not a superficially similar one. Skipping this step is how bugs come back two releases later wearing a different name." },
|
|
1044
|
-
{ name: "Test Size Model", description: "Size tests by scope, not by name: Small = pure logic, no I/O, <50ms; Medium = one process boundary, possibly filesystem or an in-memory DB; Large = multi-process / network / real external service. Small tests are the default; escalate to Medium only when a real boundary must be exercised, and to Large only for end-to-end user journeys. Record the size class in the TDD artifact so reviewers can sanity-check the pyramid shape." }
|
|
1057
|
+
{ name: "Test Size Model", description: "Size tests by scope, not by name: Small = pure logic, no I/O, <50ms; Medium = one process boundary, possibly filesystem or an in-memory DB; Large = multi-process / network / real external service. Small tests are the default; escalate to Medium only when a real boundary must be exercised, and to Large only for end-to-end user journeys. Record the size class in the TDD artifact so reviewers can sanity-check the pyramid shape." },
|
|
1058
|
+
{ name: "State Over Interaction", description: "Assert on observable outcomes (return values, state changes, persisted data, HTTP responses) — NOT on which helper methods were called, how many times, or in what order. Interaction-style assertions (`expect(mock.foo).toHaveBeenCalledWith(...)` without a state assertion) couple tests to implementation and shatter under harmless refactors. Use mocks only at trust boundaries (network, filesystem, time); for everything inside the module, let state do the asserting. If you cannot observe the outcome without a mock-spy, rework the seam before writing the test." },
|
|
1059
|
+
{ name: "Beyoncé Rule", description: "If you liked it, you should have put a test on it. Every surface that a caller can observe — public API, CLI flag, config key, exit code, persisted schema — is a contract, and every contract without a test is a silent regression waiting to happen. When a bug or production incident reveals an uncovered surface, the fix is never 'patch the code'; it is 'patch the code AND add the test that would have caught it'. Untested behavior does not exist for future refactors — it only exists until somebody accidentally removes it." }
|
|
1045
1060
|
],
|
|
1046
1061
|
reviewSections: [
|
|
1047
1062
|
{
|
|
@@ -1085,6 +1100,17 @@ const TDD = {
|
|
|
1085
1100
|
"Is there a note confirming the reproduction test fails again if the fix is reverted (or equivalent evidence that the test is actually pinned to this fix)?"
|
|
1086
1101
|
],
|
|
1087
1102
|
stopGate: false
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
title: "State-over-Interaction + Beyoncé Coverage",
|
|
1106
|
+
evaluationPoints: [
|
|
1107
|
+
"Do assertions target observable state (return values, persisted data, HTTP responses, logs) rather than which internal helpers were called?",
|
|
1108
|
+
"Are mocks/spies used only at true trust boundaries (network, filesystem, time, external services), not for module-internal collaborators?",
|
|
1109
|
+
"For every public surface touched in this slice (exported API, CLI flag, config key, env var, exit code, schema field) — does at least one test observe it?",
|
|
1110
|
+
"If a bug or review finding revealed an uncovered surface, was a test added alongside the fix, not just the code change?",
|
|
1111
|
+
"Are interaction-style assertions (e.g. `toHaveBeenCalledWith` without a state assertion) justified by an explicit boundary comment, or flagged for follow-up?"
|
|
1112
|
+
],
|
|
1113
|
+
stopGate: false
|
|
1088
1114
|
}
|
|
1089
1115
|
],
|
|
1090
1116
|
completionStatus: ["DONE", "DONE_WITH_CONCERNS", "BLOCKED"],
|
|
@@ -278,9 +278,15 @@ export const ARTIFACT_TEMPLATES = {
|
|
|
278
278
|
Execution rule: complete and verify each wave before starting the next wave.
|
|
279
279
|
|
|
280
280
|
## Task List
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
|
|
282
|
+
**Rules (apply before writing rows):**
|
|
283
|
+
- Every task fits the **2-5 minute budget**. If \`[~Nm]\` is >5, split the task.
|
|
284
|
+
- **No placeholders.** Forbidden tokens anywhere in this table: \`TODO\`, \`TBD\`, \`FIXME\`, \`<fill-in>\`, \`<your-*-here>\`, \`xxx\`, bare ellipsis. Every file path, test, and verification command must be copy-pasteable as written.
|
|
285
|
+
- If an estimate is genuinely uncertain (new library, unfamiliar subsystem), add a **spike task in wave 0** to de-risk — do NOT hide the uncertainty inside a large estimate.
|
|
286
|
+
|
|
287
|
+
| Task ID | Description | Acceptance criterion | Verification command | Effort (S/M/L) | Minutes |
|
|
288
|
+
|---|---|---|---|---|---|
|
|
289
|
+
| T-1 | | | | | [~3m] |
|
|
284
290
|
|
|
285
291
|
## Acceptance Mapping
|
|
286
292
|
| Criterion ID | Task IDs |
|
|
@@ -297,6 +303,10 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
297
303
|
|---|---|---|
|
|
298
304
|
| | | |
|
|
299
305
|
|
|
306
|
+
## No-Placeholder Scan
|
|
307
|
+
- Scanned tokens: \`TODO\`, \`TBD\`, \`FIXME\`, \`<fill-in>\`, \`<your-*-here>\`, \`xxx\`, bare ellipsis in task rows.
|
|
308
|
+
- Hits: 0 (required for WAIT_FOR_CONFIRM to resolve).
|
|
309
|
+
|
|
300
310
|
## WAIT_FOR_CONFIRM
|
|
301
311
|
- Status: pending
|
|
302
312
|
- Confirmed by:
|
package/dist/doctor.js
CHANGED
|
@@ -258,13 +258,90 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
258
258
|
const skillContent = await fs.readFile(skillPath, "utf8");
|
|
259
259
|
const lineCount = skillContent.split("\n").length;
|
|
260
260
|
const MIN_SKILL_LINES = 110;
|
|
261
|
+
const MAX_SKILL_LINES = 650;
|
|
261
262
|
checks.push({
|
|
262
263
|
name: `skill:${stage}:min_lines`,
|
|
263
264
|
ok: lineCount >= MIN_SKILL_LINES,
|
|
264
265
|
details: `${skillPath} has ${lineCount} lines (minimum ${MIN_SKILL_LINES})`
|
|
265
266
|
});
|
|
267
|
+
checks.push({
|
|
268
|
+
name: `skill:${stage}:max_lines`,
|
|
269
|
+
ok: lineCount <= MAX_SKILL_LINES,
|
|
270
|
+
details: `${skillPath} has ${lineCount} lines (soft max ${MAX_SKILL_LINES}; stage skills beyond this drift into unread bloat)`
|
|
271
|
+
});
|
|
272
|
+
const canonicalSections = [
|
|
273
|
+
{ id: "frontmatter", pattern: /^---\nname: [\w-]+\ndescription: /m, label: "YAML frontmatter (name + description)" },
|
|
274
|
+
{ id: "hard_gate", pattern: /^## HARD-GATE$/m, label: "## HARD-GATE" },
|
|
275
|
+
{ id: "checklist", pattern: /^## Checklist$/m, label: "## Checklist" },
|
|
276
|
+
{ id: "completion_protocol", pattern: /^## Stage Completion Protocol$/m, label: "## Stage Completion Protocol" },
|
|
277
|
+
{ id: "handoff_menu", pattern: /^### Handoff Menu$/m, label: "### Handoff Menu" },
|
|
278
|
+
{ id: "good_vs_bad", pattern: /Good vs Bad/i, label: "Good vs Bad examples" },
|
|
279
|
+
{ id: "anti_patterns", pattern: /^## Anti-Patterns$/m, label: "## Anti-Patterns" }
|
|
280
|
+
];
|
|
281
|
+
const missingSections = canonicalSections
|
|
282
|
+
.filter((section) => !section.pattern.test(skillContent))
|
|
283
|
+
.map((section) => section.label);
|
|
284
|
+
checks.push({
|
|
285
|
+
name: `skill:${stage}:canonical_sections`,
|
|
286
|
+
ok: missingSections.length === 0,
|
|
287
|
+
details: missingSections.length === 0
|
|
288
|
+
? `${skillPath} contains all canonical sections`
|
|
289
|
+
: `${skillPath} missing sections: ${missingSections.join(", ")}`
|
|
290
|
+
});
|
|
266
291
|
}
|
|
267
292
|
}
|
|
293
|
+
// Meta-skill health — the using-cclaw routing brain must always contain the
|
|
294
|
+
// signals that stage skills reference. When one of these drifts, every stage
|
|
295
|
+
// citation breaks silently.
|
|
296
|
+
const metaSkillPath = path.join(projectRoot, RUNTIME_ROOT, "skills", "using-cclaw", "SKILL.md");
|
|
297
|
+
if (await exists(metaSkillPath)) {
|
|
298
|
+
const metaContent = await fs.readFile(metaSkillPath, "utf8");
|
|
299
|
+
const requiredSignals = [
|
|
300
|
+
{ id: "instruction_priority", pattern: /Instruction Priority/i, label: "Instruction Priority" },
|
|
301
|
+
{ id: "spawned_detection", pattern: /Spawned Subagent Detection/i, label: "Spawned Subagent Detection" },
|
|
302
|
+
{ id: "shared_decision", pattern: /Shared Decision \+ Tool-Use Protocol/i, label: "Shared Decision + Tool-Use Protocol" },
|
|
303
|
+
{ id: "shared_completion", pattern: /Shared Stage Completion Protocol/i, label: "Shared Stage Completion Protocol" },
|
|
304
|
+
{ id: "escalation_rule", pattern: /Escalation Rule \(3 attempts\)/i, label: "Escalation Rule (3 attempts)" },
|
|
305
|
+
{ id: "invocation_preamble", pattern: /Invocation Preamble/i, label: "Invocation Preamble" },
|
|
306
|
+
{ id: "operational_self_improvement", pattern: /Operational Self-Improvement/i, label: "Operational Self-Improvement" },
|
|
307
|
+
{ id: "engineering_ethos", pattern: /Engineering Ethos/i, label: "Engineering Ethos" },
|
|
308
|
+
{ id: "task_classification", pattern: /Task Classification/i, label: "Task Classification" }
|
|
309
|
+
];
|
|
310
|
+
const missingMeta = requiredSignals
|
|
311
|
+
.filter((signal) => !signal.pattern.test(metaContent))
|
|
312
|
+
.map((signal) => signal.label);
|
|
313
|
+
checks.push({
|
|
314
|
+
name: "skill:meta:signals",
|
|
315
|
+
ok: missingMeta.length === 0,
|
|
316
|
+
details: missingMeta.length === 0
|
|
317
|
+
? `${metaSkillPath} contains all required routing signals`
|
|
318
|
+
: `${metaSkillPath} missing signals: ${missingMeta.join(", ")}`
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
// Harness tool-map references (A.1#4) must always be present — stage skills
|
|
322
|
+
// cite the paths by name.
|
|
323
|
+
const harnessRefDir = path.join(projectRoot, RUNTIME_ROOT, "references", "harness-tools");
|
|
324
|
+
const harnessRefFiles = ["README.md", "claude.md", "cursor.md", "opencode.md", "codex.md"];
|
|
325
|
+
for (const fileName of harnessRefFiles) {
|
|
326
|
+
const refPath = path.join(harnessRefDir, fileName);
|
|
327
|
+
checks.push({
|
|
328
|
+
name: `harness_tool_ref:${fileName.replace(/\.md$/, "")}`,
|
|
329
|
+
ok: await exists(refPath),
|
|
330
|
+
details: refPath
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
// Per-stage example references (A.2#8, progressive disclosure). Each stage
|
|
334
|
+
// skill's Examples section points here; the file MUST exist or the pointer
|
|
335
|
+
// is a dangling link.
|
|
336
|
+
const stageRefDir = path.join(projectRoot, RUNTIME_ROOT, "references", "stages");
|
|
337
|
+
for (const stage of COMMAND_FILE_ORDER) {
|
|
338
|
+
const refPath = path.join(stageRefDir, `${stage}-examples.md`);
|
|
339
|
+
checks.push({
|
|
340
|
+
name: `stage_examples_ref:${stage}`,
|
|
341
|
+
ok: await exists(refPath),
|
|
342
|
+
details: refPath
|
|
343
|
+
});
|
|
344
|
+
}
|
|
268
345
|
checks.push({
|
|
269
346
|
name: "gitignore:required_patterns",
|
|
270
347
|
ok: await gitignoreHasRequiredPatterns(projectRoot),
|
package/dist/install.js
CHANGED
|
@@ -17,7 +17,9 @@ import { contextMonitorScript, promptGuardScript, workflowGuardScript } from "./
|
|
|
17
17
|
import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
|
|
18
18
|
import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
|
|
19
19
|
import { stageSkillFolder, stageSkillMarkdown } from "./content/skills.js";
|
|
20
|
+
import { STAGE_EXAMPLES_REFERENCE_DIR, stageExamplesReferenceMarkdown } from "./content/examples.js";
|
|
20
21
|
import { LANGUAGE_RULE_PACK_DIR, LANGUAGE_RULE_PACK_FILES, LANGUAGE_RULE_PACK_GENERATORS, LEGACY_LANGUAGE_RULE_PACK_FOLDERS, UTILITY_SKILL_FOLDERS, UTILITY_SKILL_MAP } from "./content/utility-skills.js";
|
|
22
|
+
import { HARNESS_TOOL_REFS_DIR, HARNESS_TOOL_REFS_INDEX_MD, harnessToolRefMarkdown } from "./content/harness-tool-refs.js";
|
|
21
23
|
import { createInitialFlowState } from "./flow-state.js";
|
|
22
24
|
import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
|
|
23
25
|
import { ensureGitignore, removeGitignorePatterns } from "./gitignore.js";
|
|
@@ -169,6 +171,14 @@ async function writeSkills(projectRoot, config) {
|
|
|
169
171
|
for (const stage of COMMAND_FILE_ORDER) {
|
|
170
172
|
const folder = stageSkillFolder(stage);
|
|
171
173
|
await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), stageSkillMarkdown(stage));
|
|
174
|
+
// Progressive disclosure (A.2#8): materialize the full example artifact as
|
|
175
|
+
// a sibling reference file. The stage skill only links to it; agents load
|
|
176
|
+
// the reference on demand.
|
|
177
|
+
const referenceMarkdown = stageExamplesReferenceMarkdown(stage);
|
|
178
|
+
if (referenceMarkdown) {
|
|
179
|
+
const referenceDir = STAGE_EXAMPLES_REFERENCE_DIR.split("/");
|
|
180
|
+
await writeFileSafe(runtimePath(projectRoot, ...referenceDir, `${stage}-examples.md`), referenceMarkdown);
|
|
181
|
+
}
|
|
172
182
|
}
|
|
173
183
|
// Utility skills (not flow stages)
|
|
174
184
|
await writeFileSafe(runtimePath(projectRoot, "skills", "learnings", "SKILL.md"), learnSkillMarkdown());
|
|
@@ -201,6 +211,15 @@ async function writeSkills(projectRoot, config) {
|
|
|
201
211
|
await fs.rm(legacyPath, { recursive: true, force: true });
|
|
202
212
|
}
|
|
203
213
|
}
|
|
214
|
+
// Per-harness tool maps (A.1#4). One reference file per supported harness
|
|
215
|
+
// plus an index; stage/utility skills cite these instead of hardcoding
|
|
216
|
+
// tool names inline.
|
|
217
|
+
const harnessIds = ["claude", "cursor", "opencode", "codex"];
|
|
218
|
+
const harnessRefsDir = HARNESS_TOOL_REFS_DIR.split("/");
|
|
219
|
+
await writeFileSafe(runtimePath(projectRoot, ...harnessRefsDir, "README.md"), HARNESS_TOOL_REFS_INDEX_MD);
|
|
220
|
+
for (const harness of harnessIds) {
|
|
221
|
+
await writeFileSafe(runtimePath(projectRoot, ...harnessRefsDir, `${harness}.md`), harnessToolRefMarkdown(harness));
|
|
222
|
+
}
|
|
204
223
|
}
|
|
205
224
|
async function writeUtilityCommands(projectRoot) {
|
|
206
225
|
await writeFileSafe(runtimePath(projectRoot, "commands", "learn.md"), learnCommandContract());
|