javi-forge 1.6.0 → 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.
- package/README.md +45 -1
- package/ci-local/README.md +56 -11
- package/ci-local/ci-local.ps1 +445 -0
- package/ci-local/ci-local.sh +52 -9
- package/ci-local/docker/python.Dockerfile +7 -0
- package/ci-local/hooks/commit-msg +120 -44
- package/ci-local/hooks/commit-msg.test.sh +286 -0
- package/ci-local/hooks/pre-commit +9 -1
- package/ci-local/hooks/pre-push +13 -2
- package/ci-local/install.ps1 +367 -0
- package/ci-local/install.sh +151 -5
- package/ci-local/uninstall.ps1 +85 -0
- package/ci-local/uninstall.sh +95 -0
- package/dist/cli/dispatch/ci.d.ts +12 -0
- package/dist/cli/dispatch/ci.js +38 -0
- package/dist/cli/dispatch/security.d.ts +11 -0
- package/dist/cli/dispatch/security.js +68 -0
- package/dist/cli/dispatch/simple-renderers.d.ts +16 -0
- package/dist/cli/dispatch/simple-renderers.js +65 -0
- package/dist/cli/dispatch/skill-publish.d.ts +15 -0
- package/dist/cli/dispatch/skill-publish.js +44 -0
- package/dist/cli/dispatch/skills-cmd.d.ts +13 -0
- package/dist/cli/dispatch/skills-cmd.js +93 -0
- package/dist/cli/dispatch/tdd.d.ts +11 -0
- package/dist/cli/dispatch/tdd.js +45 -0
- package/dist/cli/dispatch/types.d.ts +29 -0
- package/dist/cli/dispatch/types.js +2 -0
- package/dist/cli/dispatch/workflow.d.ts +11 -0
- package/dist/cli/dispatch/workflow.js +67 -0
- package/dist/cli/help.d.ts +127 -0
- package/dist/cli/help.js +138 -0
- package/dist/cli/runtime.d.ts +16 -0
- package/dist/cli/runtime.js +38 -0
- package/dist/cli/validators.d.ts +17 -0
- package/dist/cli/validators.js +35 -0
- package/dist/commands/analyze.d.ts +1 -1
- package/dist/commands/analyze.js +14 -16
- package/dist/commands/ci.d.ts +50 -3
- package/dist/commands/ci.js +537 -182
- package/dist/commands/doctor.d.ts +2 -2
- package/dist/commands/doctor.js +112 -62
- package/dist/commands/init/report.d.ts +13 -0
- package/dist/commands/init/report.js +13 -0
- package/dist/commands/init/steps/agent-skills.d.ts +13 -0
- package/dist/commands/init/steps/agent-skills.js +45 -0
- package/dist/commands/init/steps/ai-sync.d.ts +15 -0
- package/dist/commands/init/steps/ai-sync.js +62 -0
- package/dist/commands/init/steps/ci.d.ts +24 -0
- package/dist/commands/init/steps/ci.js +74 -0
- package/dist/commands/init/steps/claude-md.d.ts +14 -0
- package/dist/commands/init/steps/claude-md.js +54 -0
- package/dist/commands/init/steps/code-graph.d.ts +16 -0
- package/dist/commands/init/steps/code-graph.js +68 -0
- package/dist/commands/init/steps/context-dir.d.ts +13 -0
- package/dist/commands/init/steps/context-dir.js +47 -0
- package/dist/commands/init/steps/docker-deploy.d.ts +16 -0
- package/dist/commands/init/steps/docker-deploy.js +60 -0
- package/dist/commands/init/steps/ghagga.d.ts +15 -0
- package/dist/commands/init/steps/ghagga.js +58 -0
- package/dist/commands/init/steps/git.d.ts +25 -0
- package/dist/commands/init/steps/git.js +81 -0
- package/dist/commands/init/steps/gitignore.d.ts +13 -0
- package/dist/commands/init/steps/gitignore.js +39 -0
- package/dist/commands/init/steps/local-ai.d.ts +14 -0
- package/dist/commands/init/steps/local-ai.js +53 -0
- package/dist/commands/init/steps/manifest.d.ts +13 -0
- package/dist/commands/init/steps/manifest.js +58 -0
- package/dist/commands/init/steps/memory.d.ts +15 -0
- package/dist/commands/init/steps/memory.js +60 -0
- package/dist/commands/init/steps/mock.d.ts +13 -0
- package/dist/commands/init/steps/mock.js +64 -0
- package/dist/commands/init/steps/sdd.d.ts +12 -0
- package/dist/commands/init/steps/sdd.js +40 -0
- package/dist/commands/init/steps/security.d.ts +28 -0
- package/dist/commands/init/steps/security.js +103 -0
- package/dist/commands/init/types.d.ts +30 -0
- package/dist/commands/init/types.js +2 -0
- package/dist/commands/init.d.ts +5 -4
- package/dist/commands/init.js +42 -387
- package/dist/commands/llmstxt.d.ts +1 -1
- package/dist/commands/llmstxt.js +36 -34
- package/dist/commands/plugin.d.ts +10 -1
- package/dist/commands/plugin.js +92 -47
- package/dist/commands/security.d.ts +11 -5
- package/dist/commands/security.js +236 -77
- package/dist/commands/skills/analysis.d.ts +19 -0
- package/dist/commands/skills/analysis.js +211 -0
- package/dist/commands/skills/benchmark.d.ts +6 -0
- package/dist/commands/skills/benchmark.js +82 -0
- package/dist/commands/skills/constants.d.ts +10 -0
- package/dist/commands/skills/constants.js +27 -0
- package/dist/commands/skills/directives.d.ts +21 -0
- package/dist/commands/skills/directives.js +101 -0
- package/dist/commands/skills/parsing.d.ts +16 -0
- package/dist/commands/skills/parsing.js +108 -0
- package/dist/commands/skills/rules.d.ts +5 -0
- package/dist/commands/skills/rules.js +32 -0
- package/dist/commands/skills/scoring.d.ts +47 -0
- package/dist/commands/skills/scoring.js +306 -0
- package/dist/commands/skills.d.ts +8 -69
- package/dist/commands/skills.js +9 -490
- package/dist/commands/tdd-pipeline.d.ts +17 -0
- package/dist/commands/tdd-pipeline.js +144 -0
- package/dist/commands/tdd.d.ts +1 -1
- package/dist/commands/tdd.js +21 -18
- package/dist/commands/workflow.d.ts +23 -0
- package/dist/commands/workflow.js +116 -0
- package/dist/constants.d.ts +15 -1
- package/dist/constants.js +161 -122
- package/dist/index.js +43 -280
- package/dist/lib/agent-skills.d.ts +36 -1
- package/dist/lib/agent-skills.js +168 -19
- package/dist/lib/atlassian-mcp.d.ts +42 -0
- package/dist/lib/atlassian-mcp.js +98 -0
- package/dist/lib/auto-skill-install.d.ts +37 -0
- package/dist/lib/auto-skill-install.js +91 -0
- package/dist/lib/auto-wire.d.ts +20 -0
- package/dist/lib/auto-wire.js +240 -0
- package/dist/lib/ci-config.d.ts +65 -0
- package/dist/lib/ci-config.js +297 -0
- package/dist/lib/claudemd.d.ts +13 -1
- package/dist/lib/claudemd.js +174 -24
- package/dist/lib/codex-export.d.ts +1 -1
- package/dist/lib/codex-export.js +26 -32
- package/dist/lib/common.d.ts +1 -1
- package/dist/lib/common.js +52 -44
- package/dist/lib/context.d.ts +17 -2
- package/dist/lib/context.js +142 -13
- package/dist/lib/crash-recovery.d.ts +34 -0
- package/dist/lib/crash-recovery.js +123 -0
- package/dist/lib/docker.d.ts +23 -2
- package/dist/lib/docker.js +168 -131
- package/dist/lib/exec.d.ts +10 -0
- package/dist/lib/exec.js +11 -0
- package/dist/lib/frontmatter.d.ts +1 -1
- package/dist/lib/frontmatter.js +29 -15
- package/dist/lib/harness-audit.d.ts +35 -0
- package/dist/lib/harness-audit.js +277 -0
- package/dist/lib/parallel-batch.d.ts +42 -0
- package/dist/lib/parallel-batch.js +88 -0
- package/dist/lib/plugin.d.ts +9 -3
- package/dist/lib/plugin.js +127 -70
- package/dist/lib/secret-scanner.d.ts +30 -0
- package/dist/lib/secret-scanner.js +274 -0
- package/dist/lib/security-analysis.d.ts +74 -0
- package/dist/lib/security-analysis.js +489 -0
- package/dist/lib/skill-publish.d.ts +40 -0
- package/dist/lib/skill-publish.js +164 -0
- package/dist/lib/skill-scanner.d.ts +63 -0
- package/dist/lib/skill-scanner.js +383 -0
- package/dist/lib/stack-detector.d.ts +38 -0
- package/dist/lib/stack-detector.js +207 -0
- package/dist/lib/supply-chain.d.ts +23 -0
- package/dist/lib/supply-chain.js +126 -0
- package/dist/lib/team-presets.d.ts +53 -0
- package/dist/lib/team-presets.js +201 -0
- package/dist/lib/template.d.ts +16 -1
- package/dist/lib/template.js +46 -17
- package/dist/lib/workflow/discovery.d.ts +27 -0
- package/dist/lib/workflow/discovery.js +85 -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 +59 -0
- package/dist/tasks/task-tracker.d.ts +15 -0
- package/dist/tasks/task-tracker.js +81 -0
- package/dist/types/index.d.ts +134 -6
- 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 +86 -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 +2 -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 +6 -2
- package/dist/ui/OptionSelector.js +83 -32
- package/dist/ui/Plugin.d.ts +4 -3
- package/dist/ui/Plugin.js +78 -35
- package/dist/ui/Progress.d.ts +3 -3
- package/dist/ui/Progress.js +23 -22
- package/dist/ui/Skills.d.ts +2 -2
- package/dist/ui/Skills.js +61 -32
- package/dist/ui/StackSelector.d.ts +2 -2
- package/dist/ui/StackSelector.js +27 -17
- 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/lib/common.psm1 +167 -0
- package/lib/common.sh +30 -0
- package/package.json +46 -24
- 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/commands/security.d.ts.map +0 -1
- package/dist/commands/security.js.map +0 -1
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/tdd.d.ts.map +0 -1
- package/dist/commands/tdd.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/agent-skills.d.ts.map +0 -1
- package/dist/lib/agent-skills.js.map +0 -1
- package/dist/lib/claudemd.d.ts.map +0 -1
- package/dist/lib/claudemd.js.map +0 -1
- package/dist/lib/codex-export.d.ts.map +0 -1
- package/dist/lib/codex-export.js.map +0 -1
- package/dist/lib/common.d.ts.map +0 -1
- package/dist/lib/common.js.map +0 -1
- package/dist/lib/context.d.ts.map +0 -1
- package/dist/lib/context.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/Skills.d.ts.map +0 -1
- package/dist/ui/Skills.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
package/dist/lib/frontmatter.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import YAML from
|
|
1
|
+
import YAML from "yaml";
|
|
2
2
|
/**
|
|
3
3
|
* Extract YAML frontmatter from a markdown string.
|
|
4
4
|
* Frontmatter is delimited by --- at the start of the file.
|
|
5
5
|
*/
|
|
6
6
|
export function parseFrontmatter(raw) {
|
|
7
7
|
const trimmed = raw.trimStart();
|
|
8
|
-
if (!trimmed.startsWith(
|
|
8
|
+
if (!trimmed.startsWith("---"))
|
|
9
9
|
return null;
|
|
10
|
-
const endIdx = trimmed.indexOf(
|
|
10
|
+
const endIdx = trimmed.indexOf("---", 3);
|
|
11
11
|
if (endIdx === -1)
|
|
12
12
|
return null;
|
|
13
13
|
const yamlBlock = trimmed.slice(3, endIdx).trim();
|
|
14
14
|
const content = trimmed.slice(endIdx + 3).trim();
|
|
15
15
|
try {
|
|
16
16
|
const data = YAML.parse(yamlBlock);
|
|
17
|
-
if (typeof data !==
|
|
17
|
+
if (typeof data !== "object" || data === null)
|
|
18
18
|
return null;
|
|
19
19
|
return { data, content };
|
|
20
20
|
}
|
|
@@ -29,30 +29,44 @@ const KEBAB_CASE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
|
29
29
|
export function validateFrontmatter(frontmatter, type) {
|
|
30
30
|
const errors = [];
|
|
31
31
|
// name: required, kebab-case, 2-60 chars
|
|
32
|
-
const name = frontmatter
|
|
33
|
-
if (typeof name !==
|
|
34
|
-
errors.push({
|
|
32
|
+
const name = frontmatter.name;
|
|
33
|
+
if (typeof name !== "string" || !name) {
|
|
34
|
+
errors.push({
|
|
35
|
+
field: "name",
|
|
36
|
+
message: "name is required and must be a string",
|
|
37
|
+
});
|
|
35
38
|
}
|
|
36
39
|
else {
|
|
37
40
|
if (name.length < 2 || name.length > 60) {
|
|
38
|
-
errors.push({ field:
|
|
41
|
+
errors.push({ field: "name", message: "name must be 2-60 characters" });
|
|
39
42
|
}
|
|
40
43
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
41
|
-
errors.push({
|
|
44
|
+
errors.push({
|
|
45
|
+
field: "name",
|
|
46
|
+
message: "name must be kebab-case (e.g. my-skill-name)",
|
|
47
|
+
});
|
|
42
48
|
}
|
|
43
49
|
}
|
|
44
50
|
// description: required, non-empty, min 10 chars
|
|
45
|
-
const desc = frontmatter
|
|
46
|
-
if (typeof desc !==
|
|
47
|
-
errors.push({
|
|
51
|
+
const desc = frontmatter.description;
|
|
52
|
+
if (typeof desc !== "string" || !desc) {
|
|
53
|
+
errors.push({
|
|
54
|
+
field: "description",
|
|
55
|
+
message: "description is required and must be a string",
|
|
56
|
+
});
|
|
48
57
|
}
|
|
49
58
|
else if (desc.length < 10) {
|
|
50
|
-
errors.push({
|
|
59
|
+
errors.push({
|
|
60
|
+
field: "description",
|
|
61
|
+
message: "description must be at least 10 characters",
|
|
62
|
+
});
|
|
51
63
|
}
|
|
52
64
|
// skill-specific: description should include "Trigger:" hint
|
|
53
|
-
if (type ===
|
|
65
|
+
if (type === "skill" &&
|
|
66
|
+
typeof desc === "string" &&
|
|
67
|
+
!desc.includes("Trigger:")) {
|
|
54
68
|
errors.push({
|
|
55
|
-
field:
|
|
69
|
+
field: "description",
|
|
56
70
|
message: 'skill description should include a "Trigger:" hint for auto-invoke',
|
|
57
71
|
});
|
|
58
72
|
}
|
|
@@ -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
|
|
@@ -0,0 +1,277 @@
|
|
|
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
|
+
import path from "node:path";
|
|
7
|
+
import fs from "fs-extra";
|
|
8
|
+
// ── Harness Detection ──
|
|
9
|
+
const HARNESS_SIGNATURES = [
|
|
10
|
+
{
|
|
11
|
+
harness: "claude",
|
|
12
|
+
markers: [".claude", "CLAUDE.md"],
|
|
13
|
+
configDir: ".claude",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
harness: "cursor",
|
|
17
|
+
markers: [".cursor", ".cursorrules"],
|
|
18
|
+
configDir: ".cursor",
|
|
19
|
+
},
|
|
20
|
+
{ harness: "codex", markers: [".codex", "AGENTS.md"], configDir: ".codex" },
|
|
21
|
+
{
|
|
22
|
+
harness: "copilot",
|
|
23
|
+
markers: [".github/copilot-instructions.md"],
|
|
24
|
+
configDir: ".github",
|
|
25
|
+
},
|
|
26
|
+
{ harness: "windsurf", markers: [".windsurfrules"], configDir: "." },
|
|
27
|
+
];
|
|
28
|
+
export function detectHarness(projectDir) {
|
|
29
|
+
for (const sig of HARNESS_SIGNATURES) {
|
|
30
|
+
for (const marker of sig.markers) {
|
|
31
|
+
const fullPath = path.join(projectDir, marker);
|
|
32
|
+
if (fs.existsSync(fullPath)) {
|
|
33
|
+
return {
|
|
34
|
+
harness: sig.harness,
|
|
35
|
+
configDir: path.join(projectDir, sig.configDir),
|
|
36
|
+
version: null,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Check home directory for global installs
|
|
42
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
43
|
+
if (home && fs.existsSync(path.join(home, ".claude"))) {
|
|
44
|
+
return {
|
|
45
|
+
harness: "claude",
|
|
46
|
+
configDir: path.join(home, ".claude"),
|
|
47
|
+
version: null,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return { harness: "none", configDir: null, version: null };
|
|
51
|
+
}
|
|
52
|
+
export function detectAllHarnesses(projectDir) {
|
|
53
|
+
const found = [];
|
|
54
|
+
for (const sig of HARNESS_SIGNATURES) {
|
|
55
|
+
for (const marker of sig.markers) {
|
|
56
|
+
if (fs.existsSync(path.join(projectDir, marker))) {
|
|
57
|
+
found.push({
|
|
58
|
+
harness: sig.harness,
|
|
59
|
+
configDir: path.join(projectDir, sig.configDir),
|
|
60
|
+
version: null,
|
|
61
|
+
});
|
|
62
|
+
break; // one detection per harness
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return found;
|
|
67
|
+
}
|
|
68
|
+
// ── Audit Checks ──
|
|
69
|
+
function checkFileExists(dir, relativePath) {
|
|
70
|
+
return fs.existsSync(path.join(dir, relativePath));
|
|
71
|
+
}
|
|
72
|
+
function checkDirHasFiles(dir, relativePath) {
|
|
73
|
+
const full = path.join(dir, relativePath);
|
|
74
|
+
if (!fs.existsSync(full))
|
|
75
|
+
return false;
|
|
76
|
+
try {
|
|
77
|
+
const entries = fs.readdirSync(full);
|
|
78
|
+
return entries.length > 0;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function auditClaude(projectDir, configDir) {
|
|
85
|
+
const categories = [];
|
|
86
|
+
// Config category
|
|
87
|
+
const configChecks = [
|
|
88
|
+
{
|
|
89
|
+
id: "claude-md",
|
|
90
|
+
label: "CLAUDE.md exists",
|
|
91
|
+
passed: checkFileExists(projectDir, "CLAUDE.md") ||
|
|
92
|
+
checkFileExists(configDir, "CLAUDE.md"),
|
|
93
|
+
points: 15,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "settings",
|
|
97
|
+
label: "settings.json configured",
|
|
98
|
+
passed: checkFileExists(configDir, "settings.json") ||
|
|
99
|
+
checkFileExists(configDir, "settings.local.json"),
|
|
100
|
+
points: 10,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "project-config",
|
|
104
|
+
label: "Project-level config exists",
|
|
105
|
+
passed: checkDirHasFiles(configDir, "projects"),
|
|
106
|
+
points: 5,
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
// Skills category
|
|
110
|
+
const skillChecks = [
|
|
111
|
+
{
|
|
112
|
+
id: "skills-dir",
|
|
113
|
+
label: "Skills directory exists",
|
|
114
|
+
passed: checkDirHasFiles(configDir, "skills"),
|
|
115
|
+
points: 15,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "skills-count",
|
|
119
|
+
label: "At least 5 skills installed",
|
|
120
|
+
passed: countSubdirs(path.join(configDir, "skills")) >= 5,
|
|
121
|
+
points: 10,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "shared-conventions",
|
|
125
|
+
label: "Shared conventions configured",
|
|
126
|
+
passed: checkDirHasFiles(configDir, "skills/_shared"),
|
|
127
|
+
points: 5,
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
// Hooks category
|
|
131
|
+
const hookChecks = [
|
|
132
|
+
{
|
|
133
|
+
id: "hooks-configured",
|
|
134
|
+
label: "Hooks configured in settings",
|
|
135
|
+
passed: hasHooksInSettings(configDir),
|
|
136
|
+
points: 15,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "pre-commit-hook",
|
|
140
|
+
label: "Pre-commit or CI hook exists",
|
|
141
|
+
passed: checkFileExists(projectDir, ".husky/pre-commit") ||
|
|
142
|
+
checkFileExists(projectDir, ".git/hooks/pre-commit"),
|
|
143
|
+
points: 5,
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
// Memory category
|
|
147
|
+
const memoryChecks = [
|
|
148
|
+
{
|
|
149
|
+
id: "memory-dir",
|
|
150
|
+
label: "Memory/projects directory exists",
|
|
151
|
+
passed: checkDirHasFiles(configDir, "projects"),
|
|
152
|
+
points: 10,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: "memory-md",
|
|
156
|
+
label: "MEMORY.md exists in projects",
|
|
157
|
+
passed: hasMemoryMd(configDir),
|
|
158
|
+
points: 10,
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
categories.push(buildCategory("Configuration", configChecks));
|
|
162
|
+
categories.push(buildCategory("Skills", skillChecks));
|
|
163
|
+
categories.push(buildCategory("Hooks", hookChecks));
|
|
164
|
+
categories.push(buildCategory("Memory", memoryChecks));
|
|
165
|
+
return categories;
|
|
166
|
+
}
|
|
167
|
+
function auditGeneric(projectDir) {
|
|
168
|
+
const checks = [
|
|
169
|
+
{
|
|
170
|
+
id: "has-config",
|
|
171
|
+
label: "AI agent config file exists",
|
|
172
|
+
passed: HARNESS_SIGNATURES.some((sig) => sig.markers.some((m) => checkFileExists(projectDir, m))),
|
|
173
|
+
points: 30,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: "has-gitignore",
|
|
177
|
+
label: ".gitignore excludes agent artifacts",
|
|
178
|
+
passed: gitignoreHasAgent(projectDir),
|
|
179
|
+
points: 10,
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
return [buildCategory("Basic Setup", checks)];
|
|
183
|
+
}
|
|
184
|
+
// ── Helpers ──
|
|
185
|
+
function countSubdirs(dir) {
|
|
186
|
+
if (!fs.existsSync(dir))
|
|
187
|
+
return 0;
|
|
188
|
+
try {
|
|
189
|
+
return fs
|
|
190
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
191
|
+
.filter((d) => d.isDirectory()).length;
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function hasHooksInSettings(configDir) {
|
|
198
|
+
const settingsPath = path.join(configDir, "settings.json");
|
|
199
|
+
if (!fs.existsSync(settingsPath))
|
|
200
|
+
return false;
|
|
201
|
+
try {
|
|
202
|
+
const content = fs.readFileSync(settingsPath, "utf-8");
|
|
203
|
+
return content.includes("hooks") || content.includes("hook");
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function hasMemoryMd(configDir) {
|
|
210
|
+
const projectsDir = path.join(configDir, "projects");
|
|
211
|
+
if (!fs.existsSync(projectsDir))
|
|
212
|
+
return false;
|
|
213
|
+
try {
|
|
214
|
+
const dirs = fs.readdirSync(projectsDir, { withFileTypes: true });
|
|
215
|
+
return dirs.some((d) => {
|
|
216
|
+
if (!d.isDirectory())
|
|
217
|
+
return false;
|
|
218
|
+
const memDir = path.join(projectsDir, d.name, "memory");
|
|
219
|
+
return (fs.existsSync(memDir) && fs.existsSync(path.join(memDir, "MEMORY.md")));
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function gitignoreHasAgent(projectDir) {
|
|
227
|
+
const giPath = path.join(projectDir, ".gitignore");
|
|
228
|
+
if (!fs.existsSync(giPath))
|
|
229
|
+
return false;
|
|
230
|
+
try {
|
|
231
|
+
const content = fs.readFileSync(giPath, "utf-8");
|
|
232
|
+
return (content.includes(".claude") ||
|
|
233
|
+
content.includes(".cursor") ||
|
|
234
|
+
content.includes(".codex"));
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function buildCategory(name, checks) {
|
|
241
|
+
const score = checks
|
|
242
|
+
.filter((c) => c.passed)
|
|
243
|
+
.reduce((sum, c) => sum + c.points, 0);
|
|
244
|
+
const maxScore = checks.reduce((sum, c) => sum + c.points, 0);
|
|
245
|
+
return { name, score, maxScore, checks };
|
|
246
|
+
}
|
|
247
|
+
function computeGrade(score, max) {
|
|
248
|
+
if (max === 0)
|
|
249
|
+
return "F";
|
|
250
|
+
const pct = (score / max) * 100;
|
|
251
|
+
if (pct >= 90)
|
|
252
|
+
return "A";
|
|
253
|
+
if (pct >= 75)
|
|
254
|
+
return "B";
|
|
255
|
+
if (pct >= 60)
|
|
256
|
+
return "C";
|
|
257
|
+
if (pct >= 40)
|
|
258
|
+
return "D";
|
|
259
|
+
return "F";
|
|
260
|
+
}
|
|
261
|
+
// ── Main ──
|
|
262
|
+
export function runHarnessAudit(projectDir) {
|
|
263
|
+
const detection = detectHarness(projectDir);
|
|
264
|
+
const categories = detection.harness === "claude" && detection.configDir
|
|
265
|
+
? auditClaude(projectDir, detection.configDir)
|
|
266
|
+
: auditGeneric(projectDir);
|
|
267
|
+
const totalScore = categories.reduce((s, c) => s + c.score, 0);
|
|
268
|
+
const maxScore = categories.reduce((s, c) => s + c.maxScore, 0);
|
|
269
|
+
return {
|
|
270
|
+
harness: detection.harness,
|
|
271
|
+
totalScore,
|
|
272
|
+
maxScore,
|
|
273
|
+
grade: computeGrade(totalScore, maxScore),
|
|
274
|
+
categories,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=harness-audit.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel batch sub-agents — spawn test/lint/build runners per module
|
|
3
|
+
* in parallel during quality scoring. Collects results and aggregates.
|
|
4
|
+
*/
|
|
5
|
+
export type JobStatus = "pending" | "running" | "done" | "error";
|
|
6
|
+
export interface BatchJob {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
command: string;
|
|
10
|
+
module: string;
|
|
11
|
+
status: JobStatus;
|
|
12
|
+
startedAt: number | null;
|
|
13
|
+
completedAt: number | null;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
exitCode: number | null;
|
|
16
|
+
stdout: string;
|
|
17
|
+
stderr: string;
|
|
18
|
+
}
|
|
19
|
+
export interface BatchResult {
|
|
20
|
+
jobs: BatchJob[];
|
|
21
|
+
totalDurationMs: number;
|
|
22
|
+
passedCount: number;
|
|
23
|
+
failedCount: number;
|
|
24
|
+
parallelism: number;
|
|
25
|
+
}
|
|
26
|
+
export interface BatchConfig {
|
|
27
|
+
maxParallel: number;
|
|
28
|
+
timeoutMs: number;
|
|
29
|
+
}
|
|
30
|
+
export declare const DEFAULT_BATCH_CONFIG: BatchConfig;
|
|
31
|
+
export declare function createJob(name: string, command: string, module: string): BatchJob;
|
|
32
|
+
export type JobExecutor = (job: BatchJob) => Promise<BatchJob>;
|
|
33
|
+
/**
|
|
34
|
+
* Default executor — runs command via child_process.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createShellExecutor(timeoutMs?: number): JobExecutor;
|
|
37
|
+
/**
|
|
38
|
+
* Run jobs in parallel with configurable concurrency.
|
|
39
|
+
*/
|
|
40
|
+
export declare function runBatch(jobs: BatchJob[], executor: JobExecutor, config?: BatchConfig): Promise<BatchResult>;
|
|
41
|
+
export declare function formatBatchResult(result: BatchResult): string;
|
|
42
|
+
//# sourceMappingURL=parallel-batch.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel batch sub-agents — spawn test/lint/build runners per module
|
|
3
|
+
* in parallel during quality scoring. Collects results and aggregates.
|
|
4
|
+
*/
|
|
5
|
+
import { execFileAsync } from "./exec.js";
|
|
6
|
+
export const DEFAULT_BATCH_CONFIG = {
|
|
7
|
+
maxParallel: 4,
|
|
8
|
+
timeoutMs: 60_000,
|
|
9
|
+
};
|
|
10
|
+
// ── Job creation ──
|
|
11
|
+
let _jobCounter = 0;
|
|
12
|
+
export function createJob(name, command, module) {
|
|
13
|
+
_jobCounter++;
|
|
14
|
+
return {
|
|
15
|
+
id: `job-${Date.now()}-${_jobCounter}`,
|
|
16
|
+
name,
|
|
17
|
+
command,
|
|
18
|
+
module,
|
|
19
|
+
status: "pending",
|
|
20
|
+
startedAt: null,
|
|
21
|
+
completedAt: null,
|
|
22
|
+
durationMs: 0,
|
|
23
|
+
exitCode: null,
|
|
24
|
+
stdout: "",
|
|
25
|
+
stderr: "",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Default executor — runs command via child_process.
|
|
30
|
+
*/
|
|
31
|
+
export function createShellExecutor(timeoutMs = 60_000) {
|
|
32
|
+
return async (job) => {
|
|
33
|
+
job.status = "running";
|
|
34
|
+
job.startedAt = Date.now();
|
|
35
|
+
try {
|
|
36
|
+
const { stdout, stderr } = await execFileAsync("sh", ["-c", job.command], { timeout: timeoutMs, maxBuffer: 5 * 1024 * 1024 });
|
|
37
|
+
job.stdout = stdout;
|
|
38
|
+
job.stderr = stderr;
|
|
39
|
+
job.exitCode = 0;
|
|
40
|
+
job.status = "done";
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const e = err;
|
|
44
|
+
job.stdout = e.stdout ?? "";
|
|
45
|
+
job.stderr = e.stderr ?? "";
|
|
46
|
+
job.exitCode = e.killed ? -1 : (e.code ?? 1);
|
|
47
|
+
job.status = "error";
|
|
48
|
+
}
|
|
49
|
+
job.completedAt = Date.now();
|
|
50
|
+
job.durationMs = job.completedAt - job.startedAt;
|
|
51
|
+
return job;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Run jobs in parallel with configurable concurrency.
|
|
56
|
+
*/
|
|
57
|
+
export async function runBatch(jobs, executor, config = DEFAULT_BATCH_CONFIG) {
|
|
58
|
+
const start = Date.now();
|
|
59
|
+
const results = [];
|
|
60
|
+
const queue = [...jobs];
|
|
61
|
+
// Process in waves of maxParallel
|
|
62
|
+
while (queue.length > 0) {
|
|
63
|
+
const wave = queue.splice(0, config.maxParallel);
|
|
64
|
+
const waveResults = await Promise.all(wave.map((j) => executor(j)));
|
|
65
|
+
results.push(...waveResults);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
jobs: results,
|
|
69
|
+
totalDurationMs: Date.now() - start,
|
|
70
|
+
passedCount: results.filter((j) => j.status === "done").length,
|
|
71
|
+
failedCount: results.filter((j) => j.status === "error").length,
|
|
72
|
+
parallelism: config.maxParallel,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// ── Formatting ──
|
|
76
|
+
export function formatBatchResult(result) {
|
|
77
|
+
const status = result.failedCount === 0 ? "✅ ALL PASSED" : "❌ FAILURES";
|
|
78
|
+
const lines = [];
|
|
79
|
+
lines.push(`## Batch Result: ${status}`);
|
|
80
|
+
lines.push(`**Jobs**: ${result.jobs.length} | **Passed**: ${result.passedCount} | **Failed**: ${result.failedCount} | **Time**: ${result.totalDurationMs}ms | **Parallelism**: ${result.parallelism}`);
|
|
81
|
+
lines.push("");
|
|
82
|
+
for (const job of result.jobs) {
|
|
83
|
+
const icon = job.status === "done" ? "✓" : "✗";
|
|
84
|
+
lines.push(` ${icon} ${job.name} (${job.module}) — ${job.durationMs}ms [exit ${job.exitCode}]`);
|
|
85
|
+
}
|
|
86
|
+
return lines.join("\n");
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=parallel-batch.js.map
|
package/dist/lib/plugin.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { InstalledPlugin, PluginRegistryEntry, PluginSyncResult, PluginValidationResult } from "../types/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Validate a plugin directory structure and manifest.
|
|
4
4
|
*/
|
|
@@ -37,8 +37,14 @@ export declare function searchRegistry(query?: string): Promise<PluginRegistryEn
|
|
|
37
37
|
*/
|
|
38
38
|
export declare function detectProjectPlugins(projectDir: string): Promise<string[]>;
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* Detect installed plugins with full metadata (including manifest).
|
|
41
|
+
* Used by auto-wiring to read plugin capabilities.
|
|
42
|
+
*/
|
|
43
|
+
export declare function detectProjectPluginsFull(projectDir: string): Promise<InstalledPlugin[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Sync detected plugins into the project manifest and auto-wire
|
|
46
|
+
* their capabilities into CLAUDE.md and .claude/settings.json.
|
|
47
|
+
* Returns a report of added, removed, unchanged, wired, and unwired plugins.
|
|
42
48
|
*/
|
|
43
49
|
export declare function syncPlugins(projectDir: string, options?: {
|
|
44
50
|
dryRun?: boolean;
|