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/cli.d.ts +4 -2
- package/dist/cli.js +33 -7
- package/dist/config.d.ts +11 -1
- package/dist/config.js +64 -4
- package/dist/content/learnings.js +70 -17
- package/dist/content/meta-skill.js +42 -5
- package/dist/content/stage-schema.d.ts +18 -1
- package/dist/content/stage-schema.js +25 -9
- package/dist/content/start-command.js +30 -7
- package/dist/content/status-command.js +28 -6
- package/dist/content/templates.js +28 -6
- package/dist/content/utility-skills.d.ts +11 -1
- package/dist/content/utility-skills.js +297 -7
- package/dist/delegation.d.ts +6 -1
- package/dist/delegation.js +3 -2
- package/dist/doctor.js +38 -1
- package/dist/install.d.ts +3 -1
- package/dist/install.js +82 -19
- package/dist/policy.js +2 -1
- package/dist/runs.d.ts +15 -0
- package/dist/runs.js +35 -1
- package/dist/types.d.ts +29 -0
- package/dist/types.js +20 -0
- package/package.json +1 -1
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
|
|
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
|
-
|
|
592
|
+
### Frontmatter (YAML, required)
|
|
593
|
+
|
|
594
|
+
\`\`\`yaml
|
|
582
595
|
---
|
|
596
|
+
# Required fields
|
|
583
597
|
name: <kebab-case-skill-name>
|
|
584
|
-
description:
|
|
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
|
-
##
|
|
593
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
667
|
+
## Versioning & removal
|
|
610
668
|
|
|
611
|
-
Custom skills are user-owned.
|
|
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 =
|
|
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
|
|
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"];
|