@lumenflow/cli 3.18.1 → 3.19.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 (159) hide show
  1. package/dist/docs-sync.js +123 -6
  2. package/dist/docs-sync.js.map +1 -1
  3. package/dist/gate-co-change.js +23 -4
  4. package/dist/gate-co-change.js.map +1 -1
  5. package/dist/gates-runners.js +108 -12
  6. package/dist/gates-runners.js.map +1 -1
  7. package/dist/lumenflow-upgrade.js +1 -0
  8. package/dist/lumenflow-upgrade.js.map +1 -1
  9. package/dist/public-manifest.js +1 -1
  10. package/dist/public-manifest.js.map +1 -1
  11. package/dist/sync-templates.js +13 -0
  12. package/dist/sync-templates.js.map +1 -1
  13. package/dist/wu-block.js +10 -0
  14. package/dist/wu-block.js.map +1 -1
  15. package/dist/wu-claim-validation.js +3 -1
  16. package/dist/wu-claim-validation.js.map +1 -1
  17. package/dist/wu-claim.js +3 -1
  18. package/dist/wu-claim.js.map +1 -1
  19. package/dist/wu-done-memory-telemetry.js +5 -1
  20. package/dist/wu-done-memory-telemetry.js.map +1 -1
  21. package/dist/wu-done-ownership.js +6 -0
  22. package/dist/wu-done-ownership.js.map +1 -1
  23. package/dist/wu-edit-operations.js +4 -4
  24. package/dist/wu-edit-operations.js.map +1 -1
  25. package/dist/wu-prep.js +88 -13
  26. package/dist/wu-prep.js.map +1 -1
  27. package/dist/wu-recover.js +15 -0
  28. package/dist/wu-recover.js.map +1 -1
  29. package/dist/wu-release.js +10 -1
  30. package/dist/wu-release.js.map +1 -1
  31. package/dist/wu-spawn-prompt-builders.js +27 -2
  32. package/dist/wu-spawn-prompt-builders.js.map +1 -1
  33. package/dist/wu-state-mutation-ownership.js +136 -0
  34. package/dist/wu-state-mutation-ownership.js.map +1 -0
  35. package/dist/wu-unblock.js +10 -0
  36. package/dist/wu-unblock.js.map +1 -1
  37. package/package.json +111 -110
  38. package/packs/agent-runtime/.turbo/turbo-build.log +1 -1
  39. package/packs/agent-runtime/package.json +1 -1
  40. package/packs/sidekick/.turbo/turbo-build.log +1 -1
  41. package/packs/sidekick/package.json +1 -1
  42. package/packs/software-delivery/.turbo/turbo-build.log +1 -1
  43. package/packs/software-delivery/package.json +1 -1
  44. package/templates/core/AGENTS.md.template +157 -32
  45. package/templates/core/LUMENFLOW.md.template +44 -29
  46. package/templates/core/_frameworks/lumenflow/wu-sizing-guide.md.template +644 -0
  47. package/templates/core/ai/onboarding/agent-invocation-guide.md.template +5 -5
  48. package/templates/core/ai/onboarding/agent-safety-card.md.template +1 -0
  49. package/templates/core/ai/onboarding/docs-generation.md.template +94 -4
  50. package/templates/core/ai/onboarding/first-15-mins.md.template +1 -1
  51. package/templates/core/ai/onboarding/first-wu-mistakes.md.template +2 -1
  52. package/templates/core/ai/onboarding/initiative-orchestration.md.template +21 -21
  53. package/templates/core/ai/onboarding/quick-ref-commands.md.template +102 -95
  54. package/templates/core/ai/onboarding/release-process.md.template +12 -12
  55. package/templates/core/ai/onboarding/starting-prompt.md.template +31 -31
  56. package/templates/vendors/claude/.claude/skills/initiative-management/SKILL.md.template +2 -2
  57. package/templates/vendors/claude/.claude/skills/multi-agent-coordination/SKILL.md.template +2 -2
  58. package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +3 -3
  59. package/dist/chunk-2D2VOCA4.js +0 -37
  60. package/dist/chunk-2D5KFYGX.js +0 -284
  61. package/dist/chunk-2GXVIN57.js +0 -14072
  62. package/dist/chunk-2MQ7HZWZ.js +0 -26
  63. package/dist/chunk-2UFQ3A3C.js +0 -643
  64. package/dist/chunk-3RG5ZIWI.js +0 -10
  65. package/dist/chunk-4N74J3UT.js +0 -15
  66. package/dist/chunk-5GTOXFYR.js +0 -392
  67. package/dist/chunk-5VY6MQMC.js +0 -240
  68. package/dist/chunk-67XVPMRY.js +0 -1297
  69. package/dist/chunk-6HO4GWJE.js +0 -164
  70. package/dist/chunk-6W5XHWYV.js +0 -1890
  71. package/dist/chunk-6X4EMYJQ.js +0 -64
  72. package/dist/chunk-6XYXI2NQ.js +0 -772
  73. package/dist/chunk-7ANSOV6Q.js +0 -285
  74. package/dist/chunk-A624LFLB.js +0 -1380
  75. package/dist/chunk-ADN5NHG4.js +0 -126
  76. package/dist/chunk-B7YJYJKG.js +0 -33
  77. package/dist/chunk-CCLHCPKG.js +0 -210
  78. package/dist/chunk-CK36VROC.js +0 -1584
  79. package/dist/chunk-D3UOFRSB.js +0 -81
  80. package/dist/chunk-DFR4DJBM.js +0 -230
  81. package/dist/chunk-DSYBDHYH.js +0 -79
  82. package/dist/chunk-DWMLTXKQ.js +0 -1176
  83. package/dist/chunk-E3REJTAJ.js +0 -28
  84. package/dist/chunk-EA3IVO64.js +0 -633
  85. package/dist/chunk-EK2AKZKD.js +0 -55
  86. package/dist/chunk-ELD7JTTT.js +0 -343
  87. package/dist/chunk-EX6TT2XI.js +0 -195
  88. package/dist/chunk-EXINSFZE.js +0 -82
  89. package/dist/chunk-EZ6ZBYBM.js +0 -510
  90. package/dist/chunk-FBKAPTJ2.js +0 -16
  91. package/dist/chunk-FVLV5RYH.js +0 -1118
  92. package/dist/chunk-GDNSBQVK.js +0 -2485
  93. package/dist/chunk-GPQHMBNN.js +0 -278
  94. package/dist/chunk-GTFJB67L.js +0 -68
  95. package/dist/chunk-HANJXVKW.js +0 -1127
  96. package/dist/chunk-HEVS5YLD.js +0 -269
  97. package/dist/chunk-HMEVZKPQ.js +0 -9
  98. package/dist/chunk-HRGSYNLM.js +0 -3511
  99. package/dist/chunk-ISZR5N4K.js +0 -60
  100. package/dist/chunk-J6SUPR2C.js +0 -226
  101. package/dist/chunk-JERYVEIZ.js +0 -244
  102. package/dist/chunk-JHHWGL2N.js +0 -87
  103. package/dist/chunk-JONWQUB5.js +0 -775
  104. package/dist/chunk-K2DIWWDM.js +0 -1766
  105. package/dist/chunk-KY4PGL5V.js +0 -969
  106. package/dist/chunk-L737LQ4C.js +0 -1285
  107. package/dist/chunk-LFTWYIB2.js +0 -497
  108. package/dist/chunk-LV47RFNJ.js +0 -41
  109. package/dist/chunk-MKSAITI7.js +0 -15
  110. package/dist/chunk-MZ7RKIX4.js +0 -212
  111. package/dist/chunk-NAP6CFSO.js +0 -84
  112. package/dist/chunk-ND6MY37M.js +0 -16
  113. package/dist/chunk-NMG736UR.js +0 -683
  114. package/dist/chunk-NRAXROED.js +0 -32
  115. package/dist/chunk-NRIZR3A7.js +0 -690
  116. package/dist/chunk-NX43BG3M.js +0 -233
  117. package/dist/chunk-O645XLSI.js +0 -297
  118. package/dist/chunk-OMJD6A3S.js +0 -235
  119. package/dist/chunk-QB6SJD4T.js +0 -430
  120. package/dist/chunk-QFSTL4J3.js +0 -276
  121. package/dist/chunk-QLGDFMFX.js +0 -212
  122. package/dist/chunk-RIAAGL2E.js +0 -13
  123. package/dist/chunk-RWO5XMZ6.js +0 -86
  124. package/dist/chunk-RXRKBBSM.js +0 -149
  125. package/dist/chunk-RZOZMML6.js +0 -363
  126. package/dist/chunk-U7I7FS7T.js +0 -113
  127. package/dist/chunk-UI42RODY.js +0 -717
  128. package/dist/chunk-UTVMVSCO.js +0 -519
  129. package/dist/chunk-V6OJGLBA.js +0 -1746
  130. package/dist/chunk-W2JHVH7D.js +0 -152
  131. package/dist/chunk-WD3Y7VQN.js +0 -280
  132. package/dist/chunk-WOCTQ5MS.js +0 -303
  133. package/dist/chunk-WZR3ZUNN.js +0 -696
  134. package/dist/chunk-XGI665H7.js +0 -150
  135. package/dist/chunk-XKY65P2T.js +0 -304
  136. package/dist/chunk-Y4CQZY65.js +0 -57
  137. package/dist/chunk-YFEXKLVE.js +0 -194
  138. package/dist/chunk-YHO3HS5X.js +0 -287
  139. package/dist/chunk-YLS7AZSX.js +0 -738
  140. package/dist/chunk-ZE473AO6.js +0 -49
  141. package/dist/chunk-ZF747T3O.js +0 -644
  142. package/dist/chunk-ZHCZHZH3.js +0 -43
  143. package/dist/chunk-ZZNZX2XY.js +0 -87
  144. package/dist/constants-7QAP3VQ4.js +0 -23
  145. package/dist/dist-IY3UUMWK.js +0 -33
  146. package/dist/invariants-runner-W5RGHCSU.js +0 -27
  147. package/dist/lane-lock-6J36HD5O.js +0 -35
  148. package/dist/mem-checkpoint-core-EANG2GVN.js +0 -14
  149. package/dist/mem-signal-core-2LZ2WYHW.js +0 -19
  150. package/dist/memory-store-OLB5FO7K.js +0 -18
  151. package/dist/service-6BYCOCO5.js +0 -13
  152. package/dist/spawn-policy-resolver-NTSZYQ6R.js +0 -17
  153. package/dist/spawn-task-builder-R4E2BHSW.js +0 -22
  154. package/dist/wu-done-pr-WLFFFEPJ.js +0 -25
  155. package/dist/wu-done-validation-3J5E36FE.js +0 -30
  156. package/dist/wu-duplicate-id-detector-5S7JHELK.js +0 -232
  157. package/packs/sidekick/.turbo/turbo-test.log +0 -12
  158. package/packs/sidekick/.turbo/turbo-typecheck.log +0 -4
  159. package/packs/software-delivery/.turbo/turbo-typecheck.log +0 -4
@@ -1,392 +0,0 @@
1
- import {
2
- generateEnforceWorktreeScript,
3
- generateEnforcementHooks,
4
- generateRequireWuScript,
5
- generateWarnIncompleteScript
6
- } from "./chunk-EA3IVO64.js";
7
- import {
8
- loadTemplate
9
- } from "./chunk-HEVS5YLD.js";
10
- import {
11
- generateScriptsFromManifest
12
- } from "./chunk-KY4PGL5V.js";
13
- import {
14
- WU_OPTIONS,
15
- createWUParser,
16
- runCLI
17
- } from "./chunk-2GXVIN57.js";
18
- import {
19
- CLAUDE_HOOKS,
20
- DIRECTORIES
21
- } from "./chunk-DWMLTXKQ.js";
22
- import {
23
- LUMENFLOW_CLIENT_IDS,
24
- WORKSPACE_CONFIG_FILE_NAME,
25
- WORKSPACE_V2_KEYS
26
- } from "./chunk-V6OJGLBA.js";
27
-
28
- // src/commands/integrate.ts
29
- import * as fs from "fs";
30
- import * as path from "path";
31
- import * as yaml from "yaml";
32
- var SUPPORTED_CLIENTS = [
33
- LUMENFLOW_CLIENT_IDS.CLAUDE_CODE,
34
- LUMENFLOW_CLIENT_IDS.CURSOR,
35
- LUMENFLOW_CLIENT_IDS.CODEX_CLI
36
- ];
37
- var INTEGRATE_OPTIONS = {
38
- client: {
39
- name: "client",
40
- flags: "--client <type>",
41
- description: `Client type to integrate (${SUPPORTED_CLIENTS.join(", ")})`
42
- },
43
- sync: {
44
- name: "sync",
45
- flags: "--sync",
46
- description: "Re-scaffold vendor-agnostic pre-commit/CI delegators and sync package.json command aliases"
47
- },
48
- force: WU_OPTIONS.force
49
- };
50
- function parseIntegrateOptions() {
51
- const opts = createWUParser({
52
- name: "lumenflow-integrate",
53
- description: "Integrate LumenFlow enforcement with AI client tools",
54
- options: Object.values(INTEGRATE_OPTIONS)
55
- });
56
- return {
57
- client: opts.client ?? LUMENFLOW_CLIENT_IDS.CLAUDE_CODE,
58
- sync: opts.sync ?? false,
59
- force: opts.force ?? false
60
- };
61
- }
62
- var PRE_COMMIT_TEMPLATE_PATH = "core/.husky/pre-commit.template";
63
- var CI_TEMPLATE_PATH = "core/.github/workflows/lumenflow-ci.yml.template";
64
- var PROJECT_PRE_COMMIT_PATH = ".husky/pre-commit";
65
- var PROJECT_CI_PATH = ".github/workflows/lumenflow-ci.yml";
66
- function syncCoreEnforcementDelegators(projectDir) {
67
- const created = [];
68
- const preCommitContent = loadTemplate(PRE_COMMIT_TEMPLATE_PATH);
69
- const preCommitPath = path.join(projectDir, PROJECT_PRE_COMMIT_PATH);
70
- fs.mkdirSync(path.dirname(preCommitPath), { recursive: true });
71
- fs.writeFileSync(preCommitPath, preCommitContent, { mode: 493 });
72
- created.push(PROJECT_PRE_COMMIT_PATH);
73
- const ciContent = loadTemplate(CI_TEMPLATE_PATH);
74
- const ciPath = path.join(projectDir, PROJECT_CI_PATH);
75
- fs.mkdirSync(path.dirname(ciPath), { recursive: true });
76
- fs.writeFileSync(ciPath, ciContent, "utf-8");
77
- created.push(PROJECT_CI_PATH);
78
- return created;
79
- }
80
- function syncPackageJsonScripts(projectDir) {
81
- const packageJsonPath = path.join(projectDir, "package.json");
82
- if (!fs.existsSync(packageJsonPath)) {
83
- return { added: [], modified: false };
84
- }
85
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
86
- if (!packageJson.scripts || typeof packageJson.scripts !== "object") {
87
- packageJson.scripts = {};
88
- }
89
- const scripts = packageJson.scripts;
90
- const manifestScripts = generateScriptsFromManifest();
91
- const added = [];
92
- for (const [name, command] of Object.entries(manifestScripts)) {
93
- if (!(name in scripts)) {
94
- scripts[name] = command;
95
- added.push(name);
96
- }
97
- }
98
- const modified = added.length > 0;
99
- if (modified) {
100
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
101
- }
102
- return { added, modified };
103
- }
104
- function readClaudeSettings(projectDir) {
105
- const settingsPath = path.join(projectDir, DIRECTORIES.CLAUDE, "settings.json");
106
- if (!fs.existsSync(settingsPath)) {
107
- return {
108
- $schema: "https://json.schemastore.org/claude-code-settings.json",
109
- permissions: {
110
- allow: ["Bash", "Read", "Write", "Edit", "WebFetch", "WebSearch", "Skill"]
111
- }
112
- };
113
- }
114
- try {
115
- const content = fs.readFileSync(settingsPath, "utf-8");
116
- return JSON.parse(content);
117
- } catch {
118
- return {
119
- $schema: "https://json.schemastore.org/claude-code-settings.json"
120
- };
121
- }
122
- }
123
- function mergeHooksIntoSettings(existing, generated) {
124
- const result = { ...existing };
125
- if (!result.hooks) {
126
- result.hooks = {};
127
- }
128
- if (generated.preToolUse) {
129
- if (!result.hooks.PreToolUse) {
130
- result.hooks.PreToolUse = [];
131
- }
132
- for (const newHook of generated.preToolUse) {
133
- const existingIndex = result.hooks.PreToolUse.findIndex((h) => h.matcher === newHook.matcher);
134
- if (existingIndex >= 0) {
135
- const existing2 = result.hooks.PreToolUse[existingIndex];
136
- for (const hook of newHook.hooks) {
137
- const isDuplicate = existing2.hooks.some((h) => h.command === hook.command);
138
- if (!isDuplicate) {
139
- existing2.hooks.push(hook);
140
- }
141
- }
142
- } else {
143
- result.hooks.PreToolUse.push(newHook);
144
- }
145
- }
146
- }
147
- if (generated.stop) {
148
- if (!result.hooks.Stop) {
149
- result.hooks.Stop = [];
150
- }
151
- for (const newHook of generated.stop) {
152
- const existingIndex = result.hooks.Stop.findIndex((h) => h.matcher === newHook.matcher);
153
- if (existingIndex >= 0) {
154
- const existing2 = result.hooks.Stop[existingIndex];
155
- for (const hook of newHook.hooks) {
156
- const isDuplicate = existing2.hooks.some((h) => h.command === hook.command);
157
- if (!isDuplicate) {
158
- existing2.hooks.push(hook);
159
- }
160
- }
161
- } else {
162
- result.hooks.Stop.push(newHook);
163
- }
164
- }
165
- }
166
- return result;
167
- }
168
- async function integrateClaudeCode(projectDir, config) {
169
- const enforcement = config.enforcement;
170
- const created = [];
171
- if (!enforcement?.hooks) {
172
- console.log("[integrate] Enforcement hooks not enabled, skipping");
173
- return created;
174
- }
175
- const hooksDir = path.join(projectDir, DIRECTORIES.CLAUDE_HOOKS);
176
- if (!fs.existsSync(hooksDir)) {
177
- fs.mkdirSync(hooksDir, { recursive: true });
178
- console.log("[integrate] Created hooks directory");
179
- }
180
- const generatedHooks = generateEnforcementHooks({
181
- block_outside_worktree: enforcement.block_outside_worktree ?? false,
182
- require_wu_for_edits: enforcement.require_wu_for_edits ?? false,
183
- warn_on_stop_without_wu_done: enforcement.warn_on_stop_without_wu_done ?? false
184
- });
185
- if (enforcement.block_outside_worktree) {
186
- const scriptPath = path.join(hooksDir, CLAUDE_HOOKS.SCRIPTS.ENFORCE_WORKTREE);
187
- fs.writeFileSync(scriptPath, generateEnforceWorktreeScript({ projectRoot: projectDir }), {
188
- mode: 493
189
- });
190
- console.log(`[integrate] Generated ${CLAUDE_HOOKS.SCRIPTS.ENFORCE_WORKTREE}`);
191
- created.push(path.join(DIRECTORIES.CLAUDE_HOOKS, CLAUDE_HOOKS.SCRIPTS.ENFORCE_WORKTREE));
192
- }
193
- if (enforcement.require_wu_for_edits) {
194
- const scriptPath = path.join(hooksDir, CLAUDE_HOOKS.SCRIPTS.REQUIRE_WU);
195
- fs.writeFileSync(scriptPath, generateRequireWuScript(), { mode: 493 });
196
- console.log(`[integrate] Generated ${CLAUDE_HOOKS.SCRIPTS.REQUIRE_WU}`);
197
- created.push(path.join(DIRECTORIES.CLAUDE_HOOKS, CLAUDE_HOOKS.SCRIPTS.REQUIRE_WU));
198
- }
199
- if (enforcement.warn_on_stop_without_wu_done) {
200
- const scriptPath = path.join(hooksDir, CLAUDE_HOOKS.SCRIPTS.WARN_INCOMPLETE);
201
- fs.writeFileSync(scriptPath, generateWarnIncompleteScript(), { mode: 493 });
202
- console.log(`[integrate] Generated ${CLAUDE_HOOKS.SCRIPTS.WARN_INCOMPLETE}`);
203
- created.push(path.join(DIRECTORIES.CLAUDE_HOOKS, CLAUDE_HOOKS.SCRIPTS.WARN_INCOMPLETE));
204
- }
205
- const existingSettings = readClaudeSettings(projectDir);
206
- const updatedSettings = mergeHooksIntoSettings(existingSettings, generatedHooks);
207
- const settingsPath = path.join(projectDir, DIRECTORIES.CLAUDE, "settings.json");
208
- fs.writeFileSync(settingsPath, JSON.stringify(updatedSettings, null, 2) + "\n", "utf-8");
209
- console.log("[integrate] Updated settings.json");
210
- return created;
211
- }
212
- function readWorkspaceSoftwareDeliveryConfig(projectDir) {
213
- const configPath = path.join(projectDir, WORKSPACE_CONFIG_FILE_NAME);
214
- if (!fs.existsSync(configPath)) {
215
- return null;
216
- }
217
- try {
218
- const content = fs.readFileSync(configPath, "utf-8");
219
- const workspaceDoc = yaml.parse(content);
220
- if (!workspaceDoc || typeof workspaceDoc !== "object") {
221
- return null;
222
- }
223
- const workspace = workspaceDoc;
224
- const softwareDelivery = workspace[WORKSPACE_V2_KEYS.SOFTWARE_DELIVERY];
225
- if (!softwareDelivery || typeof softwareDelivery !== "object") {
226
- return null;
227
- }
228
- return softwareDelivery;
229
- } catch {
230
- return null;
231
- }
232
- }
233
- function readEnforcementConfig(projectDir) {
234
- const softwareDelivery = readWorkspaceSoftwareDeliveryConfig(projectDir);
235
- return softwareDelivery?.agents?.clients?.[LUMENFLOW_CLIENT_IDS.CLAUDE_CODE]?.enforcement ?? null;
236
- }
237
- var CURSOR_RECOVERY_RULES = `# LumenFlow Context Recovery
238
-
239
- ## On Session Start
240
-
241
- When starting a new session or resuming work, always check for pending recovery context:
242
-
243
- \`\`\`bash
244
- # Check for recovery files
245
- ls .lumenflow/state/recovery-pending-*.md 2>/dev/null
246
- \`\`\`
247
-
248
- If recovery files exist:
249
-
250
- 1. Read the recovery file contents \u2014 they contain your last checkpoint, acceptance criteria, and code paths
251
- 2. Run \`pnpm mem:recover --wu WU-XXX\` (replace WU-XXX with the WU ID from the filename) for the latest context
252
- 3. Continue working based on the recovery context
253
-
254
- ## Context Loss Prevention
255
-
256
- Before any long operation that might lose context:
257
-
258
- \`\`\`bash
259
- pnpm mem:checkpoint "description of current progress" --wu WU-XXX
260
- \`\`\`
261
-
262
- ## Recovery Command Reference
263
-
264
- | Command | Purpose |
265
- | ------------------------------------------- | ---------------------------------- |
266
- | \`pnpm mem:recover --wu WU-XXX\` | Generate recovery context for a WU |
267
- | \`pnpm wu:brief --id WU-XXX --client cursor\` | Generate full handoff prompt |
268
- | \`pnpm wu:status --id WU-XXX\` | Check WU status and location |
269
- | \`pnpm mem:checkpoint\` | Save progress checkpoint |
270
- `;
271
- var AGENTS_RECOVERY_SECTION = `
272
- ---
273
-
274
- ## Context Recovery (WU-2157)
275
-
276
- If you are resuming work or have lost context, check for recovery files:
277
-
278
- \`\`\`bash
279
- # Check for pending recovery
280
- ls .lumenflow/state/recovery-pending-*.md 2>/dev/null
281
-
282
- # Generate fresh recovery context
283
- pnpm mem:recover --wu WU-XXX
284
-
285
- # Or generate a full handoff prompt
286
- pnpm wu:brief --id WU-XXX --client codex-cli
287
- \`\`\`
288
-
289
- Recovery files contain your last checkpoint, acceptance criteria, code paths, and changed files.
290
- Always save checkpoints before long operations: \`pnpm mem:checkpoint "progress note" --wu WU-XXX\`
291
- `;
292
- function integrateCursor(projectDir) {
293
- const created = [];
294
- const rulesDir = path.join(projectDir, ".cursor", "rules");
295
- if (!fs.existsSync(rulesDir)) {
296
- fs.mkdirSync(rulesDir, { recursive: true });
297
- console.log("[integrate] Created .cursor/rules/ directory");
298
- }
299
- const recoveryRulesPath = path.join(rulesDir, "lumenflow-recovery.md");
300
- fs.writeFileSync(recoveryRulesPath, CURSOR_RECOVERY_RULES, "utf-8");
301
- console.log("[integrate] Generated .cursor/rules/lumenflow-recovery.md");
302
- created.push(".cursor/rules/lumenflow-recovery.md");
303
- return created;
304
- }
305
- function integrateCodexCli(projectDir) {
306
- const created = [];
307
- const agentsPath = path.join(projectDir, "AGENTS.md");
308
- if (!fs.existsSync(agentsPath)) {
309
- console.log("[integrate] AGENTS.md not found, skipping Codex integration");
310
- return created;
311
- }
312
- const content = fs.readFileSync(agentsPath, "utf-8");
313
- if (content.includes("## Context Recovery (WU-2157)")) {
314
- console.log("[integrate] AGENTS.md already contains recovery section, skipping");
315
- return created;
316
- }
317
- fs.writeFileSync(agentsPath, content + AGENTS_RECOVERY_SECTION, "utf-8");
318
- console.log("[integrate] Appended recovery section to AGENTS.md");
319
- created.push("AGENTS.md");
320
- return created;
321
- }
322
- async function main() {
323
- const opts = parseIntegrateOptions();
324
- const projectDir = process.cwd();
325
- if (opts.sync) {
326
- const synced = syncCoreEnforcementDelegators(projectDir);
327
- for (const file of synced) {
328
- console.log(`[integrate] Synced ${file}`);
329
- }
330
- const scriptSync = syncPackageJsonScripts(projectDir);
331
- if (scriptSync.modified) {
332
- console.log(
333
- `[integrate] Added ${scriptSync.added.length} command alias(es): ${scriptSync.added.join(", ")}`
334
- );
335
- } else {
336
- console.log("[integrate] package.json command aliases already up to date");
337
- }
338
- }
339
- switch (opts.client) {
340
- case LUMENFLOW_CLIENT_IDS.CLAUDE_CODE: {
341
- const enforcement = readEnforcementConfig(projectDir);
342
- if (!enforcement) {
343
- console.log(
344
- `[integrate] No enforcement config found in ${WORKSPACE_CONFIG_FILE_NAME} ${WORKSPACE_V2_KEYS.SOFTWARE_DELIVERY}.agents.clients.${LUMENFLOW_CLIENT_IDS.CLAUDE_CODE}`
345
- );
346
- console.log("[integrate] Add this to your workspace config to enable enforcement hooks:");
347
- console.log(`
348
- ${WORKSPACE_V2_KEYS.SOFTWARE_DELIVERY}:
349
- agents:
350
- clients:
351
- ${LUMENFLOW_CLIENT_IDS.CLAUDE_CODE}:
352
- enforcement:
353
- hooks: true
354
- block_outside_worktree: true
355
- require_wu_for_edits: true
356
- warn_on_stop_without_wu_done: true
357
- `);
358
- return;
359
- }
360
- await integrateClaudeCode(projectDir, { enforcement });
361
- console.log("[integrate] Claude Code integration complete");
362
- break;
363
- }
364
- case LUMENFLOW_CLIENT_IDS.CURSOR: {
365
- integrateCursor(projectDir);
366
- console.log("[integrate] Cursor integration complete");
367
- break;
368
- }
369
- case LUMENFLOW_CLIENT_IDS.CODEX_CLI: {
370
- integrateCodexCli(projectDir);
371
- console.log("[integrate] Codex CLI integration complete");
372
- break;
373
- }
374
- default:
375
- console.error(`[integrate] Unsupported client: ${opts.client}`);
376
- console.error(`[integrate] Supported clients: ${SUPPORTED_CLIENTS.join(", ")}`);
377
- process.exit(1);
378
- }
379
- }
380
- if (import.meta.main) {
381
- void runCLI(main);
382
- }
383
-
384
- export {
385
- parseIntegrateOptions,
386
- syncCoreEnforcementDelegators,
387
- syncPackageJsonScripts,
388
- integrateClaudeCode,
389
- integrateCursor,
390
- integrateCodexCli,
391
- main
392
- };
@@ -1,240 +0,0 @@
1
- import {
2
- SIGNAL_FILE_NAME,
3
- SIGNAL_RECEIPTS_FILE_NAME
4
- } from "./chunk-OMJD6A3S.js";
5
- import {
6
- LUMENFLOW_MEMORY_PATHS
7
- } from "./chunk-4N74J3UT.js";
8
- import {
9
- MS_PER_DAY
10
- } from "./chunk-V6OJGLBA.js";
11
- import {
12
- ErrorCodes,
13
- createError
14
- } from "./chunk-RXRKBBSM.js";
15
-
16
- // ../memory/dist/signal-cleanup-core.js
17
- import fs from "fs/promises";
18
- import path from "path";
19
- import { createRequire } from "module";
20
- var require2 = createRequire(import.meta.url);
21
- var ms = require2("ms");
22
- var DEFAULT_SIGNAL_CLEANUP_CONFIG = {
23
- ttl: 7 * MS_PER_DAY,
24
- unreadTtl: 30 * MS_PER_DAY,
25
- maxEntries: 500
26
- };
27
- function parseSignalTtl(ttlString) {
28
- if (!ttlString || typeof ttlString !== "string") {
29
- throw createError(ErrorCodes.INVALID_DURATION, "Invalid TTL format: TTL string is required");
30
- }
31
- const trimmed = ttlString.trim();
32
- if (!trimmed) {
33
- throw createError(ErrorCodes.INVALID_DURATION, "Invalid TTL format: TTL string is required");
34
- }
35
- const result = ms(trimmed);
36
- if (result == null || result <= 0) {
37
- throw createError(ErrorCodes.INVALID_DURATION, `Invalid TTL format: "${ttlString}" is not a valid duration`);
38
- }
39
- return result;
40
- }
41
- function isSignalExpired(signal, ttlMs, now) {
42
- if (!signal.created_at) {
43
- return false;
44
- }
45
- const createdAt = new Date(signal.created_at).getTime();
46
- if (Number.isNaN(createdAt)) {
47
- return false;
48
- }
49
- const age = now - createdAt;
50
- return age > ttlMs;
51
- }
52
- function shouldRemoveSignal(signal, config, context) {
53
- const { now, activeWuIds } = context;
54
- if (signal.wu_id && activeWuIds.has(signal.wu_id)) {
55
- return { remove: false, reason: "active-wu-protected" };
56
- }
57
- if (signal.read) {
58
- if (isSignalExpired(signal, config.ttl, now)) {
59
- return { remove: true, reason: "ttl-expired" };
60
- }
61
- } else {
62
- if (isSignalExpired(signal, config.unreadTtl, now)) {
63
- return { remove: true, reason: "unread-ttl-expired" };
64
- }
65
- }
66
- return { remove: false, reason: "within-ttl" };
67
- }
68
- function estimateSignalBytes(signal) {
69
- return JSON.stringify(signal).length + 1;
70
- }
71
- function getCompactionRatio(removedCount, totalCount) {
72
- if (totalCount === 0) {
73
- return 0;
74
- }
75
- return removedCount / totalCount;
76
- }
77
- function getSignalsPath(baseDir) {
78
- return path.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR, SIGNAL_FILE_NAME);
79
- }
80
- function getReceiptsPath(baseDir) {
81
- return path.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR, SIGNAL_RECEIPTS_FILE_NAME);
82
- }
83
- async function loadAllReceipts(baseDir) {
84
- const receiptsPath = getReceiptsPath(baseDir);
85
- try {
86
- const content = await fs.readFile(receiptsPath, { encoding: "utf-8" });
87
- const lines = content.split("\n").filter((line) => line.trim());
88
- return lines.map((line) => JSON.parse(line));
89
- } catch (err) {
90
- const error = err;
91
- if (error.code === "ENOENT") {
92
- return [];
93
- }
94
- throw error;
95
- }
96
- }
97
- async function writeReceipts(baseDir, receipts) {
98
- const receiptsPath = getReceiptsPath(baseDir);
99
- const content = receipts.map((r) => JSON.stringify(r)).join("\n") + (receipts.length > 0 ? "\n" : "");
100
- await fs.writeFile(receiptsPath, content, { encoding: "utf-8" });
101
- }
102
- async function loadAllSignals(baseDir) {
103
- const signalsPath = getSignalsPath(baseDir);
104
- try {
105
- const content = await fs.readFile(signalsPath, { encoding: "utf-8" });
106
- const lines = content.split("\n").filter((line) => line.trim());
107
- return lines.map((line) => JSON.parse(line));
108
- } catch (err) {
109
- const error = err;
110
- if (error.code === "ENOENT") {
111
- return [];
112
- }
113
- throw error;
114
- }
115
- }
116
- async function writeSignals(baseDir, signals) {
117
- const signalsPath = getSignalsPath(baseDir);
118
- const content = signals.map((s) => JSON.stringify(s)).join("\n") + (signals.length > 0 ? "\n" : "");
119
- await fs.writeFile(signalsPath, content, { encoding: "utf-8" });
120
- }
121
- async function defaultGetActiveWuIds() {
122
- return /* @__PURE__ */ new Set();
123
- }
124
- function buildCleanupConfig(options) {
125
- const { ttl, ttlMs: providedTtlMs, unreadTtl, unreadTtlMs: providedUnreadTtlMs, maxEntries } = options;
126
- let ttlMs = providedTtlMs ?? DEFAULT_SIGNAL_CLEANUP_CONFIG.ttl;
127
- if (ttl && !providedTtlMs) {
128
- ttlMs = parseSignalTtl(ttl);
129
- }
130
- let unreadTtlMs = providedUnreadTtlMs ?? DEFAULT_SIGNAL_CLEANUP_CONFIG.unreadTtl;
131
- if (unreadTtl && !providedUnreadTtlMs) {
132
- unreadTtlMs = parseSignalTtl(unreadTtl);
133
- }
134
- return {
135
- ttl: ttlMs,
136
- unreadTtl: unreadTtlMs,
137
- maxEntries: maxEntries ?? DEFAULT_SIGNAL_CLEANUP_CONFIG.maxEntries
138
- };
139
- }
140
- function processSignalsForTtl(signals, config, context) {
141
- const state = {
142
- removedIds: [],
143
- retainedIds: [],
144
- retainedSignals: [],
145
- bytesFreed: 0,
146
- breakdown: {
147
- ttlExpired: 0,
148
- unreadTtlExpired: 0,
149
- countLimitExceeded: 0,
150
- activeWuProtected: 0
151
- }
152
- };
153
- for (const signal of signals) {
154
- const decision = shouldRemoveSignal(signal, config, context);
155
- processSignalDecision(signal, decision, state);
156
- }
157
- return state;
158
- }
159
- function processSignalDecision(signal, decision, state) {
160
- if (decision.remove) {
161
- state.removedIds.push(signal.id);
162
- state.bytesFreed += estimateSignalBytes(signal);
163
- updateBreakdownForRemoval(decision.reason, state.breakdown);
164
- } else {
165
- state.retainedIds.push(signal.id);
166
- state.retainedSignals.push(signal);
167
- if (decision.reason === "active-wu-protected") {
168
- state.breakdown.activeWuProtected++;
169
- }
170
- }
171
- }
172
- function updateBreakdownForRemoval(reason, breakdown) {
173
- if (reason === "ttl-expired") {
174
- breakdown.ttlExpired++;
175
- } else if (reason === "unread-ttl-expired") {
176
- breakdown.unreadTtlExpired++;
177
- }
178
- }
179
- function applyCountPruning(state, maxEntries) {
180
- if (state.retainedSignals.length <= maxEntries) {
181
- return;
182
- }
183
- state.retainedSignals.sort((a, b) => {
184
- const aTime = new Date(a.created_at).getTime();
185
- const bTime = new Date(b.created_at).getTime();
186
- return aTime - bTime;
187
- });
188
- const toRemove = state.retainedSignals.length - maxEntries;
189
- for (let i = 0; i < toRemove; i++) {
190
- const signal = state.retainedSignals[i];
191
- if (!signal)
192
- continue;
193
- const idIndex = state.retainedIds.indexOf(signal.id);
194
- if (idIndex !== -1) {
195
- state.retainedIds.splice(idIndex, 1);
196
- }
197
- state.removedIds.push(signal.id);
198
- state.bytesFreed += estimateSignalBytes(signal);
199
- state.breakdown.countLimitExceeded++;
200
- }
201
- state.retainedSignals.splice(0, toRemove);
202
- }
203
- async function cleanupSignals(baseDir, options = {}) {
204
- const { dryRun = false, now = Date.now(), getActiveWuIds = defaultGetActiveWuIds } = options;
205
- const config = buildCleanupConfig(options);
206
- const signals = await loadAllSignals(baseDir);
207
- const activeWuIds = await getActiveWuIds();
208
- const receipts = await loadAllReceipts(baseDir);
209
- const receiptSignalIds = new Set(receipts.map((r) => r.signal_id));
210
- for (const signal of signals) {
211
- if (!signal.read && receiptSignalIds.has(signal.id)) {
212
- signal.read = true;
213
- }
214
- }
215
- const state = processSignalsForTtl(signals, config, { now, activeWuIds });
216
- applyCountPruning(state, config.maxEntries);
217
- const compactionRatio = getCompactionRatio(state.removedIds.length, signals.length);
218
- const baseResult = {
219
- success: true,
220
- removedIds: state.removedIds,
221
- retainedIds: state.retainedIds,
222
- bytesFreed: state.bytesFreed,
223
- compactionRatio,
224
- breakdown: state.breakdown
225
- };
226
- if (dryRun) {
227
- return { ...baseResult, dryRun: true };
228
- }
229
- if (state.removedIds.length > 0) {
230
- await writeSignals(baseDir, state.retainedSignals);
231
- const retainedIdSet = new Set(state.retainedIds);
232
- const retainedReceipts = receipts.filter((r) => retainedIdSet.has(r.signal_id));
233
- await writeReceipts(baseDir, retainedReceipts);
234
- }
235
- return baseResult;
236
- }
237
-
238
- export {
239
- cleanupSignals
240
- };