cclaw-cli 0.6.0 → 0.7.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.
@@ -751,23 +751,22 @@ candidates exist).
751
751
  export function knowledgeCurationSkill() {
752
752
  return `---
753
753
  name: knowledge-curation
754
- description: "Read-only curation pass over .cclaw/knowledge.md. Surfaces stale, duplicate, or low-confidence entries and proposes a soft-archive plan; never deletes without explicit user approval."
754
+ description: "Read-only curation pass over the canonical strict-JSONL knowledge store at .cclaw/knowledge.jsonl. Surfaces stale, duplicate, or low-confidence entries and proposes a soft-archive plan that moves approved lines to .cclaw/knowledge.archive.jsonl. Never deletes without explicit user approval."
755
755
  ---
756
756
 
757
757
  # Knowledge Curation
758
758
 
759
759
  ## Quick Start
760
760
 
761
- > 1. This is a **read-only audit** of \`.cclaw/knowledge.md\`. Never delete or rewrite entries here.
761
+ > 1. This is a **read-only audit** of \`.cclaw/knowledge.jsonl\`. Never delete or rewrite entries here.
762
762
  > 2. Surface candidates for soft-archive when the active file > 50 entries OR contains stale/duplicate/superseded entries.
763
763
  > 3. Propose a single archive plan and require explicit user approval before any move.
764
764
 
765
765
  ## HARD-GATE
766
766
 
767
- - Do not modify \`.cclaw/knowledge.md\` from this skill except via an explicit
768
- user-approved archive plan that **moves** entries to
769
- \`.cclaw/knowledge.archive.md\` (never deletes them).
770
- - Do not silently rewrite or summarize entries — preserve original wording.
767
+ - Do not modify \`.cclaw/knowledge.jsonl\` from this skill except via an explicit user-approved archive plan that **moves** full JSON lines verbatim to \`.cclaw/knowledge.archive.jsonl\`. Never physically delete history that is already archived — the archive file is append-only too.
768
+ - Do not silently rewrite or summarize entries — preserve the original JSON line byte-for-byte.
769
+ - Operate only on the canonical JSONL store. If you see a stray \`.cclaw/knowledge.md\`, report it as a drift signal; do **not** read or rewrite it.
771
770
 
772
771
  ## When to run
773
772
 
@@ -777,28 +776,28 @@ description: "Read-only curation pass over .cclaw/knowledge.md. Surfaces stale,
777
776
 
778
777
  ## Audit dimensions
779
778
 
780
- For each entry in \`.cclaw/knowledge.md\` produce a row with:
779
+ For each JSON line in \`.cclaw/knowledge.jsonl\` produce a row with:
781
780
 
782
781
  | Field | Source |
783
782
  |---|---|
784
- | Title | \`### <ts> [type] <title>\` heading |
785
- | Type | \`rule\` / \`pattern\` / \`lesson\` / \`compound\` |
786
- | Stage | \`Stage:\` field (or \`unknown\`) |
787
- | Age | days since timestamp |
788
- | Confidence | \`Confidence:\` field if present, else \`unstated\` |
789
- | Domain | \`Domain:\` field if present |
790
- | Supersedes | \`Supersedes:\` field if present |
783
+ | # | 1-based line number in the JSONL file |
784
+ | Type | \`type\` field (\`rule\` / \`pattern\` / \`lesson\` / \`compound\`) |
785
+ | Stage | \`stage\` field (or \`null\`) |
786
+ | Age | days since \`created\` |
787
+ | Confidence | \`confidence\` field |
788
+ | Domain | \`domain\` field (or \`null\`) |
789
+ | Trigger | \`trigger\` field, truncated to 60 chars |
791
790
  | Status hint | one of: keep / supersede-candidate / archive-candidate / duplicate |
792
791
 
793
792
  ### Status rules
794
793
 
795
- - **supersede-candidate**: another entry has \`Supersedes: <this-title>\`.
796
- - **duplicate**: title or insight ≈ another entry's (caller's judgment, not regex).
794
+ - **supersede-candidate**: a newer line shares the same \`trigger\` (case-insensitive) and a different \`action\`.
795
+ - **duplicate**: \`trigger\` ≈ another line's AND \`action\` ≈ the same (caller's judgment).
797
796
  - **archive-candidate**:
798
- - Type \`lesson\` AND age > 180 days AND no \`Supersedes\` chain points to it; OR
799
- - Stage = \`brainstorm\` AND age > 90 days; OR
800
- - Confidence = \`low\` AND age > 60 days; OR
801
- - Total active entries > 50 and entry has lowest reuse signal.
797
+ - \`type=lesson\` AND age > 180 days AND no newer line references the same \`trigger\`; OR
798
+ - \`stage=brainstorm\` AND age > 90 days; OR
799
+ - \`confidence=low\` AND age > 60 days; OR
800
+ - Total active entries > 50 and entry has the lowest estimated reuse signal.
802
801
  - **keep**: everything else.
803
802
 
804
803
  ## Output format
@@ -808,7 +807,7 @@ Produce two artifacts as **chat output only** (do not write files):
808
807
  ### 1. Audit table
809
808
 
810
809
  \`\`\`markdown
811
- | # | Title | Type | Stage | Age | Confidence | Status hint |
810
+ | # | Type | Stage | Age | Confidence | Trigger | Status hint |
812
811
  |---|---|---|---|---|---|---|
813
812
  | 1 | … | … | … | … | … | … |
814
813
  \`\`\`
@@ -821,14 +820,15 @@ Produce two artifacts as **chat output only** (do not write files):
821
820
  Threshold reasoning: <why entries below were selected>
822
821
 
823
822
  Entries to archive:
824
- 1. <title> — reason
825
- 2. <title> — reason
823
+ 1. line #<N> — <trigger> — reason
824
+ 2. line #<N> — <trigger> — reason
826
825
 
827
826
  Action plan if approved:
828
- 1. Append a header to \`.cclaw/knowledge.archive.md\` with today's UTC date.
829
- 2. Move (cut/paste) selected entries verbatim from \`.cclaw/knowledge.md\` into the archive file.
830
- 3. Append a single supersession line to \`.cclaw/knowledge.md\`:
831
- \\\`### <ts> [pattern] knowledge-curation-<date> archived <N> entries, see knowledge.archive.md\\\`
827
+ 1. Ensure \`.cclaw/knowledge.archive.jsonl\` exists (create empty if missing).
828
+ 2. Move (cut/paste) the selected JSON lines verbatim from
829
+ \`.cclaw/knowledge.jsonl\` into \`.cclaw/knowledge.archive.jsonl\`,
830
+ preserving byte order within each file.
831
+ 3. Do not rewrite, re-order, or pretty-print any remaining line in the active file.
832
832
 
833
833
  After approval: ask the user to run the move themselves, or — if they explicitly grant write access — perform the move atomically and report the new active count.
834
834
  \`\`\`
@@ -1069,6 +1069,317 @@ Escalate to the main review-army under the matching severity (Critical / Importa
1069
1069
  - Only playing the hostile-user role and skipping operator + maintainer.
1070
1070
  `;
1071
1071
  }
1072
+ export function retrospectiveSkill() {
1073
+ return `---
1074
+ name: retrospective
1075
+ description: "Post-ship retrospective lens. Use after a ship to extract durable lessons (rules, patterns, accelerators) before context fades. Distinct from the inline ship Compound Step — this is a deeper, optional sweep across the whole run."
1076
+ ---
1077
+
1078
+ # Retrospective
1079
+
1080
+ ## Quick Start
1081
+
1082
+ > 1. Run **after** the ship stage closes (PR merged or release tagged), while the run is still loaded in memory.
1083
+ > 2. Walk the four lenses below; harvest concrete entries for \`.cclaw/knowledge.jsonl\`.
1084
+ > 3. Stop when you have at least one durable entry **or** an explicit "no new lesson this run".
1085
+
1086
+ ## HARD-GATE
1087
+
1088
+ Do **not** run retrospective before ship gates pass. The goal is to learn from
1089
+ a *closed* loop, not to evaluate work-in-progress.
1090
+ Do **not** invent generic platitudes ("write more tests"). Every entry must cite
1091
+ a concrete moment in *this* run (file, decision, blocker, surprise).
1092
+
1093
+ ## When to use
1094
+
1095
+ - Right after \`/cc-next\` reports the ship stage complete.
1096
+ - Before starting the next \`/cc <idea>\` — fresh context, lessons captured.
1097
+ - After an incident or surprise during ship (rollback, hotfix, regression).
1098
+
1099
+ ## When NOT to use
1100
+
1101
+ - Mid-flow (use the per-stage Operational Self-Improvement block instead).
1102
+ - For trivial changes (typo fix, config bump) — the Compound Step in the
1103
+ ship template is enough.
1104
+
1105
+ ## Four Lenses
1106
+
1107
+ For each lens, write either a knowledge entry **or** the explicit string
1108
+ "no new lesson". Skipping a lens silently is forbidden.
1109
+
1110
+ ### 1. What surprised us?
1111
+
1112
+ - A bug that hid in a place no one suspected → \`[lesson]\`.
1113
+ - A test that passed but missed a real failure mode → \`[lesson]\`.
1114
+ - A library/API behavior that contradicted our mental model → \`[rule]\`.
1115
+
1116
+ ### 2. What slowed us down?
1117
+
1118
+ - Repeated context loss between waves → \`[compound]\` accelerator.
1119
+ - Re-derivation of a fact already in upstream artifacts → \`[pattern]\` "re-read X first".
1120
+ - Tooling friction (slow test loop, flaky CI) → \`[compound]\` follow-up.
1121
+
1122
+ ### 3. What worked unreasonably well?
1123
+
1124
+ - A refactor that unlocked the next 3 tasks → \`[pattern]\`.
1125
+ - A skill/agent invocation that nailed it on first try → \`[pattern]\` (record the prompt shape).
1126
+ - Adopting an existing solution instead of building → \`[rule]\` reinforcement.
1127
+
1128
+ ### 4. What would we do differently next time?
1129
+
1130
+ - Architectural decision that aged poorly within the same run → \`[lesson]\`.
1131
+ - Scope mode chosen incorrectly → \`[rule]\` heuristic update.
1132
+ - Order-of-operations mistake (e.g. spec drift before tdd) → \`[pattern]\` ordering.
1133
+
1134
+ ## Output protocol
1135
+
1136
+ For every harvested insight, append one strict-schema JSON line to
1137
+ \`.cclaw/knowledge.jsonl\` (fields: \`type, trigger, action, confidence, domain, stage, created, project\`).
1138
+ See the \`learnings\` skill for the canonical shape. Choose \`type\`:
1139
+
1140
+ - \`compound\` for process/speed accelerators.
1141
+ - \`lesson\` for "we learned this the hard way".
1142
+ - \`pattern\` for repeatable shapes that worked.
1143
+ - \`rule\` only for hard constraints that must always hold.
1144
+
1145
+ Then write a one-paragraph **Run Summary** at the top of the next
1146
+ \`/cc <idea>\` brainstorm context citing the lessons in scope.
1147
+
1148
+ ## Anti-patterns
1149
+
1150
+ - Retrospective as performance review — frame is *system improvement*, not blame.
1151
+ - Harvesting only positive ("what worked") and skipping uncomfortable lessons.
1152
+ - Writing entries so generic they could apply to any project.
1153
+ - Letting the retrospective drift into a re-design of the shipped feature.
1154
+ `;
1155
+ }
1156
+ export function languageTypescriptSkill() {
1157
+ return `---
1158
+ name: language-typescript
1159
+ description: "TypeScript rule pack. Opt-in language lens. Use when reviewing or writing TypeScript/JavaScript diffs during tdd or review — enforces type-safety, runtime-boundary validation, and idiomatic patterns."
1160
+ ---
1161
+
1162
+ # TypeScript Rule Pack
1163
+
1164
+ ## Quick Start
1165
+
1166
+ > 1. Activate during tdd or review whenever the diff touches \`.ts\`, \`.tsx\`, \`.mts\`, \`.cts\`, or \`.js\` files.
1167
+ > 2. Walk the rule tiers in order. Tier-1 violations block merge. Tier-2 need a named follow-up.
1168
+ > 3. Cite each finding as \`file:line — <rule id> — <one-line remediation>\`.
1169
+
1170
+ ## HARD-GATE
1171
+
1172
+ Do not approve a TypeScript change that ships \`any\`, \`@ts-ignore\`, or
1173
+ \`@ts-expect-error\` *without* (a) a comment explaining why, (b) a linked issue,
1174
+ and (c) an assertion that the blast radius is bounded to the current file.
1175
+ No exceptions in production code paths.
1176
+
1177
+ ## Tier 1 — blocking rules
1178
+
1179
+ 1. **No silent \`any\`.** Unknown inputs must be typed as \`unknown\` first, then narrowed.
1180
+ 2. **Runtime validate trust boundaries.** HTTP bodies, env vars, file contents, and
1181
+ IPC payloads must be parsed through a schema validator (zod, valibot, io-ts) before
1182
+ being treated as typed data.
1183
+ 3. **No \`as\` without a narrowing reason.** \`value as Foo\` is only acceptable when
1184
+ preceded by a runtime check that proves the shape (e.g. \`if ("id" in value)\`).
1185
+ 4. **Exhaustive switches on discriminated unions.** Every \`switch\` on a tagged
1186
+ union must end with a \`default\` branch that assigns to \`never\` to surface
1187
+ missing cases at compile time.
1188
+ 5. **Promise hygiene.** No unawaited promises in \`async\` functions; no
1189
+ \`void promise\` unless documented. Use \`@typescript-eslint/no-floating-promises\`.
1190
+ 6. **Null-safety at the boundary.** Optional chaining (\`?.\`) and nullish
1191
+ coalescing (\`??\`) must only be used when the null path is handled, not as a
1192
+ silent default.
1193
+
1194
+ ## Tier 2 — follow-up rules
1195
+
1196
+ 7. Prefer \`readonly\` for arrays/object fields that are not mutated.
1197
+ 8. Prefer \`type\` aliases for unions, \`interface\` for extendable object shapes.
1198
+ 9. Name generic parameters descriptively once they carry semantic meaning (\`TEvent\`, \`TPayload\`).
1199
+ 10. Avoid re-exporting entire namespaces; named re-exports keep bundle analysis tractable.
1200
+ 11. Co-locate test fixtures with their types to keep drift visible.
1201
+
1202
+ ## Anti-patterns
1203
+
1204
+ - "It compiles, ship it" — compilation is necessary, not sufficient. Runtime boundary validation is the gate.
1205
+ - Casting library return types to tighten them without reading the library's actual contract.
1206
+ - Wrapping every function in \`try/catch\` and swallowing the error — errors must either be rethrown typed or mapped to a Result/Either shape.
1207
+ - Using enums where a string-literal union would do (enums carry runtime cost and erase at tree-shaking time only when \`const\`).
1208
+
1209
+ ## Review output shape
1210
+
1211
+ \`\`\`
1212
+ - **Rule:** T1-2 (runtime validate trust boundaries)
1213
+ - **File:line:** src/api/users.ts:42
1214
+ - **Finding:** POST body cast directly to \`UserCreateInput\`; no schema parse.
1215
+ - **Remediation:** Parse through \`userCreateSchema\` (zod) before passing to the service layer.
1216
+ \`\`\`
1217
+ `;
1218
+ }
1219
+ export function languagePythonSkill() {
1220
+ return `---
1221
+ name: language-python
1222
+ description: "Python rule pack. Opt-in language lens. Use when reviewing or writing Python diffs during tdd or review — enforces typing, exception hygiene, and idiomatic patterns."
1223
+ ---
1224
+
1225
+ # Python Rule Pack
1226
+
1227
+ ## Quick Start
1228
+
1229
+ > 1. Activate during tdd or review whenever the diff touches \`.py\` / \`.pyi\` files.
1230
+ > 2. Walk the rule tiers in order. Tier-1 violations block merge. Tier-2 need a named follow-up.
1231
+ > 3. Cite each finding as \`file:line — <rule id> — <one-line remediation>\`.
1232
+
1233
+ ## HARD-GATE
1234
+
1235
+ Do not approve a Python change that catches bare \`except:\` or \`except Exception:\`
1236
+ in production code *without* (a) re-raising, (b) logging with \`logger.exception\`, or
1237
+ (c) a comment explaining the intentional swallow. Silent broad catches are the
1238
+ single biggest source of "works on my machine" bugs in Python services.
1239
+
1240
+ ## Tier 1 — blocking rules
1241
+
1242
+ 1. **Type hints on public APIs.** Every exported function, method, and dataclass
1243
+ must have full type hints. Use \`from __future__ import annotations\` or PEP 604 union syntax.
1244
+ 2. **No mutable default arguments.** \`def f(x=[])\` is a bug. Use \`None\` + inline default.
1245
+ 3. **Exception specificity.** Catch the narrowest exception class you actually handle.
1246
+ 4. **Context managers for resources.** Files, sockets, DB sessions, locks — always \`with\`.
1247
+ 5. **No bare \`assert\` in production code.** \`assert\` is stripped under \`python -O\`.
1248
+ For invariants, raise \`ValueError\`/\`RuntimeError\` explicitly.
1249
+ 6. **Deterministic imports.** No conditional imports at module top level except for
1250
+ platform branches; no import-time side effects.
1251
+
1252
+ ## Tier 2 — follow-up rules
1253
+
1254
+ 7. Prefer \`@dataclass(slots=True, frozen=True)\` for value objects.
1255
+ 8. Prefer \`pathlib.Path\` over \`os.path\` for new code.
1256
+ 9. Use f-strings for interpolation; reserve \`%\` and \`.format\` for logger messages (lazy eval).
1257
+ 10. Use \`logging.getLogger(__name__)\` per module; never the root logger.
1258
+ 11. Pin dependency ranges in \`pyproject.toml\`; lock with \`uv lock\` / \`pip-compile\`.
1259
+
1260
+ ## Async-specific
1261
+
1262
+ - Do not mix \`requests\`/sync I/O inside \`async def\`. Use \`httpx.AsyncClient\` / \`aiofiles\`.
1263
+ - \`asyncio.gather\` with \`return_exceptions=False\` cancels siblings on first failure — be explicit.
1264
+ - Every task created with \`asyncio.create_task\` must have its reference kept and awaited.
1265
+
1266
+ ## Anti-patterns
1267
+
1268
+ - Using \`**kwargs\` to avoid writing a real signature.
1269
+ - Monkey-patching modules from tests without a \`contextlib.contextmanager\` cleanup.
1270
+ - Treating \`__init__.py\` as a place to run logic (imports only).
1271
+ - Re-inventing \`itertools\`/\`functools\` instead of using stdlib.
1272
+
1273
+ ## Review output shape
1274
+
1275
+ \`\`\`
1276
+ - **Rule:** P1-3 (exception specificity)
1277
+ - **File:line:** users/service.py:88
1278
+ - **Finding:** \`except Exception\` around DB call silently drops integrity errors.
1279
+ - **Remediation:** Catch \`IntegrityError\` explicitly; re-raise everything else.
1280
+ \`\`\`
1281
+ `;
1282
+ }
1283
+ export function languageGoSkill() {
1284
+ return `---
1285
+ name: language-go
1286
+ description: "Go rule pack. Opt-in language lens. Use when reviewing or writing Go diffs during tdd or review — enforces error handling discipline, concurrency safety, and idiomatic patterns."
1287
+ ---
1288
+
1289
+ # Go Rule Pack
1290
+
1291
+ ## Quick Start
1292
+
1293
+ > 1. Activate during tdd or review whenever the diff touches \`.go\` files.
1294
+ > 2. Walk the rule tiers in order. Tier-1 violations block merge. Tier-2 need a named follow-up.
1295
+ > 3. Cite each finding as \`file:line — <rule id> — <one-line remediation>\`.
1296
+
1297
+ ## HARD-GATE
1298
+
1299
+ Do not approve a Go change that discards an \`error\` return value with \`_ = ...\`
1300
+ in production code *without* a comment explaining why the error is provably
1301
+ irrelevant. Discarded errors are Go's #1 source of silent data loss.
1302
+
1303
+ ## Tier 1 — blocking rules
1304
+
1305
+ 1. **Every \`error\` is checked or explicitly wrapped with \`fmt.Errorf("%w", err)\`.**
1306
+ 2. **No goroutine leaks.** Every \`go func()\` must have a stop condition visible in
1307
+ the diff: a \`context.Context\` cancellation, a \`done\` channel, or a bounded
1308
+ input channel that will close.
1309
+ 3. **Context propagation.** Any function that does I/O, RPC, or long work must take
1310
+ \`ctx context.Context\` as the first parameter.
1311
+ 4. **No mutex by value.** Fields of type \`sync.Mutex\` / \`sync.RWMutex\` must be
1312
+ pointers *or* the containing struct must be used only via pointer receivers.
1313
+ 5. **Defer placement.** \`defer file.Close()\` must immediately follow a successful
1314
+ open, before any code path that can return early.
1315
+ 6. **\`for range\` capture hygiene** (pre-Go 1.22): copy loop variables before
1316
+ capturing in goroutines or deferred functions. From Go 1.22+ the language fixes
1317
+ this, but confirm the repo's \`go\` directive in \`go.mod\`.
1318
+
1319
+ ## Tier 2 — follow-up rules
1320
+
1321
+ 7. Prefer small interfaces defined at the consumer site, not upstream.
1322
+ 8. Prefer \`errors.Is\` / \`errors.As\` over string matching.
1323
+ 9. Avoid \`init()\` except for registering with a framework.
1324
+ 10. Use \`t.Helper()\` inside test helpers so failure lines point at the caller.
1325
+ 11. Use \`//go:build\` tags for OS-specific code, not runtime \`runtime.GOOS\` checks.
1326
+
1327
+ ## Concurrency-specific
1328
+
1329
+ - Buffered channels are a performance hint, not a correctness fix. Unbuffered first.
1330
+ - \`sync.WaitGroup\` \`Add\` must happen **before** \`go\`, not inside the goroutine.
1331
+ - \`atomic\` operations must be paired on the same variable — do not mix \`atomic.Load\`
1332
+ with plain reads of the same field.
1333
+ - Shared maps require a mutex or \`sync.Map\`; Go's race detector in CI is non-negotiable.
1334
+
1335
+ ## Anti-patterns
1336
+
1337
+ - Returning \`interface{}\` / \`any\` to "keep options open" — narrow it now.
1338
+ - Building "smart" error types that lose the wrapped chain.
1339
+ - Using \`panic\` for control flow in library code (allowed only for unrecoverable invariants).
1340
+ - Ignoring \`go vet\` warnings because "the code works".
1341
+
1342
+ ## Review output shape
1343
+
1344
+ \`\`\`
1345
+ - **Rule:** G1-2 (no goroutine leaks)
1346
+ - **File:line:** internal/worker/pool.go:57
1347
+ - **Finding:** \`go w.loop()\` has no stop condition; context is not threaded through.
1348
+ - **Remediation:** Accept \`ctx\` in \`Start\` and select on \`ctx.Done()\` inside \`loop\`.
1349
+ \`\`\`
1350
+ `;
1351
+ }
1352
+ /**
1353
+ * Language rule packs live under `.cclaw/rules/lang/<pack>.md`. They are NOT
1354
+ * skills (no folder, no `SKILL.md`) — they are opt-in **rule files** that the
1355
+ * meta-skill router and stage hooks consult when the corresponding language
1356
+ * appears in a diff. The pack id doubles as the on-disk filename stem.
1357
+ */
1358
+ export const LANGUAGE_RULE_PACK_FILES = {
1359
+ typescript: "typescript.md",
1360
+ python: "python.md",
1361
+ go: "go.md"
1362
+ };
1363
+ /**
1364
+ * Folder (relative to runtime root) that holds every enabled language rule
1365
+ * pack. A single folder keeps discovery trivial for hooks and for `doctor`.
1366
+ */
1367
+ export const LANGUAGE_RULE_PACK_DIR = ["rules", "lang"];
1368
+ export const LANGUAGE_RULE_PACK_GENERATORS = {
1369
+ typescript: languageTypescriptSkill,
1370
+ python: languagePythonSkill,
1371
+ go: languageGoSkill
1372
+ };
1373
+ /**
1374
+ * Legacy per-language folders under `.cclaw/skills/` used in v0.7.0. Listed
1375
+ * here so `cclaw sync` and `doctor` can surface drift and the installer can
1376
+ * clean them up after the move to `.cclaw/rules/lang/`.
1377
+ */
1378
+ export const LEGACY_LANGUAGE_RULE_PACK_FOLDERS = [
1379
+ "language-typescript",
1380
+ "language-python",
1381
+ "language-go"
1382
+ ];
1072
1383
  export const UTILITY_SKILL_FOLDERS = [
1073
1384
  "security",
1074
1385
  "debugging",
@@ -1082,7 +1393,8 @@ export const UTILITY_SKILL_FOLDERS = [
1082
1393
  "landscape-check",
1083
1394
  "adversarial-review",
1084
1395
  "security-audit",
1085
- "knowledge-curation"
1396
+ "knowledge-curation",
1397
+ "retrospective"
1086
1398
  ];
1087
1399
  export const UTILITY_SKILL_MAP = {
1088
1400
  security: securityReviewSkill,
@@ -1097,5 +1409,6 @@ export const UTILITY_SKILL_MAP = {
1097
1409
  "landscape-check": landscapeCheckSkill,
1098
1410
  "adversarial-review": adversarialReviewSkill,
1099
1411
  "security-audit": securityAuditSkill,
1100
- "knowledge-curation": knowledgeCurationSkill
1412
+ "knowledge-curation": knowledgeCurationSkill,
1413
+ retrospective: retrospectiveSkill
1101
1414
  };
@@ -2,7 +2,7 @@ import type { FlowStage } from "./types.js";
2
2
  export type DelegationEntry = {
3
3
  stage: string;
4
4
  agent: string;
5
- mode: "mandatory" | "proactive";
5
+ mode: "mandatory" | "proactive" | "conditional";
6
6
  status: "scheduled" | "completed" | "failed" | "waived";
7
7
  taskId?: string;
8
8
  waiverReason?: string;
@@ -12,6 +12,11 @@ export type DelegationEntry = {
12
12
  * consumers treat missing runId as unscoped (conservatively excluded from current-run checks).
13
13
  */
14
14
  runId?: string;
15
+ /**
16
+ * For `conditional` rows: the trigger predicate that fired (e.g. `diff_lines_gt:100`).
17
+ * Recorded for audit so reviewers can see why the second pass was required.
18
+ */
19
+ conditionTrigger?: string;
15
20
  };
16
21
  export type DelegationLedger = {
17
22
  runId: string;
@@ -14,7 +14,7 @@ function isDelegationEntry(value) {
14
14
  if (!value || typeof value !== "object" || Array.isArray(value))
15
15
  return false;
16
16
  const o = value;
17
- const modeOk = o.mode === "mandatory" || o.mode === "proactive";
17
+ const modeOk = o.mode === "mandatory" || o.mode === "proactive" || o.mode === "conditional";
18
18
  const statusOk = o.status === "scheduled" ||
19
19
  o.status === "completed" ||
20
20
  o.status === "failed" ||
@@ -26,7 +26,8 @@ function isDelegationEntry(value) {
26
26
  typeof o.ts === "string" &&
27
27
  (o.taskId === undefined || typeof o.taskId === "string") &&
28
28
  (o.waiverReason === undefined || typeof o.waiverReason === "string") &&
29
- (o.runId === undefined || typeof o.runId === "string"));
29
+ (o.runId === undefined || typeof o.runId === "string") &&
30
+ (o.conditionTrigger === undefined || typeof o.conditionTrigger === "string"));
30
31
  }
31
32
  function parseLedger(raw, runId) {
32
33
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
package/dist/doctor.js CHANGED
@@ -11,11 +11,13 @@ import { gitignoreHasRequiredPatterns } from "./gitignore.js";
11
11
  import { HARNESS_ADAPTERS, CCLAW_MARKER_START, CCLAW_MARKER_END } from "./harness-adapters.js";
12
12
  import { policyChecks } from "./policy.js";
13
13
  import { readFlowState } from "./runs.js";
14
+ import { skippedStagesForTrack } from "./flow-state.js";
15
+ import { TRACK_STAGES } from "./types.js";
14
16
  import { checkMandatoryDelegations } from "./delegation.js";
15
17
  import { buildTraceMatrix } from "./trace-matrix.js";
16
18
  import { reconcileAndWriteCurrentStageGateCatalog, verifyCompletedStagesGateClosure, verifyCurrentStageGateEvidence } from "./gate-evidence.js";
17
19
  import { stageSkillFolder } from "./content/skills.js";
18
- import { UTILITY_SKILL_FOLDERS } from "./content/utility-skills.js";
20
+ import { LANGUAGE_RULE_PACK_DIR, LANGUAGE_RULE_PACK_FILES, LEGACY_LANGUAGE_RULE_PACK_FOLDERS, UTILITY_SKILL_FOLDERS } from "./content/utility-skills.js";
19
21
  import { CONTEXT_MODES, DEFAULT_CONTEXT_MODE } from "./content/contexts.js";
20
22
  import { validateHookDocument } from "./hook-schema.js";
21
23
  const execFileAsync = promisify(execFile);
@@ -406,6 +408,32 @@ export async function doctorChecks(projectRoot, options = {}) {
406
408
  details: skillPath
407
409
  });
408
410
  }
411
+ // Opt-in language rule packs: only check presence for packs the user enabled.
412
+ // Canonical location is .cclaw/rules/lang/<pack>.md.
413
+ for (const pack of parsedConfig?.languageRulePacks ?? []) {
414
+ const fileName = LANGUAGE_RULE_PACK_FILES[pack];
415
+ if (!fileName)
416
+ continue;
417
+ const packPath = path.join(projectRoot, RUNTIME_ROOT, ...LANGUAGE_RULE_PACK_DIR, fileName);
418
+ checks.push({
419
+ name: `language_rule_pack:${pack}`,
420
+ ok: await exists(packPath),
421
+ details: packPath
422
+ });
423
+ }
424
+ // Drift: legacy per-language skill folders from v0.7.0 must not coexist with
425
+ // the new rules/lang/ layout. `cclaw sync` removes them on the next run.
426
+ for (const legacyFolder of LEGACY_LANGUAGE_RULE_PACK_FOLDERS) {
427
+ const legacyPath = path.join(projectRoot, RUNTIME_ROOT, "skills", legacyFolder);
428
+ const legacyPresent = await exists(legacyPath);
429
+ checks.push({
430
+ name: `language_rule_pack:no_legacy:${legacyFolder}`,
431
+ ok: !legacyPresent,
432
+ details: legacyPresent
433
+ ? `legacy ${legacyPath} must be removed — language packs moved to ${RUNTIME_ROOT}/${LANGUAGE_RULE_PACK_DIR.join("/")}/. Run \`cclaw sync\`.`
434
+ : `no legacy ${legacyFolder} skill folder`
435
+ });
436
+ }
409
437
  // Agent definition files
410
438
  for (const agent of CCLAW_AGENTS) {
411
439
  const agentPath = path.join(projectRoot, RUNTIME_ROOT, "agents", `${agent.name}.md`);
@@ -666,11 +694,21 @@ export async function doctorChecks(projectRoot, options = {}) {
666
694
  ok: true,
667
695
  details: hasPython ? "python3 available" : "warning: python3 not found, jq/node paths must stay healthy"
668
696
  });
669
- // Knowledge store exists
697
+ // Knowledge store exists (canonical JSONL, no markdown mirror)
670
698
  checks.push({
671
699
  name: "knowledge:store_exists",
672
- ok: await exists(path.join(projectRoot, RUNTIME_ROOT, "knowledge.md")),
673
- details: `${RUNTIME_ROOT}/knowledge.md must exist`
700
+ ok: await exists(path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl")),
701
+ details: `${RUNTIME_ROOT}/knowledge.jsonl must exist`
702
+ });
703
+ // There must be NO legacy markdown knowledge store — JSONL is the only store.
704
+ const legacyKnowledgeMdPath = path.join(projectRoot, RUNTIME_ROOT, "knowledge.md");
705
+ const legacyExists = await exists(legacyKnowledgeMdPath);
706
+ checks.push({
707
+ name: "knowledge:no_legacy_markdown",
708
+ ok: !legacyExists,
709
+ details: legacyExists
710
+ ? `legacy ${RUNTIME_ROOT}/knowledge.md must be removed — cclaw is JSONL-native`
711
+ : `no legacy markdown store present`
674
712
  });
675
713
  checks.push({
676
714
  name: "state:checkpoint_exists",
@@ -743,6 +781,29 @@ export async function doctorChecks(projectRoot, options = {}) {
743
781
  ok: activeRunId.length > 0,
744
782
  details: `${RUNTIME_ROOT}/state/flow-state.json must include activeRunId`
745
783
  });
784
+ const activeTrack = flowState.track ?? "standard";
785
+ const trackStageList = TRACK_STAGES[activeTrack];
786
+ const skippedFromState = Array.isArray(flowState.skippedStages) ? flowState.skippedStages : [];
787
+ const expectedSkipped = skippedStagesForTrack(activeTrack);
788
+ const skippedConsistent = expectedSkipped.length === skippedFromState.length &&
789
+ expectedSkipped.every((stage) => skippedFromState.includes(stage));
790
+ checks.push({
791
+ name: "flow_state:track",
792
+ ok: skippedConsistent,
793
+ details: skippedConsistent
794
+ ? `active track "${activeTrack}" (${trackStageList.length}/${COMMAND_FILE_ORDER.length} stages: ${trackStageList.join(" → ")})${expectedSkipped.length > 0 ? `; skippedStages=${expectedSkipped.join(", ")}` : ""}`
795
+ : `track "${activeTrack}" expects skippedStages=[${expectedSkipped.join(", ")}] but flow-state has [${skippedFromState.join(", ")}] — run \`cclaw sync\` to repair`
796
+ });
797
+ checks.push({
798
+ name: "flow_state:track_completed_in_track",
799
+ ok: flowState.completedStages.every((stage) => trackStageList.includes(stage) || expectedSkipped.includes(stage)),
800
+ details: (() => {
801
+ const offTrack = flowState.completedStages.filter((stage) => !trackStageList.includes(stage) && !expectedSkipped.includes(stage));
802
+ return offTrack.length === 0
803
+ ? `every completed stage belongs to track "${activeTrack}" or its skipped set`
804
+ : `completed stages contain entries outside track "${activeTrack}" and not in skipped set: ${offTrack.join(", ")}`;
805
+ })()
806
+ });
746
807
  checks.push({
747
808
  name: "artifacts:active_root",
748
809
  ok: await exists(path.join(projectRoot, RUNTIME_ROOT, "artifacts")),
package/dist/install.d.ts CHANGED
@@ -1,8 +1,10 @@
1
- import type { FlowTrack, HarnessId } from "./types.js";
1
+ import type { FlowTrack, HarnessId, InitProfile } from "./types.js";
2
2
  export interface InitOptions {
3
3
  projectRoot: string;
4
4
  harnesses?: HarnessId[];
5
5
  track?: FlowTrack;
6
+ /** When set, pre-fills config defaults from the named profile before applying flag overrides. */
7
+ profile?: InitProfile;
6
8
  }
7
9
  export declare function initCclaw(options: InitOptions): Promise<void>;
8
10
  export declare function syncCclaw(projectRoot: string): Promise<void>;