cclaw-cli 0.6.0 → 0.7.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/install.js CHANGED
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  import { COMMAND_FILE_ORDER, REQUIRED_DIRS, RUNTIME_ROOT, UTILITY_COMMANDS } from "./constants.js";
6
- import { writeConfig, createDefaultConfig, readConfig, configPath } from "./config.js";
6
+ import { writeConfig, createDefaultConfig, createProfileConfig, readConfig, configPath } from "./config.js";
7
7
  import { commandContract } from "./content/contracts.js";
8
8
  import { contextModeFiles, createInitialContextModeState } from "./content/contexts.js";
9
9
  import { learnSkillMarkdown, learnCommandContract } from "./content/learnings.js";
@@ -17,7 +17,7 @@ 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 { UTILITY_SKILL_FOLDERS, UTILITY_SKILL_MAP } from "./content/utility-skills.js";
20
+ import { LANGUAGE_RULE_PACK_FOLDERS, LANGUAGE_RULE_PACK_GENERATORS, UTILITY_SKILL_FOLDERS, UTILITY_SKILL_MAP } from "./content/utility-skills.js";
21
21
  import { createInitialFlowState } from "./flow-state.js";
22
22
  import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
23
23
  import { ensureGitignore, removeGitignorePatterns } from "./gitignore.js";
@@ -165,7 +165,7 @@ async function writeArtifactTemplates(projectRoot) {
165
165
  await writeFileSafe(runtimePath(projectRoot, "templates", fileName), content);
166
166
  }
167
167
  }
168
- async function writeSkills(projectRoot) {
168
+ async function writeSkills(projectRoot, config) {
169
169
  for (const stage of COMMAND_FILE_ORDER) {
170
170
  const folder = stageSkillFolder(stage);
171
171
  await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), stageSkillMarkdown(stage));
@@ -183,6 +183,14 @@ async function writeSkills(projectRoot) {
183
183
  const generator = UTILITY_SKILL_MAP[folder];
184
184
  await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), generator());
185
185
  }
186
+ const enabledPacks = config?.languageRulePacks ?? [];
187
+ for (const pack of enabledPacks) {
188
+ const folder = LANGUAGE_RULE_PACK_FOLDERS[pack];
189
+ const generator = LANGUAGE_RULE_PACK_GENERATORS[folder];
190
+ if (!folder || !generator)
191
+ continue;
192
+ await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), generator());
193
+ }
186
194
  }
187
195
  async function writeUtilityCommands(projectRoot) {
188
196
  await writeFileSafe(runtimePath(projectRoot, "commands", "learn.md"), learnCommandContract());
@@ -574,42 +582,92 @@ to add project-specific skills that complement the managed skills under
574
582
  If the skill is general (security, performance, accessibility, etc.) prefer
575
583
  contributing it upstream instead — the managed skills receive maintenance.
576
584
 
577
- ## File format
585
+ ## File format — public API (stable contract)
578
586
 
579
- Each skill lives at \`.cclaw/custom-skills/<folder>/SKILL.md\` with frontmatter:
587
+ Each skill lives at \`.cclaw/custom-skills/<folder>/SKILL.md\`. The format is a
588
+ **stable public API**: \`cclaw sync\` and \`cclaw upgrade\` will not rewrite
589
+ custom skills, and the fields below are guaranteed to be respected by the
590
+ meta-skill router and the stage hooks.
580
591
 
581
- \`\`\`markdown
592
+ ### Frontmatter (YAML, required)
593
+
594
+ \`\`\`yaml
582
595
  ---
596
+ # Required fields
583
597
  name: <kebab-case-skill-name>
584
- description: "One sentence describing when this skill applies. Triggers semantic routing."
598
+ description: >
599
+ One sentence (≤180 chars) that triggers semantic routing. Include the
600
+ concrete situation and the expected action
601
+ (e.g. "Audit Kafka topic contracts when a producer or consumer signature changes").
602
+
603
+ # Optional fields (omit when not applicable)
604
+ stages: [design, spec, tdd, review] # flow stages this skill applies to
605
+ triggers:
606
+ - "kafka topic"
607
+ - "producer.schema"
608
+ - "consumer.schema"
609
+ hardGate: false # true => skill body MUST include a ## HARD-GATE section
610
+ owners: ["@team-messaging"] # informational routing hint, not enforced
611
+ version: 0.1.0 # semver; bump when hardGate or algorithm changes
585
612
  ---
613
+ \`\`\`
614
+
615
+ ### Field contract
616
+
617
+ | Field | Type | Required | Meaning |
618
+ |---|---|---|---|
619
+ | \`name\` | string (kebab-case) | yes | Unique id used by the router and by \`/cc-status\` diagnostics. |
620
+ | \`description\` | string ≤180 chars (single line OR YAML \`>\` folded) | yes | Drives semantic routing. Include trigger + action. |
621
+ | \`stages\` | array of flow stages | no | When present, the meta-skill only surfaces this skill during those stages. Omit for "any stage". |
622
+ | \`triggers\` | array of strings | no | Extra literal substrings that route to this skill when found in the user prompt or the active artifact. |
623
+ | \`hardGate\` | boolean | no | When \`true\`, the body MUST include a \`## HARD-GATE\` section; the agent treats the rule as non-skippable. |
624
+ | \`owners\` | array of strings | no | Informational only — surfaced to the user, never enforced. |
625
+ | \`version\` | semver string | no | Bump when you change the HARD-GATE or algorithm so reviewers can spot changes. |
586
626
 
627
+ ### Body sections (markdown, recommended order)
628
+
629
+ \`\`\`markdown
587
630
  # <Skill title>
588
631
 
632
+ ## Overview
633
+ One-paragraph summary; context for when this skill is loaded.
634
+
589
635
  ## When to use
590
- - ...
636
+ - Bullet list of situations where this skill adds value.
591
637
 
592
- ## HARD-GATE (optional)
593
- A non-skippable rule, if any. Phrase it as a refusal, not a recommendation.
638
+ ## When NOT to use
639
+ - Situations where loading this skill is context bloat or wrong.
640
+
641
+ ## HARD-GATE (REQUIRED when frontmatter hardGate: true)
642
+ Phrase it as a refusal:
643
+ > Do not <X> while <Y>.
594
644
 
595
645
  ## Algorithm / checklist
596
- 1. ...
597
- 2. ...
646
+ 1. Concrete, observable steps with evidence (file:line, artifact, or knowledge entry).
647
+
648
+ ## Output protocol
649
+ Where the artifact / chat output lives and what shape it takes.
598
650
 
599
651
  ## Anti-patterns
600
- - ...
652
+ - Common failure modes to reject.
601
653
  \`\`\`
602
654
 
655
+ ### Stage association semantics
656
+
657
+ - \`stages: []\` or missing → skill is available at any stage. The meta-skill still only surfaces it when \`description\` or \`triggers\` match the prompt.
658
+ - \`stages: [review]\` → skill is offered only during the review stage.
659
+ - Custom skills **never** become mandatory delegations. They are opt-in lenses. If you need a mandatory dispatch, add a proper managed specialist under \`.cclaw/skills/\` instead.
660
+
603
661
  ## Routing
604
662
 
605
663
  Custom skills are surfaced via the \`using-cclaw\` meta-skill at session start.
606
664
  Mention the skill name in your prompt or let the agent semantic-route to it
607
- based on the description.
665
+ based on the description + triggers + stages frontmatter.
608
666
 
609
- ## Removing or replacing
667
+ ## Versioning & removal
610
668
 
611
- Custom skills are user-owned. Delete or edit them at any time — \`cclaw sync\`
612
- will not touch them.
669
+ Custom skills are user-owned. Bump \`version\` when you change the HARD-GATE or
670
+ algorithm; delete or edit them at any time — \`cclaw sync\` will not touch them.
613
671
  `;
614
672
  const CUSTOM_SKILLS_EXAMPLE = `---
615
673
  name: example-custom-skill
@@ -836,7 +894,7 @@ async function materializeRuntime(projectRoot, config, forceStateReset) {
836
894
  await cleanStaleFiles(projectRoot);
837
895
  await writeCommandContracts(projectRoot);
838
896
  await writeUtilityCommands(projectRoot);
839
- await writeSkills(projectRoot);
897
+ await writeSkills(projectRoot, config);
840
898
  await writeContextModes(projectRoot);
841
899
  await writeArtifactTemplates(projectRoot);
842
900
  await writeRulebook(projectRoot);
@@ -854,7 +912,12 @@ async function materializeRuntime(projectRoot, config, forceStateReset) {
854
912
  await ensureGitignore(projectRoot);
855
913
  }
856
914
  export async function initCclaw(options) {
857
- const config = createDefaultConfig(options.harnesses, options.track);
915
+ const config = options.profile
916
+ ? createProfileConfig(options.profile, {
917
+ harnesses: options.harnesses,
918
+ defaultTrack: options.track
919
+ })
920
+ : createDefaultConfig(options.harnesses, options.track);
858
921
  await writeConfig(options.projectRoot, config);
859
922
  await materializeRuntime(options.projectRoot, config, true);
860
923
  }
package/dist/policy.js CHANGED
@@ -85,7 +85,8 @@ export async function policyChecks(projectRoot, options = {}) {
85
85
  // --- utility skill checks ---
86
86
  const runtimeFile = (relativePath) => `${RUNTIME_ROOT}/${relativePath}`;
87
87
  const utilitySkillChecks = [
88
- { file: runtimeFile("skills/learnings/SKILL.md"), needle: "## Entry format (append-only)", name: "utility_skill:learnings:entry_format" },
88
+ { file: runtimeFile("skills/learnings/SKILL.md"), needle: "## Entry format", name: "utility_skill:learnings:entry_format" },
89
+ { file: runtimeFile("skills/learnings/SKILL.md"), needle: "knowledge.jsonl", name: "utility_skill:learnings:jsonl_mirror" },
89
90
  { file: runtimeFile("skills/learnings/SKILL.md"), needle: "## Subcommands", name: "utility_skill:learnings:subcommands" },
90
91
  { file: runtimeFile("skills/learnings/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:learnings:hard_gate" },
91
92
  { file: runtimeFile("commands/learn.md"), needle: "## Subcommands", name: "utility_command:learn:subcommands" },
package/dist/runs.d.ts CHANGED
@@ -24,6 +24,13 @@ export interface ArchiveRunResult {
24
24
  featureName: string;
25
25
  resetState: FlowState;
26
26
  snapshottedStateFiles: string[];
27
+ /** Knowledge curation hint: total active entries + soft threshold (50). */
28
+ knowledge: {
29
+ activeEntryCount: number;
30
+ softThreshold: number;
31
+ overThreshold: boolean;
32
+ knowledgePath: string;
33
+ };
27
34
  }
28
35
  export interface ArchiveManifest {
29
36
  version: 1;
@@ -48,4 +55,12 @@ export declare function writeFlowState(projectRoot: string, state: FlowState, op
48
55
  export declare function ensureRunSystem(projectRoot: string, _options?: EnsureRunSystemOptions): Promise<FlowState>;
49
56
  export declare function listRuns(projectRoot: string): Promise<CclawRunMeta[]>;
50
57
  export declare function archiveRun(projectRoot: string, featureName?: string): Promise<ArchiveRunResult>;
58
+ /**
59
+ * Counts active (non-superseded) knowledge entries.
60
+ * An entry is a markdown H3 heading with the canonical timestamped format produced by
61
+ * `learn add` / `learn curate`. Entries marked `Supersedes:` themselves are still active;
62
+ * this helper does not currently follow supersession chains beyond raw count, which is
63
+ * deliberate — the curator reads the file directly to make the soft-archive plan.
64
+ */
65
+ export declare function countActiveKnowledgeEntries(text: string): number;
51
66
  export {};
package/dist/runs.js CHANGED
@@ -394,12 +394,46 @@ export async function archiveRun(projectRoot, featureName) {
394
394
  snapshottedStateFiles
395
395
  };
396
396
  await writeFileSafe(path.join(archivePath, "archive-manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
397
+ const knowledgeStats = await readKnowledgeStats(projectRoot);
397
398
  return {
398
399
  archiveId,
399
400
  archivePath,
400
401
  archivedAt,
401
402
  featureName: feature,
402
403
  resetState,
403
- snapshottedStateFiles
404
+ snapshottedStateFiles,
405
+ knowledge: knowledgeStats
406
+ };
407
+ }
408
+ const KNOWLEDGE_SOFT_THRESHOLD = 50;
409
+ async function readKnowledgeStats(projectRoot) {
410
+ const knowledgePath = path.join(projectRoot, RUNTIME_ROOT, "knowledge.md");
411
+ let activeEntryCount = 0;
412
+ if (await exists(knowledgePath)) {
413
+ const text = await fs.readFile(knowledgePath, "utf8");
414
+ activeEntryCount = countActiveKnowledgeEntries(text);
415
+ }
416
+ return {
417
+ activeEntryCount,
418
+ softThreshold: KNOWLEDGE_SOFT_THRESHOLD,
419
+ overThreshold: activeEntryCount > KNOWLEDGE_SOFT_THRESHOLD,
420
+ knowledgePath: `${RUNTIME_ROOT}/knowledge.md`
404
421
  };
405
422
  }
423
+ /**
424
+ * Counts active (non-superseded) knowledge entries.
425
+ * An entry is a markdown H3 heading with the canonical timestamped format produced by
426
+ * `learn add` / `learn curate`. Entries marked `Supersedes:` themselves are still active;
427
+ * this helper does not currently follow supersession chains beyond raw count, which is
428
+ * deliberate — the curator reads the file directly to make the soft-archive plan.
429
+ */
430
+ export function countActiveKnowledgeEntries(text) {
431
+ const lines = text.split(/\r?\n/);
432
+ let count = 0;
433
+ for (const line of lines) {
434
+ if (/^###\s+\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\s+\[(rule|pattern|lesson|compound)\]/u.test(line)) {
435
+ count += 1;
436
+ }
437
+ }
438
+ return count;
439
+ }
package/dist/types.d.ts CHANGED
@@ -13,6 +13,28 @@ export type FlowTrack = (typeof FLOW_TRACKS)[number];
13
13
  export declare const TRACK_STAGES: Record<FlowTrack, readonly FlowStage[]>;
14
14
  export declare const HARNESS_IDS: readonly ["claude", "cursor", "opencode", "codex"];
15
15
  export type HarnessId = (typeof HARNESS_IDS)[number];
16
+ /**
17
+ * Init profiles pre-fill `cclaw init` flags for common install shapes.
18
+ *
19
+ * - `minimal` — single-harness (claude), quick track default, no git hook guards. For solo
20
+ * contributors or bugfix-heavy repos where most work is \`quick\` scope.
21
+ * - `standard` — default harness set, standard track, no git hook guards, advisory guards.
22
+ * Matches the pre-profile default behavior.
23
+ * - `full` — default harness set, standard track, git hook guards on, strict prompt guards.
24
+ * For teams that want every safety rail on.
25
+ */
26
+ export declare const INIT_PROFILES: readonly ["minimal", "standard", "full"];
27
+ export type InitProfile = (typeof INIT_PROFILES)[number];
28
+ /**
29
+ * Opt-in language rule packs. When enabled in config, `cclaw sync` installs the
30
+ * corresponding utility skill so the meta-skill router can load language-specific
31
+ * anti-patterns, idioms, and review heuristics during review/tdd stages.
32
+ *
33
+ * Opt-in intentional: cclaw stays language-agnostic by default; rule packs are
34
+ * additive context that the user must explicitly enable.
35
+ */
36
+ export declare const LANGUAGE_RULE_PACKS: readonly ["typescript", "python", "go"];
37
+ export type LanguageRulePack = (typeof LANGUAGE_RULE_PACKS)[number];
16
38
  export interface VibyConfig {
17
39
  version: string;
18
40
  flowVersion: string;
@@ -25,6 +47,13 @@ export interface VibyConfig {
25
47
  gitHookGuards?: boolean;
26
48
  /** Default flow track for new runs (quick = shortened path, standard = full pipeline). */
27
49
  defaultTrack?: FlowTrack;
50
+ /**
51
+ * Opt-in language rule packs. Each enabled pack materializes a matching utility
52
+ * skill under `.cclaw/skills/language-<id>/SKILL.md` on next `cclaw sync`. The
53
+ * meta-skill router loads the pack during review/tdd when the diff touches the
54
+ * language in question.
55
+ */
56
+ languageRulePacks?: LanguageRulePack[];
28
57
  }
29
58
  export interface TransitionRule {
30
59
  from: FlowStage;
package/dist/types.js CHANGED
@@ -22,3 +22,23 @@ export const TRACK_STAGES = {
22
22
  quick: ["spec", "tdd", "review", "ship"]
23
23
  };
24
24
  export const HARNESS_IDS = ["claude", "cursor", "opencode", "codex"];
25
+ /**
26
+ * Init profiles pre-fill `cclaw init` flags for common install shapes.
27
+ *
28
+ * - `minimal` — single-harness (claude), quick track default, no git hook guards. For solo
29
+ * contributors or bugfix-heavy repos where most work is \`quick\` scope.
30
+ * - `standard` — default harness set, standard track, no git hook guards, advisory guards.
31
+ * Matches the pre-profile default behavior.
32
+ * - `full` — default harness set, standard track, git hook guards on, strict prompt guards.
33
+ * For teams that want every safety rail on.
34
+ */
35
+ export const INIT_PROFILES = ["minimal", "standard", "full"];
36
+ /**
37
+ * Opt-in language rule packs. When enabled in config, `cclaw sync` installs the
38
+ * corresponding utility skill so the meta-skill router can load language-specific
39
+ * anti-patterns, idioms, and review heuristics during review/tdd stages.
40
+ *
41
+ * Opt-in intentional: cclaw stays language-agnostic by default; rule packs are
42
+ * additive context that the user must explicitly enable.
43
+ */
44
+ export const LANGUAGE_RULE_PACKS = ["typescript", "python", "go"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {