@lumenflow/cli 3.16.0 → 3.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs-sync.js +97 -46
- package/dist/docs-sync.js.map +1 -1
- package/dist/lumenflow-upgrade.js +19 -13
- package/dist/lumenflow-upgrade.js.map +1 -1
- package/package.json +8 -8
- package/packs/sidekick/.turbo/turbo-build.log +1 -1
- package/packs/sidekick/package.json +1 -1
- package/packs/software-delivery/.turbo/turbo-build.log +1 -1
- package/packs/software-delivery/package.json +1 -1
- package/dist/chunk-2D2VOCA4.js +0 -37
- package/dist/chunk-2D5KFYGX.js +0 -284
- package/dist/chunk-2GXVIN57.js +0 -14072
- package/dist/chunk-2MQ7HZWZ.js +0 -26
- package/dist/chunk-2UFQ3A3C.js +0 -643
- package/dist/chunk-3RG5ZIWI.js +0 -10
- package/dist/chunk-4N74J3UT.js +0 -15
- package/dist/chunk-5GTOXFYR.js +0 -392
- package/dist/chunk-5VY6MQMC.js +0 -240
- package/dist/chunk-67XVPMRY.js +0 -1297
- package/dist/chunk-6HO4GWJE.js +0 -164
- package/dist/chunk-6W5XHWYV.js +0 -1890
- package/dist/chunk-6X4EMYJQ.js +0 -64
- package/dist/chunk-6XYXI2NQ.js +0 -772
- package/dist/chunk-7ANSOV6Q.js +0 -285
- package/dist/chunk-A624LFLB.js +0 -1380
- package/dist/chunk-ADN5NHG4.js +0 -126
- package/dist/chunk-B7YJYJKG.js +0 -33
- package/dist/chunk-CCLHCPKG.js +0 -210
- package/dist/chunk-CK36VROC.js +0 -1584
- package/dist/chunk-D3UOFRSB.js +0 -81
- package/dist/chunk-DFR4DJBM.js +0 -230
- package/dist/chunk-DSYBDHYH.js +0 -79
- package/dist/chunk-DWMLTXKQ.js +0 -1176
- package/dist/chunk-E3REJTAJ.js +0 -28
- package/dist/chunk-EA3IVO64.js +0 -633
- package/dist/chunk-EK2AKZKD.js +0 -55
- package/dist/chunk-ELD7JTTT.js +0 -343
- package/dist/chunk-EX6TT2XI.js +0 -195
- package/dist/chunk-EXINSFZE.js +0 -82
- package/dist/chunk-EZ6ZBYBM.js +0 -510
- package/dist/chunk-FBKAPTJ2.js +0 -16
- package/dist/chunk-FVLV5RYH.js +0 -1118
- package/dist/chunk-GDNSBQVK.js +0 -2485
- package/dist/chunk-GPQHMBNN.js +0 -278
- package/dist/chunk-GTFJB67L.js +0 -68
- package/dist/chunk-HANJXVKW.js +0 -1127
- package/dist/chunk-HEVS5YLD.js +0 -269
- package/dist/chunk-HMEVZKPQ.js +0 -9
- package/dist/chunk-HRGSYNLM.js +0 -3511
- package/dist/chunk-ISZR5N4K.js +0 -60
- package/dist/chunk-J6SUPR2C.js +0 -226
- package/dist/chunk-JERYVEIZ.js +0 -244
- package/dist/chunk-JHHWGL2N.js +0 -87
- package/dist/chunk-JONWQUB5.js +0 -775
- package/dist/chunk-K2DIWWDM.js +0 -1766
- package/dist/chunk-KY4PGL5V.js +0 -969
- package/dist/chunk-L737LQ4C.js +0 -1285
- package/dist/chunk-LFTWYIB2.js +0 -497
- package/dist/chunk-LV47RFNJ.js +0 -41
- package/dist/chunk-MKSAITI7.js +0 -15
- package/dist/chunk-MZ7RKIX4.js +0 -212
- package/dist/chunk-NAP6CFSO.js +0 -84
- package/dist/chunk-ND6MY37M.js +0 -16
- package/dist/chunk-NMG736UR.js +0 -683
- package/dist/chunk-NRAXROED.js +0 -32
- package/dist/chunk-NRIZR3A7.js +0 -690
- package/dist/chunk-NX43BG3M.js +0 -233
- package/dist/chunk-O645XLSI.js +0 -297
- package/dist/chunk-OMJD6A3S.js +0 -235
- package/dist/chunk-QB6SJD4T.js +0 -430
- package/dist/chunk-QFSTL4J3.js +0 -276
- package/dist/chunk-QLGDFMFX.js +0 -212
- package/dist/chunk-RIAAGL2E.js +0 -13
- package/dist/chunk-RWO5XMZ6.js +0 -86
- package/dist/chunk-RXRKBBSM.js +0 -149
- package/dist/chunk-RZOZMML6.js +0 -363
- package/dist/chunk-U7I7FS7T.js +0 -113
- package/dist/chunk-UI42RODY.js +0 -717
- package/dist/chunk-UTVMVSCO.js +0 -519
- package/dist/chunk-V6OJGLBA.js +0 -1746
- package/dist/chunk-W2JHVH7D.js +0 -152
- package/dist/chunk-WD3Y7VQN.js +0 -280
- package/dist/chunk-WOCTQ5MS.js +0 -303
- package/dist/chunk-WZR3ZUNN.js +0 -696
- package/dist/chunk-XGI665H7.js +0 -150
- package/dist/chunk-XKY65P2T.js +0 -304
- package/dist/chunk-Y4CQZY65.js +0 -57
- package/dist/chunk-YFEXKLVE.js +0 -194
- package/dist/chunk-YHO3HS5X.js +0 -287
- package/dist/chunk-YLS7AZSX.js +0 -738
- package/dist/chunk-ZE473AO6.js +0 -49
- package/dist/chunk-ZF747T3O.js +0 -644
- package/dist/chunk-ZHCZHZH3.js +0 -43
- package/dist/chunk-ZZNZX2XY.js +0 -87
- package/dist/constants-7QAP3VQ4.js +0 -23
- package/dist/dist-IY3UUMWK.js +0 -33
- package/dist/invariants-runner-W5RGHCSU.js +0 -27
- package/dist/lane-lock-6J36HD5O.js +0 -35
- package/dist/mem-checkpoint-core-EANG2GVN.js +0 -14
- package/dist/mem-signal-core-2LZ2WYHW.js +0 -19
- package/dist/memory-store-OLB5FO7K.js +0 -18
- package/dist/service-6BYCOCO5.js +0 -13
- package/dist/spawn-policy-resolver-NTSZYQ6R.js +0 -17
- package/dist/spawn-task-builder-R4E2BHSW.js +0 -22
- package/dist/wu-done-pr-WLFFFEPJ.js +0 -25
- package/dist/wu-done-validation-3J5E36FE.js +0 -30
- package/dist/wu-duplicate-id-detector-5S7JHELK.js +0 -232
- package/packs/sidekick/.turbo/turbo-typecheck.log +0 -4
- package/packs/software-delivery/.turbo/turbo-typecheck.log +0 -4
package/dist/chunk-A624LFLB.js
DELETED
|
@@ -1,1380 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
integrateClaudeCode
|
|
3
|
-
} from "./chunk-5GTOXFYR.js";
|
|
4
|
-
import {
|
|
5
|
-
generateSessionStartRecoveryScript
|
|
6
|
-
} from "./chunk-EA3IVO64.js";
|
|
7
|
-
import {
|
|
8
|
-
CANONICAL_BOOTSTRAP_COMMAND,
|
|
9
|
-
DEFAULT_PROJECT_NAME,
|
|
10
|
-
WORKSPACE_FILENAME,
|
|
11
|
-
getDefaultWorkspaceConfig
|
|
12
|
-
} from "./chunk-WOCTQ5MS.js";
|
|
13
|
-
import {
|
|
14
|
-
createDirectory,
|
|
15
|
-
createExecutableScript,
|
|
16
|
-
createFile,
|
|
17
|
-
loadTemplate,
|
|
18
|
-
processTemplate
|
|
19
|
-
} from "./chunk-HEVS5YLD.js";
|
|
20
|
-
import {
|
|
21
|
-
AGENTS_MD_TEMPLATE,
|
|
22
|
-
AIDER_CONF_TEMPLATE,
|
|
23
|
-
BACKLOG_TEMPLATE,
|
|
24
|
-
CLAUDE_MD_TEMPLATE,
|
|
25
|
-
CLAUDE_SETTINGS_TEMPLATE,
|
|
26
|
-
CLINE_RULES_TEMPLATE,
|
|
27
|
-
CURSOR_RULES_TEMPLATE,
|
|
28
|
-
DEFAULT_LANE_DEFINITIONS,
|
|
29
|
-
FRAMEWORK_HINT_TEMPLATE,
|
|
30
|
-
FRAMEWORK_OVERLAY_TEMPLATE,
|
|
31
|
-
GATE_STUB_SCRIPTS,
|
|
32
|
-
GITIGNORE_TEMPLATE,
|
|
33
|
-
LANE_LIFECYCLE_STATUS,
|
|
34
|
-
LUMENFLOW_GATES_SKILL_TEMPLATE,
|
|
35
|
-
MCP_JSON_TEMPLATE,
|
|
36
|
-
PRETTIERIGNORE_TEMPLATE,
|
|
37
|
-
PRE_COMMIT_TEMPLATE,
|
|
38
|
-
REQUIRED_GITIGNORE_EXCLUSIONS,
|
|
39
|
-
SAFE_GIT_TEMPLATE,
|
|
40
|
-
SCRIPT_ARG_OVERRIDES,
|
|
41
|
-
STATUS_TEMPLATE,
|
|
42
|
-
WINDSURF_RULES_TEMPLATE,
|
|
43
|
-
WORKTREE_DISCIPLINE_SKILL_TEMPLATE,
|
|
44
|
-
WU_LIFECYCLE_SKILL_TEMPLATE,
|
|
45
|
-
WU_TEMPLATE_YAML,
|
|
46
|
-
buildInitLaneLifecycleMessage
|
|
47
|
-
} from "./chunk-67XVPMRY.js";
|
|
48
|
-
import {
|
|
49
|
-
getPublicManifest
|
|
50
|
-
} from "./chunk-KY4PGL5V.js";
|
|
51
|
-
import {
|
|
52
|
-
SCAFFOLDED_ONBOARDING_TEMPLATE_PATHS
|
|
53
|
-
} from "./chunk-FBKAPTJ2.js";
|
|
54
|
-
import {
|
|
55
|
-
isInWorktree,
|
|
56
|
-
isMainBranch
|
|
57
|
-
} from "./chunk-E3REJTAJ.js";
|
|
58
|
-
import {
|
|
59
|
-
runDoctorForInit
|
|
60
|
-
} from "./chunk-ZF747T3O.js";
|
|
61
|
-
import {
|
|
62
|
-
WU_OPTIONS,
|
|
63
|
-
createWUParser,
|
|
64
|
-
runCLI
|
|
65
|
-
} from "./chunk-2GXVIN57.js";
|
|
66
|
-
import {
|
|
67
|
-
CLAUDE_HOOKS,
|
|
68
|
-
ENV_VARS
|
|
69
|
-
} from "./chunk-DWMLTXKQ.js";
|
|
70
|
-
import {
|
|
71
|
-
GATE_PRESETS,
|
|
72
|
-
GIT_DIRECTORY_NAME,
|
|
73
|
-
LUMENFLOW_CLIENT_IDS,
|
|
74
|
-
WORKSPACE_V2_KEYS,
|
|
75
|
-
getDefaultConfig,
|
|
76
|
-
getDocsLayoutPreset
|
|
77
|
-
} from "./chunk-V6OJGLBA.js";
|
|
78
|
-
import {
|
|
79
|
-
ErrorCodes,
|
|
80
|
-
createError
|
|
81
|
-
} from "./chunk-RXRKBBSM.js";
|
|
82
|
-
|
|
83
|
-
// src/init.ts
|
|
84
|
-
import * as fs2 from "fs";
|
|
85
|
-
import * as path2 from "path";
|
|
86
|
-
import * as yaml from "yaml";
|
|
87
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
88
|
-
|
|
89
|
-
// src/init-detection.ts
|
|
90
|
-
import * as fs from "fs";
|
|
91
|
-
import * as path from "path";
|
|
92
|
-
import { execFileSync } from "child_process";
|
|
93
|
-
var DEFAULT_CLIENT_CLAUDE = LUMENFLOW_CLIENT_IDS.CLAUDE_CODE;
|
|
94
|
-
function detectIDEEnvironment() {
|
|
95
|
-
if (process.env[ENV_VARS.CLAUDE_PROJECT_DIR] || process.env[ENV_VARS.CLAUDE_CODE]) {
|
|
96
|
-
return "claude";
|
|
97
|
-
}
|
|
98
|
-
const cursorVars = Object.keys(process.env).filter((key) => key.startsWith("CURSOR_"));
|
|
99
|
-
if (cursorVars.length > 0) {
|
|
100
|
-
return "cursor";
|
|
101
|
-
}
|
|
102
|
-
const windsurfVars = Object.keys(process.env).filter((key) => key.startsWith("WINDSURF_"));
|
|
103
|
-
if (windsurfVars.length > 0) {
|
|
104
|
-
return "windsurf";
|
|
105
|
-
}
|
|
106
|
-
const vscodeVars = Object.keys(process.env).filter((key) => key.startsWith("VSCODE_"));
|
|
107
|
-
if (vscodeVars.length > 0) {
|
|
108
|
-
return "vscode";
|
|
109
|
-
}
|
|
110
|
-
return void 0;
|
|
111
|
-
}
|
|
112
|
-
function getCommandVersion(command, args) {
|
|
113
|
-
try {
|
|
114
|
-
const output = execFileSync(command, args, {
|
|
115
|
-
encoding: "utf-8",
|
|
116
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
117
|
-
}).trim();
|
|
118
|
-
return output;
|
|
119
|
-
} catch {
|
|
120
|
-
return "not found";
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
function parseVersion(versionStr) {
|
|
124
|
-
const match = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(versionStr);
|
|
125
|
-
if (!match) {
|
|
126
|
-
return [0, 0, 0];
|
|
127
|
-
}
|
|
128
|
-
return [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3] || "0", 10)];
|
|
129
|
-
}
|
|
130
|
-
function compareVersions(actual, required) {
|
|
131
|
-
const actualParts = parseVersion(actual);
|
|
132
|
-
const requiredParts = parseVersion(required);
|
|
133
|
-
for (let i = 0; i < 3; i++) {
|
|
134
|
-
if (actualParts[i] > requiredParts[i]) {
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
if (actualParts[i] < requiredParts[i]) {
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
function checkPrerequisites() {
|
|
144
|
-
const nodeVersion = getCommandVersion("node", ["--version"]);
|
|
145
|
-
const pnpmVersion = getCommandVersion("pnpm", ["--version"]);
|
|
146
|
-
const gitVersion = getCommandVersion("git", ["--version"]);
|
|
147
|
-
const requiredNode = "22.0.0";
|
|
148
|
-
const requiredPnpm = "9.0.0";
|
|
149
|
-
const requiredGit = "2.0.0";
|
|
150
|
-
const nodeOk = nodeVersion !== "not found" && compareVersions(nodeVersion, requiredNode);
|
|
151
|
-
const pnpmOk = pnpmVersion !== "not found" && compareVersions(pnpmVersion, requiredPnpm);
|
|
152
|
-
const gitOk = gitVersion !== "not found" && compareVersions(gitVersion, requiredGit);
|
|
153
|
-
return {
|
|
154
|
-
node: {
|
|
155
|
-
passed: nodeOk,
|
|
156
|
-
version: nodeVersion,
|
|
157
|
-
required: `>=${requiredNode}`,
|
|
158
|
-
message: nodeOk ? void 0 : `Node.js ${requiredNode}+ required`
|
|
159
|
-
},
|
|
160
|
-
pnpm: {
|
|
161
|
-
passed: pnpmOk,
|
|
162
|
-
version: pnpmVersion,
|
|
163
|
-
required: `>=${requiredPnpm}`,
|
|
164
|
-
message: pnpmOk ? void 0 : `pnpm ${requiredPnpm}+ required`
|
|
165
|
-
},
|
|
166
|
-
git: {
|
|
167
|
-
passed: gitOk,
|
|
168
|
-
version: gitVersion,
|
|
169
|
-
required: `>=${requiredGit}`,
|
|
170
|
-
message: gitOk ? void 0 : `Git ${requiredGit}+ required`
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
function getDocsPath(structure) {
|
|
175
|
-
return getDocsLayoutPreset(structure);
|
|
176
|
-
}
|
|
177
|
-
function detectDocsStructure(targetDir) {
|
|
178
|
-
const docsDir = path.join(targetDir, "docs");
|
|
179
|
-
if (!fs.existsSync(docsDir)) {
|
|
180
|
-
return "simple";
|
|
181
|
-
}
|
|
182
|
-
const entries = fs.readdirSync(docsDir);
|
|
183
|
-
const hasNumberedDir = entries.some((entry) => /^\d{2}-/.test(entry));
|
|
184
|
-
if (hasNumberedDir) {
|
|
185
|
-
return "arc42";
|
|
186
|
-
}
|
|
187
|
-
return "simple";
|
|
188
|
-
}
|
|
189
|
-
function detectDefaultClient() {
|
|
190
|
-
if (process.env[ENV_VARS.CLAUDE_PROJECT_DIR] || process.env[ENV_VARS.CLAUDE_CODE]) {
|
|
191
|
-
return DEFAULT_CLIENT_CLAUDE;
|
|
192
|
-
}
|
|
193
|
-
return "none";
|
|
194
|
-
}
|
|
195
|
-
function isGitRepo(targetDir) {
|
|
196
|
-
try {
|
|
197
|
-
execFileSync("git", ["rev-parse", "--git-dir"], {
|
|
198
|
-
cwd: targetDir,
|
|
199
|
-
stdio: "pipe"
|
|
200
|
-
});
|
|
201
|
-
return true;
|
|
202
|
-
} catch {
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
function hasGitCommits(targetDir) {
|
|
207
|
-
try {
|
|
208
|
-
execFileSync("git", ["rev-parse", "HEAD"], {
|
|
209
|
-
cwd: targetDir,
|
|
210
|
-
stdio: "pipe"
|
|
211
|
-
});
|
|
212
|
-
return true;
|
|
213
|
-
} catch {
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
function hasOriginRemote(targetDir) {
|
|
218
|
-
try {
|
|
219
|
-
const result = execFileSync("git", ["remote", "get-url", "origin"], {
|
|
220
|
-
cwd: targetDir,
|
|
221
|
-
encoding: "utf-8",
|
|
222
|
-
stdio: "pipe"
|
|
223
|
-
});
|
|
224
|
-
return result.trim().length > 0;
|
|
225
|
-
} catch {
|
|
226
|
-
return false;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
function detectGitStateConfig(targetDir) {
|
|
230
|
-
if (!isGitRepo(targetDir)) {
|
|
231
|
-
return { requireRemote: false };
|
|
232
|
-
}
|
|
233
|
-
if (!hasOriginRemote(targetDir)) {
|
|
234
|
-
return { requireRemote: false };
|
|
235
|
-
}
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// src/init.ts
|
|
240
|
-
var INIT_OPTIONS = {
|
|
241
|
-
full: {
|
|
242
|
-
name: "full",
|
|
243
|
-
flags: "--full",
|
|
244
|
-
description: "Add docs + agent onboarding + task scaffolding (default: true)"
|
|
245
|
-
},
|
|
246
|
-
minimal: {
|
|
247
|
-
name: "minimal",
|
|
248
|
-
flags: "--minimal",
|
|
249
|
-
description: "Skip agent onboarding docs (only core files)"
|
|
250
|
-
},
|
|
251
|
-
framework: {
|
|
252
|
-
name: "framework",
|
|
253
|
-
flags: "--framework <name>",
|
|
254
|
-
description: "Add framework hint + overlay docs"
|
|
255
|
-
},
|
|
256
|
-
// WU-1171: --client is the new primary flag (wu:spawn vocabulary)
|
|
257
|
-
client: {
|
|
258
|
-
name: "client",
|
|
259
|
-
flags: "--client <type>",
|
|
260
|
-
description: "Client type (claude, cursor, windsurf, codex, all, none)"
|
|
261
|
-
},
|
|
262
|
-
// WU-1171: --vendor kept as backward-compatible alias
|
|
263
|
-
vendor: {
|
|
264
|
-
name: "vendor",
|
|
265
|
-
flags: "--vendor <type>",
|
|
266
|
-
description: "Alias for --client (deprecated)"
|
|
267
|
-
},
|
|
268
|
-
// WU-1171: --merge mode for safe insertion into existing files
|
|
269
|
-
merge: {
|
|
270
|
-
name: "merge",
|
|
271
|
-
flags: "--merge",
|
|
272
|
-
description: "Merge LumenFlow config into existing files using bounded markers"
|
|
273
|
-
},
|
|
274
|
-
preset: {
|
|
275
|
-
name: "preset",
|
|
276
|
-
flags: "--preset <preset>",
|
|
277
|
-
description: "Gate preset for config (node, python, go, rust, dotnet)"
|
|
278
|
-
},
|
|
279
|
-
bootstrapDomain: {
|
|
280
|
-
name: "bootstrapDomain",
|
|
281
|
-
flags: "--bootstrap-domain <domain>",
|
|
282
|
-
description: "Bootstrap domain: software-delivery, infra, or custom"
|
|
283
|
-
},
|
|
284
|
-
skipBootstrap: {
|
|
285
|
-
name: "skipBootstrap",
|
|
286
|
-
flags: "--skip-bootstrap",
|
|
287
|
-
description: "Skip workspace bootstrap-all flow (workspace.yaml + pack install)"
|
|
288
|
-
},
|
|
289
|
-
skipBootstrapPackInstall: {
|
|
290
|
-
name: "skipBootstrapPackInstall",
|
|
291
|
-
flags: "--skip-bootstrap-pack-install",
|
|
292
|
-
description: "Skip registry pack install during bootstrap"
|
|
293
|
-
},
|
|
294
|
-
force: WU_OPTIONS.force
|
|
295
|
-
};
|
|
296
|
-
function parseBootstrapDomain(rawDomain) {
|
|
297
|
-
if (!rawDomain) {
|
|
298
|
-
return BOOTSTRAP_DEFAULT_DOMAIN;
|
|
299
|
-
}
|
|
300
|
-
if (BOOTSTRAP_VALID_DOMAINS.has(rawDomain)) {
|
|
301
|
-
return rawDomain;
|
|
302
|
-
}
|
|
303
|
-
const validDomains = Array.from(BOOTSTRAP_VALID_DOMAINS).join(", ");
|
|
304
|
-
throw createError(
|
|
305
|
-
ErrorCodes.INVALID_ARGUMENT,
|
|
306
|
-
`${BOOTSTRAP_ERROR_PREFIX} Invalid --bootstrap-domain "${rawDomain}". Valid values: ${validDomains}`
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
function parseInitOptions() {
|
|
310
|
-
const opts = createWUParser({
|
|
311
|
-
name: "lumenflow-init",
|
|
312
|
-
description: "Initialize LumenFlow in a project\n\nSubcommands:\n lumenflow commands List all available CLI commands\n lumenflow cloud connect Configure cloud control-plane access",
|
|
313
|
-
options: Object.values(INIT_OPTIONS)
|
|
314
|
-
});
|
|
315
|
-
const clientValue = opts.client || opts.vendor;
|
|
316
|
-
const fullMode = opts.minimal ? false : opts.full ?? true;
|
|
317
|
-
const bootstrapDomain = parseBootstrapDomain(opts.bootstrapDomain);
|
|
318
|
-
return {
|
|
319
|
-
force: opts.force ?? false,
|
|
320
|
-
full: fullMode,
|
|
321
|
-
merge: opts.merge ?? false,
|
|
322
|
-
framework: opts.framework,
|
|
323
|
-
client: clientValue,
|
|
324
|
-
vendor: clientValue,
|
|
325
|
-
preset: opts.preset,
|
|
326
|
-
bootstrapDomain,
|
|
327
|
-
skipBootstrap: Boolean(opts.skipBootstrap),
|
|
328
|
-
skipBootstrapPackInstall: Boolean(opts.skipBootstrapPackInstall)
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
var DEFAULT_CLIENT_CLAUDE2 = LUMENFLOW_CLIENT_IDS.CLAUDE_CODE;
|
|
332
|
-
var BOOTSTRAP_DEFAULT_DOMAIN = "software-delivery";
|
|
333
|
-
var BOOTSTRAP_INFRA_DOMAIN = "infra";
|
|
334
|
-
var BOOTSTRAP_CUSTOM_DOMAIN = "custom";
|
|
335
|
-
var BOOTSTRAP_VALID_DOMAINS = /* @__PURE__ */ new Set([
|
|
336
|
-
BOOTSTRAP_DEFAULT_DOMAIN,
|
|
337
|
-
BOOTSTRAP_INFRA_DOMAIN,
|
|
338
|
-
BOOTSTRAP_CUSTOM_DOMAIN
|
|
339
|
-
]);
|
|
340
|
-
var BOOTSTRAP_SKIP_REASON_FLAG = "--skip-bootstrap";
|
|
341
|
-
var BOOTSTRAP_SKIP_REASON_EXISTING_WORKSPACE = `${WORKSPACE_FILENAME} already exists`;
|
|
342
|
-
var BOOTSTRAP_ERROR_PREFIX = "[lumenflow bootstrap]";
|
|
343
|
-
var INIT_SUBCOMMANDS = {
|
|
344
|
-
COMMANDS: "commands",
|
|
345
|
-
CLOUD: "cloud"
|
|
346
|
-
};
|
|
347
|
-
var LEGACY_SUBCOMMANDS = {
|
|
348
|
-
ONBOARD: "onboard",
|
|
349
|
-
WORKSPACE_INIT_COLON: "workspace:init",
|
|
350
|
-
WORKSPACE_INIT_DASH: "workspace-init"
|
|
351
|
-
};
|
|
352
|
-
var CLOUD_SUBCOMMANDS = {
|
|
353
|
-
CONNECT: "connect"
|
|
354
|
-
};
|
|
355
|
-
var CLOUD_CONNECT_BIN = "cloud-connect";
|
|
356
|
-
var INIT_ERROR_PREFIX = "[lumenflow init]";
|
|
357
|
-
var INIT_CLOUD_CONNECT_HELP = "Usage: lumenflow cloud connect --endpoint <url> --org-id <id> --project-id <id> [--token-env <name>]";
|
|
358
|
-
var LEGACY_SUBCOMMAND_ERROR_PREFIX = `${INIT_ERROR_PREFIX} Legacy onboarding subcommand`;
|
|
359
|
-
var LEGACY_SUBCOMMAND_GUIDANCE = `Use "${CANONICAL_BOOTSTRAP_COMMAND}" for bootstrap-all onboarding`;
|
|
360
|
-
var LEGACY_SUBCOMMAND_HELP_HINT = `Run "${CANONICAL_BOOTSTRAP_COMMAND} --help" for supported options`;
|
|
361
|
-
var CONFIG_FILE_NAME = WORKSPACE_FILENAME;
|
|
362
|
-
var SOFTWARE_DELIVERY_KEY = WORKSPACE_V2_KEYS.SOFTWARE_DELIVERY;
|
|
363
|
-
var SOFTWARE_DELIVERY_CONFIG_KEYS = {
|
|
364
|
-
AGENTS: "agents",
|
|
365
|
-
CLIENTS: "clients",
|
|
366
|
-
ENFORCEMENT: "enforcement"
|
|
367
|
-
};
|
|
368
|
-
var FRAMEWORK_HINT_FILE = ".lumenflow.framework.yaml";
|
|
369
|
-
var LUMENFLOW_DIR = ".lumenflow";
|
|
370
|
-
var LUMENFLOW_AGENTS_DIR = `${LUMENFLOW_DIR}/agents`;
|
|
371
|
-
var CLAUDE_DIR = ".claude";
|
|
372
|
-
var CLAUDE_AGENTS_DIR = path2.join(CLAUDE_DIR, "agents");
|
|
373
|
-
async function checkBranchGuard(targetDir, result) {
|
|
374
|
-
result.warnings = result.warnings ?? [];
|
|
375
|
-
const gitDir = path2.join(targetDir, GIT_DIRECTORY_NAME);
|
|
376
|
-
if (!fs2.existsSync(gitDir)) {
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
if (isInWorktree({ cwd: targetDir })) {
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
try {
|
|
383
|
-
const onMain = await isMainBranch();
|
|
384
|
-
if (onMain) {
|
|
385
|
-
result.warnings.push(
|
|
386
|
-
"Running init on main branch in main checkout. Consider using a worktree for changes to tracked files."
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
} catch {
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
function detectProjectTooling(targetDir) {
|
|
393
|
-
const packageJsonPath = path2.join(targetDir, "package.json");
|
|
394
|
-
if (!fs2.existsSync(packageJsonPath)) {
|
|
395
|
-
return { hasTurbo: false };
|
|
396
|
-
}
|
|
397
|
-
try {
|
|
398
|
-
const content = fs2.readFileSync(packageJsonPath, "utf-8");
|
|
399
|
-
const pkg = JSON.parse(content);
|
|
400
|
-
const deps = pkg.dependencies ?? {};
|
|
401
|
-
const devDeps = pkg.devDependencies ?? {};
|
|
402
|
-
const allDeps = { ...deps, ...devDeps };
|
|
403
|
-
let testRunner;
|
|
404
|
-
if ("vitest" in allDeps) {
|
|
405
|
-
testRunner = "vitest";
|
|
406
|
-
} else if ("jest" in allDeps) {
|
|
407
|
-
testRunner = "jest";
|
|
408
|
-
} else if ("mocha" in allDeps) {
|
|
409
|
-
testRunner = "mocha";
|
|
410
|
-
}
|
|
411
|
-
const hasTurbo = "turbo" in allDeps;
|
|
412
|
-
let packageManager;
|
|
413
|
-
const pkgManager = pkg.packageManager;
|
|
414
|
-
if (pkgManager?.startsWith("pnpm")) {
|
|
415
|
-
packageManager = "pnpm";
|
|
416
|
-
} else if (pkgManager?.startsWith("yarn")) {
|
|
417
|
-
packageManager = "yarn";
|
|
418
|
-
} else if (pkgManager?.startsWith("bun")) {
|
|
419
|
-
packageManager = "bun";
|
|
420
|
-
} else if (pkgManager?.startsWith("npm")) {
|
|
421
|
-
packageManager = "npm";
|
|
422
|
-
}
|
|
423
|
-
return { testRunner, hasTurbo, packageManager };
|
|
424
|
-
} catch {
|
|
425
|
-
return { hasTurbo: false };
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
function asRecord(value) {
|
|
429
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
430
|
-
}
|
|
431
|
-
function mergeConfigDefaults(defaults, existing) {
|
|
432
|
-
const merged = { ...defaults };
|
|
433
|
-
for (const [key, existingValue] of Object.entries(existing)) {
|
|
434
|
-
const defaultValue = merged[key];
|
|
435
|
-
const existingRecord = asRecord(existingValue);
|
|
436
|
-
const defaultRecord = asRecord(defaultValue);
|
|
437
|
-
if (defaultRecord && existingRecord) {
|
|
438
|
-
merged[key] = mergeConfigDefaults(defaultRecord, existingRecord);
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
merged[key] = existingValue;
|
|
442
|
-
}
|
|
443
|
-
return merged;
|
|
444
|
-
}
|
|
445
|
-
function toWorkspaceId(value) {
|
|
446
|
-
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
447
|
-
}
|
|
448
|
-
function loadWorkspaceDocument(targetDir) {
|
|
449
|
-
const workspacePath = path2.join(targetDir, CONFIG_FILE_NAME);
|
|
450
|
-
if (!fs2.existsSync(workspacePath)) {
|
|
451
|
-
const projectName = resolveBootstrapProjectName(targetDir);
|
|
452
|
-
const projectId = toWorkspaceId(projectName);
|
|
453
|
-
const workspace2 = {
|
|
454
|
-
...getDefaultWorkspaceConfig(),
|
|
455
|
-
id: projectId,
|
|
456
|
-
name: projectName,
|
|
457
|
-
memory_namespace: projectId,
|
|
458
|
-
event_namespace: projectId
|
|
459
|
-
};
|
|
460
|
-
return { exists: false, workspace: workspace2 };
|
|
461
|
-
}
|
|
462
|
-
const content = fs2.readFileSync(workspacePath, "utf-8");
|
|
463
|
-
const workspace = asRecord(yaml.parse(content));
|
|
464
|
-
if (!workspace) {
|
|
465
|
-
throw createError(
|
|
466
|
-
ErrorCodes.WORKSPACE_MALFORMED,
|
|
467
|
-
`${INIT_ERROR_PREFIX} ${CONFIG_FILE_NAME} exists but is not a valid YAML object. Fix ${CONFIG_FILE_NAME} and re-run init.`
|
|
468
|
-
);
|
|
469
|
-
}
|
|
470
|
-
return { exists: true, workspace };
|
|
471
|
-
}
|
|
472
|
-
function upsertWorkspaceSoftwareDelivery(targetDir, softwareDeliveryConfig, result) {
|
|
473
|
-
const workspacePath = path2.join(targetDir, CONFIG_FILE_NAME);
|
|
474
|
-
const { exists, workspace } = loadWorkspaceDocument(targetDir);
|
|
475
|
-
const existingSoftwareDelivery = asRecord(workspace[SOFTWARE_DELIVERY_KEY]) ?? {};
|
|
476
|
-
workspace[SOFTWARE_DELIVERY_KEY] = mergeConfigDefaults(
|
|
477
|
-
softwareDeliveryConfig,
|
|
478
|
-
existingSoftwareDelivery
|
|
479
|
-
);
|
|
480
|
-
fs2.writeFileSync(workspacePath, yaml.stringify(workspace), "utf-8");
|
|
481
|
-
if (exists) {
|
|
482
|
-
result.overwritten = result.overwritten ?? [];
|
|
483
|
-
if (!result.overwritten.includes(CONFIG_FILE_NAME)) {
|
|
484
|
-
result.overwritten.push(CONFIG_FILE_NAME);
|
|
485
|
-
}
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
result.created.push(CONFIG_FILE_NAME);
|
|
489
|
-
}
|
|
490
|
-
function buildSoftwareDeliveryConfig(gatePreset, gitConfigOverride, client, docsPaths, targetDir) {
|
|
491
|
-
const config = getDefaultConfig();
|
|
492
|
-
config.directories.agentsDir = LUMENFLOW_AGENTS_DIR;
|
|
493
|
-
if (docsPaths) {
|
|
494
|
-
config.directories.wuDir = `${docsPaths.tasks}/wu`;
|
|
495
|
-
config.directories.initiativesDir = `${docsPaths.tasks}/initiatives`;
|
|
496
|
-
config.directories.backlogPath = `${docsPaths.tasks}/backlog.md`;
|
|
497
|
-
config.directories.statusPath = `${docsPaths.tasks}/status.md`;
|
|
498
|
-
config.directories.plansDir = `${docsPaths.operations}/plans`;
|
|
499
|
-
config.directories.onboardingDir = docsPaths.onboarding;
|
|
500
|
-
config.directories.completeGuidePath = docsPaths.completeGuidePath;
|
|
501
|
-
config.directories.quickRefPath = docsPaths.quickRefPath;
|
|
502
|
-
config.directories.startingPromptPath = docsPaths.startingPromptPath;
|
|
503
|
-
config.directories.governancePath = docsPaths.governancePath;
|
|
504
|
-
}
|
|
505
|
-
if (gatePreset && GATE_PRESETS[gatePreset]) {
|
|
506
|
-
const presetConfig = GATE_PRESETS[gatePreset];
|
|
507
|
-
config.gates.execution = {
|
|
508
|
-
preset: gatePreset,
|
|
509
|
-
...presetConfig
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
config.lanes = {
|
|
513
|
-
lifecycle: {
|
|
514
|
-
status: LANE_LIFECYCLE_STATUS.UNCONFIGURED
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
if (gitConfigOverride) {
|
|
518
|
-
config.git = {
|
|
519
|
-
requireRemote: gitConfigOverride.requireRemote
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
if (targetDir) {
|
|
523
|
-
const tooling = detectProjectTooling(targetDir);
|
|
524
|
-
if (tooling.testRunner) {
|
|
525
|
-
config.test_runner = tooling.testRunner;
|
|
526
|
-
} else {
|
|
527
|
-
config.test_runner = "jest";
|
|
528
|
-
}
|
|
529
|
-
const pm = tooling.packageManager ?? "pnpm";
|
|
530
|
-
const commands = {};
|
|
531
|
-
if (tooling.hasTurbo) {
|
|
532
|
-
commands.test_full = `${pm} turbo run test`;
|
|
533
|
-
} else {
|
|
534
|
-
commands.test_full = `${pm} test`;
|
|
535
|
-
}
|
|
536
|
-
if (tooling.testRunner === "vitest") {
|
|
537
|
-
commands.test_incremental = `${pm} vitest run --changed origin/main`;
|
|
538
|
-
} else if (tooling.testRunner === "jest") {
|
|
539
|
-
commands.test_incremental = `${pm} jest --onlyChanged`;
|
|
540
|
-
} else {
|
|
541
|
-
commands.test_incremental = `${pm} test`;
|
|
542
|
-
}
|
|
543
|
-
config.gates.commands = {
|
|
544
|
-
...config.gates.commands,
|
|
545
|
-
...commands
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
if (client === "claude") {
|
|
549
|
-
config.agents = {
|
|
550
|
-
clients: {
|
|
551
|
-
[DEFAULT_CLIENT_CLAUDE2]: {
|
|
552
|
-
enforcement: {
|
|
553
|
-
hooks: true,
|
|
554
|
-
block_outside_worktree: true,
|
|
555
|
-
require_wu_for_edits: true,
|
|
556
|
-
warn_on_stop_without_wu_done: true
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
return config;
|
|
563
|
-
}
|
|
564
|
-
function getCurrentDate() {
|
|
565
|
-
return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
566
|
-
}
|
|
567
|
-
function normalizeFrameworkName(framework) {
|
|
568
|
-
const name = framework.trim();
|
|
569
|
-
const slug = name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
|
|
570
|
-
if (!slug) {
|
|
571
|
-
throw createError(ErrorCodes.INVALID_ARGUMENT, `Invalid framework name: "${framework}"`);
|
|
572
|
-
}
|
|
573
|
-
return { name, slug };
|
|
574
|
-
}
|
|
575
|
-
function resolveClientType(client, vendor, defaultClient) {
|
|
576
|
-
if (client) {
|
|
577
|
-
return client;
|
|
578
|
-
}
|
|
579
|
-
if (vendor) {
|
|
580
|
-
return vendor;
|
|
581
|
-
}
|
|
582
|
-
return defaultClient === DEFAULT_CLIENT_CLAUDE2 ? "claude" : "none";
|
|
583
|
-
}
|
|
584
|
-
function getFileMode(options) {
|
|
585
|
-
if (options.force) {
|
|
586
|
-
return "force";
|
|
587
|
-
}
|
|
588
|
-
if (options.merge) {
|
|
589
|
-
return "merge";
|
|
590
|
-
}
|
|
591
|
-
return "skip";
|
|
592
|
-
}
|
|
593
|
-
var CLIENT_INTEGRATIONS = {
|
|
594
|
-
[LUMENFLOW_CLIENT_IDS.CLAUDE_CODE]: {
|
|
595
|
-
run: (projectDir, enforcement) => integrateClaudeCode(projectDir, { enforcement })
|
|
596
|
-
}
|
|
597
|
-
// When new clients gain enforcement: add adapter entry here.
|
|
598
|
-
};
|
|
599
|
-
async function runClientIntegrations(targetDir, result) {
|
|
600
|
-
const integrationFiles = [];
|
|
601
|
-
const configPath = path2.join(targetDir, CONFIG_FILE_NAME);
|
|
602
|
-
if (!fs2.existsSync(configPath)) return integrationFiles;
|
|
603
|
-
let softwareDeliveryConfig;
|
|
604
|
-
try {
|
|
605
|
-
const content = fs2.readFileSync(configPath, "utf-8");
|
|
606
|
-
const workspaceConfig = asRecord(yaml.parse(content));
|
|
607
|
-
softwareDeliveryConfig = workspaceConfig ? asRecord(workspaceConfig[SOFTWARE_DELIVERY_KEY]) : null;
|
|
608
|
-
} catch {
|
|
609
|
-
return integrationFiles;
|
|
610
|
-
}
|
|
611
|
-
if (!softwareDeliveryConfig) return integrationFiles;
|
|
612
|
-
const agents = asRecord(softwareDeliveryConfig[SOFTWARE_DELIVERY_CONFIG_KEYS.AGENTS]);
|
|
613
|
-
const clients = asRecord(agents?.[SOFTWARE_DELIVERY_CONFIG_KEYS.CLIENTS]);
|
|
614
|
-
if (!clients) return integrationFiles;
|
|
615
|
-
for (const [clientKey, unsafeClientConfig] of Object.entries(clients)) {
|
|
616
|
-
const clientConfig = asRecord(unsafeClientConfig);
|
|
617
|
-
if (!clientConfig) {
|
|
618
|
-
continue;
|
|
619
|
-
}
|
|
620
|
-
const enforcement = asRecord(
|
|
621
|
-
clientConfig[SOFTWARE_DELIVERY_CONFIG_KEYS.ENFORCEMENT]
|
|
622
|
-
);
|
|
623
|
-
if (!enforcement?.hooks) continue;
|
|
624
|
-
const integration = CLIENT_INTEGRATIONS[clientKey];
|
|
625
|
-
if (!integration) continue;
|
|
626
|
-
const createdFiles = await integration.run(targetDir, enforcement);
|
|
627
|
-
integrationFiles.push(...createdFiles);
|
|
628
|
-
}
|
|
629
|
-
result.created.push(...integrationFiles);
|
|
630
|
-
result.integrationFiles = integrationFiles;
|
|
631
|
-
return integrationFiles;
|
|
632
|
-
}
|
|
633
|
-
function createInitialCommitIfNeeded(targetDir) {
|
|
634
|
-
if (!isGitRepo(targetDir) || hasGitCommits(targetDir)) {
|
|
635
|
-
return false;
|
|
636
|
-
}
|
|
637
|
-
try {
|
|
638
|
-
execFileSync2("git", ["add", "."], { cwd: targetDir, stdio: "pipe" });
|
|
639
|
-
execFileSync2("git", ["commit", "-m", "chore: initialize LumenFlow project"], {
|
|
640
|
-
cwd: targetDir,
|
|
641
|
-
stdio: "pipe"
|
|
642
|
-
});
|
|
643
|
-
return true;
|
|
644
|
-
} catch {
|
|
645
|
-
return false;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
function renameMasterToMainIfNeeded(targetDir) {
|
|
649
|
-
if (!isGitRepo(targetDir)) {
|
|
650
|
-
return false;
|
|
651
|
-
}
|
|
652
|
-
try {
|
|
653
|
-
const currentBranch = execFileSync2("git", ["branch", "--show-current"], {
|
|
654
|
-
cwd: targetDir,
|
|
655
|
-
encoding: "utf-8",
|
|
656
|
-
stdio: "pipe"
|
|
657
|
-
}).trim();
|
|
658
|
-
if (currentBranch !== "master") {
|
|
659
|
-
return false;
|
|
660
|
-
}
|
|
661
|
-
execFileSync2("git", ["branch", "-m", "master", "main"], {
|
|
662
|
-
cwd: targetDir,
|
|
663
|
-
stdio: "pipe"
|
|
664
|
-
});
|
|
665
|
-
return true;
|
|
666
|
-
} catch {
|
|
667
|
-
return false;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
async function scaffoldProject(targetDir, options) {
|
|
671
|
-
const result = {
|
|
672
|
-
created: [],
|
|
673
|
-
skipped: [],
|
|
674
|
-
merged: [],
|
|
675
|
-
warnings: [],
|
|
676
|
-
overwritten: []
|
|
677
|
-
};
|
|
678
|
-
await checkBranchGuard(targetDir, result);
|
|
679
|
-
const defaultClient = options.defaultClient ?? detectDefaultClient();
|
|
680
|
-
const client = resolveClientType(options.client, options.vendor, defaultClient);
|
|
681
|
-
const fileMode = getFileMode(options);
|
|
682
|
-
if (!fs2.existsSync(targetDir)) {
|
|
683
|
-
fs2.mkdirSync(targetDir, { recursive: true });
|
|
684
|
-
}
|
|
685
|
-
const docsStructure = options.docsStructure ?? detectDocsStructure(targetDir);
|
|
686
|
-
const docsPaths = getDocsPath(docsStructure);
|
|
687
|
-
const gitConfigOverride = detectGitStateConfig(targetDir);
|
|
688
|
-
const tokenDefaults = {
|
|
689
|
-
DATE: getCurrentDate(),
|
|
690
|
-
PROJECT_ROOT: "<project-root>",
|
|
691
|
-
// WU-1309: Use portable placeholder
|
|
692
|
-
QUICK_REF_LINK: docsPaths.quickRefLink,
|
|
693
|
-
DOCS_OPERATIONS_PATH: docsPaths.operations,
|
|
694
|
-
// WU-1309: For framework overlay
|
|
695
|
-
DOCS_TASKS_PATH: docsPaths.tasks,
|
|
696
|
-
DOCS_ONBOARDING_PATH: docsPaths.onboarding,
|
|
697
|
-
DOCS_WU_DIR_PATH: `${docsPaths.tasks}/wu`,
|
|
698
|
-
DOCS_TEMPLATES_DIR_PATH: `${docsPaths.tasks}/templates`,
|
|
699
|
-
DOCS_BACKLOG_PATH: `${docsPaths.tasks}/backlog.md`,
|
|
700
|
-
DOCS_STATUS_PATH: `${docsPaths.tasks}/status.md`
|
|
701
|
-
};
|
|
702
|
-
upsertWorkspaceSoftwareDelivery(
|
|
703
|
-
targetDir,
|
|
704
|
-
buildSoftwareDeliveryConfig(
|
|
705
|
-
options.gatePreset,
|
|
706
|
-
gitConfigOverride,
|
|
707
|
-
client,
|
|
708
|
-
docsPaths,
|
|
709
|
-
targetDir
|
|
710
|
-
),
|
|
711
|
-
result
|
|
712
|
-
);
|
|
713
|
-
try {
|
|
714
|
-
const agentsTemplate = loadTemplate("core/AGENTS.md.template");
|
|
715
|
-
await createFile(
|
|
716
|
-
path2.join(targetDir, "AGENTS.md"),
|
|
717
|
-
processTemplate(agentsTemplate, tokenDefaults),
|
|
718
|
-
fileMode,
|
|
719
|
-
result,
|
|
720
|
-
targetDir
|
|
721
|
-
);
|
|
722
|
-
} catch {
|
|
723
|
-
await createFile(
|
|
724
|
-
path2.join(targetDir, "AGENTS.md"),
|
|
725
|
-
processTemplate(AGENTS_MD_TEMPLATE, tokenDefaults),
|
|
726
|
-
fileMode,
|
|
727
|
-
result,
|
|
728
|
-
targetDir
|
|
729
|
-
);
|
|
730
|
-
}
|
|
731
|
-
await createFile(
|
|
732
|
-
path2.join(targetDir, "LUMENFLOW.md"),
|
|
733
|
-
processTemplate(loadTemplate("core/LUMENFLOW.md.template"), tokenDefaults),
|
|
734
|
-
fileMode,
|
|
735
|
-
result,
|
|
736
|
-
targetDir
|
|
737
|
-
);
|
|
738
|
-
await createFile(
|
|
739
|
-
path2.join(targetDir, LUMENFLOW_DIR, "constraints.md"),
|
|
740
|
-
processTemplate(loadTemplate("core/.lumenflow/constraints.md.template"), tokenDefaults),
|
|
741
|
-
fileMode,
|
|
742
|
-
result,
|
|
743
|
-
targetDir
|
|
744
|
-
);
|
|
745
|
-
await createDirectory(path2.join(targetDir, LUMENFLOW_AGENTS_DIR), result, targetDir);
|
|
746
|
-
await createFile(
|
|
747
|
-
path2.join(targetDir, LUMENFLOW_AGENTS_DIR, ".gitkeep"),
|
|
748
|
-
"",
|
|
749
|
-
options.force ? "force" : "skip",
|
|
750
|
-
result,
|
|
751
|
-
targetDir
|
|
752
|
-
);
|
|
753
|
-
await scaffoldGitignore(targetDir, options, result);
|
|
754
|
-
await scaffoldPrettierignore(targetDir, options, result);
|
|
755
|
-
await scaffoldSafetyScripts(targetDir, options, result);
|
|
756
|
-
if (options.full) {
|
|
757
|
-
await scaffoldFullDocs(targetDir, options, result, tokenDefaults);
|
|
758
|
-
}
|
|
759
|
-
if (options.framework) {
|
|
760
|
-
await scaffoldFrameworkOverlay(targetDir, options, result, tokenDefaults);
|
|
761
|
-
}
|
|
762
|
-
await scaffoldClientFiles(targetDir, options, result, tokenDefaults, client);
|
|
763
|
-
await injectPackageJsonScripts(targetDir, options, result);
|
|
764
|
-
await runClientIntegrations(targetDir, result);
|
|
765
|
-
const createdInitialCommit = createInitialCommitIfNeeded(targetDir);
|
|
766
|
-
if (createdInitialCommit) {
|
|
767
|
-
result.created.push("Initial git commit");
|
|
768
|
-
}
|
|
769
|
-
const renamedBranch = renameMasterToMainIfNeeded(targetDir);
|
|
770
|
-
if (renamedBranch) {
|
|
771
|
-
result.created.push("Renamed branch master -> main");
|
|
772
|
-
}
|
|
773
|
-
const uniqueParents = [
|
|
774
|
-
...new Set(DEFAULT_LANE_DEFINITIONS.map((lane) => lane.name.split(":")[0].trim()))
|
|
775
|
-
];
|
|
776
|
-
console.log("");
|
|
777
|
-
console.log(
|
|
778
|
-
' Lane naming format: "Parent: Sublane" (e.g., "Framework: Core", "Content: Documentation")'
|
|
779
|
-
);
|
|
780
|
-
console.log(` Valid parent names: ${uniqueParents.join(", ")}`);
|
|
781
|
-
return result;
|
|
782
|
-
}
|
|
783
|
-
var GITIGNORE_FILE_NAME = ".gitignore";
|
|
784
|
-
async function scaffoldGitignore(targetDir, options, result) {
|
|
785
|
-
const gitignorePath = path2.join(targetDir, GITIGNORE_FILE_NAME);
|
|
786
|
-
const fileMode = getFileMode(options);
|
|
787
|
-
if ((fileMode === "merge" || fileMode === "skip") && fs2.existsSync(gitignorePath)) {
|
|
788
|
-
const existingContent = fs2.readFileSync(gitignorePath, "utf-8");
|
|
789
|
-
const linesToAdd = [];
|
|
790
|
-
for (const { pattern, line } of REQUIRED_GITIGNORE_EXCLUSIONS) {
|
|
791
|
-
if (!existingContent.includes(pattern)) {
|
|
792
|
-
linesToAdd.push(line);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
if (linesToAdd.length > 0) {
|
|
796
|
-
const separator = existingContent.endsWith("\n") ? "" : "\n";
|
|
797
|
-
const lumenflowBlock = `${separator}
|
|
798
|
-
# LumenFlow (auto-added)
|
|
799
|
-
${linesToAdd.join("\n")}
|
|
800
|
-
`;
|
|
801
|
-
fs2.writeFileSync(gitignorePath, existingContent + lumenflowBlock);
|
|
802
|
-
result.merged = result.merged ?? [];
|
|
803
|
-
result.merged.push(GITIGNORE_FILE_NAME);
|
|
804
|
-
} else {
|
|
805
|
-
result.skipped.push(GITIGNORE_FILE_NAME);
|
|
806
|
-
}
|
|
807
|
-
return;
|
|
808
|
-
}
|
|
809
|
-
await createFile(gitignorePath, GITIGNORE_TEMPLATE, fileMode, result, targetDir);
|
|
810
|
-
}
|
|
811
|
-
var PRETTIERIGNORE_FILE_NAME = ".prettierignore";
|
|
812
|
-
async function scaffoldPrettierignore(targetDir, options, result) {
|
|
813
|
-
const prettierignorePath = path2.join(targetDir, PRETTIERIGNORE_FILE_NAME);
|
|
814
|
-
const fileMode = getFileMode(options);
|
|
815
|
-
await createFile(prettierignorePath, PRETTIERIGNORE_TEMPLATE, fileMode, result, targetDir);
|
|
816
|
-
}
|
|
817
|
-
function generateLumenflowScripts() {
|
|
818
|
-
const scripts = {};
|
|
819
|
-
const manifest = getPublicManifest();
|
|
820
|
-
for (const cmd of manifest) {
|
|
821
|
-
scripts[cmd.name] = SCRIPT_ARG_OVERRIDES[cmd.name] ?? cmd.binName;
|
|
822
|
-
}
|
|
823
|
-
return scripts;
|
|
824
|
-
}
|
|
825
|
-
var SCRIPTS_DIR = "scripts";
|
|
826
|
-
var SAFE_GIT_FILE = "safe-git";
|
|
827
|
-
var HUSKY_DIR = ".husky";
|
|
828
|
-
var PRE_COMMIT_FILE = "pre-commit";
|
|
829
|
-
var SAFE_GIT_TEMPLATE_PATH = "core/scripts/safe-git.template";
|
|
830
|
-
var PRE_COMMIT_TEMPLATE_PATH = "core/.husky/pre-commit.template";
|
|
831
|
-
async function scaffoldSafetyScripts(targetDir, options, result) {
|
|
832
|
-
const fileMode = getFileMode(options);
|
|
833
|
-
const safeGitPath = path2.join(targetDir, SCRIPTS_DIR, SAFE_GIT_FILE);
|
|
834
|
-
try {
|
|
835
|
-
const safeGitTemplate = loadTemplate(SAFE_GIT_TEMPLATE_PATH);
|
|
836
|
-
await createExecutableScript(safeGitPath, safeGitTemplate, fileMode, result, targetDir);
|
|
837
|
-
} catch {
|
|
838
|
-
await createExecutableScript(safeGitPath, SAFE_GIT_TEMPLATE, fileMode, result, targetDir);
|
|
839
|
-
}
|
|
840
|
-
const preCommitPath = path2.join(targetDir, HUSKY_DIR, PRE_COMMIT_FILE);
|
|
841
|
-
try {
|
|
842
|
-
const preCommitTemplate = loadTemplate(PRE_COMMIT_TEMPLATE_PATH);
|
|
843
|
-
await createExecutableScript(preCommitPath, preCommitTemplate, fileMode, result, targetDir);
|
|
844
|
-
} catch {
|
|
845
|
-
await createExecutableScript(preCommitPath, PRE_COMMIT_TEMPLATE, fileMode, result, targetDir);
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
var PRETTIER_VERSION = "^3.8.0";
|
|
849
|
-
var PRETTIER_PACKAGE_NAME = "prettier";
|
|
850
|
-
var CLI_PACKAGE_VERSION = "^3.0.0";
|
|
851
|
-
var CLI_PACKAGE_NAME = "@lumenflow/cli";
|
|
852
|
-
async function injectPackageJsonScripts(targetDir, options, result) {
|
|
853
|
-
const packageJsonPath = path2.join(targetDir, "package.json");
|
|
854
|
-
let packageJson;
|
|
855
|
-
if (fs2.existsSync(packageJsonPath)) {
|
|
856
|
-
const content = fs2.readFileSync(packageJsonPath, "utf-8");
|
|
857
|
-
packageJson = JSON.parse(content);
|
|
858
|
-
} else {
|
|
859
|
-
packageJson = {
|
|
860
|
-
name: path2.basename(targetDir),
|
|
861
|
-
version: "0.0.1",
|
|
862
|
-
private: true
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
if (!packageJson.scripts || typeof packageJson.scripts !== "object") {
|
|
866
|
-
packageJson.scripts = {};
|
|
867
|
-
}
|
|
868
|
-
const scripts = packageJson.scripts;
|
|
869
|
-
let modified = false;
|
|
870
|
-
const lumenflowScripts = generateLumenflowScripts();
|
|
871
|
-
for (const [scriptName, scriptCommand] of Object.entries(lumenflowScripts)) {
|
|
872
|
-
if (options.force || !(scriptName in scripts)) {
|
|
873
|
-
if (!(scriptName in scripts)) {
|
|
874
|
-
scripts[scriptName] = scriptCommand;
|
|
875
|
-
modified = true;
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
for (const [scriptName, scriptCommand] of Object.entries(GATE_STUB_SCRIPTS)) {
|
|
880
|
-
if (options.force) {
|
|
881
|
-
scripts[scriptName] = scriptCommand;
|
|
882
|
-
modified = true;
|
|
883
|
-
} else if (!(scriptName in scripts)) {
|
|
884
|
-
scripts[scriptName] = scriptCommand;
|
|
885
|
-
modified = true;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
if (!packageJson.devDependencies || typeof packageJson.devDependencies !== "object") {
|
|
889
|
-
packageJson.devDependencies = {};
|
|
890
|
-
}
|
|
891
|
-
const devDeps = packageJson.devDependencies;
|
|
892
|
-
if (options.force || !(CLI_PACKAGE_NAME in devDeps)) {
|
|
893
|
-
if (options.force && CLI_PACKAGE_NAME in devDeps) {
|
|
894
|
-
devDeps[CLI_PACKAGE_NAME] = CLI_PACKAGE_VERSION;
|
|
895
|
-
modified = true;
|
|
896
|
-
} else if (!(CLI_PACKAGE_NAME in devDeps)) {
|
|
897
|
-
devDeps[CLI_PACKAGE_NAME] = CLI_PACKAGE_VERSION;
|
|
898
|
-
modified = true;
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
if (options.force || !(PRETTIER_PACKAGE_NAME in devDeps)) {
|
|
902
|
-
if (!(PRETTIER_PACKAGE_NAME in devDeps)) {
|
|
903
|
-
devDeps[PRETTIER_PACKAGE_NAME] = PRETTIER_VERSION;
|
|
904
|
-
modified = true;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
if (modified) {
|
|
908
|
-
fs2.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
|
|
909
|
-
result.created.push("package.json (scripts updated)");
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
async function runPostScaffoldInstall(targetDir, options) {
|
|
913
|
-
const packageJsonPath = path2.join(targetDir, "package.json");
|
|
914
|
-
if (!fs2.existsSync(packageJsonPath)) {
|
|
915
|
-
return { packageManager: "npm", command: "npm install", skipped: true };
|
|
916
|
-
}
|
|
917
|
-
let pm = "npm";
|
|
918
|
-
try {
|
|
919
|
-
const content = fs2.readFileSync(packageJsonPath, "utf-8");
|
|
920
|
-
const pkg = JSON.parse(content);
|
|
921
|
-
const pkgManager = pkg.packageManager;
|
|
922
|
-
if (pkgManager?.startsWith("pnpm")) {
|
|
923
|
-
pm = "pnpm";
|
|
924
|
-
} else if (pkgManager?.startsWith("yarn")) {
|
|
925
|
-
pm = "yarn";
|
|
926
|
-
} else if (pkgManager?.startsWith("bun")) {
|
|
927
|
-
pm = "bun";
|
|
928
|
-
} else if (pkgManager?.startsWith("npm")) {
|
|
929
|
-
pm = "npm";
|
|
930
|
-
} else {
|
|
931
|
-
if (fs2.existsSync(path2.join(targetDir, "pnpm-lock.yaml"))) {
|
|
932
|
-
pm = "pnpm";
|
|
933
|
-
} else if (fs2.existsSync(path2.join(targetDir, "yarn.lock"))) {
|
|
934
|
-
pm = "yarn";
|
|
935
|
-
} else if (fs2.existsSync(path2.join(targetDir, "bun.lockb")) || fs2.existsSync(path2.join(targetDir, "bun.lock"))) {
|
|
936
|
-
pm = "bun";
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
} catch {
|
|
940
|
-
}
|
|
941
|
-
const command = `${pm} install`;
|
|
942
|
-
if (options?.dryRun) {
|
|
943
|
-
return { packageManager: pm, command, skipped: false };
|
|
944
|
-
}
|
|
945
|
-
try {
|
|
946
|
-
console.log(`
|
|
947
|
-
[lumenflow init] Installing dependencies (${command})...`);
|
|
948
|
-
execFileSync2(pm, ["install"], {
|
|
949
|
-
cwd: targetDir,
|
|
950
|
-
stdio: "inherit"
|
|
951
|
-
});
|
|
952
|
-
console.log("[lumenflow init] \u2713 Dependencies installed");
|
|
953
|
-
return { packageManager: pm, command, skipped: false };
|
|
954
|
-
} catch (err) {
|
|
955
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
956
|
-
console.warn(`[lumenflow init] \u26A0 ${command} failed: ${errorMessage}`);
|
|
957
|
-
console.warn(`[lumenflow init] Run "${command}" manually to install dependencies.`);
|
|
958
|
-
return { packageManager: pm, command, skipped: false, error: errorMessage };
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
async function scaffoldFullDocs(targetDir, options, result, tokens) {
|
|
962
|
-
const wuDir = path2.join(targetDir, tokens.DOCS_WU_DIR_PATH);
|
|
963
|
-
const templatesDir = path2.join(targetDir, tokens.DOCS_TEMPLATES_DIR_PATH);
|
|
964
|
-
await createDirectory(wuDir, result, targetDir);
|
|
965
|
-
await createDirectory(templatesDir, result, targetDir);
|
|
966
|
-
await createFile(path2.join(wuDir, ".gitkeep"), "", options.force, result, targetDir);
|
|
967
|
-
await createFile(
|
|
968
|
-
path2.join(targetDir, tokens.DOCS_BACKLOG_PATH),
|
|
969
|
-
BACKLOG_TEMPLATE,
|
|
970
|
-
options.force,
|
|
971
|
-
result,
|
|
972
|
-
targetDir
|
|
973
|
-
);
|
|
974
|
-
await createFile(
|
|
975
|
-
path2.join(targetDir, tokens.DOCS_STATUS_PATH),
|
|
976
|
-
STATUS_TEMPLATE,
|
|
977
|
-
options.force,
|
|
978
|
-
result,
|
|
979
|
-
targetDir
|
|
980
|
-
);
|
|
981
|
-
await createFile(
|
|
982
|
-
path2.join(templatesDir, "wu-template.yaml"),
|
|
983
|
-
processTemplate(WU_TEMPLATE_YAML, tokens),
|
|
984
|
-
options.force,
|
|
985
|
-
result,
|
|
986
|
-
targetDir
|
|
987
|
-
);
|
|
988
|
-
await scaffoldAgentOnboardingDocs(targetDir, options, result, tokens);
|
|
989
|
-
}
|
|
990
|
-
async function scaffoldAgentOnboardingDocs(targetDir, options, result, tokens) {
|
|
991
|
-
const onboardingDir = path2.join(targetDir, tokens.DOCS_ONBOARDING_PATH);
|
|
992
|
-
await createDirectory(onboardingDir, result, targetDir);
|
|
993
|
-
for (const [outputFile, templatePath] of Object.entries(SCAFFOLDED_ONBOARDING_TEMPLATE_PATHS)) {
|
|
994
|
-
await createFile(
|
|
995
|
-
path2.join(onboardingDir, outputFile),
|
|
996
|
-
processTemplate(loadTemplate(templatePath), tokens),
|
|
997
|
-
options.force,
|
|
998
|
-
result,
|
|
999
|
-
targetDir
|
|
1000
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
async function scaffoldClaudeSkills(targetDir, options, result, tokens) {
|
|
1004
|
-
const skillsDir = path2.join(targetDir, ".claude", "skills");
|
|
1005
|
-
const wuLifecycleDir = path2.join(skillsDir, "wu-lifecycle");
|
|
1006
|
-
await createDirectory(wuLifecycleDir, result, targetDir);
|
|
1007
|
-
await createFile(
|
|
1008
|
-
path2.join(wuLifecycleDir, "SKILL.md"),
|
|
1009
|
-
processTemplate(WU_LIFECYCLE_SKILL_TEMPLATE, tokens),
|
|
1010
|
-
options.force,
|
|
1011
|
-
result,
|
|
1012
|
-
targetDir
|
|
1013
|
-
);
|
|
1014
|
-
const worktreeDir = path2.join(skillsDir, "worktree-discipline");
|
|
1015
|
-
await createDirectory(worktreeDir, result, targetDir);
|
|
1016
|
-
await createFile(
|
|
1017
|
-
path2.join(worktreeDir, "SKILL.md"),
|
|
1018
|
-
processTemplate(WORKTREE_DISCIPLINE_SKILL_TEMPLATE, tokens),
|
|
1019
|
-
options.force,
|
|
1020
|
-
result,
|
|
1021
|
-
targetDir
|
|
1022
|
-
);
|
|
1023
|
-
const gatesDir = path2.join(skillsDir, "lumenflow-gates");
|
|
1024
|
-
await createDirectory(gatesDir, result, targetDir);
|
|
1025
|
-
await createFile(
|
|
1026
|
-
path2.join(gatesDir, "SKILL.md"),
|
|
1027
|
-
processTemplate(LUMENFLOW_GATES_SKILL_TEMPLATE, tokens),
|
|
1028
|
-
options.force,
|
|
1029
|
-
result,
|
|
1030
|
-
targetDir
|
|
1031
|
-
);
|
|
1032
|
-
}
|
|
1033
|
-
async function scaffoldFrameworkOverlay(targetDir, options, result, tokens) {
|
|
1034
|
-
if (!options.framework) {
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
const { name, slug } = normalizeFrameworkName(options.framework);
|
|
1038
|
-
const frameworkTokens = {
|
|
1039
|
-
...tokens,
|
|
1040
|
-
FRAMEWORK_NAME: name,
|
|
1041
|
-
FRAMEWORK_SLUG: slug
|
|
1042
|
-
};
|
|
1043
|
-
await createFile(
|
|
1044
|
-
path2.join(targetDir, FRAMEWORK_HINT_FILE),
|
|
1045
|
-
processTemplate(FRAMEWORK_HINT_TEMPLATE, frameworkTokens),
|
|
1046
|
-
options.force,
|
|
1047
|
-
result,
|
|
1048
|
-
targetDir
|
|
1049
|
-
);
|
|
1050
|
-
const overlayDir = path2.join(targetDir, tokens.DOCS_OPERATIONS_PATH, "_frameworks", slug);
|
|
1051
|
-
await createDirectory(overlayDir, result, targetDir);
|
|
1052
|
-
await createFile(
|
|
1053
|
-
path2.join(overlayDir, "README.md"),
|
|
1054
|
-
processTemplate(FRAMEWORK_OVERLAY_TEMPLATE, frameworkTokens),
|
|
1055
|
-
options.force,
|
|
1056
|
-
result,
|
|
1057
|
-
targetDir
|
|
1058
|
-
);
|
|
1059
|
-
}
|
|
1060
|
-
async function scaffoldClientFiles(targetDir, options, result, tokens, client) {
|
|
1061
|
-
const fileMode = getFileMode(options);
|
|
1062
|
-
if (client === "claude" || client === "all") {
|
|
1063
|
-
await createFile(
|
|
1064
|
-
path2.join(targetDir, "CLAUDE.md"),
|
|
1065
|
-
processTemplate(CLAUDE_MD_TEMPLATE, tokens),
|
|
1066
|
-
fileMode,
|
|
1067
|
-
result,
|
|
1068
|
-
targetDir
|
|
1069
|
-
);
|
|
1070
|
-
await createDirectory(path2.join(targetDir, CLAUDE_AGENTS_DIR), result, targetDir);
|
|
1071
|
-
await createFile(
|
|
1072
|
-
path2.join(targetDir, CLAUDE_AGENTS_DIR, ".gitkeep"),
|
|
1073
|
-
"",
|
|
1074
|
-
options.force ? "force" : "skip",
|
|
1075
|
-
result,
|
|
1076
|
-
targetDir
|
|
1077
|
-
);
|
|
1078
|
-
let settingsContent;
|
|
1079
|
-
try {
|
|
1080
|
-
settingsContent = loadTemplate(CLAUDE_HOOKS.TEMPLATES.SETTINGS);
|
|
1081
|
-
} catch {
|
|
1082
|
-
settingsContent = CLAUDE_SETTINGS_TEMPLATE;
|
|
1083
|
-
}
|
|
1084
|
-
await createFile(
|
|
1085
|
-
path2.join(targetDir, CLAUDE_DIR, "settings.json"),
|
|
1086
|
-
settingsContent,
|
|
1087
|
-
options.force ? "force" : "skip",
|
|
1088
|
-
result,
|
|
1089
|
-
targetDir
|
|
1090
|
-
);
|
|
1091
|
-
let mcpJsonContent;
|
|
1092
|
-
try {
|
|
1093
|
-
mcpJsonContent = loadTemplate("core/.mcp.json.template");
|
|
1094
|
-
} catch {
|
|
1095
|
-
mcpJsonContent = MCP_JSON_TEMPLATE;
|
|
1096
|
-
}
|
|
1097
|
-
await createFile(
|
|
1098
|
-
path2.join(targetDir, ".mcp.json"),
|
|
1099
|
-
mcpJsonContent,
|
|
1100
|
-
fileMode,
|
|
1101
|
-
result,
|
|
1102
|
-
targetDir
|
|
1103
|
-
);
|
|
1104
|
-
const hooksDir = path2.join(targetDir, CLAUDE_DIR, "hooks");
|
|
1105
|
-
await createDirectory(hooksDir, result, targetDir);
|
|
1106
|
-
try {
|
|
1107
|
-
const preCompactScript = loadTemplate(CLAUDE_HOOKS.TEMPLATES.PRE_COMPACT);
|
|
1108
|
-
await createExecutableScript(
|
|
1109
|
-
path2.join(hooksDir, CLAUDE_HOOKS.SCRIPTS.PRE_COMPACT_CHECKPOINT),
|
|
1110
|
-
preCompactScript,
|
|
1111
|
-
options.force ? "force" : "skip",
|
|
1112
|
-
result,
|
|
1113
|
-
targetDir
|
|
1114
|
-
);
|
|
1115
|
-
} catch {
|
|
1116
|
-
}
|
|
1117
|
-
const sessionStartScript = generateSessionStartRecoveryScript();
|
|
1118
|
-
await createExecutableScript(
|
|
1119
|
-
path2.join(hooksDir, CLAUDE_HOOKS.SCRIPTS.SESSION_START_RECOVERY),
|
|
1120
|
-
sessionStartScript,
|
|
1121
|
-
options.force ? "force" : "skip",
|
|
1122
|
-
result,
|
|
1123
|
-
targetDir
|
|
1124
|
-
);
|
|
1125
|
-
await scaffoldClaudeSkills(targetDir, options, result, tokens);
|
|
1126
|
-
if (!options.full) {
|
|
1127
|
-
await scaffoldAgentOnboardingDocs(targetDir, options, result, tokens);
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
if (client === "cursor" || client === "all") {
|
|
1131
|
-
const cursorRulesDir = path2.join(targetDir, ".cursor", "rules");
|
|
1132
|
-
await createDirectory(cursorRulesDir, result, targetDir);
|
|
1133
|
-
let cursorContent;
|
|
1134
|
-
try {
|
|
1135
|
-
cursorContent = loadTemplate("vendors/cursor/.cursor/rules/lumenflow.md.template");
|
|
1136
|
-
} catch {
|
|
1137
|
-
cursorContent = CURSOR_RULES_TEMPLATE;
|
|
1138
|
-
}
|
|
1139
|
-
await createFile(
|
|
1140
|
-
path2.join(cursorRulesDir, "lumenflow.md"),
|
|
1141
|
-
processTemplate(cursorContent, tokens),
|
|
1142
|
-
fileMode,
|
|
1143
|
-
result,
|
|
1144
|
-
targetDir
|
|
1145
|
-
);
|
|
1146
|
-
}
|
|
1147
|
-
if (client === "windsurf" || client === "all") {
|
|
1148
|
-
const windsurfRulesDir = path2.join(targetDir, ".windsurf", "rules");
|
|
1149
|
-
await createDirectory(windsurfRulesDir, result, targetDir);
|
|
1150
|
-
let windsurfContent;
|
|
1151
|
-
try {
|
|
1152
|
-
windsurfContent = loadTemplate("vendors/windsurf/.windsurf/rules/lumenflow.md.template");
|
|
1153
|
-
} catch {
|
|
1154
|
-
windsurfContent = WINDSURF_RULES_TEMPLATE;
|
|
1155
|
-
}
|
|
1156
|
-
await createFile(
|
|
1157
|
-
path2.join(windsurfRulesDir, "lumenflow.md"),
|
|
1158
|
-
processTemplate(windsurfContent, tokens),
|
|
1159
|
-
fileMode,
|
|
1160
|
-
result,
|
|
1161
|
-
targetDir
|
|
1162
|
-
);
|
|
1163
|
-
}
|
|
1164
|
-
if (client === "cline" || client === "all") {
|
|
1165
|
-
let clineContent;
|
|
1166
|
-
try {
|
|
1167
|
-
clineContent = loadTemplate("vendors/cline/.clinerules.template");
|
|
1168
|
-
} catch {
|
|
1169
|
-
clineContent = CLINE_RULES_TEMPLATE;
|
|
1170
|
-
}
|
|
1171
|
-
await createFile(
|
|
1172
|
-
path2.join(targetDir, ".clinerules"),
|
|
1173
|
-
processTemplate(clineContent, tokens),
|
|
1174
|
-
fileMode,
|
|
1175
|
-
result,
|
|
1176
|
-
targetDir
|
|
1177
|
-
);
|
|
1178
|
-
}
|
|
1179
|
-
if (client === "aider" || client === "all") {
|
|
1180
|
-
await createFile(
|
|
1181
|
-
path2.join(targetDir, ".aider.conf.yml"),
|
|
1182
|
-
AIDER_CONF_TEMPLATE,
|
|
1183
|
-
fileMode,
|
|
1184
|
-
result,
|
|
1185
|
-
targetDir
|
|
1186
|
-
);
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
function resolveBootstrapProjectName(targetDir) {
|
|
1190
|
-
const basename2 = path2.basename(path2.resolve(targetDir)).trim();
|
|
1191
|
-
return basename2.length > 0 ? basename2 : DEFAULT_PROJECT_NAME;
|
|
1192
|
-
}
|
|
1193
|
-
async function runInitBootstrap(options) {
|
|
1194
|
-
if (options.skipBootstrap) {
|
|
1195
|
-
return {
|
|
1196
|
-
skipped: true,
|
|
1197
|
-
reason: BOOTSTRAP_SKIP_REASON_FLAG,
|
|
1198
|
-
workspaceGenerated: false,
|
|
1199
|
-
packInstalled: false
|
|
1200
|
-
};
|
|
1201
|
-
}
|
|
1202
|
-
const workspacePath = path2.join(options.targetDir, WORKSPACE_FILENAME);
|
|
1203
|
-
const hasExistingWorkspace = fs2.existsSync(workspacePath);
|
|
1204
|
-
if (hasExistingWorkspace && !options.force) {
|
|
1205
|
-
return {
|
|
1206
|
-
skipped: true,
|
|
1207
|
-
reason: BOOTSTRAP_SKIP_REASON_EXISTING_WORKSPACE,
|
|
1208
|
-
workspaceGenerated: false,
|
|
1209
|
-
packInstalled: false
|
|
1210
|
-
};
|
|
1211
|
-
}
|
|
1212
|
-
const onboardModule = await import("./onboard.js");
|
|
1213
|
-
const onboardResult = await onboardModule.runOnboard({
|
|
1214
|
-
targetDir: options.targetDir,
|
|
1215
|
-
nonInteractive: true,
|
|
1216
|
-
projectName: resolveBootstrapProjectName(options.targetDir),
|
|
1217
|
-
domain: options.bootstrapDomain,
|
|
1218
|
-
force: options.force,
|
|
1219
|
-
skipPackInstall: options.skipBootstrapPackInstall,
|
|
1220
|
-
skipDashboard: true,
|
|
1221
|
-
fetchFn: options.fetchFn ?? globalThis.fetch
|
|
1222
|
-
});
|
|
1223
|
-
if (!onboardResult.success) {
|
|
1224
|
-
const failureReason = onboardResult.errors.join("; ") || "unknown onboarding error";
|
|
1225
|
-
throw createError(ErrorCodes.ONBOARD_FAILED, `${BOOTSTRAP_ERROR_PREFIX} ${failureReason}`);
|
|
1226
|
-
}
|
|
1227
|
-
const packFailed = !options.skipBootstrapPackInstall && options.bootstrapDomain !== BOOTSTRAP_CUSTOM_DOMAIN && onboardResult.packInstalled !== true;
|
|
1228
|
-
const warning = packFailed ? `${BOOTSTRAP_ERROR_PREFIX} failed to install ${options.bootstrapDomain} pack with integrity metadata. Continuing without pack. Retry later with: pnpm pack:install --id ${options.bootstrapDomain}` : void 0;
|
|
1229
|
-
if (warning) {
|
|
1230
|
-
console.warn(`
|
|
1231
|
-
\u26A0 ${warning}
|
|
1232
|
-
`);
|
|
1233
|
-
}
|
|
1234
|
-
return {
|
|
1235
|
-
skipped: false,
|
|
1236
|
-
workspaceGenerated: onboardResult.workspaceGenerated === true,
|
|
1237
|
-
packInstalled: onboardResult.packInstalled === true,
|
|
1238
|
-
warning
|
|
1239
|
-
};
|
|
1240
|
-
}
|
|
1241
|
-
async function main() {
|
|
1242
|
-
const invokedBinary = path2.basename(process.argv[1] ?? "", ".js");
|
|
1243
|
-
const subcommand = process.argv[2];
|
|
1244
|
-
if (invokedBinary === CLOUD_CONNECT_BIN) {
|
|
1245
|
-
const { runCloudConnectCli } = await import("./onboard.js");
|
|
1246
|
-
await runCloudConnectCli();
|
|
1247
|
-
return;
|
|
1248
|
-
}
|
|
1249
|
-
if (subcommand === INIT_SUBCOMMANDS.COMMANDS) {
|
|
1250
|
-
const { main: commandsMain } = await import("./commands.js");
|
|
1251
|
-
process.argv.splice(2, 1);
|
|
1252
|
-
await commandsMain();
|
|
1253
|
-
return;
|
|
1254
|
-
}
|
|
1255
|
-
if (subcommand === LEGACY_SUBCOMMANDS.ONBOARD || subcommand === LEGACY_SUBCOMMANDS.WORKSPACE_INIT_COLON || subcommand === LEGACY_SUBCOMMANDS.WORKSPACE_INIT_DASH) {
|
|
1256
|
-
throw createError(
|
|
1257
|
-
ErrorCodes.DEPRECATED_API,
|
|
1258
|
-
`${LEGACY_SUBCOMMAND_ERROR_PREFIX} "${subcommand}". ${LEGACY_SUBCOMMAND_GUIDANCE}. ${LEGACY_SUBCOMMAND_HELP_HINT}.`
|
|
1259
|
-
);
|
|
1260
|
-
}
|
|
1261
|
-
if (subcommand === INIT_SUBCOMMANDS.CLOUD) {
|
|
1262
|
-
const cloudSubcommand = process.argv[3];
|
|
1263
|
-
if (cloudSubcommand !== CLOUD_SUBCOMMANDS.CONNECT) {
|
|
1264
|
-
throw createError(
|
|
1265
|
-
ErrorCodes.INVALID_ARGUMENT,
|
|
1266
|
-
`${INIT_ERROR_PREFIX} Unknown cloud subcommand "${cloudSubcommand ?? ""}". ${INIT_CLOUD_CONNECT_HELP}`
|
|
1267
|
-
);
|
|
1268
|
-
}
|
|
1269
|
-
const { runCloudConnectCli } = await import("./onboard.js");
|
|
1270
|
-
process.argv.splice(2, 2);
|
|
1271
|
-
await runCloudConnectCli();
|
|
1272
|
-
return;
|
|
1273
|
-
}
|
|
1274
|
-
const opts = parseInitOptions();
|
|
1275
|
-
const targetDir = process.cwd();
|
|
1276
|
-
console.log("[lumenflow init] Scaffolding LumenFlow project...");
|
|
1277
|
-
console.log(` Mode: ${opts.full ? "full" : "minimal"}${opts.merge ? " (merge)" : ""}`);
|
|
1278
|
-
console.log(` Framework: ${opts.framework ?? "none"}`);
|
|
1279
|
-
console.log(` Client: ${opts.client ?? "auto"}`);
|
|
1280
|
-
console.log(` Gate preset: ${opts.preset ?? "none (manual config)"}`);
|
|
1281
|
-
console.log(` Bootstrap domain: ${opts.bootstrapDomain}`);
|
|
1282
|
-
console.log(
|
|
1283
|
-
` Bootstrap pack install: ${opts.skipBootstrapPackInstall ? "skipped (--skip-bootstrap-pack-install)" : "required"}`
|
|
1284
|
-
);
|
|
1285
|
-
const bootstrapResult = await runInitBootstrap({
|
|
1286
|
-
targetDir,
|
|
1287
|
-
force: opts.force,
|
|
1288
|
-
bootstrapDomain: opts.bootstrapDomain,
|
|
1289
|
-
skipBootstrap: opts.skipBootstrap,
|
|
1290
|
-
skipBootstrapPackInstall: opts.skipBootstrapPackInstall
|
|
1291
|
-
});
|
|
1292
|
-
if (bootstrapResult.skipped) {
|
|
1293
|
-
console.log(` Bootstrap: skipped (${bootstrapResult.reason})`);
|
|
1294
|
-
} else {
|
|
1295
|
-
console.log(
|
|
1296
|
-
` Bootstrap: workspace=${bootstrapResult.workspaceGenerated ? "created" : "unchanged"}, pack=${bootstrapResult.packInstalled ? "installed" : "skipped"}`
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
|
-
const result = await scaffoldProject(targetDir, {
|
|
1300
|
-
force: opts.force,
|
|
1301
|
-
full: opts.full,
|
|
1302
|
-
merge: opts.merge,
|
|
1303
|
-
client: opts.client,
|
|
1304
|
-
vendor: opts.vendor,
|
|
1305
|
-
// Backwards compatibility
|
|
1306
|
-
framework: opts.framework,
|
|
1307
|
-
gatePreset: opts.preset
|
|
1308
|
-
});
|
|
1309
|
-
if (result.created.length > 0) {
|
|
1310
|
-
console.log("\nCreated:");
|
|
1311
|
-
result.created.forEach((f) => console.log(` + ${f}`));
|
|
1312
|
-
}
|
|
1313
|
-
if (result.merged && result.merged.length > 0) {
|
|
1314
|
-
console.log("\nMerged (LumenFlow block inserted/updated):");
|
|
1315
|
-
result.merged.forEach((f) => console.log(` ~ ${f}`));
|
|
1316
|
-
}
|
|
1317
|
-
if (result.overwritten && result.overwritten.length > 0) {
|
|
1318
|
-
console.log("\nOverwritten (existing file replaced with --force):");
|
|
1319
|
-
result.overwritten.forEach((f) => console.log(` ! ${f}`));
|
|
1320
|
-
}
|
|
1321
|
-
if (result.skipped.length > 0) {
|
|
1322
|
-
console.log("\nSkipped (already exists, use --force to overwrite or --merge to insert block):");
|
|
1323
|
-
result.skipped.forEach((f) => console.log(` - ${f}`));
|
|
1324
|
-
}
|
|
1325
|
-
if (result.warnings && result.warnings.length > 0) {
|
|
1326
|
-
console.log("\nWarnings:");
|
|
1327
|
-
result.warnings.forEach((w) => console.log(` \u26A0 ${w}`));
|
|
1328
|
-
}
|
|
1329
|
-
await runPostScaffoldInstall(targetDir);
|
|
1330
|
-
try {
|
|
1331
|
-
const doctorResult = await runDoctorForInit(targetDir);
|
|
1332
|
-
if (doctorResult.output) {
|
|
1333
|
-
console.log("");
|
|
1334
|
-
console.log(doctorResult.output);
|
|
1335
|
-
}
|
|
1336
|
-
} catch {
|
|
1337
|
-
}
|
|
1338
|
-
console.log("\n[lumenflow init] Done! Next steps:");
|
|
1339
|
-
console.log(" 1. Review AGENTS.md and LUMENFLOW.md for workflow documentation");
|
|
1340
|
-
console.log(
|
|
1341
|
-
` 2. Review ${CONFIG_FILE_NAME} ${SOFTWARE_DELIVERY_KEY} settings for project defaults`
|
|
1342
|
-
);
|
|
1343
|
-
console.log("");
|
|
1344
|
-
console.log(` ${buildInitLaneLifecycleMessage(LANE_LIFECYCLE_STATUS.UNCONFIGURED)}`);
|
|
1345
|
-
if (result.integrationFiles && result.integrationFiles.length > 0) {
|
|
1346
|
-
console.log(
|
|
1347
|
-
" \u2713 Enforcement hooks installed -- regenerate with: pnpm lumenflow:integrate"
|
|
1348
|
-
);
|
|
1349
|
-
}
|
|
1350
|
-
console.log("");
|
|
1351
|
-
console.log(" For a product vision (multi-phase work):");
|
|
1352
|
-
console.log(' pnpm initiative:create --id INIT-001 --title "Project Name" \\');
|
|
1353
|
-
console.log(' --phase "Phase 1: MVP" --phase "Phase 2: Polish"');
|
|
1354
|
-
console.log("");
|
|
1355
|
-
console.log(" For a single WU:");
|
|
1356
|
-
console.log(' pnpm wu:create --lane <lane> --title "First WU" \\');
|
|
1357
|
-
console.log(' --description "Context: ... Problem: ... Solution: ..." \\');
|
|
1358
|
-
console.log(' --acceptance "Criterion 1" --code-paths "src/..." --exposure backend-only');
|
|
1359
|
-
console.log("");
|
|
1360
|
-
console.log(" # Or for rapid prototyping (minimal validation):");
|
|
1361
|
-
console.log(' pnpm wu:proto --lane <lane> --title "Quick experiment"');
|
|
1362
|
-
console.log("");
|
|
1363
|
-
console.log(" Full lifecycle: wu:create -> wu:claim -> wu:prep -> wu:done");
|
|
1364
|
-
}
|
|
1365
|
-
if (import.meta.main) {
|
|
1366
|
-
void runCLI(main, { showHeader: true });
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
export {
|
|
1370
|
-
detectIDEEnvironment,
|
|
1371
|
-
checkPrerequisites,
|
|
1372
|
-
getDocsPath,
|
|
1373
|
-
detectDocsStructure,
|
|
1374
|
-
parseInitOptions,
|
|
1375
|
-
renameMasterToMainIfNeeded,
|
|
1376
|
-
scaffoldProject,
|
|
1377
|
-
runPostScaffoldInstall,
|
|
1378
|
-
runInitBootstrap,
|
|
1379
|
-
main
|
|
1380
|
-
};
|