cclaw-cli 0.42.0 → 0.43.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/README.md CHANGED
@@ -135,10 +135,31 @@ Plus harness-specific shims:
135
135
  folders are auto-cleaned on sync.)
136
136
  - `AGENTS.md` with a managed routing block (includes a Codex-specific note)
137
137
 
138
- `.cclaw/config.yaml` holds every tunable key (prompt guard strictness,
139
- TDD enforcement, git-hook guards, language rule packs, track heuristics).
140
- Edit it directly — `cclaw-cli upgrade` preserves your changes. Full key
141
- reference: [`docs/config.md`](./docs/config.md).
138
+ ### `.cclaw/config.yaml` the minimal surface
139
+
140
+ `cclaw init` writes five keys, on purpose:
141
+
142
+ ```yaml
143
+ version: 0.43.0
144
+ flowVersion: 1.0.0
145
+ harnesses:
146
+ - codex
147
+ strictness: advisory # advisory | strict — one knob for prompt-guard + TDD
148
+ gitHookGuards: false # opt in to managed .git/hooks/pre-commit + pre-push
149
+ ```
150
+
151
+ If cclaw detects a Node / Python / Go project at init time, a sixth
152
+ `languageRulePacks` line appears (auto-populated from `package.json`,
153
+ `pyproject.toml` / `requirements.txt`, `go.mod`). That is the full
154
+ default surface — a new user sees nothing they need to understand yet.
155
+
156
+ Advanced knobs (`promptGuardMode` / `tddEnforcement` per-axis overrides,
157
+ `tddTestGlobs`, `defaultTrack`, `trackHeuristics`, `sliceReview`) are
158
+ **opt-in**: add them by hand when you need them. `cclaw upgrade`
159
+ preserves exactly what you wrote — it never silently reintroduces
160
+ defaults you removed.
161
+
162
+ Full key-by-key reference: [`docs/config.md`](./docs/config.md).
142
163
 
143
164
  ---
144
165
 
package/dist/cli.js CHANGED
@@ -827,6 +827,11 @@ async function runCommand(parsed, ctx) {
827
827
  }
828
828
  const trackNote = effectiveTrack ? ` (track=${effectiveTrack})` : "";
829
829
  info(ctx, `Initialized .cclaw runtime and generated harness shims${trackNote}`);
830
+ // Point new users at the one config surface they might actually flip —
831
+ // `strictness` and `gitHookGuards` — without overselling the other knobs
832
+ // (those live behind docs/config.md until someone needs them).
833
+ info(ctx, "Config: .cclaw/config.yaml (strictness=advisory, gitHookGuards=false).");
834
+ info(ctx, "Need stricter guards or language rule packs? See docs/config.md.");
830
835
  await maybeEnableCodexHooksFlag(effectiveHarnesses, parsed, ctx);
831
836
  return 0;
832
837
  }
package/dist/config.d.ts CHANGED
@@ -1,5 +1,66 @@
1
- import type { FlowTrack, HarnessId, VibyConfig } from "./types.js";
1
+ import type { FlowTrack, HarnessId, LanguageRulePack, VibyConfig } from "./types.js";
2
2
  export declare function configPath(projectRoot: string): string;
3
+ /**
4
+ * Default test-file globs used by workflow-guard.sh to detect when a write
5
+ * targets a test file during TDD. Users rarely need to override this — the
6
+ * defaults cover TypeScript / JavaScript / Python / Go / Rust / Java layouts.
7
+ * Exposed so `install.ts` can reuse the same list when seeding the shell
8
+ * guard script, even though the field is no longer written to the default
9
+ * `config.yaml` template.
10
+ */
11
+ export declare const DEFAULT_TDD_TEST_GLOBS: readonly string[];
12
+ /**
13
+ * Populated runtime view of config values that downstream callers (install,
14
+ * observe, doctor) consume. Always has the derived guard modes populated,
15
+ * regardless of whether the user wrote `strictness`, the legacy keys, both,
16
+ * or neither.
17
+ */
3
18
  export declare function createDefaultConfig(harnesses?: HarnessId[], defaultTrack?: FlowTrack): VibyConfig;
19
+ /**
20
+ * Probe common project-root manifests to infer which language rule packs the
21
+ * user would reasonably want. Pure-functional best-effort: any filesystem
22
+ * error is swallowed, producing an empty list — the user can always override
23
+ * by hand.
24
+ *
25
+ * Called from `cclaw init` only (not `readConfig`), so subsequent upgrades
26
+ * never surprise a user who intentionally cleared the list.
27
+ */
28
+ export declare function detectLanguageRulePacks(projectRoot: string): Promise<LanguageRulePack[]>;
4
29
  export declare function readConfig(projectRoot: string): Promise<VibyConfig>;
5
- export declare function writeConfig(projectRoot: string, config: VibyConfig): Promise<void>;
30
+ /**
31
+ * Fields that live on the populated runtime `VibyConfig` but are considered
32
+ * "advanced" — we keep them in the in-memory object so downstream callers
33
+ * don't have to branch, but we do **not** write them to `config.yaml` unless
34
+ * the user set them explicitly. Keeps the default template small and honest:
35
+ * only knobs a new user would meaningfully flip show up.
36
+ */
37
+ type AdvancedConfigKey = "promptGuardMode" | "tddEnforcement" | "tddTestGlobs" | "defaultTrack" | "languageRulePacks" | "trackHeuristics" | "sliceReview";
38
+ /**
39
+ * Options controlling the serialisation shape of `config.yaml`.
40
+ *
41
+ * - `"full"` (default): write every field on the `VibyConfig` object that
42
+ * isn't `undefined`. Preserves existing shapes and keeps legacy callers
43
+ * working without migration.
44
+ * - `"minimal"`: write only the user-facing knobs (`MINIMAL_CONFIG_KEYS`)
45
+ * plus any non-empty `languageRulePacks` (so auto-detected values survive
46
+ * a fresh `cclaw init`). Use this when generating the default template;
47
+ * power users can still add advanced keys by hand.
48
+ *
49
+ * `advancedKeysPresent` upgrades an otherwise-minimal serialisation by
50
+ * including the listed advanced keys. `cclaw upgrade` uses it to preserve
51
+ * the exact shape a user hand-authored, while still re-minimising configs
52
+ * where the user stayed at defaults.
53
+ */
54
+ export interface WriteConfigOptions {
55
+ mode?: "full" | "minimal";
56
+ advancedKeysPresent?: ReadonlySet<AdvancedConfigKey>;
57
+ }
58
+ export declare function writeConfig(projectRoot: string, config: VibyConfig, options?: WriteConfigOptions): Promise<void>;
59
+ /**
60
+ * Enumerate which advanced keys are currently set in the on-disk config.
61
+ * Used by `cclaw upgrade` to preserve the user's existing shape — if they
62
+ * wrote `tddTestGlobs` by hand, the upgrade keeps it; if they didn't, the
63
+ * upgrade stays minimal.
64
+ */
65
+ export declare function detectAdvancedKeys(projectRoot: string): Promise<ReadonlySet<AdvancedConfigKey>>;
66
+ export {};
package/dist/config.js CHANGED
@@ -15,6 +15,7 @@ const ALLOWED_CONFIG_KEYS = new Set([
15
15
  "version",
16
16
  "flowVersion",
17
17
  "harnesses",
18
+ "strictness",
18
19
  "promptGuardMode",
19
20
  "tddEnforcement",
20
21
  "tddTestGlobs",
@@ -24,6 +25,21 @@ const ALLOWED_CONFIG_KEYS = new Set([
24
25
  "trackHeuristics",
25
26
  "sliceReview"
26
27
  ]);
28
+ /**
29
+ * Config keys always present in the minimal init template. Everything else
30
+ * is "advanced" — parsed when present, but not pre-populated by `cclaw init`.
31
+ *
32
+ * Deliberately small: a first-time user should only see knobs they might
33
+ * actually flip. Power users override by adding more keys by hand; the
34
+ * reference lives in `docs/config.md`.
35
+ */
36
+ const MINIMAL_CONFIG_KEYS = [
37
+ "version",
38
+ "flowVersion",
39
+ "harnesses",
40
+ "strictness",
41
+ "gitHookGuards"
42
+ ];
27
43
  const DEFAULT_SLICE_REVIEW_THRESHOLD = 5;
28
44
  const DEFAULT_SLICE_REVIEW_TRACKS = ["standard"];
29
45
  function configFixExample() {
@@ -57,19 +73,78 @@ function validateStringArray(value, fieldName, configFilePath) {
57
73
  export function configPath(projectRoot) {
58
74
  return path.join(projectRoot, CONFIG_PATH);
59
75
  }
76
+ /**
77
+ * Default test-file globs used by workflow-guard.sh to detect when a write
78
+ * targets a test file during TDD. Users rarely need to override this — the
79
+ * defaults cover TypeScript / JavaScript / Python / Go / Rust / Java layouts.
80
+ * Exposed so `install.ts` can reuse the same list when seeding the shell
81
+ * guard script, even though the field is no longer written to the default
82
+ * `config.yaml` template.
83
+ */
84
+ export const DEFAULT_TDD_TEST_GLOBS = [
85
+ "**/*.test.*",
86
+ "**/*.spec.*",
87
+ "**/test/**"
88
+ ];
89
+ /**
90
+ * Populated runtime view of config values that downstream callers (install,
91
+ * observe, doctor) consume. Always has the derived guard modes populated,
92
+ * regardless of whether the user wrote `strictness`, the legacy keys, both,
93
+ * or neither.
94
+ */
60
95
  export function createDefaultConfig(harnesses = DEFAULT_HARNESSES, defaultTrack = "standard") {
61
96
  return {
62
97
  version: CCLAW_VERSION,
63
98
  flowVersion: FLOW_VERSION,
64
99
  harnesses,
100
+ strictness: "advisory",
65
101
  promptGuardMode: "advisory",
66
102
  tddEnforcement: "advisory",
67
- tddTestGlobs: ["**/*.test.*", "**/*.spec.*", "**/test/**"],
103
+ tddTestGlobs: [...DEFAULT_TDD_TEST_GLOBS],
68
104
  gitHookGuards: false,
69
105
  defaultTrack,
70
106
  languageRulePacks: []
71
107
  };
72
108
  }
109
+ /**
110
+ * Probe common project-root manifests to infer which language rule packs the
111
+ * user would reasonably want. Pure-functional best-effort: any filesystem
112
+ * error is swallowed, producing an empty list — the user can always override
113
+ * by hand.
114
+ *
115
+ * Called from `cclaw init` only (not `readConfig`), so subsequent upgrades
116
+ * never surprise a user who intentionally cleared the list.
117
+ */
118
+ export async function detectLanguageRulePacks(projectRoot) {
119
+ const detected = [];
120
+ const pkgPath = path.join(projectRoot, "package.json");
121
+ if (await exists(pkgPath)) {
122
+ try {
123
+ const pkg = JSON.parse(await fs.readFile(pkgPath, "utf8"));
124
+ const deps = {
125
+ ...pkg.dependencies,
126
+ ...pkg.devDependencies
127
+ };
128
+ if ("typescript" in deps || typeof pkg.types === "string") {
129
+ detected.push("typescript");
130
+ }
131
+ }
132
+ catch {
133
+ // Malformed package.json — skip; user can set the pack manually later.
134
+ }
135
+ }
136
+ const pythonMarkers = ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"];
137
+ for (const marker of pythonMarkers) {
138
+ if (await exists(path.join(projectRoot, marker))) {
139
+ detected.push("python");
140
+ break;
141
+ }
142
+ }
143
+ if (await exists(path.join(projectRoot, "go.mod"))) {
144
+ detected.push("go");
145
+ }
146
+ return [...new Set(detected)];
147
+ }
73
148
  export async function readConfig(projectRoot) {
74
149
  const fullPath = configPath(projectRoot);
75
150
  if (!(await exists(fullPath))) {
@@ -105,23 +180,39 @@ export async function readConfig(projectRoot) {
105
180
  const harnesses = hasHarnessesField
106
181
  ? [...new Set(validatedHarnesses)]
107
182
  : DEFAULT_HARNESSES;
183
+ const strictnessRaw = parsed.strictness;
184
+ if (Object.prototype.hasOwnProperty.call(parsed, "strictness") &&
185
+ strictnessRaw !== "advisory" &&
186
+ strictnessRaw !== "strict") {
187
+ throw configValidationError(fullPath, `"strictness" must be "advisory" or "strict"`);
188
+ }
189
+ const strictness = strictnessRaw === "strict" ? "strict" : "advisory";
190
+ // Legacy guard fields — keep honouring explicit values for power users who
191
+ // want asymmetric behaviour (e.g. strict prompt guard + advisory TDD).
192
+ // When the user only set `strictness`, both axes inherit from it.
193
+ const hasExplicitPromptGuard = Object.prototype.hasOwnProperty.call(parsed, "promptGuardMode");
108
194
  const promptGuardModeRaw = parsed.promptGuardMode;
109
- if (Object.prototype.hasOwnProperty.call(parsed, "promptGuardMode") &&
195
+ if (hasExplicitPromptGuard &&
110
196
  promptGuardModeRaw !== "advisory" &&
111
197
  promptGuardModeRaw !== "strict") {
112
198
  throw configValidationError(fullPath, `"promptGuardMode" must be "advisory" or "strict"`);
113
199
  }
114
- const promptGuardMode = promptGuardModeRaw === "strict" ? "strict" : "advisory";
200
+ const promptGuardMode = hasExplicitPromptGuard
201
+ ? (promptGuardModeRaw === "strict" ? "strict" : "advisory")
202
+ : strictness;
203
+ const hasExplicitTddEnforcement = Object.prototype.hasOwnProperty.call(parsed, "tddEnforcement");
115
204
  const tddEnforcementRaw = parsed.tddEnforcement;
116
- if (Object.prototype.hasOwnProperty.call(parsed, "tddEnforcement") &&
205
+ if (hasExplicitTddEnforcement &&
117
206
  tddEnforcementRaw !== "advisory" &&
118
207
  tddEnforcementRaw !== "strict") {
119
208
  throw configValidationError(fullPath, `"tddEnforcement" must be "advisory" or "strict"`);
120
209
  }
121
- const tddEnforcement = tddEnforcementRaw === "strict" ? "strict" : "advisory";
210
+ const tddEnforcement = hasExplicitTddEnforcement
211
+ ? (tddEnforcementRaw === "strict" ? "strict" : "advisory")
212
+ : strictness;
122
213
  const tddTestGlobsRaw = parsed.tddTestGlobs;
123
214
  const tddTestGlobs = validateStringArray(tddTestGlobsRaw, "tddTestGlobs", fullPath)
124
- ?? ["**/*.test.*", "**/*.spec.*", "**/test/**"];
215
+ ?? [...DEFAULT_TDD_TEST_GLOBS];
125
216
  const gitHookGuardsRaw = parsed.gitHookGuards;
126
217
  if (Object.prototype.hasOwnProperty.call(parsed, "gitHookGuards") &&
127
218
  typeof gitHookGuardsRaw !== "boolean") {
@@ -232,6 +323,7 @@ export async function readConfig(projectRoot) {
232
323
  version: parsed.version ?? CCLAW_VERSION,
233
324
  flowVersion: parsed.flowVersion ?? FLOW_VERSION,
234
325
  harnesses,
326
+ strictness,
235
327
  promptGuardMode,
236
328
  tddEnforcement,
237
329
  tddTestGlobs,
@@ -242,6 +334,88 @@ export async function readConfig(projectRoot) {
242
334
  sliceReview
243
335
  };
244
336
  }
245
- export async function writeConfig(projectRoot, config) {
246
- await writeFileSafe(configPath(projectRoot), stringify(config));
337
+ function isMinimalKey(key) {
338
+ return MINIMAL_CONFIG_KEYS.includes(key);
339
+ }
340
+ function buildSerializableConfig(config, options = {}) {
341
+ const mode = options.mode ?? "full";
342
+ const advanced = options.advancedKeysPresent;
343
+ const output = {};
344
+ const ordered = [
345
+ "version",
346
+ "flowVersion",
347
+ "harnesses",
348
+ "strictness",
349
+ "promptGuardMode",
350
+ "tddEnforcement",
351
+ "tddTestGlobs",
352
+ "gitHookGuards",
353
+ "defaultTrack",
354
+ "languageRulePacks",
355
+ "trackHeuristics",
356
+ "sliceReview"
357
+ ];
358
+ for (const key of ordered) {
359
+ const value = config[key];
360
+ if (value === undefined)
361
+ continue;
362
+ if (mode === "full") {
363
+ output[key] = value;
364
+ continue;
365
+ }
366
+ // Minimal mode: always include the short list; advanced keys only when
367
+ // the caller explicitly opted in, or for auto-detected non-empty
368
+ // `languageRulePacks`.
369
+ if (isMinimalKey(key)) {
370
+ output[key] = value;
371
+ continue;
372
+ }
373
+ if (advanced?.has(key)) {
374
+ output[key] = value;
375
+ continue;
376
+ }
377
+ if (key === "languageRulePacks" && Array.isArray(value) && value.length > 0) {
378
+ output[key] = value;
379
+ }
380
+ }
381
+ return output;
382
+ }
383
+ export async function writeConfig(projectRoot, config, options = {}) {
384
+ const serialisable = buildSerializableConfig(config, options);
385
+ await writeFileSafe(configPath(projectRoot), stringify(serialisable));
386
+ }
387
+ /**
388
+ * Enumerate which advanced keys are currently set in the on-disk config.
389
+ * Used by `cclaw upgrade` to preserve the user's existing shape — if they
390
+ * wrote `tddTestGlobs` by hand, the upgrade keeps it; if they didn't, the
391
+ * upgrade stays minimal.
392
+ */
393
+ export async function detectAdvancedKeys(projectRoot) {
394
+ const fullPath = configPath(projectRoot);
395
+ if (!(await exists(fullPath)))
396
+ return new Set();
397
+ try {
398
+ const parsedUnknown = parse(await fs.readFile(fullPath, "utf8"));
399
+ if (!isRecord(parsedUnknown))
400
+ return new Set();
401
+ const advancedCandidates = [
402
+ "promptGuardMode",
403
+ "tddEnforcement",
404
+ "tddTestGlobs",
405
+ "defaultTrack",
406
+ "languageRulePacks",
407
+ "trackHeuristics",
408
+ "sliceReview"
409
+ ];
410
+ const present = new Set();
411
+ for (const key of advancedCandidates) {
412
+ if (Object.prototype.hasOwnProperty.call(parsedUnknown, key)) {
413
+ present.add(key);
414
+ }
415
+ }
416
+ return present;
417
+ }
418
+ catch {
419
+ return new Set();
420
+ }
247
421
  }
package/dist/install.d.ts CHANGED
@@ -9,13 +9,12 @@ export declare function syncCclaw(projectRoot: string): Promise<void>;
9
9
  /**
10
10
  * Refresh generated files in `.cclaw/` without touching user-authored
11
11
  * artifacts, state, or custom config keys. Only the `version` + `flowVersion`
12
- * stamps are rewritten so the on-disk config reflects the installed CLI;
13
- * `promptGuardMode`, `tddEnforcement`, `gitHookGuards`, `languageRulePacks`,
14
- * `trackHeuristics`, and `sliceReview` are preserved verbatim from the
15
- * existing config.
12
+ * stamps are rewritten so the on-disk config reflects the installed CLI.
16
13
  *
17
- * For an explicit reset, run `cclaw-cli uninstall && cclaw-cli init`
18
- * (after optionally archiving the current run via `/cc-ops archive`).
14
+ * Shape preservation: if the user previously hand-authored advanced keys
15
+ * (e.g. `tddTestGlobs`, `trackHeuristics`, `sliceReview`), those stay in the
16
+ * yaml. If their existing config is minimal, the upgrade keeps it minimal —
17
+ * advanced knobs are never silently added.
19
18
  */
20
19
  export declare function upgradeCclaw(projectRoot: string): Promise<void>;
21
20
  export declare function uninstallCclaw(projectRoot: string): Promise<void>;
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 { CCLAW_VERSION, COMMAND_FILE_ORDER, FLOW_VERSION, REQUIRED_DIRS, RUNTIME_ROOT } from "./constants.js";
6
- import { writeConfig, createDefaultConfig, readConfig, configPath } from "./config.js";
6
+ import { writeConfig, createDefaultConfig, readConfig, configPath, detectLanguageRulePacks, detectAdvancedKeys } 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";
@@ -1156,13 +1156,24 @@ async function materializeRuntime(projectRoot, config, forceStateReset) {
1156
1156
  await ensureGitignore(projectRoot);
1157
1157
  }
1158
1158
  export async function initCclaw(options) {
1159
- const config = createDefaultConfig(options.harnesses, options.track);
1160
- await writeConfig(options.projectRoot, config);
1159
+ const baseConfig = createDefaultConfig(options.harnesses, options.track);
1160
+ // Best-effort auto-detect: a Node project gets `typescript`, a Go module
1161
+ // gets `go`, etc. Skipped entirely when the project root has no manifests.
1162
+ const detectedPacks = await detectLanguageRulePacks(options.projectRoot);
1163
+ const config = {
1164
+ ...baseConfig,
1165
+ languageRulePacks: detectedPacks
1166
+ };
1167
+ // Write a minimal `config.yaml` — advanced knobs live in docs/config.md
1168
+ // and only appear in the on-disk file when the user sets them explicitly
1169
+ // or a non-default value was detected (e.g. languageRulePacks).
1170
+ await writeConfig(options.projectRoot, config, { mode: "minimal" });
1161
1171
  await materializeRuntime(options.projectRoot, config, true);
1162
1172
  }
1163
1173
  export async function syncCclaw(projectRoot) {
1174
+ const configExists = await exists(configPath(projectRoot));
1164
1175
  const config = await readConfig(projectRoot);
1165
- if (!(await exists(configPath(projectRoot)))) {
1176
+ if (!configExists) {
1166
1177
  await writeConfig(projectRoot, createDefaultConfig(config.harnesses));
1167
1178
  }
1168
1179
  await materializeRuntime(projectRoot, config, false);
@@ -1170,22 +1181,25 @@ export async function syncCclaw(projectRoot) {
1170
1181
  /**
1171
1182
  * Refresh generated files in `.cclaw/` without touching user-authored
1172
1183
  * artifacts, state, or custom config keys. Only the `version` + `flowVersion`
1173
- * stamps are rewritten so the on-disk config reflects the installed CLI;
1174
- * `promptGuardMode`, `tddEnforcement`, `gitHookGuards`, `languageRulePacks`,
1175
- * `trackHeuristics`, and `sliceReview` are preserved verbatim from the
1176
- * existing config.
1184
+ * stamps are rewritten so the on-disk config reflects the installed CLI.
1177
1185
  *
1178
- * For an explicit reset, run `cclaw-cli uninstall && cclaw-cli init`
1179
- * (after optionally archiving the current run via `/cc-ops archive`).
1186
+ * Shape preservation: if the user previously hand-authored advanced keys
1187
+ * (e.g. `tddTestGlobs`, `trackHeuristics`, `sliceReview`), those stay in the
1188
+ * yaml. If their existing config is minimal, the upgrade keeps it minimal —
1189
+ * advanced knobs are never silently added.
1180
1190
  */
1181
1191
  export async function upgradeCclaw(projectRoot) {
1192
+ const advancedKeysPresent = await detectAdvancedKeys(projectRoot);
1182
1193
  const existing = await readConfig(projectRoot);
1183
1194
  const upgraded = {
1184
1195
  ...existing,
1185
1196
  version: CCLAW_VERSION,
1186
1197
  flowVersion: FLOW_VERSION
1187
1198
  };
1188
- await writeConfig(projectRoot, upgraded);
1199
+ await writeConfig(projectRoot, upgraded, {
1200
+ mode: "minimal",
1201
+ advancedKeysPresent
1202
+ });
1189
1203
  await materializeRuntime(projectRoot, upgraded, false);
1190
1204
  }
1191
1205
  function stripManagedHookCommands(value) {
package/dist/types.d.ts CHANGED
@@ -92,9 +92,30 @@ export interface VibyConfig {
92
92
  version: string;
93
93
  flowVersion: string;
94
94
  harnesses: HarnessId[];
95
- /** Prompt guard behavior for runtime write-risk detection hooks. */
95
+ /**
96
+ * Single-knob strictness for both guard families. When set, cclaw derives
97
+ * `promptGuardMode` and `tddEnforcement` from this value unless the legacy
98
+ * fields are explicitly provided. Default: "advisory".
99
+ *
100
+ * Added in v0.43.0 to collapse two fields that always moved together for
101
+ * ~99% of users. Power users who want asymmetric strictness (e.g. strict
102
+ * prompt guard, advisory TDD) can still set the legacy fields directly —
103
+ * explicit per-axis values override the derived strictness.
104
+ */
105
+ strictness?: "advisory" | "strict";
106
+ /**
107
+ * Prompt guard behavior for runtime write-risk detection hooks.
108
+ *
109
+ * Since v0.43.0 this is an advanced override. Prefer `strictness` in new
110
+ * configs; set this explicitly only when you need strict prompt guarding
111
+ * while keeping TDD advisory, or vice versa.
112
+ */
96
113
  promptGuardMode?: "advisory" | "strict";
97
- /** TDD red->green->refactor enforcement mode used by workflow guard hooks. */
114
+ /**
115
+ * TDD red->green->refactor enforcement mode used by workflow guard hooks.
116
+ *
117
+ * Since v0.43.0 this is an advanced override — see `strictness`.
118
+ */
98
119
  tddEnforcement?: "advisory" | "strict";
99
120
  /** Optional test file globs used by guard guidance and /cc-ops tdd-log docs. */
100
121
  tddTestGlobs?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {