cclaw-cli 0.39.1 → 0.41.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
@@ -127,9 +127,12 @@ Plus harness-specific shims:
127
127
  - `.claude/commands/cc*.md` + `.claude/hooks/hooks.json`
128
128
  - `.cursor/commands/cc*.md` + `.cursor/hooks.json` + `.cursor/rules/cclaw-workflow.mdc`
129
129
  - `.opencode/commands/cc*.md` + `.opencode/plugins/cclaw-plugin.mjs`
130
- - `.agents/skills/cclaw-cc*/SKILL.md` (Codex; activated via `/use cclaw-cc`
131
- or description-based auto-matching — Codex no longer reads `.codex/commands/`
132
- or `.codex/hooks.json`, and `cclaw sync` cleans those up if present)
130
+ - `.agents/skills/cc*/SKILL.md` + `.codex/hooks.json` (Codex; skills are
131
+ activated via `/use cc` or description-based auto-matching. Hooks
132
+ require Codex CLI ≥ v0.114 and `[features] codex_hooks = true` in
133
+ `~/.codex/config.toml`; `cclaw init --codex` offers to patch that flag
134
+ for you. `.codex/commands/` and the legacy `.agents/skills/cclaw-cc*/`
135
+ folders are auto-cleaned on sync.)
133
136
  - `AGENTS.md` with a managed routing block (includes a Codex-specific note)
134
137
 
135
138
  `.cclaw/config.yaml` holds every tunable key (prompt guard strictness,
@@ -356,8 +359,8 @@ closes every real gap with a documented fallback — not a silent waiver.
356
359
  |---|---|---|---|---|---|
357
360
  | Claude Code | full (named subagents) | `native` | full | `AskUserQuestion` | [`claude-playbook.md`](./src/content/harness-playbooks.ts) |
358
361
  | Cursor | generic Task dispatcher | `generic-dispatch` | full | `AskQuestion` | `cursor-playbook.md` |
359
- | OpenCode | plugin / in-session | `role-switch` | plugin | plain-text | `opencode-playbook.md` |
360
- | OpenAI Codex | in-session only | `role-switch` (evidenceRefs required) | none (no hooks API) | plain-text | `codex-playbook.md` |
362
+ | OpenCode | plugin / in-session | `role-switch` | plugin | `question` (permission-gated; `permission.question: "allow"`) | `opencode-playbook.md` |
363
+ | OpenAI Codex | in-session only | `role-switch` (evidenceRefs required) | limited (Bash-only `PreToolUse`/`PostToolUse`; requires `codex_hooks` feature flag) | `request_user_input` (experimental; Plan / Collaboration mode) | `codex-playbook.md` |
361
364
 
362
365
  What the fallbacks mean:
363
366
 
@@ -380,16 +383,21 @@ What the fallbacks mean:
380
383
  harness declares it. Currently unused — v0.33 removed the old
381
384
  Codex-only auto-waiver path.
382
385
 
383
- > **Codex note (v0.39+).** Codex CLI deprecated custom prompts and the
384
- > `.codex/hooks.json` API, so cclaw installs Codex entry points as
385
- > native **skills** under `.agents/skills/cclaw-cc*/SKILL.md`. Invoke
386
- > them with `/use cclaw-cc`, `/use cclaw-cc-next`, `/use cclaw-cc-view`,
387
- > `/use cclaw-cc-ops`, `/use cclaw-cc-ideate`, or just say something
388
- > like *"run cc for payments refund fix"* — Codex auto-matches skills
389
- > from their description. Hook-driven checks (prompt-guard, stop-save,
390
- > post-tool context monitor) are substituted in the `cclaw-cc*` skill
391
- > bodies as explicit agent steps; run `cclaw doctor` to see what's
392
- > missing and how the playbook compensates.
386
+ > **Codex note (v0.40+).** Codex CLI deprecated custom prompts in v0.89
387
+ > (Jan 2026), but Codex v0.114 (Mar 2026) grew an experimental
388
+ > lifecycle hooks API. cclaw installs Codex entry points as native
389
+ > **skills** under `.agents/skills/cc*/SKILL.md` (invoke with `/use cc`,
390
+ > `/use cc-next`, `/use cc-view`, `/use cc-ops`, `/use cc-ideate`, or
391
+ > by typing `/cc …` in plain text — Codex auto-matches from the skill
392
+ > description) **and** writes `.codex/hooks.json` so session-start
393
+ > rehydration, stop-checkpoint, prompt-guard, workflow-guard, and
394
+ > context-monitor fire automatically as long as you enable the
395
+ > `codex_hooks` feature flag in `~/.codex/config.toml`. `cclaw init
396
+ > --codex` asks for consent before patching that file. Codex's
397
+ > `PreToolUse`/`PostToolUse` are Bash-only; the stage skills compensate
398
+ > for `Write`/`Edit`/`MCP` tool calls with explicit in-turn checks. Run
399
+ > `cclaw doctor` to see the current state of hooks, the feature flag,
400
+ > and any legacy layout to clean up.
393
401
 
394
402
  The full capability matrix lives in
395
403
  [`docs/harnesses.md`](./docs/harnesses.md). Per-harness playbooks are
package/dist/cli.js CHANGED
@@ -15,6 +15,7 @@ import { CCLAW_VERSION, RUNTIME_ROOT } from "./constants.js";
15
15
  import { createDefaultConfig } from "./config.js";
16
16
  import { detectHarnesses } from "./init-detect.js";
17
17
  import { HARNESS_ADAPTERS } from "./harness-adapters.js";
18
+ import { classifyCodexHooksFlag, codexConfigPath, patchCodexHooksFlag, readCodexConfig, writeCodexConfig } from "./codex-feature-flag.js";
18
19
  import { runEval } from "./eval/runner.js";
19
20
  import { createStderrProgressLogger } from "./eval/progress.js";
20
21
  import { writeBaselinesFromReport } from "./eval/baseline.js";
@@ -152,7 +153,7 @@ function buildInitSurfacePreview(harnesses) {
152
153
  for (const harness of harnesses) {
153
154
  const adapter = HARNESS_ADAPTERS[harness];
154
155
  if (adapter.shimKind === "skill") {
155
- lines.push(`${adapter.commandDir}/cclaw-cc*/SKILL.md`);
156
+ lines.push(`${adapter.commandDir}/cc*/SKILL.md`);
156
157
  }
157
158
  else {
158
159
  lines.push(`${adapter.commandDir}/cc*.md`);
@@ -164,9 +165,12 @@ function buildInitSurfacePreview(harnesses) {
164
165
  lines.push(".cursor/hooks.json");
165
166
  lines.push(".cursor/rules/cclaw-workflow.mdc");
166
167
  }
167
- // Codex has no hooks file — it reads skills from `.agents/skills/` only
168
- // (v0.39.0+). Legacy `.codex/commands/*` and `.codex/hooks.json` are
169
- // auto-cleaned on sync.
168
+ if (harness === "codex") {
169
+ // v0.40.0: .codex/hooks.json is managed again now that Codex CLI
170
+ // grew a real hooks API (v0.114+, behind the `codex_hooks`
171
+ // feature flag). Legacy `.codex/commands/*` is still auto-cleaned.
172
+ lines.push(".codex/hooks.json (requires `codex_hooks = true` in ~/.codex/config.toml)");
173
+ }
170
174
  if (harness === "opencode") {
171
175
  lines.push(".opencode/plugins/cclaw-plugin.mjs");
172
176
  lines.push("opencode.json(.c) plugin registration");
@@ -207,6 +211,85 @@ async function promptInitConfig(defaults, ctx) {
207
211
  rl.close();
208
212
  }
209
213
  }
214
+ /**
215
+ * When Codex is one of the installed harnesses, check the Codex CLI
216
+ * config file for the `codex_hooks` feature flag. If it is missing or
217
+ * disabled, offer to patch it in with the user's explicit consent.
218
+ *
219
+ * The function is deliberately advisory: it never fails init — the worst
220
+ * case is that Codex runs without the hooks engine, which is exactly
221
+ * how v0.39.x already shipped. We always print a resolution hint so
222
+ * the user knows what to do next regardless of which branch was taken.
223
+ */
224
+ async function maybeEnableCodexHooksFlag(harnesses, parsed, ctx) {
225
+ if (!harnesses || !harnesses.includes("codex"))
226
+ return;
227
+ const configPath = codexConfigPath();
228
+ let existing;
229
+ try {
230
+ existing = await readCodexConfig(configPath);
231
+ }
232
+ catch (err) {
233
+ ctx.stdout.write(`note: Could not read ${configPath} to check the codex_hooks flag: ` +
234
+ `${err instanceof Error ? err.message : String(err)}\n`);
235
+ return;
236
+ }
237
+ const state = classifyCodexHooksFlag(existing);
238
+ if (state === "enabled") {
239
+ return;
240
+ }
241
+ const humanState = state === "missing-file"
242
+ ? "Codex config file does not exist yet"
243
+ : state === "missing-section"
244
+ ? "no [features] section"
245
+ : state === "missing-key"
246
+ ? "no codex_hooks key"
247
+ : "codex_hooks is not enabled";
248
+ const instructions = `To enable Codex hooks manually later, ensure ${configPath} contains:\n` +
249
+ ` [features]\n codex_hooks = true\n`;
250
+ if (parsed.interactive === false) {
251
+ ctx.stdout.write(`note: codex_hooks feature flag is not enabled (${humanState}).\n` +
252
+ ` cclaw wrote .codex/hooks.json, but Codex will ignore it until you enable the flag.\n` +
253
+ ` ${instructions}`);
254
+ return;
255
+ }
256
+ if (!isInitPromptAllowed(ctx)) {
257
+ ctx.stdout.write(`note: codex_hooks feature flag is not enabled (${humanState}).\n` +
258
+ ` cclaw wrote .codex/hooks.json, but Codex will ignore it until you enable the flag.\n` +
259
+ ` ${instructions}`);
260
+ return;
261
+ }
262
+ const rl = createInterface({
263
+ input: process.stdin,
264
+ output: ctx.stdout
265
+ });
266
+ try {
267
+ const answer = (await rl.question(`\nCodex CLI hooks are off (${humanState}).\n` +
268
+ `Enable [features] codex_hooks = true in ${configPath} now? [y/N]: `)).trim().toLowerCase();
269
+ const yes = answer === "y" || answer === "yes";
270
+ if (!yes) {
271
+ ctx.stdout.write(`Leaving ${configPath} untouched. ${instructions}`);
272
+ return;
273
+ }
274
+ const { updated, changed } = patchCodexHooksFlag(existing);
275
+ if (!changed) {
276
+ ctx.stdout.write(`codex_hooks is already enabled — no changes written.\n`);
277
+ return;
278
+ }
279
+ try {
280
+ await writeCodexConfig(configPath, updated);
281
+ ctx.stdout.write(`Enabled [features] codex_hooks = true in ${configPath}.\n`);
282
+ }
283
+ catch (err) {
284
+ ctx.stdout.write(`Could not write ${configPath}: ` +
285
+ `${err instanceof Error ? err.message : String(err)}\n` +
286
+ `${instructions}`);
287
+ }
288
+ }
289
+ finally {
290
+ rl.close();
291
+ }
292
+ }
210
293
  async function resolveInitInputs(parsed, ctx) {
211
294
  const detectedHarnesses = parsed.harnesses ? [] : await detectHarnesses(ctx.cwd);
212
295
  const autoHarnesses = parsed.harnesses
@@ -744,6 +827,7 @@ async function runCommand(parsed, ctx) {
744
827
  }
745
828
  const trackNote = effectiveTrack ? ` (track=${effectiveTrack})` : "";
746
829
  info(ctx, `Initialized .cclaw runtime and generated harness shims${trackNote}`);
830
+ await maybeEnableCodexHooksFlag(effectiveHarnesses, parsed, ctx);
747
831
  return 0;
748
832
  }
749
833
  if (command === "sync") {
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Manage the `codex_hooks` feature flag in `~/.codex/config.toml`.
3
+ *
4
+ * Codex CLI ≥ v0.114 (Mar 2026) exposes lifecycle hooks via
5
+ * `.codex/hooks.json`, but the hooks engine is inert unless the user has
6
+ * opted into it with:
7
+ *
8
+ * ```toml
9
+ * [features]
10
+ * codex_hooks = true
11
+ * ```
12
+ *
13
+ * in `$CODEX_HOME/config.toml` (default: `~/.codex/config.toml`).
14
+ * cclaw's `init --codex` prompts the user to flip this flag for them;
15
+ * this module owns the detection / mutation code so the prompt logic in
16
+ * `cli.ts` stays small and testable.
17
+ *
18
+ * The TOML mutations here are intentionally surgical — we never reparse
19
+ * or rewrite the whole document. A deliberately narrow regex based
20
+ * approach lets the function stay dependency-free and preserves the
21
+ * user's comments, whitespace, and custom key ordering.
22
+ */
23
+ /**
24
+ * Absolute path of the Codex config file. Respects `$CODEX_HOME` when
25
+ * present (the only override Codex CLI documents); falls back to
26
+ * `~/.codex/config.toml` otherwise.
27
+ */
28
+ export declare function codexConfigPath(env?: NodeJS.ProcessEnv): string;
29
+ export type CodexHooksFlagState = "enabled" | "disabled" | "missing-key" | "missing-section" | "missing-file";
30
+ /**
31
+ * Inspect a TOML document and decide which of the five canonical states
32
+ * it represents. Comments and blank lines are ignored. Only the first
33
+ * `[features]` section is considered — duplicates are technically invalid
34
+ * TOML and Codex rejects them, so cclaw does not try to be clever there.
35
+ */
36
+ export declare function classifyCodexHooksFlag(toml: string | null): CodexHooksFlagState;
37
+ /**
38
+ * Return a TOML document with `[features] codex_hooks = true` set.
39
+ * Preserves all other content verbatim:
40
+ * - If the document lacks a `[features]` section, we append one at the
41
+ * end of the file (separated by a blank line).
42
+ * - If `[features]` exists without `codex_hooks`, we insert the key
43
+ * immediately after the header.
44
+ * - If `codex_hooks` exists with any non-`true` value, we rewrite
45
+ * just that line.
46
+ * - If the flag is already `true`, the input is returned unchanged.
47
+ */
48
+ export declare function patchCodexHooksFlag(toml: string | null): {
49
+ updated: string;
50
+ changed: boolean;
51
+ };
52
+ /**
53
+ * Read the Codex config, return `null` when the file does not exist.
54
+ * All other read errors propagate so callers can surface a useful
55
+ * message instead of silently degrading.
56
+ */
57
+ export declare function readCodexConfig(configPath: string): Promise<string | null>;
58
+ export declare function writeCodexConfig(configPath: string, content: string): Promise<void>;
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Manage the `codex_hooks` feature flag in `~/.codex/config.toml`.
3
+ *
4
+ * Codex CLI ≥ v0.114 (Mar 2026) exposes lifecycle hooks via
5
+ * `.codex/hooks.json`, but the hooks engine is inert unless the user has
6
+ * opted into it with:
7
+ *
8
+ * ```toml
9
+ * [features]
10
+ * codex_hooks = true
11
+ * ```
12
+ *
13
+ * in `$CODEX_HOME/config.toml` (default: `~/.codex/config.toml`).
14
+ * cclaw's `init --codex` prompts the user to flip this flag for them;
15
+ * this module owns the detection / mutation code so the prompt logic in
16
+ * `cli.ts` stays small and testable.
17
+ *
18
+ * The TOML mutations here are intentionally surgical — we never reparse
19
+ * or rewrite the whole document. A deliberately narrow regex based
20
+ * approach lets the function stay dependency-free and preserves the
21
+ * user's comments, whitespace, and custom key ordering.
22
+ */
23
+ import fs from "node:fs/promises";
24
+ import os from "node:os";
25
+ import path from "node:path";
26
+ /**
27
+ * Absolute path of the Codex config file. Respects `$CODEX_HOME` when
28
+ * present (the only override Codex CLI documents); falls back to
29
+ * `~/.codex/config.toml` otherwise.
30
+ */
31
+ export function codexConfigPath(env = process.env) {
32
+ const codexHome = env.CODEX_HOME && env.CODEX_HOME.trim().length > 0
33
+ ? env.CODEX_HOME
34
+ : path.join(os.homedir(), ".codex");
35
+ return path.join(codexHome, "config.toml");
36
+ }
37
+ /**
38
+ * Inspect a TOML document and decide which of the five canonical states
39
+ * it represents. Comments and blank lines are ignored. Only the first
40
+ * `[features]` section is considered — duplicates are technically invalid
41
+ * TOML and Codex rejects them, so cclaw does not try to be clever there.
42
+ */
43
+ export function classifyCodexHooksFlag(toml) {
44
+ if (toml === null) {
45
+ return "missing-file";
46
+ }
47
+ const lines = toml.split(/\r?\n/);
48
+ let inFeaturesSection = false;
49
+ let sawFeaturesHeader = false;
50
+ for (const rawLine of lines) {
51
+ const stripped = stripTomlComment(rawLine).trim();
52
+ if (stripped.length === 0)
53
+ continue;
54
+ const headerMatch = /^\[\s*([A-Za-z0-9_.-]+)\s*\]$/u.exec(stripped);
55
+ if (headerMatch) {
56
+ const section = headerMatch[1];
57
+ if (section === "features") {
58
+ inFeaturesSection = true;
59
+ sawFeaturesHeader = true;
60
+ }
61
+ else {
62
+ inFeaturesSection = false;
63
+ }
64
+ continue;
65
+ }
66
+ if (inFeaturesSection) {
67
+ const keyMatch = /^codex_hooks\s*=\s*(.*)$/u.exec(stripped);
68
+ if (keyMatch) {
69
+ const value = keyMatch[1].trim().toLowerCase();
70
+ return value === "true" ? "enabled" : "disabled";
71
+ }
72
+ }
73
+ }
74
+ if (sawFeaturesHeader)
75
+ return "missing-key";
76
+ return "missing-section";
77
+ }
78
+ function stripTomlComment(line) {
79
+ // Naive but sufficient for our narrow use case: we only read cclaw's
80
+ // own writes back, and cclaw never emits `=` after a `#` inside a
81
+ // string literal in config.toml. If a user has complex string values
82
+ // with `#` inside them, worst case we trip `classifyCodexHooksFlag`
83
+ // and prompt them again — non-destructive.
84
+ const hashIndex = line.indexOf("#");
85
+ return hashIndex === -1 ? line : line.slice(0, hashIndex);
86
+ }
87
+ /**
88
+ * Return a TOML document with `[features] codex_hooks = true` set.
89
+ * Preserves all other content verbatim:
90
+ * - If the document lacks a `[features]` section, we append one at the
91
+ * end of the file (separated by a blank line).
92
+ * - If `[features]` exists without `codex_hooks`, we insert the key
93
+ * immediately after the header.
94
+ * - If `codex_hooks` exists with any non-`true` value, we rewrite
95
+ * just that line.
96
+ * - If the flag is already `true`, the input is returned unchanged.
97
+ */
98
+ export function patchCodexHooksFlag(toml) {
99
+ const initial = toml ?? "";
100
+ const state = classifyCodexHooksFlag(toml);
101
+ if (state === "enabled") {
102
+ return { updated: initial, changed: false };
103
+ }
104
+ if (state === "missing-file" || state === "missing-section") {
105
+ const prefix = initial.length === 0
106
+ ? ""
107
+ : initial.endsWith("\n") ? initial : `${initial}\n`;
108
+ const separator = initial.trim().length === 0 ? "" : "\n";
109
+ const block = `${separator}[features]\ncodex_hooks = true\n`;
110
+ return { updated: `${prefix}${block}`, changed: true };
111
+ }
112
+ if (state === "missing-key") {
113
+ const updated = insertKeyInFeaturesSection(initial);
114
+ return { updated, changed: true };
115
+ }
116
+ const updated = replaceCodexHooksLineInFeaturesSection(initial);
117
+ return { updated, changed: true };
118
+ }
119
+ function insertKeyInFeaturesSection(toml) {
120
+ // Walk into `[features]`, remember the index of the last key / non-blank
121
+ // line inside that section, and splice `codex_hooks = true` immediately
122
+ // after it. This keeps the inserted key adjacent to existing features,
123
+ // never stranded after a blank line or pushed down past a later section
124
+ // header. If `[features]` is empty, we insert right after its header.
125
+ const lines = toml.split(/\r?\n/);
126
+ let inFeaturesSection = false;
127
+ let featuresHeaderIndex = -1;
128
+ let lastFeatureKeyIndex = -1;
129
+ for (let index = 0; index < lines.length; index += 1) {
130
+ const rawLine = lines[index];
131
+ const stripped = stripTomlComment(rawLine).trim();
132
+ const headerMatch = /^\[\s*([A-Za-z0-9_.-]+)\s*\]$/u.exec(stripped);
133
+ if (headerMatch) {
134
+ if (inFeaturesSection)
135
+ break;
136
+ if (headerMatch[1] === "features") {
137
+ inFeaturesSection = true;
138
+ featuresHeaderIndex = index;
139
+ lastFeatureKeyIndex = index;
140
+ }
141
+ continue;
142
+ }
143
+ if (inFeaturesSection && stripped.length > 0) {
144
+ lastFeatureKeyIndex = index;
145
+ }
146
+ }
147
+ if (featuresHeaderIndex === -1) {
148
+ // caller should have short-circuited before getting here; defensive
149
+ return toml;
150
+ }
151
+ lines.splice(lastFeatureKeyIndex + 1, 0, "codex_hooks = true");
152
+ const joined = lines.join("\n");
153
+ return joined.endsWith("\n") ? joined : `${joined}\n`;
154
+ }
155
+ function replaceCodexHooksLineInFeaturesSection(toml) {
156
+ const lines = toml.split(/\r?\n/);
157
+ let inFeaturesSection = false;
158
+ for (let index = 0; index < lines.length; index += 1) {
159
+ const rawLine = lines[index];
160
+ const stripped = stripTomlComment(rawLine).trim();
161
+ const headerMatch = /^\[\s*([A-Za-z0-9_.-]+)\s*\]$/u.exec(stripped);
162
+ if (headerMatch) {
163
+ inFeaturesSection = headerMatch[1] === "features";
164
+ continue;
165
+ }
166
+ if (inFeaturesSection && /^codex_hooks\s*=/u.test(stripped)) {
167
+ const indent = /^\s*/u.exec(rawLine)?.[0] ?? "";
168
+ lines[index] = `${indent}codex_hooks = true`;
169
+ }
170
+ }
171
+ const joined = lines.join("\n");
172
+ return joined.endsWith("\n") ? joined : `${joined}\n`;
173
+ }
174
+ /**
175
+ * Read the Codex config, return `null` when the file does not exist.
176
+ * All other read errors propagate so callers can surface a useful
177
+ * message instead of silently degrading.
178
+ */
179
+ export async function readCodexConfig(configPath) {
180
+ try {
181
+ return await fs.readFile(configPath, "utf8");
182
+ }
183
+ catch (err) {
184
+ if (err.code === "ENOENT") {
185
+ return null;
186
+ }
187
+ throw err;
188
+ }
189
+ }
190
+ export async function writeCodexConfig(configPath, content) {
191
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
192
+ await fs.writeFile(configPath, content, "utf8");
193
+ }
@@ -14,7 +14,7 @@ export declare const EVALS_ROOT = ".cclaw/evals";
14
14
  export declare const EVALS_CONFIG_PATH = ".cclaw/evals/config.yaml";
15
15
  export declare const EVALS_DIRS: readonly [".cclaw/evals", ".cclaw/evals/corpus", ".cclaw/evals/rubrics", ".cclaw/evals/baselines", ".cclaw/evals/reports"];
16
16
  export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/contexts", ".cclaw/templates", ".cclaw/artifacts", ".cclaw/worktrees", ".cclaw/state", ".cclaw/runs", ".cclaw/rules", ".cclaw/adapters", ".cclaw/agents", ".cclaw/hooks", ".cclaw/custom-skills", ".cclaw/evals", ".cclaw/evals/corpus", ".cclaw/evals/rubrics", ".cclaw/evals/baselines", ".cclaw/evals/reports"];
17
- export declare const REQUIRED_GITIGNORE_PATTERNS: readonly ["# cclaw generated artifacts", ".cclaw/", "# cclaw evals: user-owned, track in git", "!.cclaw/evals/", "!.cclaw/evals/config.yaml", "!.cclaw/evals/corpus/", "!.cclaw/evals/corpus/**", "!.cclaw/evals/rubrics/", "!.cclaw/evals/rubrics/**", "!.cclaw/evals/baselines/", "!.cclaw/evals/baselines/**", ".claude/commands/cc-*.md", ".claude/commands/cc.md", ".cursor/commands/cc-*.md", ".cursor/commands/cc.md", ".opencode/commands/cc-*.md", ".opencode/commands/cc.md", ".agents/skills/cclaw-cc/SKILL.md", ".agents/skills/cclaw-cc-*/SKILL.md", ".claude/hooks/hooks.json", ".cursor/hooks.json", ".opencode/plugins/cclaw-plugin.mjs", ".cursor/rules/cclaw-workflow.mdc"];
17
+ export declare const REQUIRED_GITIGNORE_PATTERNS: readonly ["# cclaw generated artifacts", ".cclaw/", "# cclaw evals: user-owned, track in git", "!.cclaw/evals/", "!.cclaw/evals/config.yaml", "!.cclaw/evals/corpus/", "!.cclaw/evals/corpus/**", "!.cclaw/evals/rubrics/", "!.cclaw/evals/rubrics/**", "!.cclaw/evals/baselines/", "!.cclaw/evals/baselines/**", ".claude/commands/cc-*.md", ".claude/commands/cc.md", ".cursor/commands/cc-*.md", ".cursor/commands/cc.md", ".opencode/commands/cc-*.md", ".opencode/commands/cc.md", ".agents/skills/cc/SKILL.md", ".agents/skills/cc-*/SKILL.md", ".claude/hooks/hooks.json", ".cursor/hooks.json", ".codex/hooks.json", ".opencode/plugins/cclaw-plugin.mjs", ".cursor/rules/cclaw-workflow.mdc"];
18
18
  export declare const COMMAND_FILE_ORDER: FlowStage[];
19
19
  export declare const UTILITY_COMMANDS: readonly ["learn", "next", "ideate", "view", "status", "tree", "diff", "ops", "feature", "tdd-log", "retro", "compound", "archive", "rewind"];
20
20
  export declare const SUBAGENT_SKILL_FOLDERS: readonly ["subagent-dev", "parallel-dispatch"];
package/dist/constants.js CHANGED
@@ -91,12 +91,15 @@ export const REQUIRED_GITIGNORE_PATTERNS = [
91
91
  ".cursor/commands/cc.md",
92
92
  ".opencode/commands/cc-*.md",
93
93
  ".opencode/commands/cc.md",
94
- // Codex uses skill-kind shims under `.agents/skills/cclaw-cc*/` since
95
- // v0.39.0; legacy `.codex/commands/*` is auto-cleaned on sync.
96
- ".agents/skills/cclaw-cc/SKILL.md",
97
- ".agents/skills/cclaw-cc-*/SKILL.md",
94
+ // Codex uses skill-kind shims under `.agents/skills/cc*/` since
95
+ // v0.40.0 (renamed from the `cclaw-cc*` layout in v0.39.0/v0.39.1).
96
+ // `cclaw sync` and `cclaw uninstall` both auto-remove the legacy
97
+ // `cclaw-cc*` directories.
98
+ ".agents/skills/cc/SKILL.md",
99
+ ".agents/skills/cc-*/SKILL.md",
98
100
  ".claude/hooks/hooks.json",
99
101
  ".cursor/hooks.json",
102
+ ".codex/hooks.json",
100
103
  ".opencode/plugins/cclaw-plugin.mjs",
101
104
  ".cursor/rules/cclaw-workflow.mdc"
102
105
  ];
@@ -39,8 +39,10 @@ the user can approve individual lifts, accept-all, or skip.
39
39
  "Drift check" section in the skill): confirm the lift target file is
40
40
  current, spot-check the repo for contradictions, demote stale clusters
41
41
  into a new superseding entry instead of a lift.
42
- 6. Otherwise, present **one** structured ask (AskUserQuestion / AskQuestion /
43
- plain text) summarising all candidates at once:
42
+ 6. Otherwise, present **one** structured ask via the harness's native ask
43
+ tool (\`AskUserQuestion\` / \`AskQuestion\` / \`question\` /
44
+ \`request_user_input\`; plain-text lettered list as fallback) summarising
45
+ all candidates at once:
44
46
  - \`apply-all\` (default) — apply every listed lift,
45
47
  - \`apply-selected\` — prompt per-candidate,
46
48
  - \`skip\` — record a skip reason and advance without changes.