@paths.design/caws-cli 11.0.0 → 11.1.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.
Files changed (100) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/init/harness-detect.d.ts +18 -0
  4. package/dist/init/harness-detect.d.ts.map +1 -0
  5. package/dist/init/harness-detect.js +90 -0
  6. package/dist/init/harness-detect.js.map +1 -0
  7. package/dist/init/hook-install.d.ts +53 -0
  8. package/dist/init/hook-install.d.ts.map +1 -0
  9. package/dist/init/hook-install.js +421 -0
  10. package/dist/init/hook-install.js.map +1 -0
  11. package/dist/init/hook-packs/manifest-claude-code.d.ts +4 -0
  12. package/dist/init/hook-packs/manifest-claude-code.d.ts.map +1 -0
  13. package/dist/init/hook-packs/manifest-claude-code.js +190 -0
  14. package/dist/init/hook-packs/manifest-claude-code.js.map +1 -0
  15. package/dist/init/hook-packs/register.d.ts +19 -0
  16. package/dist/init/hook-packs/register.d.ts.map +1 -0
  17. package/dist/init/hook-packs/register.js +37 -0
  18. package/dist/init/hook-packs/register.js.map +1 -0
  19. package/dist/init/hook-packs/types.d.ts +123 -0
  20. package/dist/init/hook-packs/types.d.ts.map +1 -0
  21. package/dist/init/hook-packs/types.js +29 -0
  22. package/dist/init/hook-packs/types.js.map +1 -0
  23. package/dist/shell/commands/gates.d.ts.map +1 -1
  24. package/dist/shell/commands/gates.js +28 -1
  25. package/dist/shell/commands/gates.js.map +1 -1
  26. package/dist/shell/commands/init.d.ts +9 -0
  27. package/dist/shell/commands/init.d.ts.map +1 -1
  28. package/dist/shell/commands/init.js +131 -27
  29. package/dist/shell/commands/init.js.map +1 -1
  30. package/dist/shell/commands/specs.d.ts +41 -0
  31. package/dist/shell/commands/specs.d.ts.map +1 -0
  32. package/dist/shell/commands/specs.js +264 -0
  33. package/dist/shell/commands/specs.js.map +1 -0
  34. package/dist/shell/commands/worktree.d.ts +38 -0
  35. package/dist/shell/commands/worktree.d.ts.map +1 -0
  36. package/dist/shell/commands/worktree.js +286 -0
  37. package/dist/shell/commands/worktree.js.map +1 -0
  38. package/dist/shell/gates/disposition.d.ts.map +1 -1
  39. package/dist/shell/gates/disposition.js +33 -3
  40. package/dist/shell/gates/disposition.js.map +1 -1
  41. package/dist/shell/gates/local-evaluators/budget-limit.d.ts +24 -0
  42. package/dist/shell/gates/local-evaluators/budget-limit.d.ts.map +1 -0
  43. package/dist/shell/gates/local-evaluators/budget-limit.js +67 -0
  44. package/dist/shell/gates/local-evaluators/budget-limit.js.map +1 -0
  45. package/dist/shell/gates/local-evaluators/diff-helpers.d.ts +25 -0
  46. package/dist/shell/gates/local-evaluators/diff-helpers.d.ts.map +1 -0
  47. package/dist/shell/gates/local-evaluators/diff-helpers.js +74 -0
  48. package/dist/shell/gates/local-evaluators/diff-helpers.js.map +1 -0
  49. package/dist/shell/gates/local-evaluators/index.d.ts +28 -0
  50. package/dist/shell/gates/local-evaluators/index.d.ts.map +1 -0
  51. package/dist/shell/gates/local-evaluators/index.js +67 -0
  52. package/dist/shell/gates/local-evaluators/index.js.map +1 -0
  53. package/dist/shell/gates/local-evaluators/scope-boundary.d.ts +23 -0
  54. package/dist/shell/gates/local-evaluators/scope-boundary.d.ts.map +1 -0
  55. package/dist/shell/gates/local-evaluators/scope-boundary.js +67 -0
  56. package/dist/shell/gates/local-evaluators/scope-boundary.js.map +1 -0
  57. package/dist/shell/gates/local-evaluators/spec-completeness.d.ts +12 -0
  58. package/dist/shell/gates/local-evaluators/spec-completeness.d.ts.map +1 -0
  59. package/dist/shell/gates/local-evaluators/spec-completeness.js +73 -0
  60. package/dist/shell/gates/local-evaluators/spec-completeness.js.map +1 -0
  61. package/dist/shell/index.d.ts +4 -0
  62. package/dist/shell/index.d.ts.map +1 -1
  63. package/dist/shell/index.js +13 -1
  64. package/dist/shell/index.js.map +1 -1
  65. package/dist/shell/register.d.ts.map +1 -1
  66. package/dist/shell/register.js +192 -2
  67. package/dist/shell/register.js.map +1 -1
  68. package/dist/shell/render/init-hook-pack.d.ts +16 -0
  69. package/dist/shell/render/init-hook-pack.d.ts.map +1 -0
  70. package/dist/shell/render/init-hook-pack.js +206 -0
  71. package/dist/shell/render/init-hook-pack.js.map +1 -0
  72. package/dist/store/atomic-write.d.ts +20 -2
  73. package/dist/store/atomic-write.d.ts.map +1 -1
  74. package/dist/store/atomic-write.js +44 -2
  75. package/dist/store/atomic-write.js.map +1 -1
  76. package/dist/store/lifecycle-lock.d.ts +34 -0
  77. package/dist/store/lifecycle-lock.d.ts.map +1 -0
  78. package/dist/store/lifecycle-lock.js +168 -0
  79. package/dist/store/lifecycle-lock.js.map +1 -0
  80. package/dist/store/lifecycle-transaction.d.ts +79 -0
  81. package/dist/store/lifecycle-transaction.d.ts.map +1 -0
  82. package/dist/store/lifecycle-transaction.js +319 -0
  83. package/dist/store/lifecycle-transaction.js.map +1 -0
  84. package/dist/store/rules.d.ts +16 -0
  85. package/dist/store/rules.d.ts.map +1 -1
  86. package/dist/store/rules.js +17 -0
  87. package/dist/store/rules.js.map +1 -1
  88. package/dist/store/specs-writer.d.ts +61 -0
  89. package/dist/store/specs-writer.d.ts.map +1 -0
  90. package/dist/store/specs-writer.js +506 -0
  91. package/dist/store/specs-writer.js.map +1 -0
  92. package/dist/store/worktrees-writer.d.ts +77 -0
  93. package/dist/store/worktrees-writer.d.ts.map +1 -0
  94. package/dist/store/worktrees-writer.js +674 -0
  95. package/dist/store/worktrees-writer.js.map +1 -0
  96. package/dist/store/yaml-patch.d.ts +7 -0
  97. package/dist/store/yaml-patch.d.ts.map +1 -0
  98. package/dist/store/yaml-patch.js +250 -0
  99. package/dist/store/yaml-patch.js.map +1 -0
  100. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-hook-pack.d.ts","sourceRoot":"","sources":["../../../src/shell/render/init-hook-pack.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAWzE,iCAAiC;AACjC,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,qBAAqB,GAC5B,MAAM,CAkGR;AAED,yDAAyD;AACzD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,oBAAoB,GAC3B,MAAM,CA0ER;AAED;;;;;;;gDAOgD;AAChD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,qBAAqB,EAC7B,YAAY,CAAC,EAAE,oBAAoB,GAClC,MAAM,CAyFR"}
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ // Pure formatter for hook-pack install output and settings.json wiring
3
+ // guidance. Each step of `caws init` emits a labeled section so an
4
+ // agent reading the output turn-by-turn can pick up what happened and
5
+ // what to do next.
6
+ //
7
+ // This renderer never decides outcomes; install/inspect decide.
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.renderHookPackInstall = renderHookPackInstall;
10
+ exports.renderSettingsWiring = renderSettingsWiring;
11
+ exports.renderActivationContract = renderActivationContract;
12
+ const hook_install_1 = require("../../init/hook-install");
13
+ function repeatChar(ch, n) {
14
+ return ch.repeat(Math.max(0, n));
15
+ }
16
+ function section(title) {
17
+ const bar = repeatChar('─', 64);
18
+ return `\n┌${bar}\n│ ${title}\n└${bar}`;
19
+ }
20
+ /** Render the install result. */
21
+ function renderHookPackInstall(result) {
22
+ const lines = [];
23
+ if (!result.pack) {
24
+ if (result.outcome === 'skipped_explicit_none') {
25
+ lines.push(section('Step: hook-pack install'));
26
+ lines.push(' Skipped — --agent-surface none.');
27
+ lines.push(' No pre-tool-call governance was installed.');
28
+ lines.push(' This repo is NOT agent-safe for multi-session work without external governance.');
29
+ lines.push(' If you intended to enable a hook pack, rerun with --agent-surface claude-code.');
30
+ return lines.join('\n');
31
+ }
32
+ if (result.outcome === 'skipped_ambiguous') {
33
+ lines.push(section('Step: hook-pack install'));
34
+ lines.push(' Skipped — no harness detected and no --agent-surface flag passed.');
35
+ lines.push(' No pre-tool-call governance was installed.');
36
+ lines.push(' To enable a hook pack now, rerun with one of:');
37
+ lines.push(' caws init --agent-surface claude-code');
38
+ lines.push(' caws init --agent-surface none # explicit opt-out');
39
+ return lines.join('\n');
40
+ }
41
+ }
42
+ const pack = result.pack;
43
+ lines.push(section(`Step: hook-pack install (${pack.id} v${pack.packVersion})`));
44
+ lines.push(` ${pack.summary}`);
45
+ lines.push('');
46
+ // Per-file action lines.
47
+ const created = [];
48
+ const updated = [];
49
+ const unchanged = [];
50
+ const refused = [];
51
+ for (const a of result.actions) {
52
+ switch (a.action) {
53
+ case 'created':
54
+ created.push(a.destPath);
55
+ break;
56
+ case 'updated':
57
+ updated.push(a.destPath);
58
+ break;
59
+ case 'unchanged':
60
+ unchanged.push(a.destPath);
61
+ break;
62
+ case 'refused':
63
+ refused.push(`${a.destPath} [refused: ${a.refusalReason ?? 'unknown'}]`);
64
+ break;
65
+ }
66
+ }
67
+ if (created.length > 0) {
68
+ lines.push(` Created (${created.length}):`);
69
+ for (const p of created)
70
+ lines.push(` + ${p}`);
71
+ }
72
+ if (updated.length > 0) {
73
+ lines.push(` Updated (${updated.length}):`);
74
+ for (const p of updated)
75
+ lines.push(` ↑ ${p}`);
76
+ }
77
+ if (unchanged.length > 0) {
78
+ lines.push(` Unchanged (${unchanged.length}):`);
79
+ for (const p of unchanged)
80
+ lines.push(` = ${p}`);
81
+ }
82
+ if (refused.length > 0) {
83
+ lines.push(` Refused (${refused.length}):`);
84
+ for (const p of refused)
85
+ lines.push(` ! ${p}`);
86
+ lines.push('');
87
+ lines.push(' One or more files were refused. To resolve, choose one of:');
88
+ lines.push(' --overwrite Replace the file with the canonical pack version.');
89
+ lines.push(' CAUTION: local edits to that file will be lost.');
90
+ lines.push(' --adopt Leave the file in place; do not enforce that it');
91
+ lines.push(' matches the pack. CAUTION: pack drift is no longer');
92
+ lines.push(' tracked for this file until the marker is restored.');
93
+ lines.push(' Alternative: rename or remove the conflicting file, then re-run init.');
94
+ }
95
+ return lines.join('\n');
96
+ }
97
+ /** Render the settings.json wiring inspection result. */
98
+ function renderSettingsWiring(status) {
99
+ const lines = [];
100
+ lines.push(section('Step: .claude/settings.json wiring'));
101
+ if (status.kind === 'wired') {
102
+ lines.push(' OK — .claude/settings.json already wires all four CAWS dispatch entrypoints.');
103
+ lines.push(' No action needed.');
104
+ return lines.join('\n');
105
+ }
106
+ if (status.kind === 'invalid') {
107
+ lines.push(` ERROR — .claude/settings.json exists but could not be parsed: ${status.error}`);
108
+ lines.push(' Repair the JSON syntax, then re-run `caws doctor` to verify.');
109
+ lines.push(' The CAWS init does NOT modify settings.json; you must fix this by hand.');
110
+ return lines.join('\n');
111
+ }
112
+ if (status.kind === 'absent') {
113
+ lines.push(' No .claude/settings.json present. Hooks are installed but will');
114
+ lines.push(' NOT fire until Claude Code reads a settings.json that wires them.');
115
+ lines.push('');
116
+ lines.push(' init does not write this file directly because merging with your');
117
+ lines.push(' existing permissions, env, and hook entries is your responsibility.');
118
+ lines.push('');
119
+ lines.push(' Create .claude/settings.json with the following content:');
120
+ lines.push('');
121
+ for (const line of hook_install_1.CANONICAL_SETTINGS_SNIPPET.split('\n')) {
122
+ lines.push(` ${line}`);
123
+ }
124
+ return lines.join('\n');
125
+ }
126
+ // partial
127
+ lines.push(' .claude/settings.json exists but is missing one or more canonical');
128
+ lines.push(' CAWS hook entries. Hooks may not fire as expected.');
129
+ lines.push('');
130
+ lines.push(` Missing entries (${status.missing.length}): ${status.missing.join(', ')}`);
131
+ lines.push('');
132
+ lines.push(' Add the following blocks to the `hooks` object in your settings.json:');
133
+ lines.push('');
134
+ for (const line of hook_install_1.CANONICAL_SETTINGS_SNIPPET.split('\n')) {
135
+ lines.push(` ${line}`);
136
+ }
137
+ lines.push('');
138
+ lines.push(' CAWS init does NOT modify settings.json automatically — it');
139
+ lines.push(' commonly carries user-authored `permissions` and `env` blocks that');
140
+ lines.push(' the pack should not overwrite. Merge the snippet by hand.');
141
+ return lines.join('\n');
142
+ }
143
+ /** Render the activation contract. Drives what the agent should do
144
+ * immediately after init. The message tailors to three signals:
145
+ * - did this run actually install or update files? (changed vs no-op)
146
+ * - is settings.json wired? (only known when caller passes wiringStatus)
147
+ * - what is the harness's activation model? (from pack)
148
+ *
149
+ * Without these signals the panel becomes a constant STOP sign on every
150
+ * re-run, which trains agents to ignore it. */
151
+ function renderActivationContract(result, wiringStatus) {
152
+ const lines = [];
153
+ lines.push(section('Step: activation'));
154
+ if (!result.pack || result.outcome === 'skipped_explicit_none') {
155
+ lines.push(' No hook pack was installed. Pre-tool-call governance is NOT in effect.');
156
+ return lines.join('\n');
157
+ }
158
+ if (result.outcome === 'skipped_ambiguous') {
159
+ lines.push(' No hook pack was selected. Pre-tool-call governance is NOT in effect.');
160
+ return lines.join('\n');
161
+ }
162
+ const changed = result.outcome === 'installed' || result.outcome === 'updated';
163
+ const wired = wiringStatus?.kind === 'wired';
164
+ switch (result.activation) {
165
+ case 'immediate':
166
+ lines.push(' Hooks are active in the current session. No restart required.');
167
+ break;
168
+ case 'restart_required':
169
+ if (!changed && wired) {
170
+ // No-op re-run on a fully-wired install. Positive confirmation.
171
+ lines.push(' Hooks are installed and wired. They are active in any Claude Code');
172
+ lines.push(' session started AFTER the install. If you are running in a session');
173
+ lines.push(' that pre-dates the install, restart the session to load them.');
174
+ }
175
+ else if (changed && wired) {
176
+ // Files updated; wiring is already in place but the new versions
177
+ // need a session restart to load.
178
+ lines.push(' Hook files were installed or updated. Settings.json is wired.');
179
+ lines.push(' Restart the Claude Code session so the updated hooks load.');
180
+ }
181
+ else if (changed && !wired) {
182
+ // The original STOP-sign case. Files just landed AND wiring is
183
+ // missing or partial. Agent must not continue.
184
+ lines.push(' RESTART REQUIRED: Claude Code reads .claude/settings.json at session');
185
+ lines.push(' start. The hooks you just installed are NOT enforcing in this session.');
186
+ lines.push('');
187
+ lines.push(' STOP. Do not continue substantive work. Wire .claude/settings.json');
188
+ lines.push(' (see the previous panel) and ask the user to restart or reopen');
189
+ lines.push(' the Claude Code session so the hooks become active.');
190
+ }
191
+ else {
192
+ // Idempotent re-run with wiring still missing or invalid.
193
+ lines.push(' Hook files are installed but settings.json wiring is not complete.');
194
+ lines.push(' Hooks will not fire until the wiring is finished (see the previous');
195
+ lines.push(' panel) and the Claude Code session is restarted.');
196
+ }
197
+ break;
198
+ case 'unknown':
199
+ case 'not_applicable':
200
+ lines.push(' Activation semantics for this harness are not known. Consult the');
201
+ lines.push(' harness documentation for whether hooks take effect mid-session.');
202
+ break;
203
+ }
204
+ return lines.join('\n');
205
+ }
206
+ //# sourceMappingURL=init-hook-pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-hook-pack.js","sourceRoot":"","sources":["../../../src/shell/render/init-hook-pack.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,29 @@
1
1
  import { type Result } from '@paths.design/caws-kernel';
2
+ export interface WriteFileAtomicOptions {
3
+ /**
4
+ * When true and `targetPath` exists, the temp file is created with the
5
+ * target's existing mode, and the final on-disk file has the same mode
6
+ * after rename. Used by the lifecycle substrate to preserve the
7
+ * executable bit on managed hook scripts.
8
+ *
9
+ * When false (default), the temp file is created with the standard
10
+ * `'w'` mode (0o666 masked by umask). This preserves the historical
11
+ * behavior of writeFileAtomic so existing callers are not silently
12
+ * affected.
13
+ *
14
+ * When `targetPath` does NOT exist, this option has no effect; the
15
+ * file is created with the default mode regardless.
16
+ */
17
+ readonly preserveMode?: boolean;
18
+ }
2
19
  /**
3
20
  * Write `contents` to `targetPath` atomically.
4
21
  *
5
22
  * On Err, the temp file is cleaned up. On Ok, the target file holds the
6
- * new bytes.
23
+ * new bytes. When `options.preserveMode` is true and the target exists,
24
+ * the new file has the same mode bits as the prior file.
7
25
  */
8
- export declare function writeFileAtomic(targetPath: string, contents: string | Buffer): Result<true>;
26
+ export declare function writeFileAtomic(targetPath: string, contents: string | Buffer, options?: WriteFileAtomicOptions): Result<true>;
9
27
  /**
10
28
  * Best-effort parent-directory fsync. Returns true on success, false on
11
29
  * failure (some filesystems / platforms don't support it). Callers that
@@ -1 +1 @@
1
- {"version":3,"file":"atomic-write.d.ts","sourceRoot":"","sources":["../../src/store/atomic-write.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAajE;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAgC3F;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAiBjD"}
1
+ {"version":3,"file":"atomic-write.d.ts","sourceRoot":"","sources":["../../src/store/atomic-write.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAajE,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GAAG,MAAM,EACzB,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAAC,IAAI,CAAC,CA4Ed;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAiBjD"}
@@ -66,18 +66,60 @@ function nextTempName(targetPath) {
66
66
  * Write `contents` to `targetPath` atomically.
67
67
  *
68
68
  * On Err, the temp file is cleaned up. On Ok, the target file holds the
69
- * new bytes.
69
+ * new bytes. When `options.preserveMode` is true and the target exists,
70
+ * the new file has the same mode bits as the prior file.
70
71
  */
71
- function writeFileAtomic(targetPath, contents) {
72
+ function writeFileAtomic(targetPath, contents, options = {}) {
72
73
  const tmpPath = nextTempName(targetPath);
73
74
  let fd;
75
+ // When preserveMode is requested, stat the target BEFORE the temp
76
+ // write so we can match the mode at creation time (reducing the
77
+ // window where the file has a wrong mode) and verify/restore after
78
+ // rename (so chmod failures on the temp file don't silently drop
79
+ // the exec bit).
80
+ let preservedMode;
81
+ if (options.preserveMode === true) {
82
+ try {
83
+ preservedMode = fs.statSync(targetPath).mode & 0o7777;
84
+ }
85
+ catch {
86
+ // Target doesn't exist; preserveMode has no effect.
87
+ }
88
+ }
74
89
  try {
75
90
  fd = fs.openSync(tmpPath, 'w');
76
91
  fs.writeFileSync(fd, contents);
77
92
  fs.fsyncSync(fd);
78
93
  fs.closeSync(fd);
79
94
  fd = undefined;
95
+ // Apply preserved mode to the temp file before rename so the
96
+ // window between rename and chmod is minimal.
97
+ if (preservedMode !== undefined) {
98
+ try {
99
+ fs.chmodSync(tmpPath, preservedMode);
100
+ }
101
+ catch {
102
+ // chmod on temp may fail on some filesystems (e.g., NFS);
103
+ // post-rename chmod below is the safety net.
104
+ }
105
+ }
80
106
  fs.renameSync(tmpPath, targetPath);
107
+ // Post-rename verification: if preserveMode was requested, ensure
108
+ // the final file has the right bits. This is the safety net for
109
+ // pre-rename chmod failure, and a no-op when the pre-rename chmod
110
+ // succeeded.
111
+ if (preservedMode !== undefined) {
112
+ try {
113
+ const actualMode = fs.statSync(targetPath).mode & 0o7777;
114
+ if (actualMode !== preservedMode) {
115
+ fs.chmodSync(targetPath, preservedMode);
116
+ }
117
+ }
118
+ catch {
119
+ // Best-effort: if the verify or chmod itself fails, the write
120
+ // already succeeded; the caller may need to chmod manually.
121
+ }
122
+ }
81
123
  return (0, caws_kernel_1.ok)(true);
82
124
  }
83
125
  catch (e) {
@@ -1 +1 @@
1
- {"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../src/store/atomic-write.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../src/store/atomic-write.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { type Result } from '@paths.design/caws-kernel';
2
+ export interface LifecycleLockHandle {
3
+ readonly fd: number;
4
+ readonly lockPath: string;
5
+ }
6
+ export interface AcquireLifecycleLockOptions {
7
+ /** Override the global lock path. Used by tests; production callers
8
+ * pass cawsDir and get the canonical `<cawsDir>/state.lock`. */
9
+ readonly lockPath?: string;
10
+ /** Override stale-lock threshold. Defaults to 30s. Tests can lower it
11
+ * to validate recovery without sleeping. */
12
+ readonly staleThresholdMs?: number;
13
+ /** Override max attempts. Defaults to 3. */
14
+ readonly maxAttempts?: number;
15
+ /** Override retry delay. Defaults to 50ms. */
16
+ readonly retryDelayMs?: number;
17
+ }
18
+ /**
19
+ * Acquire the global lifecycle mutation lock. Returns a `LifecycleLockHandle`
20
+ * the caller MUST release via `releaseLifecycleLock` in a `finally` block.
21
+ *
22
+ * The lock is held by the existence of `<cawsDir>/state.lock`. Concurrent
23
+ * callers retry up to `maxAttempts` with `retryDelayMs` between attempts.
24
+ * Stale locks (older than `staleThresholdMs`) are reclaimed.
25
+ */
26
+ export declare function acquireLifecycleLock(cawsDir: string, options?: AcquireLifecycleLockOptions): Result<LifecycleLockHandle>;
27
+ /** Release the lifecycle lock. Always safe to call; errors are swallowed
28
+ * because lock-release is best-effort (the lockfile is the contract, not
29
+ * the fd state). */
30
+ export declare function releaseLifecycleLock(handle: LifecycleLockHandle): void;
31
+ /** Convenience wrapper: acquire, run the body, release in a `finally`.
32
+ * Returns the body's `Result` or the acquire-failure `Result`. */
33
+ export declare function withLifecycleLock<T>(cawsDir: string, body: () => Result<T>, options?: AcquireLifecycleLockOptions): Result<T>;
34
+ //# sourceMappingURL=lifecycle-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-lock.d.ts","sourceRoot":"","sources":["../../src/store/lifecycle-lock.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAUjE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,2BAA2B;IAC1C;qEACiE;IACjE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;iDAC6C;IAC7C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,4CAA4C;IAC5C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AA6BD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,2BAAgC,GACxC,MAAM,CAAC,mBAAmB,CAAC,CAmD7B;AAED;;qBAEqB;AACrB,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAWtE;AAED;mEACmE;AACnE,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,MAAM,CAAC,CAAC,CAAC,EACrB,OAAO,GAAE,2BAAgC,GACxC,MAAM,CAAC,CAAC,CAAC,CAUX"}
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ // Lifecycle mutation lock.
3
+ //
4
+ // LIFECYCLE-MUTATION-001 invariant: multi-file lifecycle mutations
5
+ // serialize through a single global `.caws/state.lock`. This lock is
6
+ // distinct from `.caws/events.jsonl.lock` (held by appendEvent only)
7
+ // because lifecycle mutations cross file boundaries — spec YAML +
8
+ // registry + event append — and need a wider critical section than
9
+ // the per-file event lock provides.
10
+ //
11
+ // Per-resource locks (per-spec, per-worktree) are deferred. The plan
12
+ // is: get the lifecycle mutation graph stable on a single conservative
13
+ // lock first; introduce finer-grained locking only when the mutation
14
+ // surface is well understood. Premature optimization here recreates
15
+ // exactly the cross-file split-brain hazard the substrate exists to
16
+ // prevent.
17
+ //
18
+ // Implementation parity with events-store.ts:
19
+ // - existence-based lockfile (`openSync(path, 'wx')`)
20
+ // - bounded retry with stale-lock recovery
21
+ // - same constants: 30s stale threshold, 3 attempts, 50ms retry
22
+ // - release in `finally` of caller's wrapper (transaction layer)
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || (function () {
40
+ var ownKeys = function(o) {
41
+ ownKeys = Object.getOwnPropertyNames || function (o) {
42
+ var ar = [];
43
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
44
+ return ar;
45
+ };
46
+ return ownKeys(o);
47
+ };
48
+ return function (mod) {
49
+ if (mod && mod.__esModule) return mod;
50
+ var result = {};
51
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
52
+ __setModuleDefault(result, mod);
53
+ return result;
54
+ };
55
+ })();
56
+ Object.defineProperty(exports, "__esModule", { value: true });
57
+ exports.acquireLifecycleLock = acquireLifecycleLock;
58
+ exports.releaseLifecycleLock = releaseLifecycleLock;
59
+ exports.withLifecycleLock = withLifecycleLock;
60
+ const fs = __importStar(require("fs"));
61
+ const path = __importStar(require("path"));
62
+ const caws_kernel_1 = require("@paths.design/caws-kernel");
63
+ const repo_root_1 = require("./repo-root");
64
+ const rules_1 = require("./rules");
65
+ const LOCK_FILE_NAME = 'state.lock';
66
+ const LOCK_MAX_ATTEMPTS = 3;
67
+ const LOCK_RETRY_DELAY_MS = 50;
68
+ const LOCK_STALE_MS = 30_000;
69
+ function sleepSyncMs(ms) {
70
+ // Busy-wait short sleep. Matches events-store.ts pattern.
71
+ const end = Date.now() + ms;
72
+ while (Date.now() < end) {
73
+ // intentional spin
74
+ }
75
+ }
76
+ function tryRecoverStaleLock(lockPath, staleThresholdMs) {
77
+ try {
78
+ const stat = fs.statSync(lockPath);
79
+ const ageMs = Date.now() - stat.mtimeMs;
80
+ if (ageMs > staleThresholdMs) {
81
+ fs.unlinkSync(lockPath);
82
+ return true;
83
+ }
84
+ }
85
+ catch {
86
+ // Race: lock was released between EEXIST and statSync. Treat as
87
+ // recoverable so the next attempt can take it.
88
+ return true;
89
+ }
90
+ return false;
91
+ }
92
+ /**
93
+ * Acquire the global lifecycle mutation lock. Returns a `LifecycleLockHandle`
94
+ * the caller MUST release via `releaseLifecycleLock` in a `finally` block.
95
+ *
96
+ * The lock is held by the existence of `<cawsDir>/state.lock`. Concurrent
97
+ * callers retry up to `maxAttempts` with `retryDelayMs` between attempts.
98
+ * Stale locks (older than `staleThresholdMs`) are reclaimed.
99
+ */
100
+ function acquireLifecycleLock(cawsDir, options = {}) {
101
+ const lockPath = options.lockPath ?? path.join(cawsDir, LOCK_FILE_NAME);
102
+ const staleThresholdMs = options.staleThresholdMs ?? LOCK_STALE_MS;
103
+ const maxAttempts = options.maxAttempts ?? LOCK_MAX_ATTEMPTS;
104
+ const retryDelayMs = options.retryDelayMs ?? LOCK_RETRY_DELAY_MS;
105
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
106
+ try {
107
+ const fd = fs.openSync(lockPath, 'wx');
108
+ // Best-effort metadata so a future doctor or operator can see who
109
+ // is holding the lock.
110
+ try {
111
+ fs.writeFileSync(fd, JSON.stringify({
112
+ pid: process.pid,
113
+ at: new Date().toISOString(),
114
+ purpose: 'lifecycle-mutation',
115
+ }));
116
+ fs.fsyncSync(fd);
117
+ }
118
+ catch {
119
+ // Lock content is best-effort; the lock itself is the file's
120
+ // existence.
121
+ }
122
+ return (0, caws_kernel_1.ok)({ fd, lockPath });
123
+ }
124
+ catch (e) {
125
+ const cause = e;
126
+ if (cause.code !== 'EEXIST') {
127
+ return (0, caws_kernel_1.err)((0, repo_root_1.storeDiagnostic)(rules_1.STORE_RULES.LIFECYCLE_LOCK_CONTENTION, `Unexpected error acquiring lifecycle lock: ${cause.message ?? 'unknown error'}.`, { subject: lockPath, data: { code: cause.code ?? null } }));
128
+ }
129
+ const recovered = tryRecoverStaleLock(lockPath, staleThresholdMs);
130
+ if (!recovered) {
131
+ sleepSyncMs(retryDelayMs);
132
+ }
133
+ }
134
+ }
135
+ return (0, caws_kernel_1.err)((0, repo_root_1.storeDiagnostic)(rules_1.STORE_RULES.LIFECYCLE_LOCK_CONTENTION, `Could not acquire lifecycle lock after ${maxAttempts} attempts.`, { subject: lockPath }));
136
+ }
137
+ /** Release the lifecycle lock. Always safe to call; errors are swallowed
138
+ * because lock-release is best-effort (the lockfile is the contract, not
139
+ * the fd state). */
140
+ function releaseLifecycleLock(handle) {
141
+ try {
142
+ fs.closeSync(handle.fd);
143
+ }
144
+ catch {
145
+ /* ignore */
146
+ }
147
+ try {
148
+ fs.unlinkSync(handle.lockPath);
149
+ }
150
+ catch {
151
+ /* ignore */
152
+ }
153
+ }
154
+ /** Convenience wrapper: acquire, run the body, release in a `finally`.
155
+ * Returns the body's `Result` or the acquire-failure `Result`. */
156
+ function withLifecycleLock(cawsDir, body, options = {}) {
157
+ const acquired = acquireLifecycleLock(cawsDir, options);
158
+ if (!acquired.ok) {
159
+ return acquired;
160
+ }
161
+ try {
162
+ return body();
163
+ }
164
+ finally {
165
+ releaseLifecycleLock(acquired.value);
166
+ }
167
+ }
168
+ //# sourceMappingURL=lifecycle-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-lock.js","sourceRoot":"","sources":["../../src/store/lifecycle-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import { type ChainedEvent, type EventBody, type Result, type Diagnostic } from '@paths.design/caws-kernel';
2
+ /** A single file write the transaction will perform. Order in
3
+ * `plannedWrites` is the order writes are applied. */
4
+ export interface LifecycleFileWrite {
5
+ /** Absolute path of the file to write. */
6
+ readonly path: string;
7
+ /** New contents for this file. */
8
+ readonly contents: string | Buffer;
9
+ /** When true, writeFileAtomic preserves the target's existing mode. */
10
+ readonly preserveMode?: boolean;
11
+ }
12
+ /** Inputs to a lifecycle transaction. */
13
+ export interface LifecycleTransactionPlan {
14
+ /** Path to the .caws/ directory (passed through to appendEvent). */
15
+ readonly cawsDir: string;
16
+ /** Files to write in deterministic order. */
17
+ readonly plannedWrites: readonly LifecycleFileWrite[];
18
+ /** Event(s) to append after state writes succeed. Appended in array
19
+ * order. The transaction supports multiple events per command (e.g.,
20
+ * worktree create emits worktree_created then worktree_bound). */
21
+ readonly events: readonly EventBody[];
22
+ /** Optional pre-write validation. Runs AFTER the lock is held and
23
+ * files are read, BEFORE any write. Returns Err to abort the
24
+ * transaction with LIFECYCLE_PLAN_REJECTED. */
25
+ readonly validate?: () => Result<void>;
26
+ /** When true, call fsyncDir on each affected directory after writes.
27
+ * Defaults to false; see module header for rationale. */
28
+ readonly fsyncAfter?: boolean;
29
+ }
30
+ /** Success outcome: all state writes + all event appends succeeded. */
31
+ export interface LifecycleTransactionSuccess {
32
+ readonly kind: 'success';
33
+ readonly writes: readonly {
34
+ readonly path: string;
35
+ }[];
36
+ readonly appendedEvents: readonly ChainedEvent[];
37
+ }
38
+ /** Partial-failure-recovered: state writes succeeded, event append
39
+ * failed, rollback succeeded. The repository is in its pre-transaction
40
+ * state. */
41
+ export interface LifecyclePartialRecovered {
42
+ readonly kind: 'partial_failure_recovered';
43
+ readonly cause: readonly Diagnostic[];
44
+ readonly rolledBack: readonly string[];
45
+ }
46
+ /** Partial-failure-unrecovered: state writes succeeded, event append
47
+ * failed, AND rollback also failed. The repository may be in a
48
+ * partial state. The caller MUST handle the recovery instruction. */
49
+ export interface LifecyclePartialUnrecovered {
50
+ readonly kind: 'partial_failure_unrecovered';
51
+ readonly cause: readonly Diagnostic[];
52
+ readonly plannedEvents: readonly EventBody[];
53
+ readonly writesCompleted: readonly string[];
54
+ readonly rolledBack: readonly string[];
55
+ readonly rollbackFailed: readonly {
56
+ readonly path: string;
57
+ readonly reason: string;
58
+ }[];
59
+ readonly recoveryInstruction: string;
60
+ }
61
+ export type LifecycleTransactionResult = LifecycleTransactionSuccess | LifecyclePartialRecovered | LifecyclePartialUnrecovered;
62
+ /**
63
+ * Run a lifecycle transaction.
64
+ *
65
+ * The caller MUST hold the lifecycle lock (via withLifecycleLock or
66
+ * acquireLifecycleLock). This function does not acquire the lock
67
+ * itself; that responsibility lives in the caller so a higher-level
68
+ * orchestrator (CLI-SPECS-001, CLI-WORKTREE-001) can group multiple
69
+ * conceptual mutations inside one lock if needed.
70
+ *
71
+ * Returns:
72
+ * Ok({ kind: 'success', ... }) — everything worked
73
+ * Ok({ kind: 'partial_failure_recovered', ... }) — rolled back cleanly
74
+ * Err([LIFECYCLE_PARTIAL_FAILURE_UNRECOVERED]) — partial state remains
75
+ * Err([LIFECYCLE_PLAN_REJECTED]) — validate() rejected
76
+ * Err([LIFECYCLE_WRITE_FAILED]) — a write failed before events
77
+ */
78
+ export declare function runLifecycleTransaction(plan: LifecycleTransactionPlan): Result<LifecycleTransactionResult>;
79
+ //# sourceMappingURL=lifecycle-transaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-transaction.d.ts","sourceRoot":"","sources":["../../src/store/lifecycle-transaction.ts"],"names":[],"mappings":"AAuCA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,SAAS,EAGd,KAAK,MAAM,EACX,KAAK,UAAU,EAChB,MAAM,2BAA2B,CAAC;AASnC;uDACuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,uEAAuE;IACvE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,yCAAyC;AACzC,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,QAAQ,CAAC,aAAa,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACtD;;uEAEmE;IACnE,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC;;oDAEgD;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;IACvC;8DAC0D;IAC1D,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAWD,uEAAuE;AACvE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,QAAQ,CAAC,cAAc,EAAE,SAAS,YAAY,EAAE,CAAC;CAClD;AAED;;aAEa;AACb,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,2BAA2B,CAAC;IAC3C,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;;sEAEsE;AACtE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,6BAA6B,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7C,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,cAAc,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvF,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,MAAM,0BAA0B,GAClC,2BAA2B,GAC3B,yBAAyB,GACzB,2BAA2B,CAAC;AAkGhC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,wBAAwB,GAC7B,MAAM,CAAC,0BAA0B,CAAC,CA+JpC"}