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.
- package/README.md +191 -3
- package/ci-local/hooks/pre-push +17 -13
- package/dist/commands/analyze.d.ts +1 -1
- package/dist/commands/analyze.js +15 -15
- package/dist/commands/atlassian-mcp.d.ts +42 -0
- package/dist/commands/atlassian-mcp.js +98 -0
- package/dist/commands/ci.d.ts +3 -3
- package/dist/commands/ci.js +185 -147
- package/dist/commands/crash-recovery.d.ts +34 -0
- package/dist/commands/crash-recovery.js +123 -0
- package/dist/commands/doctor.d.ts +2 -2
- package/dist/commands/doctor.js +113 -61
- package/dist/commands/harness-audit.d.ts +35 -0
- package/dist/commands/harness-audit.js +277 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +415 -118
- package/dist/commands/llmstxt.d.ts +1 -1
- package/dist/commands/llmstxt.js +36 -34
- package/dist/commands/parallel-batch.d.ts +42 -0
- package/dist/commands/parallel-batch.js +90 -0
- package/dist/commands/plugin.d.ts +26 -1
- package/dist/commands/plugin.js +138 -24
- package/dist/commands/secret-scanner.d.ts +30 -0
- package/dist/commands/secret-scanner.js +272 -0
- package/dist/commands/security-analysis.d.ts +74 -0
- package/dist/commands/security-analysis.js +487 -0
- package/dist/commands/security.d.ts +31 -0
- package/dist/commands/security.js +445 -0
- package/dist/commands/skill-scanner.d.ts +63 -0
- package/dist/commands/skill-scanner.js +383 -0
- package/dist/commands/skills.d.ts +139 -0
- package/dist/commands/skills.js +895 -0
- package/dist/commands/supply-chain.d.ts +23 -0
- package/dist/commands/supply-chain.js +126 -0
- package/dist/commands/tdd-pipeline.d.ts +17 -0
- package/dist/commands/tdd-pipeline.js +144 -0
- package/dist/commands/tdd.d.ts +21 -0
- package/dist/commands/tdd.js +120 -0
- package/dist/commands/team-presets.d.ts +53 -0
- package/dist/commands/team-presets.js +201 -0
- package/dist/commands/workflow.d.ts +23 -0
- package/dist/commands/workflow.js +114 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.js +208 -37
- package/dist/index.js +400 -54
- package/dist/lib/agent-skills.d.ts +73 -0
- package/dist/lib/agent-skills.js +260 -0
- package/dist/lib/auto-skill-install.d.ts +37 -0
- package/dist/lib/auto-skill-install.js +92 -0
- package/dist/lib/auto-wire.d.ts +20 -0
- package/dist/lib/auto-wire.js +240 -0
- package/dist/lib/claudemd.d.ts +20 -0
- package/dist/lib/claudemd.js +222 -0
- package/dist/lib/codex-export.d.ts +16 -0
- package/dist/lib/codex-export.js +109 -0
- package/dist/lib/common.d.ts +1 -1
- package/dist/lib/common.js +52 -44
- package/dist/lib/context.d.ts +27 -0
- package/dist/lib/context.js +204 -0
- package/dist/lib/docker.d.ts +1 -1
- package/dist/lib/docker.js +141 -112
- package/dist/lib/frontmatter.d.ts +1 -1
- package/dist/lib/frontmatter.js +29 -15
- package/dist/lib/plugin.d.ts +19 -1
- package/dist/lib/plugin.js +174 -47
- package/dist/lib/skill-publish.d.ts +40 -0
- package/dist/lib/skill-publish.js +146 -0
- package/dist/lib/stack-detector.d.ts +38 -0
- package/dist/lib/stack-detector.js +207 -0
- package/dist/lib/template.d.ts +16 -1
- package/dist/lib/template.js +46 -17
- package/dist/lib/workflow/discovery.d.ts +19 -0
- package/dist/lib/workflow/discovery.js +68 -0
- package/dist/lib/workflow/index.d.ts +5 -0
- package/dist/lib/workflow/index.js +5 -0
- package/dist/lib/workflow/parser.d.ts +16 -0
- package/dist/lib/workflow/parser.js +198 -0
- package/dist/lib/workflow/renderer.d.ts +9 -0
- package/dist/lib/workflow/renderer.js +152 -0
- package/dist/lib/workflow/validator.d.ts +10 -0
- package/dist/lib/workflow/validator.js +189 -0
- package/dist/tasks/index.d.ts +4 -0
- package/dist/tasks/index.js +4 -0
- package/dist/tasks/scaffold-tasks.d.ts +3 -0
- package/dist/tasks/scaffold-tasks.js +14 -0
- package/dist/tasks/task-id.d.ts +30 -0
- package/dist/tasks/task-id.js +55 -0
- package/dist/tasks/task-tracker.d.ts +15 -0
- package/dist/tasks/task-tracker.js +81 -0
- package/dist/types/index.d.ts +252 -5
- package/dist/types/index.js +11 -1
- package/dist/ui/AnalyzeUI.d.ts +1 -1
- package/dist/ui/AnalyzeUI.js +38 -39
- package/dist/ui/App.d.ts +5 -3
- package/dist/ui/App.js +92 -46
- package/dist/ui/AutoSkills.d.ts +9 -0
- package/dist/ui/AutoSkills.js +124 -0
- package/dist/ui/CI.d.ts +2 -2
- package/dist/ui/CI.js +24 -26
- package/dist/ui/CIContext.d.ts +1 -1
- package/dist/ui/CIContext.js +3 -2
- package/dist/ui/CISelector.d.ts +2 -2
- package/dist/ui/CISelector.js +23 -15
- package/dist/ui/Doctor.d.ts +1 -1
- package/dist/ui/Doctor.js +35 -29
- package/dist/ui/Header.d.ts +1 -1
- package/dist/ui/Header.js +14 -14
- package/dist/ui/HookProfileSelector.d.ts +9 -0
- package/dist/ui/HookProfileSelector.js +54 -0
- package/dist/ui/LlmsTxt.d.ts +1 -1
- package/dist/ui/LlmsTxt.js +31 -22
- package/dist/ui/MemorySelector.d.ts +2 -2
- package/dist/ui/MemorySelector.js +28 -16
- package/dist/ui/NameInput.d.ts +1 -1
- package/dist/ui/NameInput.js +21 -21
- package/dist/ui/OptionSelector.d.ts +8 -2
- package/dist/ui/OptionSelector.js +83 -26
- package/dist/ui/Plugin.d.ts +4 -3
- package/dist/ui/Plugin.js +89 -29
- package/dist/ui/Progress.d.ts +3 -3
- package/dist/ui/Progress.js +23 -22
- package/dist/ui/Skills.d.ts +11 -0
- package/dist/ui/Skills.js +148 -0
- package/dist/ui/StackSelector.d.ts +2 -2
- package/dist/ui/StackSelector.js +26 -16
- package/dist/ui/Summary.d.ts +3 -3
- package/dist/ui/Summary.js +60 -50
- package/dist/ui/Welcome.d.ts +1 -1
- package/dist/ui/Welcome.js +15 -16
- package/dist/ui/theme.d.ts +1 -1
- package/dist/ui/theme.js +6 -6
- package/package.json +9 -6
- package/templates/common/atlassian/mcp-atlassian-snippet.json +16 -0
- package/templates/common/repoforge/mcp-repoforge-snippet.json +11 -0
- package/templates/common/repoforge/repoforge.yaml +34 -0
- package/templates/github/deploy-docker-zero-downtime.yml +140 -0
- package/templates/github/repoforge-graph.yml +45 -0
- package/templates/gitlab/deploy-docker-zero-downtime.yml +57 -0
- package/templates/local-ai/.env.example +17 -0
- package/templates/local-ai/docker-compose.yml +95 -0
- package/templates/security-hooks/claude-settings-security.json +30 -0
- package/templates/security-hooks/commit-msg-signing +29 -0
- package/templates/security-hooks/pre-commit-permissions +74 -0
- package/templates/security-hooks/pre-commit-secrets +74 -0
- package/templates/security-hooks/pre-push-branch-protection +62 -0
- package/templates/security-hooks/pre-push-deps +83 -0
- package/templates/security-hooks/pre-push-signing +67 -0
- package/templates/woodpecker/deploy-docker-zero-downtime.yml +50 -0
- package/templates/workflows/ci-pipeline.dot +15 -0
- package/templates/workflows/feature-flow.dot +21 -0
- package/templates/workflows/release.dot +16 -0
- package/dist/__integration__/helpers.d.ts +0 -20
- package/dist/__integration__/helpers.d.ts.map +0 -1
- package/dist/__integration__/helpers.js +0 -31
- package/dist/__integration__/helpers.js.map +0 -1
- package/dist/commands/analyze.d.ts.map +0 -1
- package/dist/commands/analyze.js.map +0 -1
- package/dist/commands/ci.d.ts.map +0 -1
- package/dist/commands/ci.js.map +0 -1
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/llmstxt.d.ts.map +0 -1
- package/dist/commands/llmstxt.js.map +0 -1
- package/dist/commands/plugin.d.ts.map +0 -1
- package/dist/commands/plugin.js.map +0 -1
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/common.d.ts.map +0 -1
- package/dist/lib/common.js.map +0 -1
- package/dist/lib/docker.d.ts.map +0 -1
- package/dist/lib/docker.js.map +0 -1
- package/dist/lib/frontmatter.d.ts.map +0 -1
- package/dist/lib/frontmatter.js.map +0 -1
- package/dist/lib/plugin.d.ts.map +0 -1
- package/dist/lib/plugin.js.map +0 -1
- package/dist/lib/template.d.ts.map +0 -1
- package/dist/lib/template.js.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/ui/AnalyzeUI.d.ts.map +0 -1
- package/dist/ui/AnalyzeUI.js.map +0 -1
- package/dist/ui/App.d.ts.map +0 -1
- package/dist/ui/App.js.map +0 -1
- package/dist/ui/CI.d.ts.map +0 -1
- package/dist/ui/CI.js.map +0 -1
- package/dist/ui/CIContext.d.ts.map +0 -1
- package/dist/ui/CIContext.js.map +0 -1
- package/dist/ui/CISelector.d.ts.map +0 -1
- package/dist/ui/CISelector.js.map +0 -1
- package/dist/ui/Doctor.d.ts.map +0 -1
- package/dist/ui/Doctor.js.map +0 -1
- package/dist/ui/Header.d.ts.map +0 -1
- package/dist/ui/Header.js.map +0 -1
- package/dist/ui/LlmsTxt.d.ts.map +0 -1
- package/dist/ui/LlmsTxt.js.map +0 -1
- package/dist/ui/MemorySelector.d.ts.map +0 -1
- package/dist/ui/MemorySelector.js.map +0 -1
- package/dist/ui/NameInput.d.ts.map +0 -1
- package/dist/ui/NameInput.js.map +0 -1
- package/dist/ui/OptionSelector.d.ts.map +0 -1
- package/dist/ui/OptionSelector.js.map +0 -1
- package/dist/ui/Plugin.d.ts.map +0 -1
- package/dist/ui/Plugin.js.map +0 -1
- package/dist/ui/Progress.d.ts.map +0 -1
- package/dist/ui/Progress.js.map +0 -1
- package/dist/ui/StackSelector.d.ts.map +0 -1
- package/dist/ui/StackSelector.js.map +0 -1
- package/dist/ui/Summary.d.ts.map +0 -1
- package/dist/ui/Summary.js.map +0 -1
- package/dist/ui/Welcome.d.ts.map +0 -1
- package/dist/ui/Welcome.js.map +0 -1
- package/dist/ui/theme.d.ts.map +0 -1
- 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
|
|
2
|
-
export type CheckStatus =
|
|
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
|
*/
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { promisify } from
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
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(
|
|
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,
|
|
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(
|
|
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:
|
|
48
|
-
{ name:
|
|
49
|
-
{ name:
|
|
50
|
-
{ name:
|
|
51
|
-
{ name:
|
|
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, [
|
|
60
|
-
version = stdout.trim().split(
|
|
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:
|
|
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 ===
|
|
73
|
-
detail:
|
|
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:
|
|
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:
|
|
82
|
-
{ path: MODULES_DIR, label:
|
|
83
|
-
{ path: path.join(FORGE_ROOT,
|
|
84
|
-
{ path: path.join(FORGE_ROOT,
|
|
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({
|
|
92
|
+
structureChecks.push({
|
|
93
|
+
label: dir.label,
|
|
94
|
+
status: "ok",
|
|
95
|
+
detail: `${count} entries`,
|
|
96
|
+
});
|
|
90
97
|
}
|
|
91
98
|
else {
|
|
92
|
-
structureChecks.push({
|
|
99
|
+
structureChecks.push({
|
|
100
|
+
label: dir.label,
|
|
101
|
+
status: "fail",
|
|
102
|
+
detail: "missing",
|
|
103
|
+
});
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
|
-
sections.push({ title:
|
|
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:
|
|
102
|
-
status:
|
|
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:
|
|
109
|
-
status:
|
|
110
|
-
detail:
|
|
119
|
+
label: "Detected stack",
|
|
120
|
+
status: "skip",
|
|
121
|
+
detail: "no recognizable project files in current directory",
|
|
111
122
|
});
|
|
112
123
|
}
|
|
113
|
-
sections.push({ title:
|
|
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:
|
|
120
|
-
status:
|
|
130
|
+
label: "Forge manifest",
|
|
131
|
+
status: "ok",
|
|
121
132
|
detail: `project: ${manifest.projectName}, stack: ${manifest.stack}`,
|
|
122
133
|
});
|
|
123
134
|
manifestChecks.push({
|
|
124
|
-
label:
|
|
125
|
-
status:
|
|
126
|
-
detail: manifest.createdAt.split(
|
|
135
|
+
label: "Created",
|
|
136
|
+
status: "ok",
|
|
137
|
+
detail: manifest.createdAt.split("T")[0],
|
|
127
138
|
});
|
|
128
139
|
manifestChecks.push({
|
|
129
|
-
label:
|
|
130
|
-
status: manifest.modules.length > 0 ?
|
|
131
|
-
detail: manifest.modules.length > 0
|
|
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:
|
|
137
|
-
status:
|
|
138
|
-
detail:
|
|
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:
|
|
154
|
+
sections.push({ title: "Project Manifest", checks: manifestChecks });
|
|
142
155
|
// ── 5. Installed Modules ───────────────────────────────────────────────────
|
|
143
156
|
const moduleChecks = [];
|
|
144
|
-
const moduleNames = [
|
|
157
|
+
const moduleNames = ["engram", "obsidian-brain", "memory-simple", "ghagga"];
|
|
145
158
|
for (const mod of moduleNames) {
|
|
146
|
-
const modPath = path.join(cwd,
|
|
159
|
+
const modPath = path.join(cwd, ".javi-forge", "modules", mod);
|
|
147
160
|
if (await fs.pathExists(modPath)) {
|
|
148
|
-
moduleChecks.push({ label: mod, status:
|
|
161
|
+
moduleChecks.push({ label: mod, status: "ok", detail: "installed" });
|
|
149
162
|
}
|
|
150
163
|
else {
|
|
151
|
-
moduleChecks.push({
|
|
164
|
+
moduleChecks.push({
|
|
165
|
+
label: mod,
|
|
166
|
+
status: "skip",
|
|
167
|
+
detail: "not installed",
|
|
168
|
+
});
|
|
152
169
|
}
|
|
153
170
|
}
|
|
154
|
-
sections.push({ title:
|
|
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:
|
|
181
|
+
status: "ok",
|
|
165
182
|
detail: `v${plugin.version} from ${plugin.source}`,
|
|
166
183
|
});
|
|
167
184
|
}
|
|
168
185
|
}
|
|
169
186
|
else {
|
|
170
|
-
pluginChecks.push({
|
|
187
|
+
pluginChecks.push({
|
|
188
|
+
label: "Plugins",
|
|
189
|
+
status: "skip",
|
|
190
|
+
detail: "none installed",
|
|
191
|
+
});
|
|
171
192
|
}
|
|
172
193
|
}
|
|
173
194
|
else {
|
|
174
|
-
pluginChecks.push({
|
|
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:
|
|
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
|