javi-forge 1.6.1 → 1.7.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/README.md +45 -1
  2. package/ci-local/README.md +56 -11
  3. package/ci-local/ci-local.ps1 +445 -0
  4. package/ci-local/ci-local.sh +52 -9
  5. package/ci-local/docker/python.Dockerfile +7 -0
  6. package/ci-local/hooks/commit-msg +120 -44
  7. package/ci-local/hooks/commit-msg.test.sh +286 -0
  8. package/ci-local/hooks/pre-commit +9 -1
  9. package/ci-local/hooks/pre-push +13 -2
  10. package/ci-local/install.ps1 +367 -0
  11. package/ci-local/install.sh +151 -5
  12. package/ci-local/uninstall.ps1 +85 -0
  13. package/ci-local/uninstall.sh +95 -0
  14. package/dist/cli/dispatch/ci.d.ts +12 -0
  15. package/dist/cli/dispatch/ci.js +38 -0
  16. package/dist/cli/dispatch/security.d.ts +11 -0
  17. package/dist/cli/dispatch/security.js +68 -0
  18. package/dist/cli/dispatch/simple-renderers.d.ts +16 -0
  19. package/dist/cli/dispatch/simple-renderers.js +65 -0
  20. package/dist/cli/dispatch/skill-publish.d.ts +15 -0
  21. package/dist/cli/dispatch/skill-publish.js +44 -0
  22. package/dist/cli/dispatch/skills-cmd.d.ts +13 -0
  23. package/dist/cli/dispatch/skills-cmd.js +93 -0
  24. package/dist/cli/dispatch/tdd.d.ts +11 -0
  25. package/dist/cli/dispatch/tdd.js +45 -0
  26. package/dist/cli/dispatch/types.d.ts +29 -0
  27. package/dist/cli/dispatch/types.js +2 -0
  28. package/dist/cli/dispatch/workflow.d.ts +11 -0
  29. package/dist/cli/dispatch/workflow.js +67 -0
  30. package/dist/cli/help.d.ts +127 -0
  31. package/dist/cli/help.js +138 -0
  32. package/dist/cli/runtime.d.ts +16 -0
  33. package/dist/cli/runtime.js +38 -0
  34. package/dist/cli/validators.d.ts +17 -0
  35. package/dist/cli/validators.js +35 -0
  36. package/dist/commands/analyze.js +1 -3
  37. package/dist/commands/ci.d.ts +47 -0
  38. package/dist/commands/ci.js +380 -63
  39. package/dist/commands/doctor.js +2 -4
  40. package/dist/commands/init/report.d.ts +13 -0
  41. package/dist/commands/init/report.js +13 -0
  42. package/dist/commands/init/steps/agent-skills.d.ts +13 -0
  43. package/dist/commands/init/steps/agent-skills.js +45 -0
  44. package/dist/commands/init/steps/ai-sync.d.ts +15 -0
  45. package/dist/commands/init/steps/ai-sync.js +62 -0
  46. package/dist/commands/init/steps/ci.d.ts +24 -0
  47. package/dist/commands/init/steps/ci.js +74 -0
  48. package/dist/commands/init/steps/claude-md.d.ts +14 -0
  49. package/dist/commands/init/steps/claude-md.js +54 -0
  50. package/dist/commands/init/steps/code-graph.d.ts +16 -0
  51. package/dist/commands/init/steps/code-graph.js +68 -0
  52. package/dist/commands/init/steps/context-dir.d.ts +13 -0
  53. package/dist/commands/init/steps/context-dir.js +47 -0
  54. package/dist/commands/init/steps/docker-deploy.d.ts +16 -0
  55. package/dist/commands/init/steps/docker-deploy.js +60 -0
  56. package/dist/commands/init/steps/ghagga.d.ts +15 -0
  57. package/dist/commands/init/steps/ghagga.js +58 -0
  58. package/dist/commands/init/steps/git.d.ts +25 -0
  59. package/dist/commands/init/steps/git.js +81 -0
  60. package/dist/commands/init/steps/gitignore.d.ts +13 -0
  61. package/dist/commands/init/steps/gitignore.js +39 -0
  62. package/dist/commands/init/steps/local-ai.d.ts +14 -0
  63. package/dist/commands/init/steps/local-ai.js +53 -0
  64. package/dist/commands/init/steps/manifest.d.ts +13 -0
  65. package/dist/commands/init/steps/manifest.js +58 -0
  66. package/dist/commands/init/steps/memory.d.ts +15 -0
  67. package/dist/commands/init/steps/memory.js +60 -0
  68. package/dist/commands/init/steps/mock.d.ts +13 -0
  69. package/dist/commands/init/steps/mock.js +64 -0
  70. package/dist/commands/init/steps/sdd.d.ts +12 -0
  71. package/dist/commands/init/steps/sdd.js +40 -0
  72. package/dist/commands/init/steps/security.d.ts +28 -0
  73. package/dist/commands/init/steps/security.js +103 -0
  74. package/dist/commands/init/types.d.ts +30 -0
  75. package/dist/commands/init/types.js +2 -0
  76. package/dist/commands/init.d.ts +5 -4
  77. package/dist/commands/init.js +42 -630
  78. package/dist/commands/llmstxt.js +1 -1
  79. package/dist/commands/plugin.js +1 -1
  80. package/dist/commands/security.js +23 -4
  81. package/dist/commands/skills/analysis.d.ts +19 -0
  82. package/dist/commands/skills/analysis.js +211 -0
  83. package/dist/commands/skills/benchmark.d.ts +6 -0
  84. package/dist/commands/skills/benchmark.js +82 -0
  85. package/dist/commands/skills/constants.d.ts +10 -0
  86. package/dist/commands/skills/constants.js +27 -0
  87. package/dist/commands/skills/directives.d.ts +21 -0
  88. package/dist/commands/skills/directives.js +101 -0
  89. package/dist/commands/skills/parsing.d.ts +16 -0
  90. package/dist/commands/skills/parsing.js +108 -0
  91. package/dist/commands/skills/rules.d.ts +5 -0
  92. package/dist/commands/skills/rules.js +32 -0
  93. package/dist/commands/skills/scoring.d.ts +47 -0
  94. package/dist/commands/skills/scoring.js +306 -0
  95. package/dist/commands/skills.d.ts +7 -125
  96. package/dist/commands/skills.js +8 -874
  97. package/dist/commands/tdd-pipeline.js +1 -1
  98. package/dist/commands/tdd.js +1 -1
  99. package/dist/commands/workflow.js +2 -0
  100. package/dist/constants.js +2 -2
  101. package/dist/index.js +26 -473
  102. package/dist/lib/agent-skills.js +1 -1
  103. package/dist/{commands → lib}/atlassian-mcp.js +2 -2
  104. package/dist/lib/auto-skill-install.js +2 -3
  105. package/dist/lib/auto-wire.js +7 -7
  106. package/dist/lib/ci-config.d.ts +65 -0
  107. package/dist/lib/ci-config.js +297 -0
  108. package/dist/lib/claudemd.js +1 -1
  109. package/dist/lib/codex-export.js +4 -8
  110. package/dist/lib/common.js +2 -2
  111. package/dist/lib/context.js +2 -2
  112. package/dist/{commands → lib}/crash-recovery.js +4 -4
  113. package/dist/lib/docker.d.ts +22 -1
  114. package/dist/lib/docker.js +57 -49
  115. package/dist/lib/exec.d.ts +10 -0
  116. package/dist/lib/exec.js +11 -0
  117. package/dist/lib/frontmatter.js +2 -2
  118. package/dist/{commands → lib}/harness-audit.js +1 -1
  119. package/dist/{commands → lib}/parallel-batch.js +1 -3
  120. package/dist/lib/plugin.js +2 -4
  121. package/dist/{commands → lib}/secret-scanner.js +3 -1
  122. package/dist/{commands → lib}/security-analysis.js +4 -2
  123. package/dist/lib/skill-publish.js +23 -5
  124. package/dist/{commands → lib}/skill-scanner.d.ts +1 -1
  125. package/dist/{commands → lib}/skill-scanner.js +3 -3
  126. package/dist/lib/stack-detector.js +3 -3
  127. package/dist/{commands → lib}/supply-chain.js +1 -1
  128. package/dist/lib/template.js +2 -2
  129. package/dist/lib/workflow/discovery.d.ts +8 -0
  130. package/dist/lib/workflow/discovery.js +18 -1
  131. package/dist/lib/workflow/parser.js +15 -15
  132. package/dist/lib/workflow/validator.js +4 -4
  133. package/dist/tasks/index.d.ts +3 -3
  134. package/dist/tasks/index.js +3 -3
  135. package/dist/tasks/scaffold-tasks.d.ts +1 -1
  136. package/dist/tasks/scaffold-tasks.js +10 -10
  137. package/dist/tasks/task-id.d.ts +1 -1
  138. package/dist/tasks/task-id.js +9 -5
  139. package/dist/tasks/task-tracker.d.ts +1 -1
  140. package/dist/tasks/task-tracker.js +7 -7
  141. package/dist/ui/App.js +1 -1
  142. package/dist/ui/CIContext.js +1 -2
  143. package/dist/ui/Header.js +4 -4
  144. package/dist/ui/NameInput.js +1 -1
  145. package/dist/ui/Progress.d.ts +1 -1
  146. package/dist/ui/Progress.js +1 -1
  147. package/dist/ui/StackSelector.js +1 -1
  148. package/lib/common.psm1 +167 -0
  149. package/lib/common.sh +30 -0
  150. package/package.json +38 -19
  151. /package/dist/{commands → lib}/atlassian-mcp.d.ts +0 -0
  152. /package/dist/{commands → lib}/crash-recovery.d.ts +0 -0
  153. /package/dist/{commands → lib}/harness-audit.d.ts +0 -0
  154. /package/dist/{commands → lib}/parallel-batch.d.ts +0 -0
  155. /package/dist/{commands → lib}/secret-scanner.d.ts +0 -0
  156. /package/dist/{commands → lib}/security-analysis.d.ts +0 -0
  157. /package/dist/{commands → lib}/supply-chain.d.ts +0 -0
  158. /package/dist/{commands → lib}/team-presets.d.ts +0 -0
  159. /package/dist/{commands → lib}/team-presets.js +0 -0
@@ -1,640 +1,52 @@
1
- import { execFile } from "child_process";
2
- import fs from "fs-extra";
3
- import path from "path";
4
- import { promisify } from "util";
5
- import { AGENT_SKILLS_MANIFEST_FILE, CI_LOCAL_DIR, FORGE_ROOT, LOCAL_AI_TEMPLATE_DIR, MODULES_DIR, SECURITY_HOOKS_DIR, TEMPLATES_DIR, } from "../constants.js";
6
- import { generateSmartClaudeMd } from "../lib/claudemd.js";
7
- import { backupIfExists, ensureDirExists } from "../lib/common.js";
8
- import { generateContextDir } from "../lib/context.js";
9
- import { detectProjectStack } from "../lib/stack-detector.js";
10
- import { generateCIWorkflow, generateDependabotYml, generateDeployWorkflow, getCIDestination, getDeployDestination, } from "../lib/template.js";
11
- const execFileAsync = promisify(execFile);
12
- function report(onStep, id, label, status, detail) {
13
- onStep({ id, label, status, detail });
14
- }
1
+ import { ensureDirExists } from "../lib/common.js";
2
+ import { stepAgentSkills } from "./init/steps/agent-skills.js";
3
+ import { stepAISync } from "./init/steps/ai-sync.js";
4
+ import { stepCITemplate, stepDependabot } from "./init/steps/ci.js";
5
+ import { stepClaudeMd } from "./init/steps/claude-md.js";
6
+ import { stepCodeGraph } from "./init/steps/code-graph.js";
7
+ import { stepContextDir } from "./init/steps/context-dir.js";
8
+ import { stepDockerDeploy } from "./init/steps/docker-deploy.js";
9
+ import { stepGhagga } from "./init/steps/ghagga.js";
10
+ import { stepGitHooks, stepGitInit } from "./init/steps/git.js";
11
+ import { stepGitignore } from "./init/steps/gitignore.js";
12
+ import { stepLocalAi } from "./init/steps/local-ai.js";
13
+ import { stepManifest } from "./init/steps/manifest.js";
14
+ import { stepMemory } from "./init/steps/memory.js";
15
+ import { stepMock } from "./init/steps/mock.js";
16
+ import { stepSDD } from "./init/steps/sdd.js";
17
+ import { stepHookProfile, stepSecurityHooks } from "./init/steps/security.js";
15
18
  /**
16
19
  * Main init orchestrator: bootstraps a project with CI, git hooks,
17
- * memory module, AI config sync, SDD, and ghagga.
20
+ * memory module, AI config sync, SDD, ghagga, and friends.
21
+ *
22
+ * Pure dispatch: every step lives in src/commands/init/steps/.
18
23
  */
19
24
  export async function initProject(options, onStep) {
20
- const { projectDir, projectName, stack, ciProvider, memory, aiSync, sdd, ghagga, contextDir, claudeMd, securityHooks, hookProfile, dryRun, } = options;
25
+ const { projectDir, dryRun } = options;
21
26
  // Ensure project directory exists before any steps
22
27
  if (!dryRun && projectDir) {
23
28
  await ensureDirExists(projectDir);
24
29
  }
25
- // ── Step 1: Initialize git ────────────────────────────────────────────────
26
- const stepGit = "git-init";
27
- report(onStep, stepGit, "Initialize git repository", "running");
28
- try {
29
- const gitDir = path.join(projectDir, ".git");
30
- if (!(await fs.pathExists(gitDir))) {
31
- if (!dryRun) {
32
- await execFileAsync("git", ["init"], { cwd: projectDir });
33
- }
34
- report(onStep, stepGit, "Initialize git repository", "done", "initialized");
35
- }
36
- else {
37
- report(onStep, stepGit, "Initialize git repository", "done", "already exists");
38
- }
39
- }
40
- catch (e) {
41
- report(onStep, stepGit, "Initialize git repository", "error", String(e));
42
- }
43
- // ── Step 2: Configure git hooks path ──────────────────────────────────────
44
- const stepHooks = "git-hooks";
45
- report(onStep, stepHooks, "Configure git hooks path", "running");
46
- try {
47
- const ciLocalSrc = CI_LOCAL_DIR;
48
- const ciLocalDest = path.join(projectDir, "ci-local");
49
- if (await fs.pathExists(ciLocalSrc)) {
50
- if (!dryRun) {
51
- await fs.copy(ciLocalSrc, ciLocalDest, {
52
- overwrite: false,
53
- errorOnExist: false,
54
- });
55
- // Set core.hooksPath to ci-local/hooks
56
- const hooksDir = path.join(ciLocalDest, "hooks");
57
- if (await fs.pathExists(hooksDir)) {
58
- // Ensure hooks are executable
59
- const hookFiles = await fs.readdir(hooksDir);
60
- for (const hook of hookFiles) {
61
- await fs.chmod(path.join(hooksDir, hook), 0o755);
62
- }
63
- await execFileAsync("git", ["config", "core.hooksPath", "ci-local/hooks"], { cwd: projectDir });
64
- }
65
- }
66
- report(onStep, stepHooks, "Configure git hooks path", "done", "ci-local/hooks");
67
- }
68
- else {
69
- report(onStep, stepHooks, "Configure git hooks path", "skipped", "no ci-local dir");
70
- }
71
- }
72
- catch (e) {
73
- report(onStep, stepHooks, "Configure git hooks path", "error", String(e));
74
- }
75
- // ── Step 3: Copy CI template ──────────────────────────────────────────────
76
- const stepCI = "ci-template";
77
- report(onStep, stepCI, `Copy ${ciProvider} CI workflow`, "running");
78
- try {
79
- const ciContent = await generateCIWorkflow(stack, ciProvider);
80
- if (ciContent) {
81
- const dest = path.join(projectDir, getCIDestination(ciProvider));
82
- if (!dryRun) {
83
- await backupIfExists(dest);
84
- await ensureDirExists(path.dirname(dest));
85
- await fs.writeFile(dest, ciContent, "utf-8");
86
- }
87
- report(onStep, stepCI, `Copy ${ciProvider} CI workflow`, "done", getCIDestination(ciProvider));
88
- }
89
- else {
90
- report(onStep, stepCI, `Copy ${ciProvider} CI workflow`, "skipped", `no template for ${stack}`);
91
- }
92
- }
93
- catch (e) {
94
- report(onStep, stepCI, `Copy ${ciProvider} CI workflow`, "error", String(e));
95
- }
96
- // ── Step 4: Generate .gitignore ───────────────────────────────────────────
97
- const stepGitignore = "gitignore";
98
- report(onStep, stepGitignore, "Generate .gitignore", "running");
99
- try {
100
- const templatePath = path.join(FORGE_ROOT, ".gitignore.template");
101
- const dest = path.join(projectDir, ".gitignore");
102
- if ((await fs.pathExists(templatePath)) && !(await fs.pathExists(dest))) {
103
- if (!dryRun) {
104
- await fs.copy(templatePath, dest);
105
- }
106
- report(onStep, stepGitignore, "Generate .gitignore", "done", "from template");
107
- }
108
- else if (await fs.pathExists(dest)) {
109
- report(onStep, stepGitignore, "Generate .gitignore", "done", "already exists");
110
- }
111
- else {
112
- report(onStep, stepGitignore, "Generate .gitignore", "skipped", "no template");
113
- }
114
- }
115
- catch (e) {
116
- report(onStep, stepGitignore, "Generate .gitignore", "error", String(e));
117
- }
118
- // ── Step 5: Generate dependabot.yml ───────────────────────────────────────
119
- const stepDeps = "dependabot";
120
- report(onStep, stepDeps, "Generate dependabot.yml", "running");
121
- try {
122
- if (ciProvider === "github") {
123
- const content = await generateDependabotYml([stack], true);
124
- const dest = path.join(projectDir, ".github", "dependabot.yml");
125
- if (!dryRun) {
126
- await backupIfExists(dest);
127
- await ensureDirExists(path.dirname(dest));
128
- await fs.writeFile(dest, content, "utf-8");
129
- }
130
- report(onStep, stepDeps, "Generate dependabot.yml", "done");
131
- }
132
- else {
133
- report(onStep, stepDeps, "Generate dependabot.yml", "skipped", `not needed for ${ciProvider}`);
134
- }
135
- }
136
- catch (e) {
137
- report(onStep, stepDeps, "Generate dependabot.yml", "error", String(e));
138
- }
139
- // ── Step 6: Install memory module ─────────────────────────────────────────
140
- const stepMem = "memory";
141
- report(onStep, stepMem, `Install memory module: ${memory}`, "running");
142
- try {
143
- if (memory !== "none") {
144
- const moduleSrc = path.join(MODULES_DIR, memory);
145
- if (await fs.pathExists(moduleSrc)) {
146
- if (!dryRun) {
147
- // Copy module files to project
148
- const moduleDest = path.join(projectDir, ".javi-forge", "modules", memory);
149
- await ensureDirExists(moduleDest);
150
- await fs.copy(moduleSrc, moduleDest, {
151
- overwrite: false,
152
- errorOnExist: false,
153
- });
154
- // If engram, copy .mcp-config-snippet.json to project with placeholder replacement
155
- if (memory === "engram") {
156
- const snippetSrc = path.join(moduleSrc, ".mcp-config-snippet.json");
157
- if (await fs.pathExists(snippetSrc)) {
158
- const snippetDest = path.join(projectDir, ".mcp-config-snippet.json");
159
- let content = await fs.readFile(snippetSrc, "utf-8");
160
- content = content.replace(/__PROJECT_NAME__/g, projectName);
161
- await fs.writeFile(snippetDest, content, "utf-8");
162
- }
163
- }
164
- }
165
- report(onStep, stepMem, `Install memory module: ${memory}`, "done");
166
- }
167
- else {
168
- report(onStep, stepMem, `Install memory module: ${memory}`, "error", "module not found");
169
- }
170
- }
171
- else {
172
- report(onStep, stepMem, `Install memory module: ${memory}`, "skipped", "none selected");
173
- }
174
- }
175
- catch (e) {
176
- report(onStep, stepMem, `Install memory module: ${memory}`, "error", String(e));
177
- }
178
- // ── Step 7: AI config sync (delegated to javi-ai) ──────────────────────────
179
- const stepAI = "ai-sync";
180
- report(onStep, stepAI, "Sync AI config via javi-ai", "running");
181
- try {
182
- if (aiSync) {
183
- if (!dryRun) {
184
- try {
185
- const { stderr } = await execFileAsync("npx", ["javi-ai", "sync", "--project-dir", projectDir, "--target", "all"], {
186
- cwd: projectDir,
187
- timeout: 120_000,
188
- });
189
- // javi-ai may exit 0 but crash (e.g. Ink raw mode error) — detect via stderr
190
- if (stderr &&
191
- (stderr.includes("Raw mode is not supported") ||
192
- stderr.includes("ERROR"))) {
193
- report(onStep, stepAI, "Sync AI config via javi-ai", "error", "javi-ai crashed. Run manually: npx javi-ai sync --project-dir . --target all");
194
- }
195
- else {
196
- report(onStep, stepAI, "Sync AI config via javi-ai", "done", "javi-ai sync --target all");
197
- }
198
- }
199
- catch (syncErr) {
200
- const msg = syncErr instanceof Error ? syncErr.message : String(syncErr);
201
- if (msg.includes("ENOENT") ||
202
- msg.includes("not found") ||
203
- msg.includes("ERR_MODULE_NOT_FOUND")) {
204
- report(onStep, stepAI, "Sync AI config via javi-ai", "error", "javi-ai not found. Install with: npm install -g javi-ai (or run npx javi-ai sync manually)");
205
- }
206
- else {
207
- report(onStep, stepAI, "Sync AI config via javi-ai", "error", msg);
208
- }
209
- }
210
- }
211
- else {
212
- report(onStep, stepAI, "Sync AI config via javi-ai", "done", "dry-run: would run javi-ai sync --target all");
213
- }
214
- }
215
- else {
216
- report(onStep, stepAI, "Sync AI config via javi-ai", "skipped", "not selected");
217
- }
218
- }
219
- catch (e) {
220
- report(onStep, stepAI, "Sync AI config via javi-ai", "error", String(e));
221
- }
222
- // ── Step 8: SDD (Spec-Driven Development) ─────────────────────────────────
223
- const stepSDD = "sdd";
224
- report(onStep, stepSDD, "Set up SDD (openspec/)", "running");
225
- try {
226
- if (sdd) {
227
- if (!dryRun) {
228
- const openspecDir = path.join(projectDir, "openspec");
229
- await ensureDirExists(openspecDir);
230
- // Create a README if none exists
231
- const readmePath = path.join(openspecDir, "README.md");
232
- if (!(await fs.pathExists(readmePath))) {
233
- await fs.writeFile(readmePath, `# openspec/\n\nSpec-Driven Development artifacts for ${projectName}.\n\nSee: /sdd:new <name> to start a new change.\n`, "utf-8");
234
- }
235
- }
236
- report(onStep, stepSDD, "Set up SDD (openspec/)", "done");
237
- }
238
- else {
239
- report(onStep, stepSDD, "Set up SDD (openspec/)", "skipped", "not selected");
240
- }
241
- }
242
- catch (e) {
243
- report(onStep, stepSDD, "Set up SDD (openspec/)", "error", String(e));
244
- }
245
- // ── Step 9: GHAGGA ────────────────────────────────────────────────────────
246
- const stepGhagga = "ghagga";
247
- report(onStep, stepGhagga, "Install GHAGGA review system", "running");
248
- try {
249
- if (ghagga) {
250
- const ghaggaSrc = path.join(MODULES_DIR, "ghagga");
251
- if (await fs.pathExists(ghaggaSrc)) {
252
- if (!dryRun) {
253
- const ghaggaDest = path.join(projectDir, ".javi-forge", "modules", "ghagga");
254
- await ensureDirExists(ghaggaDest);
255
- await fs.copy(ghaggaSrc, ghaggaDest, {
256
- overwrite: false,
257
- errorOnExist: false,
258
- });
259
- // Copy ghagga caller workflow to CI provider location
260
- if (ciProvider === "github") {
261
- const workflowSrc = path.join(FORGE_ROOT, "templates", "github", "ghagga-review.yml");
262
- if (await fs.pathExists(workflowSrc)) {
263
- const workflowDest = path.join(projectDir, ".github", "workflows", "ghagga-review.yml");
264
- await ensureDirExists(path.dirname(workflowDest));
265
- await fs.copy(workflowSrc, workflowDest, { overwrite: false });
266
- }
267
- }
268
- }
269
- report(onStep, stepGhagga, "Install GHAGGA review system", "done");
270
- }
271
- else {
272
- report(onStep, stepGhagga, "Install GHAGGA review system", "error", "module not found");
273
- }
274
- }
275
- else {
276
- report(onStep, stepGhagga, "Install GHAGGA review system", "skipped", "not selected");
277
- }
278
- }
279
- catch (e) {
280
- report(onStep, stepGhagga, "Install GHAGGA review system", "error", String(e));
281
- }
282
- // ── Step 10: Mock-first mode ───────────────────────────────────────────────
283
- const stepMock = "mock";
284
- if (options.mock) {
285
- report(onStep, stepMock, "Configure mock-first mode", "running");
286
- try {
287
- if (!dryRun) {
288
- // Create .env.example with mock values
289
- const envExample = `# Mock environment — no real API keys required
290
- # Copy to .env to use: cp .env.example .env
291
-
292
- # Database
293
- DATABASE_URL=postgresql://mock:mock@localhost:5432/mock_db
294
-
295
- # Auth
296
- JWT_SECRET=mock-jwt-secret-for-local-development
297
- SESSION_SECRET=mock-session-secret
298
-
299
- # External APIs (mock mode — no real calls)
300
- MOCK_MODE=true
301
- API_KEY=mock-api-key-not-real
302
- STRIPE_KEY=sk_test_mock_not_real
303
- SENDGRID_KEY=SG.mock_not_real
304
-
305
- # Feature flags
306
- ENABLE_ANALYTICS=false
307
- ENABLE_EMAILS=false
308
- ENABLE_WEBHOOKS=false
309
- `;
310
- const envExamplePath = path.join(projectDir, ".env.example");
311
- if (!(await fs.pathExists(envExamplePath))) {
312
- await fs.writeFile(envExamplePath, envExample, "utf-8");
313
- }
314
- // Create .env from example
315
- const envPath = path.join(projectDir, ".env");
316
- if (!(await fs.pathExists(envPath))) {
317
- await fs.writeFile(envPath, envExample, "utf-8");
318
- }
319
- }
320
- report(onStep, stepMock, "Configure mock-first mode", "done", ".env.example + .env with mock values");
321
- }
322
- catch (e) {
323
- report(onStep, stepMock, "Configure mock-first mode", "error", String(e));
324
- }
325
- }
326
- else {
327
- report(onStep, stepMock, "Configure mock-first mode", "skipped", "not selected");
328
- }
329
- // ── Step 11: Generate .context/ directory ──────────────────────────────────
330
- const stepContext = "context-dir";
331
- report(onStep, stepContext, "Generate .context/ directory", "running");
332
- try {
333
- if (contextDir) {
334
- const contextDirPath = path.join(projectDir, ".context");
335
- if (await fs.pathExists(contextDirPath)) {
336
- report(onStep, stepContext, "Generate .context/ directory", "done", "already exists");
337
- }
338
- else {
339
- if (!dryRun) {
340
- const { index, summary } = await generateContextDir(options);
341
- await ensureDirExists(contextDirPath);
342
- await fs.writeFile(path.join(contextDirPath, "INDEX.md"), index, "utf-8");
343
- await fs.writeFile(path.join(contextDirPath, "summary.md"), summary, "utf-8");
344
- }
345
- report(onStep, stepContext, "Generate .context/ directory", "done", dryRun
346
- ? "dry-run: would generate .context/"
347
- : ".context/INDEX.md + summary.md");
348
- }
349
- }
350
- else {
351
- report(onStep, stepContext, "Generate .context/ directory", "skipped", "not selected");
352
- }
353
- }
354
- catch (e) {
355
- report(onStep, stepContext, "Generate .context/ directory", "error", String(e));
356
- }
357
- // ── Step 12: Generate CLAUDE.md (smart: project-aware) ─────────────────────
358
- const stepClaudeMd = "claude-md";
359
- report(onStep, stepClaudeMd, "Generate CLAUDE.md", "running");
360
- try {
361
- if (claudeMd) {
362
- const claudeMdPath = path.join(projectDir, "CLAUDE.md");
363
- if (await fs.pathExists(claudeMdPath)) {
364
- report(onStep, stepClaudeMd, "Generate CLAUDE.md", "done", "already exists");
365
- }
366
- else {
367
- if (!dryRun) {
368
- // Detect project stack for smart CLAUDE.md generation
369
- const detection = await detectProjectStack(projectDir).catch(() => null);
370
- const content = generateSmartClaudeMd(options, detection);
371
- await fs.writeFile(claudeMdPath, content, "utf-8");
372
- const skillCount = detection?.recommendedSkills.length ?? 0;
373
- report(onStep, stepClaudeMd, "Generate CLAUDE.md", "done", skillCount > 0
374
- ? `CLAUDE.md (${skillCount} skills detected)`
375
- : "CLAUDE.md");
376
- }
377
- else {
378
- report(onStep, stepClaudeMd, "Generate CLAUDE.md", "done", "dry-run: would generate CLAUDE.md");
379
- }
380
- }
381
- }
382
- else {
383
- report(onStep, stepClaudeMd, "Generate CLAUDE.md", "skipped", "not selected");
384
- }
385
- }
386
- catch (e) {
387
- report(onStep, stepClaudeMd, "Generate CLAUDE.md", "error", String(e));
388
- }
389
- // ── Step 13: Docker zero-downtime deploy ───────────────────────────────────
390
- const stepDeploy = "docker-deploy";
391
- report(onStep, stepDeploy, "Scaffold Docker zero-downtime deploy", "running");
392
- try {
393
- if (options.dockerDeploy) {
394
- const deployDest = getDeployDestination(ciProvider);
395
- if (deployDest) {
396
- const fullDest = path.join(projectDir, deployDest);
397
- if (await fs.pathExists(fullDest)) {
398
- report(onStep, stepDeploy, "Scaffold Docker zero-downtime deploy", "done", "already exists");
399
- }
400
- else {
401
- const serviceName = options.dockerServiceName || "app";
402
- const content = await generateDeployWorkflow(ciProvider, serviceName);
403
- if (content) {
404
- if (!dryRun) {
405
- await backupIfExists(fullDest);
406
- await ensureDirExists(path.dirname(fullDest));
407
- await fs.writeFile(fullDest, content, "utf-8");
408
- }
409
- report(onStep, stepDeploy, "Scaffold Docker zero-downtime deploy", "done", dryRun ? `dry-run: would create ${deployDest}` : deployDest);
410
- }
411
- else {
412
- report(onStep, stepDeploy, "Scaffold Docker zero-downtime deploy", "error", `no deploy template for ${ciProvider}`);
413
- }
414
- }
415
- }
416
- else {
417
- report(onStep, stepDeploy, "Scaffold Docker zero-downtime deploy", "error", `no deploy destination for ${ciProvider}`);
418
- }
419
- }
420
- else {
421
- report(onStep, stepDeploy, "Scaffold Docker zero-downtime deploy", "skipped", "not selected");
422
- }
423
- }
424
- catch (e) {
425
- report(onStep, stepDeploy, "Scaffold Docker zero-downtime deploy", "error", String(e));
426
- }
427
- // ── Step 14: Security hooks scaffold ────────────────────────────────────────
428
- const stepSecurity = "security-hooks";
429
- report(onStep, stepSecurity, "Scaffold security hooks", "running");
430
- try {
431
- if (securityHooks) {
432
- if (await fs.pathExists(SECURITY_HOOKS_DIR)) {
433
- if (!dryRun) {
434
- // Copy 6-layer git security hooks into ci-local/hooks/security/
435
- const secHooksDest = path.join(projectDir, "ci-local", "hooks", "security");
436
- await ensureDirExists(secHooksDest);
437
- const hookFiles = await fs.readdir(SECURITY_HOOKS_DIR);
438
- const gitHooks = hookFiles.filter((f) => !f.endsWith(".json"));
439
- for (const hook of gitHooks) {
440
- const src = path.join(SECURITY_HOOKS_DIR, hook);
441
- const dest = path.join(secHooksDest, hook);
442
- await fs.copy(src, dest, { overwrite: false });
443
- await fs.chmod(dest, 0o755);
444
- }
445
- // Copy runtime security settings (kiteguard-style) to .claude/
446
- const settingsSrc = path.join(SECURITY_HOOKS_DIR, "claude-settings-security.json");
447
- if (await fs.pathExists(settingsSrc)) {
448
- const claudeDir = path.join(projectDir, ".claude");
449
- await ensureDirExists(claudeDir);
450
- const settingsDest = path.join(claudeDir, "settings.json");
451
- if (!(await fs.pathExists(settingsDest))) {
452
- await fs.copy(settingsSrc, settingsDest);
453
- }
454
- }
455
- }
456
- report(onStep, stepSecurity, "Scaffold security hooks", "done", dryRun
457
- ? "dry-run: would scaffold security hooks"
458
- : "6 git layers + runtime hooks");
459
- }
460
- else {
461
- report(onStep, stepSecurity, "Scaffold security hooks", "error", "security-hooks templates not found");
462
- }
463
- }
464
- else {
465
- report(onStep, stepSecurity, "Scaffold security hooks", "skipped", "not selected");
466
- }
467
- }
468
- catch (e) {
469
- report(onStep, stepSecurity, "Scaffold security hooks", "error", String(e));
470
- }
471
- // ── Step 14b: Write hook profile ─────────────────────────────────────────────
472
- const stepHookProfile = "hook-profile";
473
- report(onStep, stepHookProfile, "Write hook reliability profile", "running");
474
- try {
475
- if (securityHooks) {
476
- if (!dryRun) {
477
- const hooksDir = path.join(projectDir, "ci-local", "hooks");
478
- await ensureDirExists(hooksDir);
479
- const profilePath = path.join(hooksDir, "profile.json");
480
- const resolvedProfile = hookProfile ?? "standard";
481
- await fs.writeJson(profilePath, { profile: resolvedProfile }, { spaces: 2 });
482
- }
483
- report(onStep, stepHookProfile, "Write hook reliability profile", "done", dryRun
484
- ? `dry-run: would write profile.json (${hookProfile ?? "standard"})`
485
- : `ci-local/hooks/profile.json (${hookProfile ?? "standard"})`);
486
- }
487
- else {
488
- report(onStep, stepHookProfile, "Write hook reliability profile", "skipped", "security hooks not selected");
489
- }
490
- }
491
- catch (e) {
492
- report(onStep, stepHookProfile, "Write hook reliability profile", "error", String(e));
493
- }
494
- // ── Step 15: RepoForge code graph scaffolding ───────────────────────────────
495
- const stepGraph = "code-graph";
496
- report(onStep, stepGraph, "Scaffold RepoForge code graph", "running");
497
- try {
498
- if (options.codeGraph) {
499
- if (!dryRun) {
500
- // 1. Copy .repoforge.yaml config
501
- const repoforgeConfigSrc = path.join(TEMPLATES_DIR, "common", "repoforge", "repoforge.yaml");
502
- const repoforgeConfigDest = path.join(projectDir, ".repoforge.yaml");
503
- if (!(await fs.pathExists(repoforgeConfigDest))) {
504
- await fs.copy(repoforgeConfigSrc, repoforgeConfigDest);
505
- }
506
- // 2. Ensure .repoforge/ output dir exists
507
- await ensureDirExists(path.join(projectDir, ".repoforge"));
508
- // 3. Copy CI workflow for graph generation (GitHub only)
509
- if (ciProvider === "github") {
510
- const graphWorkflowSrc = path.join(TEMPLATES_DIR, "github", "repoforge-graph.yml");
511
- if (await fs.pathExists(graphWorkflowSrc)) {
512
- const graphWorkflowDest = path.join(projectDir, ".github", "workflows", "repoforge-graph.yml");
513
- await ensureDirExists(path.dirname(graphWorkflowDest));
514
- await backupIfExists(graphWorkflowDest);
515
- await fs.copy(graphWorkflowSrc, graphWorkflowDest, {
516
- overwrite: false,
517
- });
518
- }
519
- }
520
- // 4. Copy MCP config snippet for repoforge code intelligence
521
- const mcpSnippetSrc = path.join(TEMPLATES_DIR, "common", "repoforge", "mcp-repoforge-snippet.json");
522
- if (await fs.pathExists(mcpSnippetSrc)) {
523
- const mcpSnippetDest = path.join(projectDir, ".repoforge", "mcp-config-snippet.json");
524
- let content = await fs.readFile(mcpSnippetSrc, "utf-8");
525
- content = content.replace(/__PROJECT_NAME__/g, projectName);
526
- await fs.writeFile(mcpSnippetDest, content, "utf-8");
527
- }
528
- }
529
- report(onStep, stepGraph, "Scaffold RepoForge code graph", "done", dryRun
530
- ? "dry-run: would scaffold .repoforge.yaml + CI + MCP"
531
- : ".repoforge.yaml + CI + MCP snippet");
532
- }
533
- else {
534
- report(onStep, stepGraph, "Scaffold RepoForge code graph", "skipped", "not selected");
535
- }
536
- }
537
- catch (e) {
538
- report(onStep, stepGraph, "Scaffold RepoForge code graph", "error", String(e));
539
- }
540
- // ── Step 16: Local AI dev stack ────────────────────────────────────────────
541
- const stepLocalAi = "local-ai";
542
- report(onStep, stepLocalAi, "Scaffold local AI dev stack", "running");
543
- try {
544
- if (options.localAi) {
545
- if (await fs.pathExists(LOCAL_AI_TEMPLATE_DIR)) {
546
- const composeDest = path.join(projectDir, "docker-compose.yml");
547
- const envDest = path.join(projectDir, ".env.local-ai");
548
- if (!dryRun) {
549
- // Copy docker-compose.yml (skip if exists)
550
- if (!(await fs.pathExists(composeDest))) {
551
- await fs.copy(path.join(LOCAL_AI_TEMPLATE_DIR, "docker-compose.yml"), composeDest);
552
- }
553
- // Copy .env.example as .env.local-ai
554
- const envSrc = path.join(LOCAL_AI_TEMPLATE_DIR, ".env.example");
555
- if ((await fs.pathExists(envSrc)) &&
556
- !(await fs.pathExists(envDest))) {
557
- await fs.copy(envSrc, envDest);
558
- }
559
- }
560
- report(onStep, stepLocalAi, "Scaffold local AI dev stack", "done", dryRun
561
- ? "dry-run: would create docker-compose.yml + .env.local-ai"
562
- : "docker-compose.yml + .env.local-ai");
563
- }
564
- else {
565
- report(onStep, stepLocalAi, "Scaffold local AI dev stack", "error", "local-ai template not found");
566
- }
567
- }
568
- else {
569
- report(onStep, stepLocalAi, "Scaffold local AI dev stack", "skipped", "not selected");
570
- }
571
- }
572
- catch (e) {
573
- report(onStep, stepLocalAi, "Scaffold local AI dev stack", "error", String(e));
574
- }
575
- // ── Step 17: Generate Agent Skills manifest (skills.json) ─────────────────
576
- const stepSkills = "agent-skills";
577
- report(onStep, stepSkills, "Generate Agent Skills manifest (skills.json)", "running");
578
- try {
579
- if (!dryRun) {
580
- const skillsManifest = {
581
- name: projectName,
582
- version: "0.1.0",
583
- description: `Agent Skills manifest for ${projectName}`,
584
- skills: [],
585
- };
586
- const skillsJsonPath = path.join(projectDir, AGENT_SKILLS_MANIFEST_FILE);
587
- if (!(await fs.pathExists(skillsJsonPath))) {
588
- await fs.writeJson(skillsJsonPath, skillsManifest, { spaces: 2 });
589
- report(onStep, stepSkills, "Generate Agent Skills manifest (skills.json)", "done", AGENT_SKILLS_MANIFEST_FILE);
590
- }
591
- else {
592
- report(onStep, stepSkills, "Generate Agent Skills manifest (skills.json)", "done", "already exists");
593
- }
594
- }
595
- else {
596
- report(onStep, stepSkills, "Generate Agent Skills manifest (skills.json)", "done", `dry-run: would generate ${AGENT_SKILLS_MANIFEST_FILE}`);
597
- }
598
- }
599
- catch (e) {
600
- report(onStep, stepSkills, "Generate Agent Skills manifest (skills.json)", "error", String(e));
601
- }
602
- // ── Step 18: Write manifest ───────────────────────────────────────────────
603
- const stepManifest = "manifest";
604
- report(onStep, stepManifest, "Write forge manifest", "running");
605
- try {
606
- if (!dryRun) {
607
- const manifestDir = path.join(projectDir, ".javi-forge");
608
- await ensureDirExists(manifestDir);
609
- const manifest = {
610
- version: "0.1.0",
611
- projectName,
612
- stack,
613
- ciProvider,
614
- memory,
615
- createdAt: new Date().toISOString(),
616
- updatedAt: new Date().toISOString(),
617
- modules: [
618
- ...(memory !== "none" ? [memory] : []),
619
- ...(ghagga ? ["ghagga"] : []),
620
- ...(sdd ? ["sdd"] : []),
621
- ...(aiSync ? ["ai-config"] : []),
622
- ...(contextDir ? ["context"] : []),
623
- ...(claudeMd ? ["claude-md"] : []),
624
- ...(options.dockerDeploy ? ["docker-deploy"] : []),
625
- ...(securityHooks ? ["security-hooks"] : []),
626
- ...(options.codeGraph ? ["code-graph"] : []),
627
- ...(options.localAi ? ["local-ai"] : []),
628
- ],
629
- };
630
- await fs.writeJson(path.join(manifestDir, "manifest.json"), manifest, {
631
- spaces: 2,
632
- });
633
- }
634
- report(onStep, stepManifest, "Write forge manifest", "done", ".javi-forge/manifest.json");
635
- }
636
- catch (e) {
637
- report(onStep, stepManifest, "Write forge manifest", "error", String(e));
638
- }
30
+ // Shared context passed to every step.
31
+ const ctx = { options, projectDir, dryRun, onStep };
32
+ await stepGitInit(ctx);
33
+ await stepGitHooks(ctx);
34
+ await stepCITemplate(ctx);
35
+ await stepGitignore(ctx);
36
+ await stepDependabot(ctx);
37
+ await stepMemory(ctx);
38
+ await stepAISync(ctx);
39
+ await stepSDD(ctx);
40
+ await stepGhagga(ctx);
41
+ await stepMock(ctx);
42
+ await stepContextDir(ctx);
43
+ await stepClaudeMd(ctx);
44
+ await stepDockerDeploy(ctx);
45
+ await stepSecurityHooks(ctx);
46
+ await stepHookProfile(ctx);
47
+ await stepCodeGraph(ctx);
48
+ await stepLocalAi(ctx);
49
+ await stepAgentSkills(ctx);
50
+ await stepManifest(ctx);
639
51
  }
640
52
  //# sourceMappingURL=init.js.map