javi-forge 1.6.0 → 1.6.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.
Files changed (231) hide show
  1. package/dist/commands/analyze.d.ts +1 -1
  2. package/dist/commands/analyze.js +15 -15
  3. package/dist/commands/atlassian-mcp.d.ts +42 -0
  4. package/dist/commands/atlassian-mcp.js +98 -0
  5. package/dist/commands/ci.d.ts +3 -3
  6. package/dist/commands/ci.js +185 -147
  7. package/dist/commands/crash-recovery.d.ts +34 -0
  8. package/dist/commands/crash-recovery.js +123 -0
  9. package/dist/commands/doctor.d.ts +2 -2
  10. package/dist/commands/doctor.js +113 -61
  11. package/dist/commands/harness-audit.d.ts +35 -0
  12. package/dist/commands/harness-audit.js +277 -0
  13. package/dist/commands/init.d.ts +1 -1
  14. package/dist/commands/init.js +384 -141
  15. package/dist/commands/llmstxt.d.ts +1 -1
  16. package/dist/commands/llmstxt.js +36 -34
  17. package/dist/commands/parallel-batch.d.ts +42 -0
  18. package/dist/commands/parallel-batch.js +90 -0
  19. package/dist/commands/plugin.d.ts +10 -1
  20. package/dist/commands/plugin.js +92 -47
  21. package/dist/commands/secret-scanner.d.ts +30 -0
  22. package/dist/commands/secret-scanner.js +272 -0
  23. package/dist/commands/security-analysis.d.ts +74 -0
  24. package/dist/commands/security-analysis.js +487 -0
  25. package/dist/commands/security.d.ts +11 -5
  26. package/dist/commands/security.js +216 -76
  27. package/dist/commands/skill-scanner.d.ts +63 -0
  28. package/dist/commands/skill-scanner.js +383 -0
  29. package/dist/commands/skills.d.ts +62 -5
  30. package/dist/commands/skills.js +439 -54
  31. package/dist/commands/supply-chain.d.ts +23 -0
  32. package/dist/commands/supply-chain.js +126 -0
  33. package/dist/commands/tdd-pipeline.d.ts +17 -0
  34. package/dist/commands/tdd-pipeline.js +144 -0
  35. package/dist/commands/tdd.d.ts +1 -1
  36. package/dist/commands/tdd.js +21 -18
  37. package/dist/commands/team-presets.d.ts +53 -0
  38. package/dist/commands/team-presets.js +201 -0
  39. package/dist/commands/workflow.d.ts +23 -0
  40. package/dist/commands/workflow.js +114 -0
  41. package/dist/constants.d.ts +15 -1
  42. package/dist/constants.js +161 -122
  43. package/dist/index.js +308 -98
  44. package/dist/lib/agent-skills.d.ts +36 -1
  45. package/dist/lib/agent-skills.js +168 -19
  46. package/dist/lib/auto-skill-install.d.ts +37 -0
  47. package/dist/lib/auto-skill-install.js +92 -0
  48. package/dist/lib/auto-wire.d.ts +20 -0
  49. package/dist/lib/auto-wire.js +240 -0
  50. package/dist/lib/claudemd.d.ts +13 -1
  51. package/dist/lib/claudemd.js +174 -24
  52. package/dist/lib/codex-export.d.ts +1 -1
  53. package/dist/lib/codex-export.js +29 -31
  54. package/dist/lib/common.d.ts +1 -1
  55. package/dist/lib/common.js +52 -44
  56. package/dist/lib/context.d.ts +17 -2
  57. package/dist/lib/context.js +142 -13
  58. package/dist/lib/docker.d.ts +1 -1
  59. package/dist/lib/docker.js +141 -112
  60. package/dist/lib/frontmatter.d.ts +1 -1
  61. package/dist/lib/frontmatter.js +29 -15
  62. package/dist/lib/plugin.d.ts +9 -3
  63. package/dist/lib/plugin.js +128 -69
  64. package/dist/lib/skill-publish.d.ts +40 -0
  65. package/dist/lib/skill-publish.js +146 -0
  66. package/dist/lib/stack-detector.d.ts +38 -0
  67. package/dist/lib/stack-detector.js +207 -0
  68. package/dist/lib/template.d.ts +16 -1
  69. package/dist/lib/template.js +46 -17
  70. package/dist/lib/workflow/discovery.d.ts +19 -0
  71. package/dist/lib/workflow/discovery.js +68 -0
  72. package/dist/lib/workflow/index.d.ts +5 -0
  73. package/dist/lib/workflow/index.js +5 -0
  74. package/dist/lib/workflow/parser.d.ts +16 -0
  75. package/dist/lib/workflow/parser.js +198 -0
  76. package/dist/lib/workflow/renderer.d.ts +9 -0
  77. package/dist/lib/workflow/renderer.js +152 -0
  78. package/dist/lib/workflow/validator.d.ts +10 -0
  79. package/dist/lib/workflow/validator.js +189 -0
  80. package/dist/tasks/index.d.ts +4 -0
  81. package/dist/tasks/index.js +4 -0
  82. package/dist/tasks/scaffold-tasks.d.ts +3 -0
  83. package/dist/tasks/scaffold-tasks.js +14 -0
  84. package/dist/tasks/task-id.d.ts +30 -0
  85. package/dist/tasks/task-id.js +55 -0
  86. package/dist/tasks/task-tracker.d.ts +15 -0
  87. package/dist/tasks/task-tracker.js +81 -0
  88. package/dist/types/index.d.ts +134 -6
  89. package/dist/types/index.js +11 -1
  90. package/dist/ui/AnalyzeUI.d.ts +1 -1
  91. package/dist/ui/AnalyzeUI.js +38 -39
  92. package/dist/ui/App.d.ts +5 -3
  93. package/dist/ui/App.js +86 -46
  94. package/dist/ui/AutoSkills.d.ts +9 -0
  95. package/dist/ui/AutoSkills.js +124 -0
  96. package/dist/ui/CI.d.ts +2 -2
  97. package/dist/ui/CI.js +24 -26
  98. package/dist/ui/CIContext.d.ts +1 -1
  99. package/dist/ui/CIContext.js +3 -2
  100. package/dist/ui/CISelector.d.ts +2 -2
  101. package/dist/ui/CISelector.js +23 -15
  102. package/dist/ui/Doctor.d.ts +1 -1
  103. package/dist/ui/Doctor.js +35 -29
  104. package/dist/ui/Header.d.ts +1 -1
  105. package/dist/ui/Header.js +14 -14
  106. package/dist/ui/HookProfileSelector.d.ts +9 -0
  107. package/dist/ui/HookProfileSelector.js +54 -0
  108. package/dist/ui/LlmsTxt.d.ts +1 -1
  109. package/dist/ui/LlmsTxt.js +31 -22
  110. package/dist/ui/MemorySelector.d.ts +2 -2
  111. package/dist/ui/MemorySelector.js +28 -16
  112. package/dist/ui/NameInput.d.ts +1 -1
  113. package/dist/ui/NameInput.js +21 -21
  114. package/dist/ui/OptionSelector.d.ts +6 -2
  115. package/dist/ui/OptionSelector.js +83 -32
  116. package/dist/ui/Plugin.d.ts +4 -3
  117. package/dist/ui/Plugin.js +78 -35
  118. package/dist/ui/Progress.d.ts +3 -3
  119. package/dist/ui/Progress.js +23 -22
  120. package/dist/ui/Skills.d.ts +2 -2
  121. package/dist/ui/Skills.js +61 -32
  122. package/dist/ui/StackSelector.d.ts +2 -2
  123. package/dist/ui/StackSelector.js +26 -16
  124. package/dist/ui/Summary.d.ts +3 -3
  125. package/dist/ui/Summary.js +60 -50
  126. package/dist/ui/Welcome.d.ts +1 -1
  127. package/dist/ui/Welcome.js +15 -16
  128. package/dist/ui/theme.d.ts +1 -1
  129. package/dist/ui/theme.js +6 -6
  130. package/package.json +9 -6
  131. package/templates/common/atlassian/mcp-atlassian-snippet.json +16 -0
  132. package/templates/common/repoforge/mcp-repoforge-snippet.json +11 -0
  133. package/templates/common/repoforge/repoforge.yaml +34 -0
  134. package/templates/github/deploy-docker-zero-downtime.yml +140 -0
  135. package/templates/github/repoforge-graph.yml +45 -0
  136. package/templates/gitlab/deploy-docker-zero-downtime.yml +57 -0
  137. package/templates/local-ai/.env.example +17 -0
  138. package/templates/local-ai/docker-compose.yml +95 -0
  139. package/templates/security-hooks/claude-settings-security.json +30 -0
  140. package/templates/security-hooks/commit-msg-signing +29 -0
  141. package/templates/security-hooks/pre-commit-permissions +74 -0
  142. package/templates/security-hooks/pre-commit-secrets +74 -0
  143. package/templates/security-hooks/pre-push-branch-protection +62 -0
  144. package/templates/security-hooks/pre-push-deps +83 -0
  145. package/templates/security-hooks/pre-push-signing +67 -0
  146. package/templates/woodpecker/deploy-docker-zero-downtime.yml +50 -0
  147. package/templates/workflows/ci-pipeline.dot +15 -0
  148. package/templates/workflows/feature-flow.dot +21 -0
  149. package/templates/workflows/release.dot +16 -0
  150. package/dist/__integration__/helpers.d.ts +0 -20
  151. package/dist/__integration__/helpers.d.ts.map +0 -1
  152. package/dist/__integration__/helpers.js +0 -31
  153. package/dist/__integration__/helpers.js.map +0 -1
  154. package/dist/commands/analyze.d.ts.map +0 -1
  155. package/dist/commands/analyze.js.map +0 -1
  156. package/dist/commands/ci.d.ts.map +0 -1
  157. package/dist/commands/ci.js.map +0 -1
  158. package/dist/commands/doctor.d.ts.map +0 -1
  159. package/dist/commands/doctor.js.map +0 -1
  160. package/dist/commands/init.d.ts.map +0 -1
  161. package/dist/commands/init.js.map +0 -1
  162. package/dist/commands/llmstxt.d.ts.map +0 -1
  163. package/dist/commands/llmstxt.js.map +0 -1
  164. package/dist/commands/plugin.d.ts.map +0 -1
  165. package/dist/commands/plugin.js.map +0 -1
  166. package/dist/commands/security.d.ts.map +0 -1
  167. package/dist/commands/security.js.map +0 -1
  168. package/dist/commands/skills.d.ts.map +0 -1
  169. package/dist/commands/skills.js.map +0 -1
  170. package/dist/commands/tdd.d.ts.map +0 -1
  171. package/dist/commands/tdd.js.map +0 -1
  172. package/dist/constants.d.ts.map +0 -1
  173. package/dist/constants.js.map +0 -1
  174. package/dist/index.d.ts.map +0 -1
  175. package/dist/index.js.map +0 -1
  176. package/dist/lib/agent-skills.d.ts.map +0 -1
  177. package/dist/lib/agent-skills.js.map +0 -1
  178. package/dist/lib/claudemd.d.ts.map +0 -1
  179. package/dist/lib/claudemd.js.map +0 -1
  180. package/dist/lib/codex-export.d.ts.map +0 -1
  181. package/dist/lib/codex-export.js.map +0 -1
  182. package/dist/lib/common.d.ts.map +0 -1
  183. package/dist/lib/common.js.map +0 -1
  184. package/dist/lib/context.d.ts.map +0 -1
  185. package/dist/lib/context.js.map +0 -1
  186. package/dist/lib/docker.d.ts.map +0 -1
  187. package/dist/lib/docker.js.map +0 -1
  188. package/dist/lib/frontmatter.d.ts.map +0 -1
  189. package/dist/lib/frontmatter.js.map +0 -1
  190. package/dist/lib/plugin.d.ts.map +0 -1
  191. package/dist/lib/plugin.js.map +0 -1
  192. package/dist/lib/template.d.ts.map +0 -1
  193. package/dist/lib/template.js.map +0 -1
  194. package/dist/types/index.d.ts.map +0 -1
  195. package/dist/types/index.js.map +0 -1
  196. package/dist/ui/AnalyzeUI.d.ts.map +0 -1
  197. package/dist/ui/AnalyzeUI.js.map +0 -1
  198. package/dist/ui/App.d.ts.map +0 -1
  199. package/dist/ui/App.js.map +0 -1
  200. package/dist/ui/CI.d.ts.map +0 -1
  201. package/dist/ui/CI.js.map +0 -1
  202. package/dist/ui/CIContext.d.ts.map +0 -1
  203. package/dist/ui/CIContext.js.map +0 -1
  204. package/dist/ui/CISelector.d.ts.map +0 -1
  205. package/dist/ui/CISelector.js.map +0 -1
  206. package/dist/ui/Doctor.d.ts.map +0 -1
  207. package/dist/ui/Doctor.js.map +0 -1
  208. package/dist/ui/Header.d.ts.map +0 -1
  209. package/dist/ui/Header.js.map +0 -1
  210. package/dist/ui/LlmsTxt.d.ts.map +0 -1
  211. package/dist/ui/LlmsTxt.js.map +0 -1
  212. package/dist/ui/MemorySelector.d.ts.map +0 -1
  213. package/dist/ui/MemorySelector.js.map +0 -1
  214. package/dist/ui/NameInput.d.ts.map +0 -1
  215. package/dist/ui/NameInput.js.map +0 -1
  216. package/dist/ui/OptionSelector.d.ts.map +0 -1
  217. package/dist/ui/OptionSelector.js.map +0 -1
  218. package/dist/ui/Plugin.d.ts.map +0 -1
  219. package/dist/ui/Plugin.js.map +0 -1
  220. package/dist/ui/Progress.d.ts.map +0 -1
  221. package/dist/ui/Progress.js.map +0 -1
  222. package/dist/ui/Skills.d.ts.map +0 -1
  223. package/dist/ui/Skills.js.map +0 -1
  224. package/dist/ui/StackSelector.d.ts.map +0 -1
  225. package/dist/ui/StackSelector.js.map +0 -1
  226. package/dist/ui/Summary.d.ts.map +0 -1
  227. package/dist/ui/Summary.js.map +0 -1
  228. package/dist/ui/Welcome.d.ts.map +0 -1
  229. package/dist/ui/Welcome.js.map +0 -1
  230. package/dist/ui/theme.d.ts.map +0 -1
  231. package/dist/ui/theme.js.map +0 -1
@@ -1,73 +1,74 @@
1
- import { execFile, spawn } from 'child_process';
2
- import { promisify } from 'util';
3
- import fs from 'fs-extra';
4
- import path from 'path';
5
- import { isDockerAvailable, ensureImage, runInContainer, openShell } from '../lib/docker.js';
1
+ import { execFile, spawn } from "child_process";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import { promisify } from "util";
5
+ import { refreshContextDir } from "../lib/context.js";
6
+ import { ensureImage, isDockerAvailable, openShell, runInContainer, } from "../lib/docker.js";
6
7
  const execFileAsync = promisify(execFile);
7
8
  // =============================================================================
8
9
  // Stack detection
9
10
  // =============================================================================
10
11
  export async function detectCIStack(projectDir) {
11
- let stackType = 'node';
12
- let buildTool = 'npm';
13
- let javaVersion = '21';
12
+ let stackType = "node";
13
+ let buildTool = "npm";
14
+ let javaVersion = "21";
14
15
  // Java Gradle
15
- if (await fs.pathExists(path.join(projectDir, 'build.gradle.kts')) ||
16
- await fs.pathExists(path.join(projectDir, 'build.gradle'))) {
17
- stackType = 'java-gradle';
18
- buildTool = 'gradle';
16
+ if ((await fs.pathExists(path.join(projectDir, "build.gradle.kts"))) ||
17
+ (await fs.pathExists(path.join(projectDir, "build.gradle")))) {
18
+ stackType = "java-gradle";
19
+ buildTool = "gradle";
19
20
  // Try to read java version from build files
20
- const ktsPath = path.join(projectDir, 'build.gradle.kts');
21
- const gradlePath = path.join(projectDir, 'build.gradle');
21
+ const ktsPath = path.join(projectDir, "build.gradle.kts");
22
+ const gradlePath = path.join(projectDir, "build.gradle");
22
23
  if (await fs.pathExists(ktsPath)) {
23
- const content = await fs.readFile(ktsPath, 'utf-8');
24
+ const content = await fs.readFile(ktsPath, "utf-8");
24
25
  const match = content.match(/JavaLanguageVersion\.of\((\d+)\)/);
25
26
  if (match?.[1])
26
27
  javaVersion = match[1];
27
28
  }
28
29
  else if (await fs.pathExists(gradlePath)) {
29
- const content = await fs.readFile(gradlePath, 'utf-8');
30
+ const content = await fs.readFile(gradlePath, "utf-8");
30
31
  const match = content.match(/sourceCompatibility\s*=\s*['"]*(\d+)/);
31
32
  if (match?.[1])
32
33
  javaVersion = match[1];
33
34
  }
34
35
  }
35
36
  // Java Maven
36
- else if (await fs.pathExists(path.join(projectDir, 'pom.xml'))) {
37
- stackType = 'java-maven';
38
- buildTool = 'mvn';
37
+ else if (await fs.pathExists(path.join(projectDir, "pom.xml"))) {
38
+ stackType = "java-maven";
39
+ buildTool = "mvn";
39
40
  }
40
41
  // Node
41
- else if (await fs.pathExists(path.join(projectDir, 'package.json'))) {
42
- stackType = 'node';
43
- if (await fs.pathExists(path.join(projectDir, 'pnpm-lock.yaml')))
44
- buildTool = 'pnpm';
45
- else if (await fs.pathExists(path.join(projectDir, 'yarn.lock')))
46
- buildTool = 'yarn';
42
+ else if (await fs.pathExists(path.join(projectDir, "package.json"))) {
43
+ stackType = "node";
44
+ if (await fs.pathExists(path.join(projectDir, "pnpm-lock.yaml")))
45
+ buildTool = "pnpm";
46
+ else if (await fs.pathExists(path.join(projectDir, "yarn.lock")))
47
+ buildTool = "yarn";
47
48
  else
48
- buildTool = 'npm';
49
+ buildTool = "npm";
49
50
  }
50
51
  // Go
51
- else if (await fs.pathExists(path.join(projectDir, 'go.mod'))) {
52
- stackType = 'go';
53
- buildTool = 'go';
52
+ else if (await fs.pathExists(path.join(projectDir, "go.mod"))) {
53
+ stackType = "go";
54
+ buildTool = "go";
54
55
  }
55
56
  // Rust
56
- else if (await fs.pathExists(path.join(projectDir, 'Cargo.toml'))) {
57
- stackType = 'rust';
58
- buildTool = 'cargo';
57
+ else if (await fs.pathExists(path.join(projectDir, "Cargo.toml"))) {
58
+ stackType = "rust";
59
+ buildTool = "cargo";
59
60
  }
60
61
  // Python
61
- else if (await fs.pathExists(path.join(projectDir, 'pyproject.toml')) ||
62
- await fs.pathExists(path.join(projectDir, 'requirements.txt')) ||
63
- await fs.pathExists(path.join(projectDir, 'setup.py'))) {
64
- stackType = 'python';
65
- if (await fs.pathExists(path.join(projectDir, 'uv.lock')))
66
- buildTool = 'uv';
67
- else if (await fs.pathExists(path.join(projectDir, 'poetry.lock')))
68
- buildTool = 'poetry';
62
+ else if ((await fs.pathExists(path.join(projectDir, "pyproject.toml"))) ||
63
+ (await fs.pathExists(path.join(projectDir, "requirements.txt"))) ||
64
+ (await fs.pathExists(path.join(projectDir, "setup.py")))) {
65
+ stackType = "python";
66
+ if (await fs.pathExists(path.join(projectDir, "uv.lock")))
67
+ buildTool = "uv";
68
+ else if (await fs.pathExists(path.join(projectDir, "poetry.lock")))
69
+ buildTool = "poetry";
69
70
  else
70
- buildTool = 'pip';
71
+ buildTool = "pip";
71
72
  }
72
73
  // Build CI commands per stack
73
74
  const { lintCmd, compileCmd, testCmd } = await buildCICommands(stackType, buildTool, projectDir);
@@ -75,52 +76,58 @@ export async function detectCIStack(projectDir) {
75
76
  }
76
77
  async function buildCICommands(stack, buildTool, projectDir) {
77
78
  switch (stack) {
78
- case 'java-gradle':
79
+ case "java-gradle":
79
80
  return {
80
- lintCmd: './gradlew spotlessCheck --no-daemon',
81
- compileCmd: './gradlew clean classes testClasses --no-daemon && chown -R runner:runner build/ .gradle/ 2>/dev/null || true',
82
- testCmd: './gradlew test --no-daemon',
81
+ lintCmd: "./gradlew spotlessCheck --no-daemon",
82
+ compileCmd: "./gradlew clean classes testClasses --no-daemon && chown -R runner:runner build/ .gradle/ 2>/dev/null || true",
83
+ testCmd: "./gradlew test --no-daemon",
83
84
  };
84
- case 'java-maven':
85
+ case "java-maven":
85
86
  return {
86
- lintCmd: './mvnw spotless:check',
87
- compileCmd: './mvnw clean compile test-compile && chown -R runner:runner target/ .mvn/ 2>/dev/null || true',
88
- testCmd: './mvnw test',
87
+ lintCmd: "./mvnw spotless:check",
88
+ compileCmd: "./mvnw clean compile test-compile && chown -R runner:runner target/ .mvn/ 2>/dev/null || true",
89
+ testCmd: "./mvnw test",
89
90
  };
90
- case 'node': {
91
- const pkgPath = path.join(projectDir, 'package.json');
92
- let pkgContent = '';
91
+ case "node": {
92
+ const pkgPath = path.join(projectDir, "package.json");
93
+ let pkgContent = "";
93
94
  try {
94
- pkgContent = await fs.readFile(pkgPath, 'utf-8');
95
+ pkgContent = await fs.readFile(pkgPath, "utf-8");
96
+ }
97
+ catch {
98
+ /* no package.json */
95
99
  }
96
- catch { /* no package.json */ }
97
100
  // Clean dist/ before build and chown after so tests (as runner) can access output.
98
101
  // Runs as root inside the container to handle host-owned output dirs.
99
- const buildPrefix = 'rm -rf dist/ && ';
100
- const buildSuffix = ' && chown -R runner:runner dist/ 2>/dev/null || true';
102
+ const buildPrefix = "rm -rf dist/ && ";
103
+ const buildSuffix = " && chown -R runner:runner dist/ 2>/dev/null || true";
101
104
  return {
102
105
  lintCmd: pkgContent.includes('"lint"') ? `${buildTool} run lint` : null,
103
- compileCmd: pkgContent.includes('"build"') ? `${buildPrefix}${buildTool} run build${buildSuffix}` : null,
104
- testCmd: pkgContent.includes('"test"') ? `${buildTool} ${buildTool === 'npm' ? 'test' : 'run test'}` : null,
106
+ compileCmd: pkgContent.includes('"build"')
107
+ ? `${buildPrefix}${buildTool} run build${buildSuffix}`
108
+ : null,
109
+ testCmd: pkgContent.includes('"test"')
110
+ ? `${buildTool} ${buildTool === "npm" ? "test" : "run test"}`
111
+ : null,
105
112
  };
106
113
  }
107
- case 'python':
114
+ case "python":
108
115
  return {
109
- lintCmd: 'ruff check . && { pylint **/*.py 2>/dev/null || true; }',
116
+ lintCmd: "ruff check . && { pylint **/*.py 2>/dev/null || true; }",
110
117
  compileCmd: null,
111
- testCmd: 'pytest',
118
+ testCmd: "pytest",
112
119
  };
113
- case 'go':
120
+ case "go":
114
121
  return {
115
- lintCmd: 'golangci-lint run',
116
- compileCmd: 'go clean -cache && go build ./... && chown -R runner:runner . 2>/dev/null || true',
117
- testCmd: 'go test ./...',
122
+ lintCmd: "golangci-lint run",
123
+ compileCmd: "go clean -cache && go build ./... && chown -R runner:runner . 2>/dev/null || true",
124
+ testCmd: "go test ./...",
118
125
  };
119
- case 'rust':
126
+ case "rust":
120
127
  return {
121
- lintCmd: 'cargo clippy -- -D warnings',
122
- compileCmd: 'cargo clean && cargo build && chown -R runner:runner target/ 2>/dev/null || true',
123
- testCmd: 'cargo test',
128
+ lintCmd: "cargo clippy -- -D warnings",
129
+ compileCmd: "cargo clean && cargo build && chown -R runner:runner target/ 2>/dev/null || true",
130
+ testCmd: "cargo test",
124
131
  };
125
132
  default:
126
133
  return { lintCmd: null, compileCmd: null, testCmd: null };
@@ -131,7 +138,7 @@ async function buildCICommands(stack, buildTool, projectDir) {
131
138
  // =============================================================================
132
139
  async function isGhaggaAvailable() {
133
140
  try {
134
- await execFileAsync('ghagga', ['--version'], { timeout: 3000 });
141
+ await execFileAsync("ghagga", ["--version"], { timeout: 3000 });
135
142
  return true;
136
143
  }
137
144
  catch {
@@ -145,35 +152,38 @@ function report(onStep, id, label, status, detail) {
145
152
  onStep({ id, label, status, detail });
146
153
  }
147
154
  export async function runCI(options, onStep) {
148
- const { projectDir = process.cwd(), mode = 'full', noDocker = false, noGhagga = false, noSecurity = false, timeout = 600, } = options;
155
+ const { projectDir = process.cwd(), mode = "full", noDocker = false, noGhagga = false, noSecurity = false, timeout = 600, } = options;
149
156
  // ── Detect stack ────────────────────────────────────────────────────────────
150
- const stepDetect = 'detect';
151
- report(onStep, stepDetect, 'Detecting stack', 'running');
157
+ const stepDetect = "detect";
158
+ report(onStep, stepDetect, "Detecting stack", "running");
152
159
  let stackInfo;
153
160
  try {
154
161
  stackInfo = await detectCIStack(projectDir);
155
- report(onStep, stepDetect, `Stack: ${stackInfo.stackType} (${stackInfo.buildTool})`, 'done');
162
+ report(onStep, stepDetect, `Stack: ${stackInfo.stackType} (${stackInfo.buildTool})`, "done");
156
163
  }
157
164
  catch (e) {
158
- report(onStep, stepDetect, 'Detecting stack', 'error', String(e));
165
+ report(onStep, stepDetect, "Detecting stack", "error", String(e));
159
166
  throw e;
160
167
  }
161
168
  // ── Detect mode ─────────────────────────────────────────────────────────────
162
- if (mode === 'detect')
169
+ if (mode === "detect")
163
170
  return;
164
171
  // ── Shell mode ──────────────────────────────────────────────────────────────
165
- if (mode === 'shell') {
172
+ if (mode === "shell") {
166
173
  if (noDocker) {
167
- report(onStep, 'shell', 'Shell', 'error', '--shell requires Docker');
168
- throw new Error('--shell requires Docker');
174
+ report(onStep, "shell", "Shell", "error", "--shell requires Docker");
175
+ throw new Error("--shell requires Docker");
169
176
  }
170
- report(onStep, 'docker-image', 'Building Docker image', 'running');
177
+ report(onStep, "docker-image", "Building Docker image", "running");
171
178
  try {
172
- await ensureImage({ stack: stackInfo.stackType, javaVersion: stackInfo.javaVersion });
173
- report(onStep, 'docker-image', 'Docker image ready', 'done');
179
+ await ensureImage({
180
+ stack: stackInfo.stackType,
181
+ javaVersion: stackInfo.javaVersion,
182
+ });
183
+ report(onStep, "docker-image", "Docker image ready", "done");
174
184
  }
175
185
  catch (e) {
176
- report(onStep, 'docker-image', 'Building Docker image', 'error', String(e));
186
+ report(onStep, "docker-image", "Building Docker image", "error", String(e));
177
187
  throw e;
178
188
  }
179
189
  await openShell(projectDir);
@@ -181,103 +191,122 @@ export async function runCI(options, onStep) {
181
191
  }
182
192
  // ── Check Docker ─────────────────────────────────────────────────────────────
183
193
  if (!noDocker) {
184
- const stepDocker = 'docker-check';
185
- report(onStep, stepDocker, 'Checking Docker', 'running');
194
+ const stepDocker = "docker-check";
195
+ report(onStep, stepDocker, "Checking Docker", "running");
186
196
  const dockerOk = await isDockerAvailable();
187
197
  if (!dockerOk) {
188
- report(onStep, stepDocker, 'Docker not available', 'error', 'Start Docker or use --no-docker');
189
- throw new Error('Docker is not available');
198
+ report(onStep, stepDocker, "Docker not available", "error", "Start Docker or use --no-docker");
199
+ throw new Error("Docker is not available");
190
200
  }
191
- report(onStep, stepDocker, 'Docker available', 'done');
201
+ report(onStep, stepDocker, "Docker available", "done");
192
202
  // Build image
193
- const stepImage = 'docker-image';
194
- report(onStep, stepImage, `Building image for ${stackInfo.stackType}`, 'running');
203
+ const stepImage = "docker-image";
204
+ report(onStep, stepImage, `Building image for ${stackInfo.stackType}`, "running");
195
205
  try {
196
- await ensureImage({ stack: stackInfo.stackType, javaVersion: stackInfo.javaVersion });
197
- report(onStep, stepImage, 'Docker image ready', 'done');
206
+ await ensureImage({
207
+ stack: stackInfo.stackType,
208
+ javaVersion: stackInfo.javaVersion,
209
+ });
210
+ report(onStep, stepImage, "Docker image ready", "done");
198
211
  }
199
212
  catch (e) {
200
- report(onStep, stepImage, 'Building Docker image', 'error', String(e));
213
+ report(onStep, stepImage, "Building Docker image", "error", String(e));
201
214
  throw e;
202
215
  }
203
216
  }
217
+ // ── Refresh .context/ ────────────────────────────────────────────────────────
218
+ const stepContext = "context-refresh";
219
+ report(onStep, stepContext, "Refresh .context/ directory", "running");
220
+ try {
221
+ const ctxResult = await refreshContextDir(projectDir);
222
+ if (ctxResult) {
223
+ report(onStep, stepContext, "Refresh .context/ directory", "done", "INDEX.md + summary.md updated");
224
+ }
225
+ else {
226
+ report(onStep, stepContext, "Refresh .context/ directory", "skipped", "no .context/ or no manifest");
227
+ }
228
+ }
229
+ catch (e) {
230
+ // Non-fatal: context refresh failure should not block CI
231
+ report(onStep, stepContext, "Refresh .context/ directory", "error", String(e));
232
+ }
204
233
  // ── Lint ─────────────────────────────────────────────────────────────────────
205
234
  if (stackInfo.lintCmd) {
206
- const stepLint = 'lint';
207
- report(onStep, stepLint, `Lint: ${stackInfo.lintCmd}`, 'running');
235
+ const stepLint = "lint";
236
+ report(onStep, stepLint, `Lint: ${stackInfo.lintCmd}`, "running");
208
237
  try {
209
238
  await runStep(stackInfo.lintCmd, projectDir, noDocker, timeout);
210
- report(onStep, stepLint, 'Lint passed', 'done');
239
+ report(onStep, stepLint, "Lint passed", "done");
211
240
  }
212
241
  catch (e) {
213
- report(onStep, stepLint, 'Lint failed', 'error', String(e));
242
+ report(onStep, stepLint, "Lint failed", "error", String(e));
214
243
  throw e;
215
244
  }
216
245
  }
217
246
  // ── Compile ──────────────────────────────────────────────────────────────────
218
247
  if (stackInfo.compileCmd) {
219
- const stepCompile = 'compile';
220
- report(onStep, stepCompile, `Compile: ${stackInfo.compileCmd}`, 'running');
248
+ const stepCompile = "compile";
249
+ report(onStep, stepCompile, `Compile: ${stackInfo.compileCmd}`, "running");
221
250
  try {
222
251
  // Run as root inside the container to rm/build output dirs owned by any host user,
223
252
  // then chown back to runner so subsequent test steps can read the output.
224
- await runStep(stackInfo.compileCmd, projectDir, noDocker, timeout, 'root');
225
- report(onStep, stepCompile, 'Compile passed', 'done');
253
+ await runStep(stackInfo.compileCmd, projectDir, noDocker, timeout, "root");
254
+ report(onStep, stepCompile, "Compile passed", "done");
226
255
  }
227
256
  catch (e) {
228
- report(onStep, stepCompile, 'Compile failed', 'error', String(e));
257
+ report(onStep, stepCompile, "Compile failed", "error", String(e));
229
258
  throw e;
230
259
  }
231
260
  }
232
261
  // ── Test (full mode only) ────────────────────────────────────────────────────
233
- if (mode === 'full' && stackInfo.testCmd) {
234
- const stepTest = 'test';
235
- report(onStep, stepTest, `Test: ${stackInfo.testCmd}`, 'running');
262
+ if (mode === "full" && stackInfo.testCmd) {
263
+ const stepTest = "test";
264
+ report(onStep, stepTest, `Test: ${stackInfo.testCmd}`, "running");
236
265
  try {
237
266
  await runStep(stackInfo.testCmd, projectDir, noDocker, timeout);
238
- report(onStep, stepTest, 'Tests passed', 'done');
267
+ report(onStep, stepTest, "Tests passed", "done");
239
268
  }
240
269
  catch (e) {
241
- report(onStep, stepTest, 'Tests failed', 'error', String(e));
270
+ report(onStep, stepTest, "Tests failed", "error", String(e));
242
271
  throw e;
243
272
  }
244
273
  }
245
274
  // ── Security scan (full mode only) ──────────────────────────────────────────
246
- if (mode === 'full' && !noSecurity) {
247
- const stepSecurity = 'security';
275
+ if (mode === "full" && !noSecurity) {
276
+ const stepSecurity = "security";
248
277
  const semgrepAvailable = await isSemgrepAvailable();
249
278
  if (semgrepAvailable) {
250
- report(onStep, stepSecurity, 'Security scan (Semgrep)', 'running');
279
+ report(onStep, stepSecurity, "Security scan (Semgrep)", "running");
251
280
  try {
252
281
  await runSemgrep(projectDir);
253
- report(onStep, stepSecurity, 'Security scan passed', 'done');
282
+ report(onStep, stepSecurity, "Security scan passed", "done");
254
283
  }
255
284
  catch (e) {
256
- report(onStep, stepSecurity, 'Security scan failed', 'error', String(e));
285
+ report(onStep, stepSecurity, "Security scan failed", "error", String(e));
257
286
  throw e;
258
287
  }
259
288
  }
260
289
  else {
261
- report(onStep, stepSecurity, 'Security scan', 'skipped', 'Semgrep not available — install semgrep or Docker');
290
+ report(onStep, stepSecurity, "Security scan", "skipped", "Semgrep not available — install semgrep or Docker");
262
291
  }
263
292
  }
264
293
  // ── GHAGGA review (full mode only) ──────────────────────────────────────────
265
- if (mode === 'full' && !noGhagga) {
266
- const stepGhagga = 'ghagga';
294
+ if (mode === "full" && !noGhagga) {
295
+ const stepGhagga = "ghagga";
267
296
  const ghagga = await isGhaggaAvailable();
268
297
  if (ghagga) {
269
- report(onStep, stepGhagga, 'GHAGGA review', 'running');
298
+ report(onStep, stepGhagga, "GHAGGA review", "running");
270
299
  try {
271
300
  await runGhagga(projectDir);
272
- report(onStep, stepGhagga, 'GHAGGA review passed', 'done');
301
+ report(onStep, stepGhagga, "GHAGGA review passed", "done");
273
302
  }
274
303
  catch (e) {
275
- report(onStep, stepGhagga, 'GHAGGA review failed', 'error', String(e));
304
+ report(onStep, stepGhagga, "GHAGGA review failed", "error", String(e));
276
305
  throw e;
277
306
  }
278
307
  }
279
308
  else {
280
- report(onStep, stepGhagga, 'GHAGGA review', 'skipped', 'ghagga not installed');
309
+ report(onStep, stepGhagga, "GHAGGA review", "skipped", "ghagga not installed");
281
310
  }
282
311
  }
283
312
  }
@@ -288,13 +317,15 @@ async function runStep(command, projectDir, noDocker, timeout, user) {
288
317
  if (noDocker) {
289
318
  // Run natively
290
319
  await new Promise((resolve, reject) => {
291
- const proc = spawn('bash', ['-c', command], {
320
+ const proc = spawn("bash", ["-c", command], {
292
321
  cwd: projectDir,
293
- stdio: 'inherit',
294
- env: { ...process.env, CI: 'true' },
322
+ stdio: "inherit",
323
+ env: { ...process.env, CI: "true" },
295
324
  });
296
- proc.on('close', code => code === 0 ? resolve() : reject(new Error(`Command failed with code ${code}`)));
297
- proc.on('error', reject);
325
+ proc.on("close", (code) => code === 0
326
+ ? resolve()
327
+ : reject(new Error(`Command failed with code ${code}`)));
328
+ proc.on("error", reject);
298
329
  });
299
330
  }
300
331
  else {
@@ -312,7 +343,7 @@ async function runStep(command, projectDir, noDocker, timeout, user) {
312
343
  }
313
344
  async function isSemgrepAvailable() {
314
345
  try {
315
- await execFileAsync('semgrep', ['--version'], { timeout: 3000 });
346
+ await execFileAsync("semgrep", ["--version"], { timeout: 3000 });
316
347
  return true;
317
348
  }
318
349
  catch {
@@ -321,26 +352,30 @@ async function isSemgrepAvailable() {
321
352
  }
322
353
  async function runSemgrep(projectDir) {
323
354
  // Look for semgrep config in project or use auto
324
- const semgrepConfig = await fs.pathExists(path.join(projectDir, '.semgrep.yml'))
325
- ? path.join(projectDir, '.semgrep.yml')
326
- : 'auto';
355
+ const semgrepConfig = (await fs.pathExists(path.join(projectDir, ".semgrep.yml")))
356
+ ? path.join(projectDir, ".semgrep.yml")
357
+ : "auto";
327
358
  await new Promise((resolve, reject) => {
328
- const proc = spawn('semgrep', ['--config', semgrepConfig, '--severity', 'ERROR', '--quiet', '.'], {
359
+ const proc = spawn("semgrep", ["--config", semgrepConfig, "--severity", "ERROR", "--quiet", "."], {
329
360
  cwd: projectDir,
330
- stdio: 'inherit',
361
+ stdio: "inherit",
331
362
  });
332
- proc.on('close', code => code === 0 ? resolve() : reject(new Error(`Semgrep found issues (exit ${code})`)));
333
- proc.on('error', reject);
363
+ proc.on("close", (code) => code === 0
364
+ ? resolve()
365
+ : reject(new Error(`Semgrep found issues (exit ${code})`)));
366
+ proc.on("error", reject);
334
367
  });
335
368
  }
336
369
  async function runGhagga(projectDir) {
337
370
  await new Promise((resolve, reject) => {
338
- const proc = spawn('ghagga', ['review', '--plain', '--exit-on-issues'], {
371
+ const proc = spawn("ghagga", ["review", "--plain", "--exit-on-issues"], {
339
372
  cwd: projectDir,
340
- stdio: 'inherit',
373
+ stdio: "inherit",
341
374
  });
342
- proc.on('close', code => code === 0 ? resolve() : reject(new Error(`GHAGGA review found issues (exit ${code})`)));
343
- proc.on('error', reject);
375
+ proc.on("close", (code) => code === 0
376
+ ? resolve()
377
+ : reject(new Error(`GHAGGA review found issues (exit ${code})`)));
378
+ proc.on("error", reject);
344
379
  });
345
380
  }
346
381
  // =============================================================================
@@ -386,8 +421,8 @@ fi || {
386
421
  const COMMIT_MSG_HOOK = `#!/bin/bash
387
422
  # Commit-msg: block AI attribution in commit messages
388
423
  set -e
389
- COMMIT_MSG_FILE="\$1"
390
- COMMIT_MSG=\$(cat "\$COMMIT_MSG_FILE")
424
+ COMMIT_MSG_FILE="$1"
425
+ COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
391
426
 
392
427
  AI_PATTERNS=(
393
428
  "co-authored-by:.*claude" "co-authored-by:.*anthropic"
@@ -403,10 +438,10 @@ AI_PATTERNS=(
403
438
  )
404
439
 
405
440
  for pattern in "\${AI_PATTERNS[@]}"; do
406
- if echo "\$COMMIT_MSG" | grep -iqE "\$pattern"; then
441
+ if echo "$COMMIT_MSG" | grep -iqE "$pattern"; then
407
442
  echo ""
408
443
  echo "COMMIT BLOCKED: AI Attribution Detected"
409
- echo " Pattern: \$pattern"
444
+ echo " Pattern: $pattern"
410
445
  echo " Remove AI attribution. You are the sole author."
411
446
  echo ""
412
447
  exit 1
@@ -415,16 +450,19 @@ done
415
450
  exit 0
416
451
  `;
417
452
  export async function installCIHooks(projectDir) {
418
- const gitDir = path.join(projectDir, '.git');
419
- if (!await fs.pathExists(gitDir)) {
420
- return { installed: [], errors: ['Not a git repository. Run git init first.'] };
453
+ const gitDir = path.join(projectDir, ".git");
454
+ if (!(await fs.pathExists(gitDir))) {
455
+ return {
456
+ installed: [],
457
+ errors: ["Not a git repository. Run git init first."],
458
+ };
421
459
  }
422
- const hooksDir = path.join(gitDir, 'hooks');
460
+ const hooksDir = path.join(gitDir, "hooks");
423
461
  await fs.ensureDir(hooksDir);
424
462
  const hooks = [
425
- { name: 'pre-commit', content: PRE_COMMIT_HOOK },
426
- { name: 'pre-push', content: PRE_PUSH_HOOK },
427
- { name: 'commit-msg', content: COMMIT_MSG_HOOK },
463
+ { name: "pre-commit", content: PRE_COMMIT_HOOK },
464
+ { name: "pre-push", content: PRE_PUSH_HOOK },
465
+ { name: "commit-msg", content: COMMIT_MSG_HOOK },
428
466
  ];
429
467
  const installed = [];
430
468
  const errors = [];
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Crash recovery — reconstruct pipeline state from git commit history
3
+ * when checkpoint files are corrupted or lost. Git is the single
4
+ * source of truth for what was actually done.
5
+ */
6
+ export interface RecoveredTask {
7
+ commitHash: string;
8
+ message: string;
9
+ timestamp: string;
10
+ filesChanged: string[];
11
+ taskId: string | null;
12
+ phase: string | null;
13
+ }
14
+ export interface RecoveryReport {
15
+ branch: string;
16
+ totalCommits: number;
17
+ tasks: RecoveredTask[];
18
+ phases: Record<string, number>;
19
+ lastActivity: string | null;
20
+ }
21
+ export declare function getGitLog(projectDir: string, options?: {
22
+ maxCommits?: number;
23
+ since?: string;
24
+ }): Promise<string>;
25
+ export declare function getCurrentBranch(projectDir: string): Promise<string>;
26
+ export declare function parseCommitPhase(message: string): string | null;
27
+ export declare function extractTaskId(message: string): string | null;
28
+ export declare function parseGitLog(raw: string): RecoveredTask[];
29
+ export declare function recoverFromGit(projectDir: string, options?: {
30
+ maxCommits?: number;
31
+ since?: string;
32
+ }): Promise<RecoveryReport>;
33
+ export declare function formatRecovery(report: RecoveryReport): string;
34
+ //# sourceMappingURL=crash-recovery.d.ts.map