javi-forge 1.5.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 (217) hide show
  1. package/README.md +191 -3
  2. package/ci-local/hooks/pre-push +17 -13
  3. package/dist/commands/analyze.d.ts +1 -1
  4. package/dist/commands/analyze.js +15 -15
  5. package/dist/commands/atlassian-mcp.d.ts +42 -0
  6. package/dist/commands/atlassian-mcp.js +98 -0
  7. package/dist/commands/ci.d.ts +3 -3
  8. package/dist/commands/ci.js +185 -147
  9. package/dist/commands/crash-recovery.d.ts +34 -0
  10. package/dist/commands/crash-recovery.js +123 -0
  11. package/dist/commands/doctor.d.ts +2 -2
  12. package/dist/commands/doctor.js +113 -61
  13. package/dist/commands/harness-audit.d.ts +35 -0
  14. package/dist/commands/harness-audit.js +277 -0
  15. package/dist/commands/init.d.ts +1 -1
  16. package/dist/commands/init.js +415 -118
  17. package/dist/commands/llmstxt.d.ts +1 -1
  18. package/dist/commands/llmstxt.js +36 -34
  19. package/dist/commands/parallel-batch.d.ts +42 -0
  20. package/dist/commands/parallel-batch.js +90 -0
  21. package/dist/commands/plugin.d.ts +26 -1
  22. package/dist/commands/plugin.js +138 -24
  23. package/dist/commands/secret-scanner.d.ts +30 -0
  24. package/dist/commands/secret-scanner.js +272 -0
  25. package/dist/commands/security-analysis.d.ts +74 -0
  26. package/dist/commands/security-analysis.js +487 -0
  27. package/dist/commands/security.d.ts +31 -0
  28. package/dist/commands/security.js +445 -0
  29. package/dist/commands/skill-scanner.d.ts +63 -0
  30. package/dist/commands/skill-scanner.js +383 -0
  31. package/dist/commands/skills.d.ts +139 -0
  32. package/dist/commands/skills.js +895 -0
  33. package/dist/commands/supply-chain.d.ts +23 -0
  34. package/dist/commands/supply-chain.js +126 -0
  35. package/dist/commands/tdd-pipeline.d.ts +17 -0
  36. package/dist/commands/tdd-pipeline.js +144 -0
  37. package/dist/commands/tdd.d.ts +21 -0
  38. package/dist/commands/tdd.js +120 -0
  39. package/dist/commands/team-presets.d.ts +53 -0
  40. package/dist/commands/team-presets.js +201 -0
  41. package/dist/commands/workflow.d.ts +23 -0
  42. package/dist/commands/workflow.js +114 -0
  43. package/dist/constants.d.ts +21 -0
  44. package/dist/constants.js +208 -37
  45. package/dist/index.js +400 -54
  46. package/dist/lib/agent-skills.d.ts +73 -0
  47. package/dist/lib/agent-skills.js +260 -0
  48. package/dist/lib/auto-skill-install.d.ts +37 -0
  49. package/dist/lib/auto-skill-install.js +92 -0
  50. package/dist/lib/auto-wire.d.ts +20 -0
  51. package/dist/lib/auto-wire.js +240 -0
  52. package/dist/lib/claudemd.d.ts +20 -0
  53. package/dist/lib/claudemd.js +222 -0
  54. package/dist/lib/codex-export.d.ts +16 -0
  55. package/dist/lib/codex-export.js +109 -0
  56. package/dist/lib/common.d.ts +1 -1
  57. package/dist/lib/common.js +52 -44
  58. package/dist/lib/context.d.ts +27 -0
  59. package/dist/lib/context.js +204 -0
  60. package/dist/lib/docker.d.ts +1 -1
  61. package/dist/lib/docker.js +141 -112
  62. package/dist/lib/frontmatter.d.ts +1 -1
  63. package/dist/lib/frontmatter.js +29 -15
  64. package/dist/lib/plugin.d.ts +19 -1
  65. package/dist/lib/plugin.js +174 -47
  66. package/dist/lib/skill-publish.d.ts +40 -0
  67. package/dist/lib/skill-publish.js +146 -0
  68. package/dist/lib/stack-detector.d.ts +38 -0
  69. package/dist/lib/stack-detector.js +207 -0
  70. package/dist/lib/template.d.ts +16 -1
  71. package/dist/lib/template.js +46 -17
  72. package/dist/lib/workflow/discovery.d.ts +19 -0
  73. package/dist/lib/workflow/discovery.js +68 -0
  74. package/dist/lib/workflow/index.d.ts +5 -0
  75. package/dist/lib/workflow/index.js +5 -0
  76. package/dist/lib/workflow/parser.d.ts +16 -0
  77. package/dist/lib/workflow/parser.js +198 -0
  78. package/dist/lib/workflow/renderer.d.ts +9 -0
  79. package/dist/lib/workflow/renderer.js +152 -0
  80. package/dist/lib/workflow/validator.d.ts +10 -0
  81. package/dist/lib/workflow/validator.js +189 -0
  82. package/dist/tasks/index.d.ts +4 -0
  83. package/dist/tasks/index.js +4 -0
  84. package/dist/tasks/scaffold-tasks.d.ts +3 -0
  85. package/dist/tasks/scaffold-tasks.js +14 -0
  86. package/dist/tasks/task-id.d.ts +30 -0
  87. package/dist/tasks/task-id.js +55 -0
  88. package/dist/tasks/task-tracker.d.ts +15 -0
  89. package/dist/tasks/task-tracker.js +81 -0
  90. package/dist/types/index.d.ts +252 -5
  91. package/dist/types/index.js +11 -1
  92. package/dist/ui/AnalyzeUI.d.ts +1 -1
  93. package/dist/ui/AnalyzeUI.js +38 -39
  94. package/dist/ui/App.d.ts +5 -3
  95. package/dist/ui/App.js +92 -46
  96. package/dist/ui/AutoSkills.d.ts +9 -0
  97. package/dist/ui/AutoSkills.js +124 -0
  98. package/dist/ui/CI.d.ts +2 -2
  99. package/dist/ui/CI.js +24 -26
  100. package/dist/ui/CIContext.d.ts +1 -1
  101. package/dist/ui/CIContext.js +3 -2
  102. package/dist/ui/CISelector.d.ts +2 -2
  103. package/dist/ui/CISelector.js +23 -15
  104. package/dist/ui/Doctor.d.ts +1 -1
  105. package/dist/ui/Doctor.js +35 -29
  106. package/dist/ui/Header.d.ts +1 -1
  107. package/dist/ui/Header.js +14 -14
  108. package/dist/ui/HookProfileSelector.d.ts +9 -0
  109. package/dist/ui/HookProfileSelector.js +54 -0
  110. package/dist/ui/LlmsTxt.d.ts +1 -1
  111. package/dist/ui/LlmsTxt.js +31 -22
  112. package/dist/ui/MemorySelector.d.ts +2 -2
  113. package/dist/ui/MemorySelector.js +28 -16
  114. package/dist/ui/NameInput.d.ts +1 -1
  115. package/dist/ui/NameInput.js +21 -21
  116. package/dist/ui/OptionSelector.d.ts +8 -2
  117. package/dist/ui/OptionSelector.js +83 -26
  118. package/dist/ui/Plugin.d.ts +4 -3
  119. package/dist/ui/Plugin.js +89 -29
  120. package/dist/ui/Progress.d.ts +3 -3
  121. package/dist/ui/Progress.js +23 -22
  122. package/dist/ui/Skills.d.ts +11 -0
  123. package/dist/ui/Skills.js +148 -0
  124. package/dist/ui/StackSelector.d.ts +2 -2
  125. package/dist/ui/StackSelector.js +26 -16
  126. package/dist/ui/Summary.d.ts +3 -3
  127. package/dist/ui/Summary.js +60 -50
  128. package/dist/ui/Welcome.d.ts +1 -1
  129. package/dist/ui/Welcome.js +15 -16
  130. package/dist/ui/theme.d.ts +1 -1
  131. package/dist/ui/theme.js +6 -6
  132. package/package.json +9 -6
  133. package/templates/common/atlassian/mcp-atlassian-snippet.json +16 -0
  134. package/templates/common/repoforge/mcp-repoforge-snippet.json +11 -0
  135. package/templates/common/repoforge/repoforge.yaml +34 -0
  136. package/templates/github/deploy-docker-zero-downtime.yml +140 -0
  137. package/templates/github/repoforge-graph.yml +45 -0
  138. package/templates/gitlab/deploy-docker-zero-downtime.yml +57 -0
  139. package/templates/local-ai/.env.example +17 -0
  140. package/templates/local-ai/docker-compose.yml +95 -0
  141. package/templates/security-hooks/claude-settings-security.json +30 -0
  142. package/templates/security-hooks/commit-msg-signing +29 -0
  143. package/templates/security-hooks/pre-commit-permissions +74 -0
  144. package/templates/security-hooks/pre-commit-secrets +74 -0
  145. package/templates/security-hooks/pre-push-branch-protection +62 -0
  146. package/templates/security-hooks/pre-push-deps +83 -0
  147. package/templates/security-hooks/pre-push-signing +67 -0
  148. package/templates/woodpecker/deploy-docker-zero-downtime.yml +50 -0
  149. package/templates/workflows/ci-pipeline.dot +15 -0
  150. package/templates/workflows/feature-flow.dot +21 -0
  151. package/templates/workflows/release.dot +16 -0
  152. package/dist/__integration__/helpers.d.ts +0 -20
  153. package/dist/__integration__/helpers.d.ts.map +0 -1
  154. package/dist/__integration__/helpers.js +0 -31
  155. package/dist/__integration__/helpers.js.map +0 -1
  156. package/dist/commands/analyze.d.ts.map +0 -1
  157. package/dist/commands/analyze.js.map +0 -1
  158. package/dist/commands/ci.d.ts.map +0 -1
  159. package/dist/commands/ci.js.map +0 -1
  160. package/dist/commands/doctor.d.ts.map +0 -1
  161. package/dist/commands/doctor.js.map +0 -1
  162. package/dist/commands/init.d.ts.map +0 -1
  163. package/dist/commands/init.js.map +0 -1
  164. package/dist/commands/llmstxt.d.ts.map +0 -1
  165. package/dist/commands/llmstxt.js.map +0 -1
  166. package/dist/commands/plugin.d.ts.map +0 -1
  167. package/dist/commands/plugin.js.map +0 -1
  168. package/dist/constants.d.ts.map +0 -1
  169. package/dist/constants.js.map +0 -1
  170. package/dist/index.d.ts.map +0 -1
  171. package/dist/index.js.map +0 -1
  172. package/dist/lib/common.d.ts.map +0 -1
  173. package/dist/lib/common.js.map +0 -1
  174. package/dist/lib/docker.d.ts.map +0 -1
  175. package/dist/lib/docker.js.map +0 -1
  176. package/dist/lib/frontmatter.d.ts.map +0 -1
  177. package/dist/lib/frontmatter.js.map +0 -1
  178. package/dist/lib/plugin.d.ts.map +0 -1
  179. package/dist/lib/plugin.js.map +0 -1
  180. package/dist/lib/template.d.ts.map +0 -1
  181. package/dist/lib/template.js.map +0 -1
  182. package/dist/types/index.d.ts.map +0 -1
  183. package/dist/types/index.js.map +0 -1
  184. package/dist/ui/AnalyzeUI.d.ts.map +0 -1
  185. package/dist/ui/AnalyzeUI.js.map +0 -1
  186. package/dist/ui/App.d.ts.map +0 -1
  187. package/dist/ui/App.js.map +0 -1
  188. package/dist/ui/CI.d.ts.map +0 -1
  189. package/dist/ui/CI.js.map +0 -1
  190. package/dist/ui/CIContext.d.ts.map +0 -1
  191. package/dist/ui/CIContext.js.map +0 -1
  192. package/dist/ui/CISelector.d.ts.map +0 -1
  193. package/dist/ui/CISelector.js.map +0 -1
  194. package/dist/ui/Doctor.d.ts.map +0 -1
  195. package/dist/ui/Doctor.js.map +0 -1
  196. package/dist/ui/Header.d.ts.map +0 -1
  197. package/dist/ui/Header.js.map +0 -1
  198. package/dist/ui/LlmsTxt.d.ts.map +0 -1
  199. package/dist/ui/LlmsTxt.js.map +0 -1
  200. package/dist/ui/MemorySelector.d.ts.map +0 -1
  201. package/dist/ui/MemorySelector.js.map +0 -1
  202. package/dist/ui/NameInput.d.ts.map +0 -1
  203. package/dist/ui/NameInput.js.map +0 -1
  204. package/dist/ui/OptionSelector.d.ts.map +0 -1
  205. package/dist/ui/OptionSelector.js.map +0 -1
  206. package/dist/ui/Plugin.d.ts.map +0 -1
  207. package/dist/ui/Plugin.js.map +0 -1
  208. package/dist/ui/Progress.d.ts.map +0 -1
  209. package/dist/ui/Progress.js.map +0 -1
  210. package/dist/ui/StackSelector.d.ts.map +0 -1
  211. package/dist/ui/StackSelector.js.map +0 -1
  212. package/dist/ui/Summary.d.ts.map +0 -1
  213. package/dist/ui/Summary.js.map +0 -1
  214. package/dist/ui/Welcome.d.ts.map +0 -1
  215. package/dist/ui/Welcome.js.map +0 -1
  216. package/dist/ui/theme.d.ts.map +0 -1
  217. package/dist/ui/theme.js.map +0 -1
@@ -0,0 +1,123 @@
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
+ import { execFile } from "child_process";
7
+ import { promisify } from "util";
8
+ const execFileAsync = promisify(execFile);
9
+ // ── Git operations ──
10
+ export async function getGitLog(projectDir, options = {}) {
11
+ const args = [
12
+ "log",
13
+ `--max-count=${options.maxCommits ?? 50}`,
14
+ "--pretty=format:%H|%s|%aI",
15
+ "--name-only",
16
+ ];
17
+ if (options.since)
18
+ args.push(`--since=${options.since}`);
19
+ try {
20
+ const { stdout } = await execFileAsync("git", args, {
21
+ cwd: projectDir,
22
+ timeout: 10_000,
23
+ });
24
+ return stdout;
25
+ }
26
+ catch {
27
+ return "";
28
+ }
29
+ }
30
+ export async function getCurrentBranch(projectDir) {
31
+ try {
32
+ const { stdout } = await execFileAsync("git", ["branch", "--show-current"], { cwd: projectDir, timeout: 5_000 });
33
+ return stdout.trim();
34
+ }
35
+ catch {
36
+ return "unknown";
37
+ }
38
+ }
39
+ // ── Parsing ──
40
+ const CONVENTIONAL_RE = /^(feat|fix|test|refactor|docs|chore|ci|perf|style|build)(?:\(([^)]*)\))?:\s*(.+)/;
41
+ const TASK_ID_RE = /#(\d+)|task[- ](\d+[.\d]*)/i;
42
+ export function parseCommitPhase(message) {
43
+ const match = CONVENTIONAL_RE.exec(message);
44
+ return match ? match[1] : null;
45
+ }
46
+ export function extractTaskId(message) {
47
+ const match = TASK_ID_RE.exec(message);
48
+ if (match)
49
+ return match[1] ?? match[2] ?? null;
50
+ return null;
51
+ }
52
+ export function parseGitLog(raw) {
53
+ if (!raw.trim())
54
+ return [];
55
+ const tasks = [];
56
+ const blocks = raw.split("\n\n").filter((b) => b.trim());
57
+ for (const block of blocks) {
58
+ const lines = block.split("\n").filter((l) => l.trim());
59
+ if (lines.length === 0)
60
+ continue;
61
+ const headerLine = lines[0];
62
+ const parts = headerLine.split("|");
63
+ if (parts.length < 3)
64
+ continue;
65
+ const [hash, message, timestamp] = parts;
66
+ const filesChanged = lines.slice(1).filter((l) => !l.includes("|"));
67
+ tasks.push({
68
+ commitHash: hash.slice(0, 7),
69
+ message,
70
+ timestamp,
71
+ filesChanged,
72
+ taskId: extractTaskId(message),
73
+ phase: parseCommitPhase(message),
74
+ });
75
+ }
76
+ return tasks;
77
+ }
78
+ // ── Recovery ──
79
+ export async function recoverFromGit(projectDir, options = {}) {
80
+ const branch = await getCurrentBranch(projectDir);
81
+ const raw = await getGitLog(projectDir, options);
82
+ const tasks = parseGitLog(raw);
83
+ const phases = {};
84
+ for (const task of tasks) {
85
+ if (task.phase) {
86
+ phases[task.phase] = (phases[task.phase] ?? 0) + 1;
87
+ }
88
+ }
89
+ return {
90
+ branch,
91
+ totalCommits: tasks.length,
92
+ tasks,
93
+ phases,
94
+ lastActivity: tasks[0]?.timestamp ?? null,
95
+ };
96
+ }
97
+ // ── Formatting ──
98
+ export function formatRecovery(report) {
99
+ const lines = [];
100
+ lines.push(`## Crash Recovery Report`);
101
+ lines.push(`**Branch**: ${report.branch} | **Commits**: ${report.totalCommits}`);
102
+ if (report.lastActivity) {
103
+ lines.push(`**Last activity**: ${report.lastActivity}`);
104
+ }
105
+ lines.push("");
106
+ if (Object.keys(report.phases).length > 0) {
107
+ const phaseStr = Object.entries(report.phases)
108
+ .map(([p, c]) => `${p}: ${c}`)
109
+ .join(", ");
110
+ lines.push(`**Phases**: ${phaseStr}`);
111
+ lines.push("");
112
+ }
113
+ for (const task of report.tasks.slice(0, 10)) {
114
+ const tag = task.phase ? `[${task.phase}]` : "";
115
+ const id = task.taskId ? ` #${task.taskId}` : "";
116
+ lines.push(`- \`${task.commitHash}\` ${tag}${id} ${task.message} (${task.filesChanged.length} files)`);
117
+ }
118
+ if (report.totalCommits > 10) {
119
+ lines.push(`\n... and ${report.totalCommits - 10} more commits`);
120
+ }
121
+ return lines.join("\n");
122
+ }
123
+ //# sourceMappingURL=crash-recovery.js.map
@@ -1,5 +1,5 @@
1
- import type { DoctorResult } from '../types/index.js';
2
- export type CheckStatus = 'ok' | 'fail' | 'skip';
1
+ import type { DoctorResult } from "../types/index.js";
2
+ export type CheckStatus = "ok" | "fail" | "skip";
3
3
  /**
4
4
  * Run comprehensive health checks for the project and framework.
5
5
  */
@@ -1,15 +1,16 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import { execFile } from 'child_process';
4
- import { promisify } from 'util';
5
- import { detectStack } from '../lib/common.js';
6
- import { listInstalledPlugins } from '../lib/plugin.js';
7
- import { FORGE_ROOT, TEMPLATES_DIR, MODULES_DIR, PLUGINS_DIR } from '../constants.js';
1
+ import { execFile } from "child_process";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import { promisify } from "util";
5
+ import { FORGE_ROOT, MODULES_DIR, PLUGINS_DIR, TEMPLATES_DIR, } from "../constants.js";
6
+ import { detectStack } from "../lib/common.js";
7
+ import { refreshContextDir } from "../lib/context.js";
8
+ import { listInstalledPlugins } from "../lib/plugin.js";
8
9
  const execFileAsync = promisify(execFile);
9
10
  /** Resolve a binary name to its full path, returns null if not found */
10
11
  async function which(bin) {
11
12
  try {
12
- const { stdout } = await execFileAsync('which', [bin]);
13
+ const { stdout } = await execFileAsync("which", [bin]);
13
14
  return stdout.trim() || null;
14
15
  }
15
16
  catch {
@@ -18,11 +19,11 @@ async function which(bin) {
18
19
  }
19
20
  /** Read the forge manifest from a project directory */
20
21
  async function readManifest(projectDir) {
21
- const manifestPath = path.join(projectDir, '.javi-forge', 'manifest.json');
22
- if (!await fs.pathExists(manifestPath))
22
+ const manifestPath = path.join(projectDir, ".javi-forge", "manifest.json");
23
+ if (!(await fs.pathExists(manifestPath)))
23
24
  return null;
24
25
  try {
25
- return await fs.readJson(manifestPath);
26
+ return (await fs.readJson(manifestPath));
26
27
  }
27
28
  catch {
28
29
  return null;
@@ -30,10 +31,10 @@ async function readManifest(projectDir) {
30
31
  }
31
32
  /** Count entries in a directory */
32
33
  async function countDir(dir) {
33
- if (!await fs.pathExists(dir))
34
+ if (!(await fs.pathExists(dir)))
34
35
  return 0;
35
36
  const entries = await fs.readdir(dir);
36
- return entries.filter(e => !e.startsWith('.')).length;
37
+ return entries.filter((e) => !e.startsWith(".")).length;
37
38
  }
38
39
  /**
39
40
  * Run comprehensive health checks for the project and framework.
@@ -44,114 +45,130 @@ export async function runDoctor(projectDir) {
44
45
  // ── 1. System Tools ────────────────────────────────────────────────────────
45
46
  const toolChecks = [];
46
47
  const tools = [
47
- { name: 'git', label: 'Git' },
48
- { name: 'docker', label: 'Docker' },
49
- { name: 'semgrep', label: 'Semgrep' },
50
- { name: 'node', label: 'Node.js' },
51
- { name: 'pnpm', label: 'pnpm' },
48
+ { name: "git", label: "Git" },
49
+ { name: "docker", label: "Docker" },
50
+ { name: "semgrep", label: "Semgrep" },
51
+ { name: "node", label: "Node.js" },
52
+ { name: "pnpm", label: "pnpm" },
52
53
  ];
53
54
  for (const tool of tools) {
54
55
  const bin = await which(tool.name);
55
56
  if (bin) {
56
57
  // Try to get version
57
- let version = '';
58
+ let version = "";
58
59
  try {
59
- const { stdout } = await execFileAsync(tool.name, ['--version']);
60
- version = stdout.trim().split('\n')[0] ?? '';
60
+ const { stdout } = await execFileAsync(tool.name, ["--version"]);
61
+ version = stdout.trim().split("\n")[0] ?? "";
62
+ }
63
+ catch {
64
+ /* ignore */
61
65
  }
62
- catch { /* ignore */ }
63
66
  toolChecks.push({
64
67
  label: tool.label,
65
- status: 'ok',
68
+ status: "ok",
66
69
  detail: version ? `${version}` : `found at ${bin}`,
67
70
  });
68
71
  }
69
72
  else {
70
73
  toolChecks.push({
71
74
  label: tool.label,
72
- status: tool.name === 'docker' || tool.name === 'semgrep' ? 'skip' : 'fail',
73
- detail: 'not found in PATH',
75
+ status: tool.name === "docker" || tool.name === "semgrep" ? "skip" : "fail",
76
+ detail: "not found in PATH",
74
77
  });
75
78
  }
76
79
  }
77
- sections.push({ title: 'System Tools', checks: toolChecks });
80
+ sections.push({ title: "System Tools", checks: toolChecks });
78
81
  // ── 2. Framework Structure ─────────────────────────────────────────────────
79
82
  const structureChecks = [];
80
83
  const expectedDirs = [
81
- { path: TEMPLATES_DIR, label: 'templates/' },
82
- { path: MODULES_DIR, label: 'modules/' },
83
- { path: path.join(FORGE_ROOT, 'workflows'), label: 'workflows/' },
84
- { path: path.join(FORGE_ROOT, 'ci-local'), label: 'ci-local/' },
84
+ { path: TEMPLATES_DIR, label: "templates/" },
85
+ { path: MODULES_DIR, label: "modules/" },
86
+ { path: path.join(FORGE_ROOT, "workflows"), label: "workflows/" },
87
+ { path: path.join(FORGE_ROOT, "ci-local"), label: "ci-local/" },
85
88
  ];
86
89
  for (const dir of expectedDirs) {
87
90
  if (await fs.pathExists(dir.path)) {
88
91
  const count = await countDir(dir.path);
89
- structureChecks.push({ label: dir.label, status: 'ok', detail: `${count} entries` });
92
+ structureChecks.push({
93
+ label: dir.label,
94
+ status: "ok",
95
+ detail: `${count} entries`,
96
+ });
90
97
  }
91
98
  else {
92
- structureChecks.push({ label: dir.label, status: 'fail', detail: 'missing' });
99
+ structureChecks.push({
100
+ label: dir.label,
101
+ status: "fail",
102
+ detail: "missing",
103
+ });
93
104
  }
94
105
  }
95
- sections.push({ title: 'Framework Structure', checks: structureChecks });
106
+ sections.push({ title: "Framework Structure", checks: structureChecks });
96
107
  // ── 3. Stack Detection ─────────────────────────────────────────────────────
97
108
  const stackChecks = [];
98
109
  const detection = await detectStack(cwd);
99
110
  if (detection) {
100
111
  stackChecks.push({
101
- label: 'Detected stack',
102
- status: 'ok',
103
- detail: `${detection.stackType} (${detection.buildTool})${detection.javaVersion ? ` Java ${detection.javaVersion}` : ''}`,
112
+ label: "Detected stack",
113
+ status: "ok",
114
+ detail: `${detection.stackType} (${detection.buildTool})${detection.javaVersion ? ` Java ${detection.javaVersion}` : ""}`,
104
115
  });
105
116
  }
106
117
  else {
107
118
  stackChecks.push({
108
- label: 'Detected stack',
109
- status: 'skip',
110
- detail: 'no recognizable project files in current directory',
119
+ label: "Detected stack",
120
+ status: "skip",
121
+ detail: "no recognizable project files in current directory",
111
122
  });
112
123
  }
113
- sections.push({ title: 'Stack Detection', checks: stackChecks });
124
+ sections.push({ title: "Stack Detection", checks: stackChecks });
114
125
  // ── 4. Project Manifest ────────────────────────────────────────────────────
115
126
  const manifestChecks = [];
116
127
  const manifest = await readManifest(cwd);
117
128
  if (manifest) {
118
129
  manifestChecks.push({
119
- label: 'Forge manifest',
120
- status: 'ok',
130
+ label: "Forge manifest",
131
+ status: "ok",
121
132
  detail: `project: ${manifest.projectName}, stack: ${manifest.stack}`,
122
133
  });
123
134
  manifestChecks.push({
124
- label: 'Created',
125
- status: 'ok',
126
- detail: manifest.createdAt.split('T')[0],
135
+ label: "Created",
136
+ status: "ok",
137
+ detail: manifest.createdAt.split("T")[0],
127
138
  });
128
139
  manifestChecks.push({
129
- label: 'Modules',
130
- status: manifest.modules.length > 0 ? 'ok' : 'skip',
131
- detail: manifest.modules.length > 0 ? manifest.modules.join(', ') : 'none installed',
140
+ label: "Modules",
141
+ status: manifest.modules.length > 0 ? "ok" : "skip",
142
+ detail: manifest.modules.length > 0
143
+ ? manifest.modules.join(", ")
144
+ : "none installed",
132
145
  });
133
146
  }
134
147
  else {
135
148
  manifestChecks.push({
136
- label: 'Forge manifest',
137
- status: 'skip',
138
- detail: 'not a forge-managed project (run javi-forge init)',
149
+ label: "Forge manifest",
150
+ status: "skip",
151
+ detail: "not a forge-managed project (run javi-forge init)",
139
152
  });
140
153
  }
141
- sections.push({ title: 'Project Manifest', checks: manifestChecks });
154
+ sections.push({ title: "Project Manifest", checks: manifestChecks });
142
155
  // ── 5. Installed Modules ───────────────────────────────────────────────────
143
156
  const moduleChecks = [];
144
- const moduleNames = ['engram', 'obsidian-brain', 'memory-simple', 'ghagga'];
157
+ const moduleNames = ["engram", "obsidian-brain", "memory-simple", "ghagga"];
145
158
  for (const mod of moduleNames) {
146
- const modPath = path.join(cwd, '.javi-forge', 'modules', mod);
159
+ const modPath = path.join(cwd, ".javi-forge", "modules", mod);
147
160
  if (await fs.pathExists(modPath)) {
148
- moduleChecks.push({ label: mod, status: 'ok', detail: 'installed' });
161
+ moduleChecks.push({ label: mod, status: "ok", detail: "installed" });
149
162
  }
150
163
  else {
151
- moduleChecks.push({ label: mod, status: 'skip', detail: 'not installed' });
164
+ moduleChecks.push({
165
+ label: mod,
166
+ status: "skip",
167
+ detail: "not installed",
168
+ });
152
169
  }
153
170
  }
154
- sections.push({ title: 'Installed Modules', checks: moduleChecks });
171
+ sections.push({ title: "Installed Modules", checks: moduleChecks });
155
172
  // ── 6. Plugins ─────────────────────────────────────────────────────────────
156
173
  const pluginChecks = [];
157
174
  const pluginsDirExists = await fs.pathExists(PLUGINS_DIR);
@@ -161,19 +178,54 @@ export async function runDoctor(projectDir) {
161
178
  for (const plugin of plugins) {
162
179
  pluginChecks.push({
163
180
  label: plugin.name,
164
- status: 'ok',
181
+ status: "ok",
165
182
  detail: `v${plugin.version} from ${plugin.source}`,
166
183
  });
167
184
  }
168
185
  }
169
186
  else {
170
- pluginChecks.push({ label: 'Plugins', status: 'skip', detail: 'none installed' });
187
+ pluginChecks.push({
188
+ label: "Plugins",
189
+ status: "skip",
190
+ detail: "none installed",
191
+ });
171
192
  }
172
193
  }
173
194
  else {
174
- pluginChecks.push({ label: 'Plugins directory', status: 'skip', detail: 'not created yet' });
195
+ pluginChecks.push({
196
+ label: "Plugins directory",
197
+ status: "skip",
198
+ detail: "not created yet",
199
+ });
200
+ }
201
+ sections.push({ title: "Plugins", checks: pluginChecks });
202
+ // ── 7. Context Directory Refresh ───────────────────────────────────────────
203
+ const contextChecks = [];
204
+ try {
205
+ const result = await refreshContextDir(cwd);
206
+ if (result) {
207
+ contextChecks.push({
208
+ label: ".context/ refresh",
209
+ status: "ok",
210
+ detail: "INDEX.md + summary.md updated",
211
+ });
212
+ }
213
+ else {
214
+ contextChecks.push({
215
+ label: ".context/ refresh",
216
+ status: "skip",
217
+ detail: "no .context/ or no manifest found",
218
+ });
219
+ }
220
+ }
221
+ catch (e) {
222
+ contextChecks.push({
223
+ label: ".context/ refresh",
224
+ status: "fail",
225
+ detail: `refresh failed: ${String(e)}`,
226
+ });
175
227
  }
176
- sections.push({ title: 'Plugins', checks: pluginChecks });
228
+ sections.push({ title: "Context Directory", checks: contextChecks });
177
229
  return { sections };
178
230
  }
179
231
  //# sourceMappingURL=doctor.js.map
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Harness audit — scores how well-configured the AI agent development
3
+ * environment is (hooks, skills, memory, configs). Detects which AI
4
+ * agent harness is installed and generates appropriate config.
5
+ */
6
+ export type Harness = "claude" | "cursor" | "codex" | "copilot" | "windsurf" | "none";
7
+ export interface HarnessDetection {
8
+ harness: Harness;
9
+ configDir: string | null;
10
+ version: string | null;
11
+ }
12
+ export interface AuditCategory {
13
+ name: string;
14
+ score: number;
15
+ maxScore: number;
16
+ checks: AuditCheck[];
17
+ }
18
+ export interface AuditCheck {
19
+ id: string;
20
+ label: string;
21
+ passed: boolean;
22
+ points: number;
23
+ detail?: string;
24
+ }
25
+ export interface HarnessAuditResult {
26
+ harness: Harness;
27
+ totalScore: number;
28
+ maxScore: number;
29
+ grade: string;
30
+ categories: AuditCategory[];
31
+ }
32
+ export declare function detectHarness(projectDir: string): HarnessDetection;
33
+ export declare function detectAllHarnesses(projectDir: string): HarnessDetection[];
34
+ export declare function runHarnessAudit(projectDir: string): HarnessAuditResult;
35
+ //# sourceMappingURL=harness-audit.d.ts.map