cclaw-cli 0.7.0 → 0.8.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
@@ -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 { LANGUAGE_RULE_PACK_FOLDERS, LANGUAGE_RULE_PACK_GENERATORS, UTILITY_SKILL_FOLDERS, UTILITY_SKILL_MAP } from "./content/utility-skills.js";
20
+ 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";
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";
@@ -183,13 +183,23 @@ async function writeSkills(projectRoot, config) {
183
183
  const generator = UTILITY_SKILL_MAP[folder];
184
184
  await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), generator());
185
185
  }
186
+ // Language rule packs live under .cclaw/rules/lang/<pack>.md. They are opt-in:
187
+ // only the packs listed in config.languageRulePacks are materialised. Any
188
+ // legacy per-language skill folders from v0.7.0 (.cclaw/skills/language-*)
189
+ // are cleaned up below so the new rules/lang layout is the only truth.
186
190
  const enabledPacks = config?.languageRulePacks ?? [];
187
191
  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)
192
+ const fileName = LANGUAGE_RULE_PACK_FILES[pack];
193
+ const generator = LANGUAGE_RULE_PACK_GENERATORS[pack];
194
+ if (!fileName || !generator)
191
195
  continue;
192
- await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), generator());
196
+ await writeFileSafe(runtimePath(projectRoot, ...LANGUAGE_RULE_PACK_DIR, fileName), generator());
197
+ }
198
+ for (const legacyFolder of LEGACY_LANGUAGE_RULE_PACK_FOLDERS) {
199
+ const legacyPath = runtimePath(projectRoot, "skills", legacyFolder);
200
+ if (await exists(legacyPath)) {
201
+ await fs.rm(legacyPath, { recursive: true, force: true });
202
+ }
193
203
  }
194
204
  }
195
205
  async function writeUtilityCommands(projectRoot) {
@@ -550,9 +560,13 @@ async function writeHooks(projectRoot, config) {
550
560
  }
551
561
  }
552
562
  async function ensureKnowledgeStore(projectRoot) {
553
- const storePath = runtimePath(projectRoot, "knowledge.md");
563
+ const storePath = runtimePath(projectRoot, "knowledge.jsonl");
554
564
  if (!(await exists(storePath))) {
555
- await writeFileSafe(storePath, "# Project Knowledge\n\n");
565
+ await writeFileSafe(storePath, "");
566
+ }
567
+ const legacyMdPath = runtimePath(projectRoot, "knowledge.md");
568
+ if (await exists(legacyMdPath)) {
569
+ await fs.rm(legacyMdPath, { force: true });
556
570
  }
557
571
  }
558
572
  async function ensureCustomSkillsScaffold(projectRoot) {
package/dist/policy.js CHANGED
@@ -85,8 +85,9 @@ 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", name: "utility_skill:learnings:entry_format" },
89
- { file: runtimeFile("skills/learnings/SKILL.md"), needle: "knowledge.jsonl", name: "utility_skill:learnings:jsonl_mirror" },
88
+ { file: runtimeFile("skills/learnings/SKILL.md"), needle: "strict JSONL schema", name: "utility_skill:learnings:jsonl_schema" },
89
+ { file: runtimeFile("skills/learnings/SKILL.md"), needle: "knowledge.jsonl", name: "utility_skill:learnings:jsonl_store" },
90
+ { file: runtimeFile("skills/learnings/SKILL.md"), needle: "type, trigger, action, confidence, domain, stage, created, project", name: "utility_skill:learnings:field_order" },
90
91
  { file: runtimeFile("skills/learnings/SKILL.md"), needle: "## Subcommands", name: "utility_skill:learnings:subcommands" },
91
92
  { file: runtimeFile("skills/learnings/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:learnings:hard_gate" },
92
93
  { file: runtimeFile("commands/learn.md"), needle: "## Subcommands", name: "utility_command:learn:subcommands" },
package/dist/runs.d.ts CHANGED
@@ -56,11 +56,10 @@ export declare function ensureRunSystem(projectRoot: string, _options?: EnsureRu
56
56
  export declare function listRuns(projectRoot: string): Promise<CclawRunMeta[]>;
57
57
  export declare function archiveRun(projectRoot: string, featureName?: string): Promise<ArchiveRunResult>;
58
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.
59
+ * Counts entries in the canonical JSONL knowledge store. An "active" entry is one
60
+ * non-empty line that parses as JSON with the required `type` field belonging to the
61
+ * allowed set. Malformed lines are ignored (not counted) but do not throw so that a
62
+ * hand-edited file cannot break doctor/archive flows.
64
63
  */
65
64
  export declare function countActiveKnowledgeEntries(text: string): number;
66
65
  export {};
package/dist/runs.js CHANGED
@@ -407,7 +407,7 @@ export async function archiveRun(projectRoot, featureName) {
407
407
  }
408
408
  const KNOWLEDGE_SOFT_THRESHOLD = 50;
409
409
  async function readKnowledgeStats(projectRoot) {
410
- const knowledgePath = path.join(projectRoot, RUNTIME_ROOT, "knowledge.md");
410
+ const knowledgePath = path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl");
411
411
  let activeEntryCount = 0;
412
412
  if (await exists(knowledgePath)) {
413
413
  const text = await fs.readFile(knowledgePath, "utf8");
@@ -417,22 +417,30 @@ async function readKnowledgeStats(projectRoot) {
417
417
  activeEntryCount,
418
418
  softThreshold: KNOWLEDGE_SOFT_THRESHOLD,
419
419
  overThreshold: activeEntryCount > KNOWLEDGE_SOFT_THRESHOLD,
420
- knowledgePath: `${RUNTIME_ROOT}/knowledge.md`
420
+ knowledgePath: `${RUNTIME_ROOT}/knowledge.jsonl`
421
421
  };
422
422
  }
423
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.
424
+ * Counts entries in the canonical JSONL knowledge store. An "active" entry is one
425
+ * non-empty line that parses as JSON with the required `type` field belonging to the
426
+ * allowed set. Malformed lines are ignored (not counted) but do not throw so that a
427
+ * hand-edited file cannot break doctor/archive flows.
429
428
  */
430
429
  export function countActiveKnowledgeEntries(text) {
431
- const lines = text.split(/\r?\n/);
430
+ const allowed = new Set(["rule", "pattern", "lesson", "compound"]);
432
431
  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;
432
+ for (const raw of text.split(/\r?\n/)) {
433
+ const line = raw.trim();
434
+ if (line.length === 0)
435
+ continue;
436
+ try {
437
+ const parsed = JSON.parse(line);
438
+ if (typeof parsed.type === "string" && allowed.has(parsed.type)) {
439
+ count += 1;
440
+ }
441
+ }
442
+ catch {
443
+ // Skip malformed lines silently; curation surfaces them separately.
436
444
  }
437
445
  }
438
446
  return count;
package/dist/types.d.ts CHANGED
@@ -48,10 +48,10 @@ export interface VibyConfig {
48
48
  /** Default flow track for new runs (quick = shortened path, standard = full pipeline). */
49
49
  defaultTrack?: FlowTrack;
50
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.
51
+ * Opt-in language rule packs. Each enabled pack materializes a matching rule
52
+ * file under `.cclaw/rules/lang/<id>.md` on the next `cclaw sync`. The
53
+ * meta-skill router loads the pack during review/tdd when the diff touches
54
+ * the language in question. Disabled packs have no on-disk footprint.
55
55
  */
56
56
  languageRulePacks?: LanguageRulePack[];
57
57
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {