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
@@ -0,0 +1,81 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { CI_LOCAL_DIR } from "../../../constants.js";
4
+ import { execFileAsync } from "../../../lib/exec.js";
5
+ import { report } from "../report.js";
6
+ /**
7
+ * Step 1: Initialize git repository.
8
+ *
9
+ * - If .git is missing, runs `git init` in the project dir.
10
+ * - If .git already exists, reports "done" with detail "already exists".
11
+ * - In dry-run, skips the actual `git init` call but still reports.
12
+ * - Errors are swallowed and reported as status:"error" — never thrown.
13
+ *
14
+ * Extracted VERBATIM from src/commands/init.ts (PR 1 of 6).
15
+ */
16
+ export const stepGitInit = async (ctx) => {
17
+ const { projectDir, dryRun, onStep } = ctx;
18
+ const stepId = "git-init";
19
+ report(onStep, stepId, "Initialize git repository", "running");
20
+ try {
21
+ const gitDir = path.join(projectDir, ".git");
22
+ if (!(await fs.pathExists(gitDir))) {
23
+ if (!dryRun) {
24
+ await execFileAsync("git", ["init"], { cwd: projectDir });
25
+ }
26
+ report(onStep, stepId, "Initialize git repository", "done", "initialized");
27
+ }
28
+ else {
29
+ report(onStep, stepId, "Initialize git repository", "done", "already exists");
30
+ }
31
+ }
32
+ catch (e) {
33
+ report(onStep, stepId, "Initialize git repository", "error", String(e));
34
+ }
35
+ };
36
+ /**
37
+ * Step 2: Configure git hooks path.
38
+ *
39
+ * - Copies templates/ci-local/ → <project>/ci-local/ (no overwrite).
40
+ * - chmod 0755 on hook files.
41
+ * - Sets git config core.hooksPath to ci-local/hooks.
42
+ * - Skips entirely when CI_LOCAL_DIR template is missing.
43
+ * - Errors are swallowed and reported as status:"error" — never thrown.
44
+ *
45
+ * Extracted VERBATIM from src/commands/init.ts (PR 1 of 6).
46
+ */
47
+ export const stepGitHooks = async (ctx) => {
48
+ const { projectDir, dryRun, onStep } = ctx;
49
+ const stepId = "git-hooks";
50
+ report(onStep, stepId, "Configure git hooks path", "running");
51
+ try {
52
+ const ciLocalSrc = CI_LOCAL_DIR;
53
+ const ciLocalDest = path.join(projectDir, "ci-local");
54
+ if (await fs.pathExists(ciLocalSrc)) {
55
+ if (!dryRun) {
56
+ await fs.copy(ciLocalSrc, ciLocalDest, {
57
+ overwrite: false,
58
+ errorOnExist: false,
59
+ });
60
+ // Set core.hooksPath to ci-local/hooks
61
+ const hooksDir = path.join(ciLocalDest, "hooks");
62
+ if (await fs.pathExists(hooksDir)) {
63
+ // Ensure hooks are executable
64
+ const hookFiles = await fs.readdir(hooksDir);
65
+ for (const hook of hookFiles) {
66
+ await fs.chmod(path.join(hooksDir, hook), 0o755);
67
+ }
68
+ await execFileAsync("git", ["config", "core.hooksPath", "ci-local/hooks"], { cwd: projectDir });
69
+ }
70
+ }
71
+ report(onStep, stepId, "Configure git hooks path", "done", "ci-local/hooks");
72
+ }
73
+ else {
74
+ report(onStep, stepId, "Configure git hooks path", "skipped", "no ci-local dir");
75
+ }
76
+ }
77
+ catch (e) {
78
+ report(onStep, stepId, "Configure git hooks path", "error", String(e));
79
+ }
80
+ };
81
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1,13 @@
1
+ import type { StepFn } from "../types.js";
2
+ /**
3
+ * Step 4: Generate .gitignore.
4
+ *
5
+ * - Copies <FORGE_ROOT>/.gitignore.template → <project>/.gitignore if absent.
6
+ * - If destination already exists, reports "done" with detail "already exists".
7
+ * - If template is missing, reports "skipped" with detail "no template".
8
+ * - Errors are swallowed and reported as status:"error" — never thrown.
9
+ *
10
+ * Extracted VERBATIM from src/commands/init.ts (PR 2 of 6).
11
+ */
12
+ export declare const stepGitignore: StepFn;
13
+ //# sourceMappingURL=gitignore.d.ts.map
@@ -0,0 +1,39 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { FORGE_ROOT } from "../../../constants.js";
4
+ import { report } from "../report.js";
5
+ /**
6
+ * Step 4: Generate .gitignore.
7
+ *
8
+ * - Copies <FORGE_ROOT>/.gitignore.template → <project>/.gitignore if absent.
9
+ * - If destination already exists, reports "done" with detail "already exists".
10
+ * - If template is missing, reports "skipped" with detail "no template".
11
+ * - Errors are swallowed and reported as status:"error" — never thrown.
12
+ *
13
+ * Extracted VERBATIM from src/commands/init.ts (PR 2 of 6).
14
+ */
15
+ export const stepGitignore = async (ctx) => {
16
+ const { projectDir, dryRun, onStep } = ctx;
17
+ const stepId = "gitignore";
18
+ report(onStep, stepId, "Generate .gitignore", "running");
19
+ try {
20
+ const templatePath = path.join(FORGE_ROOT, ".gitignore.template");
21
+ const dest = path.join(projectDir, ".gitignore");
22
+ if ((await fs.pathExists(templatePath)) && !(await fs.pathExists(dest))) {
23
+ if (!dryRun) {
24
+ await fs.copy(templatePath, dest);
25
+ }
26
+ report(onStep, stepId, "Generate .gitignore", "done", "from template");
27
+ }
28
+ else if (await fs.pathExists(dest)) {
29
+ report(onStep, stepId, "Generate .gitignore", "done", "already exists");
30
+ }
31
+ else {
32
+ report(onStep, stepId, "Generate .gitignore", "skipped", "no template");
33
+ }
34
+ }
35
+ catch (e) {
36
+ report(onStep, stepId, "Generate .gitignore", "error", String(e));
37
+ }
38
+ };
39
+ //# sourceMappingURL=gitignore.js.map
@@ -0,0 +1,14 @@
1
+ import type { StepFn } from "../types.js";
2
+ /**
3
+ * Step 16: Scaffold local AI dev stack.
4
+ *
5
+ * - When options.localAi is false, reports "skipped".
6
+ * - When LOCAL_AI_TEMPLATE_DIR is missing, reports "error".
7
+ * - Otherwise copies docker-compose.yml and .env.example -> .env.local-ai
8
+ * (both skip if the destination already exists).
9
+ * - Errors are swallowed and reported as status:"error" — never thrown.
10
+ *
11
+ * Extracted VERBATIM from src/commands/init.ts (PR 5 of 6).
12
+ */
13
+ export declare const stepLocalAi: StepFn;
14
+ //# sourceMappingURL=local-ai.d.ts.map
@@ -0,0 +1,53 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { LOCAL_AI_TEMPLATE_DIR } from "../../../constants.js";
4
+ import { report } from "../report.js";
5
+ /**
6
+ * Step 16: Scaffold local AI dev stack.
7
+ *
8
+ * - When options.localAi is false, reports "skipped".
9
+ * - When LOCAL_AI_TEMPLATE_DIR is missing, reports "error".
10
+ * - Otherwise copies docker-compose.yml and .env.example -> .env.local-ai
11
+ * (both skip if the destination already exists).
12
+ * - Errors are swallowed and reported as status:"error" — never thrown.
13
+ *
14
+ * Extracted VERBATIM from src/commands/init.ts (PR 5 of 6).
15
+ */
16
+ export const stepLocalAi = async (ctx) => {
17
+ const { projectDir, dryRun, onStep, options } = ctx;
18
+ const stepId = "local-ai";
19
+ report(onStep, stepId, "Scaffold local AI dev stack", "running");
20
+ try {
21
+ if (options.localAi) {
22
+ if (await fs.pathExists(LOCAL_AI_TEMPLATE_DIR)) {
23
+ const composeDest = path.join(projectDir, "docker-compose.yml");
24
+ const envDest = path.join(projectDir, ".env.local-ai");
25
+ if (!dryRun) {
26
+ // Copy docker-compose.yml (skip if exists)
27
+ if (!(await fs.pathExists(composeDest))) {
28
+ await fs.copy(path.join(LOCAL_AI_TEMPLATE_DIR, "docker-compose.yml"), composeDest);
29
+ }
30
+ // Copy .env.example as .env.local-ai
31
+ const envSrc = path.join(LOCAL_AI_TEMPLATE_DIR, ".env.example");
32
+ if ((await fs.pathExists(envSrc)) &&
33
+ !(await fs.pathExists(envDest))) {
34
+ await fs.copy(envSrc, envDest);
35
+ }
36
+ }
37
+ report(onStep, stepId, "Scaffold local AI dev stack", "done", dryRun
38
+ ? "dry-run: would create docker-compose.yml + .env.local-ai"
39
+ : "docker-compose.yml + .env.local-ai");
40
+ }
41
+ else {
42
+ report(onStep, stepId, "Scaffold local AI dev stack", "error", "local-ai template not found");
43
+ }
44
+ }
45
+ else {
46
+ report(onStep, stepId, "Scaffold local AI dev stack", "skipped", "not selected");
47
+ }
48
+ }
49
+ catch (e) {
50
+ report(onStep, stepId, "Scaffold local AI dev stack", "error", String(e));
51
+ }
52
+ };
53
+ //# sourceMappingURL=local-ai.js.map
@@ -0,0 +1,13 @@
1
+ import type { StepFn } from "../types.js";
2
+ /**
3
+ * Step 18: Write the forge manifest under .javi-forge/manifest.json.
4
+ *
5
+ * - In dry-run mode, reports what would be written without touching disk.
6
+ * - Otherwise materialises .javi-forge/, computes the modules array from
7
+ * selected options, and writes manifest.json (overwriting any existing).
8
+ * - Errors are swallowed and reported as status:"error" — never thrown.
9
+ *
10
+ * Extracted VERBATIM from src/commands/init.ts (PR 6 of 6).
11
+ */
12
+ export declare const stepManifest: StepFn;
13
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1,58 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { ensureDirExists } from "../../../lib/common.js";
4
+ import { report } from "../report.js";
5
+ /**
6
+ * Step 18: Write the forge manifest under .javi-forge/manifest.json.
7
+ *
8
+ * - In dry-run mode, reports what would be written without touching disk.
9
+ * - Otherwise materialises .javi-forge/, computes the modules array from
10
+ * selected options, and writes manifest.json (overwriting any existing).
11
+ * - Errors are swallowed and reported as status:"error" — never thrown.
12
+ *
13
+ * Extracted VERBATIM from src/commands/init.ts (PR 6 of 6).
14
+ */
15
+ export const stepManifest = async (ctx) => {
16
+ const { projectDir, dryRun, onStep, options } = ctx;
17
+ const { stack, ciProvider, memory, aiSync, sdd, ghagga, contextDir, claudeMd, securityHooks, projectName, dockerDeploy, codeGraph, localAi, } = options;
18
+ const stepId = "manifest";
19
+ report(onStep, stepId, "Write forge manifest", "running");
20
+ try {
21
+ if (!dryRun) {
22
+ const manifestDir = path.join(projectDir, ".javi-forge");
23
+ await ensureDirExists(manifestDir);
24
+ const manifest = {
25
+ version: "0.1.0",
26
+ projectName,
27
+ stack,
28
+ ciProvider,
29
+ memory,
30
+ createdAt: new Date().toISOString(),
31
+ updatedAt: new Date().toISOString(),
32
+ modules: [
33
+ ...(memory !== "none" ? [memory] : []),
34
+ ...(ghagga ? ["ghagga"] : []),
35
+ ...(sdd ? ["sdd"] : []),
36
+ ...(aiSync ? ["ai-config"] : []),
37
+ ...(contextDir ? ["context"] : []),
38
+ ...(claudeMd ? ["claude-md"] : []),
39
+ ...(dockerDeploy ? ["docker-deploy"] : []),
40
+ ...(securityHooks ? ["security-hooks"] : []),
41
+ ...(codeGraph ? ["code-graph"] : []),
42
+ ...(localAi ? ["local-ai"] : []),
43
+ ],
44
+ };
45
+ await fs.writeJson(path.join(manifestDir, "manifest.json"), manifest, {
46
+ spaces: 2,
47
+ });
48
+ report(onStep, stepId, "Write forge manifest", "done", ".javi-forge/manifest.json");
49
+ }
50
+ else {
51
+ report(onStep, stepId, "Write forge manifest", "done", "dry-run: would write .javi-forge/manifest.json");
52
+ }
53
+ }
54
+ catch (e) {
55
+ report(onStep, stepId, "Write forge manifest", "error", String(e));
56
+ }
57
+ };
58
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1,15 @@
1
+ import type { StepFn } from "../types.js";
2
+ /**
3
+ * Step 6: Install memory module.
4
+ *
5
+ * - If memory === "none", reports "skipped".
6
+ * - Otherwise copies <MODULES_DIR>/<memory>/ → <project>/.javi-forge/modules/<memory>/.
7
+ * - For engram specifically, copies .mcp-config-snippet.json to project root
8
+ * replacing __PROJECT_NAME__ placeholders.
9
+ * - If the module source dir is missing, reports "error" with "module not found".
10
+ * - Errors are swallowed and reported as status:"error" — never thrown.
11
+ *
12
+ * Extracted VERBATIM from src/commands/init.ts (PR 2 of 6).
13
+ */
14
+ export declare const stepMemory: StepFn;
15
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1,60 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { MODULES_DIR } from "../../../constants.js";
4
+ import { ensureDirExists } from "../../../lib/common.js";
5
+ import { report } from "../report.js";
6
+ /**
7
+ * Step 6: Install memory module.
8
+ *
9
+ * - If memory === "none", reports "skipped".
10
+ * - Otherwise copies <MODULES_DIR>/<memory>/ → <project>/.javi-forge/modules/<memory>/.
11
+ * - For engram specifically, copies .mcp-config-snippet.json to project root
12
+ * replacing __PROJECT_NAME__ placeholders.
13
+ * - If the module source dir is missing, reports "error" with "module not found".
14
+ * - Errors are swallowed and reported as status:"error" — never thrown.
15
+ *
16
+ * Extracted VERBATIM from src/commands/init.ts (PR 2 of 6).
17
+ */
18
+ export const stepMemory = async (ctx) => {
19
+ const { projectDir, dryRun, onStep, options } = ctx;
20
+ const { memory, projectName } = options;
21
+ const stepId = "memory";
22
+ report(onStep, stepId, `Install memory module: ${memory}`, "running");
23
+ try {
24
+ if (memory !== "none") {
25
+ const moduleSrc = path.join(MODULES_DIR, memory);
26
+ if (await fs.pathExists(moduleSrc)) {
27
+ if (!dryRun) {
28
+ // Copy module files to project
29
+ const moduleDest = path.join(projectDir, ".javi-forge", "modules", memory);
30
+ await ensureDirExists(moduleDest);
31
+ await fs.copy(moduleSrc, moduleDest, {
32
+ overwrite: false,
33
+ errorOnExist: false,
34
+ });
35
+ // If engram, copy .mcp-config-snippet.json to project with placeholder replacement
36
+ if (memory === "engram") {
37
+ const snippetSrc = path.join(moduleSrc, ".mcp-config-snippet.json");
38
+ if (await fs.pathExists(snippetSrc)) {
39
+ const snippetDest = path.join(projectDir, ".mcp-config-snippet.json");
40
+ let content = await fs.readFile(snippetSrc, "utf-8");
41
+ content = content.replace(/__PROJECT_NAME__/g, projectName);
42
+ await fs.writeFile(snippetDest, content, "utf-8");
43
+ }
44
+ }
45
+ }
46
+ report(onStep, stepId, `Install memory module: ${memory}`, "done");
47
+ }
48
+ else {
49
+ report(onStep, stepId, `Install memory module: ${memory}`, "error", "module not found");
50
+ }
51
+ }
52
+ else {
53
+ report(onStep, stepId, `Install memory module: ${memory}`, "skipped", "none selected");
54
+ }
55
+ }
56
+ catch (e) {
57
+ report(onStep, stepId, `Install memory module: ${memory}`, "error", String(e));
58
+ }
59
+ };
60
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1,13 @@
1
+ import type { StepFn } from "../types.js";
2
+ /**
3
+ * Step 10: Configure mock-first mode.
4
+ *
5
+ * - When options.mock is false, reports "skipped".
6
+ * - Otherwise writes .env.example (and .env from it) with mock values, only if
7
+ * the targets do not already exist.
8
+ * - Errors are swallowed and reported as status:"error" — never thrown.
9
+ *
10
+ * Extracted VERBATIM from src/commands/init.ts (PR 3 of 6).
11
+ */
12
+ export declare const stepMock: StepFn;
13
+ //# sourceMappingURL=mock.d.ts.map
@@ -0,0 +1,64 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { report } from "../report.js";
4
+ // Mock environment template — written verbatim to .env.example and .env
5
+ // when mock-first mode is enabled. Preserved byte-for-byte from init.ts baseline.
6
+ const envExample = `# Mock environment — no real API keys required
7
+ # Copy to .env to use: cp .env.example .env
8
+
9
+ # Database
10
+ DATABASE_URL=postgresql://mock:mock@localhost:5432/mock_db
11
+
12
+ # Auth
13
+ JWT_SECRET=mock-jwt-secret-for-local-development
14
+ SESSION_SECRET=mock-session-secret
15
+
16
+ # External APIs (mock mode — no real calls)
17
+ MOCK_MODE=true
18
+ API_KEY=mock-api-key-not-real
19
+ STRIPE_KEY=sk_test_mock_not_real
20
+ SENDGRID_KEY=SG.mock_not_real
21
+
22
+ # Feature flags
23
+ ENABLE_ANALYTICS=false
24
+ ENABLE_EMAILS=false
25
+ ENABLE_WEBHOOKS=false
26
+ `;
27
+ /**
28
+ * Step 10: Configure mock-first mode.
29
+ *
30
+ * - When options.mock is false, reports "skipped".
31
+ * - Otherwise writes .env.example (and .env from it) with mock values, only if
32
+ * the targets do not already exist.
33
+ * - Errors are swallowed and reported as status:"error" — never thrown.
34
+ *
35
+ * Extracted VERBATIM from src/commands/init.ts (PR 3 of 6).
36
+ */
37
+ export const stepMock = async (ctx) => {
38
+ const { projectDir, dryRun, onStep, options } = ctx;
39
+ const stepId = "mock";
40
+ if (options.mock) {
41
+ report(onStep, stepId, "Configure mock-first mode", "running");
42
+ try {
43
+ if (!dryRun) {
44
+ const envExamplePath = path.join(projectDir, ".env.example");
45
+ if (!(await fs.pathExists(envExamplePath))) {
46
+ await fs.writeFile(envExamplePath, envExample, "utf-8");
47
+ }
48
+ // Create .env from example
49
+ const envPath = path.join(projectDir, ".env");
50
+ if (!(await fs.pathExists(envPath))) {
51
+ await fs.writeFile(envPath, envExample, "utf-8");
52
+ }
53
+ }
54
+ report(onStep, stepId, "Configure mock-first mode", "done", ".env.example + .env with mock values");
55
+ }
56
+ catch (e) {
57
+ report(onStep, stepId, "Configure mock-first mode", "error", String(e));
58
+ }
59
+ }
60
+ else {
61
+ report(onStep, stepId, "Configure mock-first mode", "skipped", "not selected");
62
+ }
63
+ };
64
+ //# sourceMappingURL=mock.js.map
@@ -0,0 +1,12 @@
1
+ import type { StepFn } from "../types.js";
2
+ /**
3
+ * Step 8: Set up SDD (openspec/) scaffolding.
4
+ *
5
+ * - When sdd is false, reports "skipped".
6
+ * - Otherwise ensures openspec/ exists and writes a default README.md if absent.
7
+ * - Errors are swallowed and reported as status:"error" — never thrown.
8
+ *
9
+ * Extracted VERBATIM from src/commands/init.ts (PR 3 of 6).
10
+ */
11
+ export declare const stepSDD: StepFn;
12
+ //# sourceMappingURL=sdd.d.ts.map
@@ -0,0 +1,40 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { ensureDirExists } from "../../../lib/common.js";
4
+ import { report } from "../report.js";
5
+ /**
6
+ * Step 8: Set up SDD (openspec/) scaffolding.
7
+ *
8
+ * - When sdd is false, reports "skipped".
9
+ * - Otherwise ensures openspec/ exists and writes a default README.md if absent.
10
+ * - Errors are swallowed and reported as status:"error" — never thrown.
11
+ *
12
+ * Extracted VERBATIM from src/commands/init.ts (PR 3 of 6).
13
+ */
14
+ export const stepSDD = async (ctx) => {
15
+ const { projectDir, dryRun, onStep, options } = ctx;
16
+ const { sdd, projectName } = options;
17
+ const stepId = "sdd";
18
+ report(onStep, stepId, "Set up SDD (openspec/)", "running");
19
+ try {
20
+ if (sdd) {
21
+ if (!dryRun) {
22
+ const openspecDir = path.join(projectDir, "openspec");
23
+ await ensureDirExists(openspecDir);
24
+ // Create a README if none exists
25
+ const readmePath = path.join(openspecDir, "README.md");
26
+ if (!(await fs.pathExists(readmePath))) {
27
+ 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");
28
+ }
29
+ }
30
+ report(onStep, stepId, "Set up SDD (openspec/)", "done");
31
+ }
32
+ else {
33
+ report(onStep, stepId, "Set up SDD (openspec/)", "skipped", "not selected");
34
+ }
35
+ }
36
+ catch (e) {
37
+ report(onStep, stepId, "Set up SDD (openspec/)", "error", String(e));
38
+ }
39
+ };
40
+ //# sourceMappingURL=sdd.js.map
@@ -0,0 +1,28 @@
1
+ import type { StepFn } from "../types.js";
2
+ /**
3
+ * Step 14: Scaffold security hooks.
4
+ *
5
+ * - When options.securityHooks is false, reports "skipped".
6
+ * - When SECURITY_HOOKS_DIR templates are missing, reports "error".
7
+ * - Otherwise copies the 6-layer git security hooks into
8
+ * ci-local/hooks/security/ (skipping existing files, chmod 0755) and copies
9
+ * the kiteguard-style runtime settings to .claude/settings.json when absent.
10
+ * - Errors are swallowed and reported as status:"error" — never thrown.
11
+ *
12
+ * Extracted VERBATIM from src/commands/init.ts (PR 5 of 6).
13
+ * Grouped with stepHookProfile for cohesion — both manage ci-local/hooks/.
14
+ */
15
+ export declare const stepSecurityHooks: StepFn;
16
+ /**
17
+ * Step 14b: Write hook reliability profile.
18
+ *
19
+ * - When options.securityHooks is false, reports "skipped".
20
+ * - Otherwise writes ci-local/hooks/profile.json with the resolved profile
21
+ * (defaults to "standard" when hookProfile is undefined).
22
+ * - Errors are swallowed and reported as status:"error" — never thrown.
23
+ *
24
+ * Extracted VERBATIM from src/commands/init.ts (PR 5 of 6).
25
+ * Grouped with stepSecurityHooks for cohesion — both manage ci-local/hooks/.
26
+ */
27
+ export declare const stepHookProfile: StepFn;
28
+ //# sourceMappingURL=security.d.ts.map
@@ -0,0 +1,103 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { SECURITY_HOOKS_DIR } from "../../../constants.js";
4
+ import { ensureDirExists } from "../../../lib/common.js";
5
+ import { report } from "../report.js";
6
+ /**
7
+ * Step 14: Scaffold security hooks.
8
+ *
9
+ * - When options.securityHooks is false, reports "skipped".
10
+ * - When SECURITY_HOOKS_DIR templates are missing, reports "error".
11
+ * - Otherwise copies the 6-layer git security hooks into
12
+ * ci-local/hooks/security/ (skipping existing files, chmod 0755) and copies
13
+ * the kiteguard-style runtime settings to .claude/settings.json when absent.
14
+ * - Errors are swallowed and reported as status:"error" — never thrown.
15
+ *
16
+ * Extracted VERBATIM from src/commands/init.ts (PR 5 of 6).
17
+ * Grouped with stepHookProfile for cohesion — both manage ci-local/hooks/.
18
+ */
19
+ export const stepSecurityHooks = async (ctx) => {
20
+ const { projectDir, dryRun, onStep, options } = ctx;
21
+ const { securityHooks } = options;
22
+ const stepId = "security-hooks";
23
+ report(onStep, stepId, "Scaffold security hooks", "running");
24
+ try {
25
+ if (securityHooks) {
26
+ if (await fs.pathExists(SECURITY_HOOKS_DIR)) {
27
+ if (!dryRun) {
28
+ // Copy 6-layer git security hooks into ci-local/hooks/security/
29
+ const secHooksDest = path.join(projectDir, "ci-local", "hooks", "security");
30
+ await ensureDirExists(secHooksDest);
31
+ const hookFiles = await fs.readdir(SECURITY_HOOKS_DIR);
32
+ const gitHooks = hookFiles.filter((f) => !f.endsWith(".json"));
33
+ for (const hook of gitHooks) {
34
+ const src = path.join(SECURITY_HOOKS_DIR, hook);
35
+ const dest = path.join(secHooksDest, hook);
36
+ await fs.copy(src, dest, { overwrite: false });
37
+ await fs.chmod(dest, 0o755);
38
+ }
39
+ // Copy runtime security settings (kiteguard-style) to .claude/
40
+ const settingsSrc = path.join(SECURITY_HOOKS_DIR, "claude-settings-security.json");
41
+ if (await fs.pathExists(settingsSrc)) {
42
+ const claudeDir = path.join(projectDir, ".claude");
43
+ await ensureDirExists(claudeDir);
44
+ const settingsDest = path.join(claudeDir, "settings.json");
45
+ if (!(await fs.pathExists(settingsDest))) {
46
+ await fs.copy(settingsSrc, settingsDest);
47
+ }
48
+ }
49
+ }
50
+ report(onStep, stepId, "Scaffold security hooks", "done", dryRun
51
+ ? "dry-run: would scaffold security hooks"
52
+ : "6 git layers + runtime hooks");
53
+ }
54
+ else {
55
+ report(onStep, stepId, "Scaffold security hooks", "error", "security-hooks templates not found");
56
+ }
57
+ }
58
+ else {
59
+ report(onStep, stepId, "Scaffold security hooks", "skipped", "not selected");
60
+ }
61
+ }
62
+ catch (e) {
63
+ report(onStep, stepId, "Scaffold security hooks", "error", String(e));
64
+ }
65
+ };
66
+ /**
67
+ * Step 14b: Write hook reliability profile.
68
+ *
69
+ * - When options.securityHooks is false, reports "skipped".
70
+ * - Otherwise writes ci-local/hooks/profile.json with the resolved profile
71
+ * (defaults to "standard" when hookProfile is undefined).
72
+ * - Errors are swallowed and reported as status:"error" — never thrown.
73
+ *
74
+ * Extracted VERBATIM from src/commands/init.ts (PR 5 of 6).
75
+ * Grouped with stepSecurityHooks for cohesion — both manage ci-local/hooks/.
76
+ */
77
+ export const stepHookProfile = async (ctx) => {
78
+ const { projectDir, dryRun, onStep, options } = ctx;
79
+ const { securityHooks, hookProfile } = options;
80
+ const stepId = "hook-profile";
81
+ report(onStep, stepId, "Write hook reliability profile", "running");
82
+ try {
83
+ if (securityHooks) {
84
+ if (!dryRun) {
85
+ const hooksDir = path.join(projectDir, "ci-local", "hooks");
86
+ await ensureDirExists(hooksDir);
87
+ const profilePath = path.join(hooksDir, "profile.json");
88
+ const resolvedProfile = hookProfile ?? "standard";
89
+ await fs.writeJson(profilePath, { profile: resolvedProfile }, { spaces: 2 });
90
+ }
91
+ report(onStep, stepId, "Write hook reliability profile", "done", dryRun
92
+ ? `dry-run: would write profile.json (${hookProfile ?? "standard"})`
93
+ : `ci-local/hooks/profile.json (${hookProfile ?? "standard"})`);
94
+ }
95
+ else {
96
+ report(onStep, stepId, "Write hook reliability profile", "skipped", "security hooks not selected");
97
+ }
98
+ }
99
+ catch (e) {
100
+ report(onStep, stepId, "Write hook reliability profile", "error", String(e));
101
+ }
102
+ };
103
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1,30 @@
1
+ import type { InitOptions, InitStep } from "../../types/index.js";
2
+ /**
3
+ * Callback invoked by each step to report progress.
4
+ * Steps emit at least 2 events: status:"running" followed by a terminal status
5
+ * ("done" | "skipped" | "error").
6
+ */
7
+ export type StepCallback = (step: InitStep) => void;
8
+ /**
9
+ * Context passed to every step. Built once by initProject and shared
10
+ * across all extracted step functions.
11
+ *
12
+ * Fields are intentionally minimal:
13
+ * - `options`: full InitOptions (steps can read any flag they need)
14
+ * - `projectDir`: convenience alias for options.projectDir
15
+ * - `dryRun`: convenience alias for options.dryRun
16
+ * - `onStep`: the progress callback (passed to report())
17
+ */
18
+ export type StepContext = {
19
+ options: InitOptions;
20
+ projectDir: string;
21
+ dryRun: boolean;
22
+ onStep: StepCallback;
23
+ };
24
+ /**
25
+ * Step function signature. All step* functions follow this shape.
26
+ * Returns a promise that resolves when the step is complete
27
+ * (success, skipped, or recovered error — steps must NOT throw).
28
+ */
29
+ export type StepFn = (ctx: StepContext) => Promise<void>;
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map