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
package/dist/index.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import { render } from
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
2
|
+
import { PassThrough } from "node:stream";
|
|
3
|
+
import { render } from "ink";
|
|
4
|
+
import meow from "meow";
|
|
5
|
+
import { createRequire } from "module";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import React from "react";
|
|
8
|
+
import updateNotifier from "update-notifier";
|
|
9
|
+
import AnalyzeUI from "./ui/AnalyzeUI.js";
|
|
10
|
+
import App from "./ui/App.js";
|
|
11
|
+
import AutoSkills from "./ui/AutoSkills.js";
|
|
12
|
+
import CI from "./ui/CI.js";
|
|
13
|
+
import { CIProvider as CIContextProvider } from "./ui/CIContext.js";
|
|
14
|
+
import Doctor from "./ui/Doctor.js";
|
|
15
|
+
import LlmsTxt from "./ui/LlmsTxt.js";
|
|
16
|
+
import Plugin from "./ui/Plugin.js";
|
|
17
|
+
import Skills from "./ui/Skills.js";
|
|
15
18
|
// Check for updates in background (non-blocking, cached 24h)
|
|
16
19
|
const _require = createRequire(import.meta.url);
|
|
17
|
-
const pkg = _require(
|
|
20
|
+
const pkg = _require("../package.json");
|
|
18
21
|
updateNotifier({ pkg, updateCheckInterval: 1000 * 60 * 60 * 24 }).notify();
|
|
19
22
|
const cli = meow(`
|
|
20
23
|
Usage
|
|
@@ -23,13 +26,35 @@ const cli = meow(`
|
|
|
23
26
|
Commands
|
|
24
27
|
init Bootstrap a new project (default)
|
|
25
28
|
ci Run CI simulation (lint + compile + test + security + ghagga)
|
|
29
|
+
tdd init Install TDD-enforcing pre-commit hook (auto-detects stack)
|
|
30
|
+
tdd pipeline Install TDD pipeline pre-push hook (--mode strict|warn)
|
|
26
31
|
analyze Run repoforge skills analysis
|
|
27
32
|
doctor Show health report
|
|
33
|
+
workflow show Render a workflow graph as ASCII (--template <name> or file path)
|
|
34
|
+
workflow validate Validate project state against a workflow graph
|
|
35
|
+
workflow list List available workflows and built-in templates
|
|
28
36
|
plugin add Install a plugin from GitHub (org/repo)
|
|
29
37
|
plugin remove Remove an installed plugin
|
|
30
38
|
plugin list List installed plugins
|
|
31
39
|
plugin search Search the plugin registry
|
|
32
40
|
plugin validate Validate a local plugin directory
|
|
41
|
+
plugin sync Auto-detect and wire installed plugins
|
|
42
|
+
plugin export Export plugin to Agent Skills spec format (skills.json)
|
|
43
|
+
plugin export --codex: Export plugin to Codex-compatible TOML subagent files
|
|
44
|
+
plugin export-skills Generate aggregated skills.json from all installed plugins
|
|
45
|
+
plugin export-skills global Generate global skills.json from all globally installed plugins
|
|
46
|
+
plugin import Import an Agent Skills spec package as a javi-forge plugin
|
|
47
|
+
skills doctor Show skills health report (add --deep for conflict detection)
|
|
48
|
+
skills budget Show token cost of loaded skills (add -b N for custom budget)
|
|
49
|
+
skills score Score a skill on quality dimensions (completeness, clarity, testability, token-efficiency)
|
|
50
|
+
skills benchmark Benchmark a skill with structural quality checks
|
|
51
|
+
skills auto Auto-detect project stack and suggest/install matching AI skills
|
|
52
|
+
skills auto-install Alias for skills auto
|
|
53
|
+
skill publish Package a skill directory for marketplace distribution (generates plugin.json)
|
|
54
|
+
security baseline Create security baseline from current audit findings
|
|
55
|
+
security check Check for regressions against baseline (exits non-zero if found)
|
|
56
|
+
security update Re-snapshot baseline (acknowledge current vulns)
|
|
57
|
+
security allowlist Add all current findings to the allowlist (suppress in future checks)
|
|
33
58
|
llms-txt Generate AI-friendly llms.txt for current project
|
|
34
59
|
|
|
35
60
|
Options
|
|
@@ -40,7 +65,13 @@ const cli = meow(`
|
|
|
40
65
|
--project-name Project name (skips name prompt)
|
|
41
66
|
--ghagga Enable GHAGGA review system
|
|
42
67
|
--mock Enable mock-first mode (no real API keys needed)
|
|
68
|
+
--local-ai Include local AI dev stack (Ollama + Docker Compose)
|
|
43
69
|
--batch Non-interactive mode (auto-proceed, no keyboard input)
|
|
70
|
+
--deep Enable deep analysis (conflict + duplicate detection)
|
|
71
|
+
--budget, -b Token budget limit for skills (default: 8000)
|
|
72
|
+
--skills-dir Custom skills directory path
|
|
73
|
+
--author Author name for skill publish
|
|
74
|
+
--repo Repository URL for skill publish
|
|
44
75
|
--version Show version
|
|
45
76
|
--help Show this help
|
|
46
77
|
|
|
@@ -63,6 +94,7 @@ const cli = meow(`
|
|
|
63
94
|
$ javi-forge init --stack node --ci github
|
|
64
95
|
$ javi-forge ci
|
|
65
96
|
$ javi-forge ci init
|
|
97
|
+
$ javi-forge tdd init
|
|
66
98
|
$ javi-forge ci --quick
|
|
67
99
|
$ javi-forge ci --no-ci-ghagga --no-security
|
|
68
100
|
$ javi-forge ci --no-docker
|
|
@@ -74,44 +106,107 @@ const cli = meow(`
|
|
|
74
106
|
`, {
|
|
75
107
|
importMeta: import.meta,
|
|
76
108
|
flags: {
|
|
77
|
-
dryRun: { type:
|
|
78
|
-
stack: { type:
|
|
79
|
-
ci: { type:
|
|
80
|
-
memory: { type:
|
|
81
|
-
projectName: { type:
|
|
82
|
-
ghagga: { type:
|
|
83
|
-
mock: { type:
|
|
84
|
-
|
|
109
|
+
dryRun: { type: "boolean", default: false },
|
|
110
|
+
stack: { type: "string", default: "" },
|
|
111
|
+
ci: { type: "string", default: "" },
|
|
112
|
+
memory: { type: "string", default: "" },
|
|
113
|
+
projectName: { type: "string", default: "" },
|
|
114
|
+
ghagga: { type: "boolean", default: false },
|
|
115
|
+
mock: { type: "boolean", default: false },
|
|
116
|
+
localAi: { type: "boolean", default: false },
|
|
117
|
+
batch: { type: "boolean", default: false },
|
|
85
118
|
// CI flags
|
|
86
|
-
quick: { type:
|
|
87
|
-
shell: { type:
|
|
88
|
-
detect: { type:
|
|
89
|
-
docker: { type:
|
|
90
|
-
ciGhagga: { type:
|
|
91
|
-
security: { type:
|
|
92
|
-
timeout: { type:
|
|
93
|
-
|
|
119
|
+
quick: { type: "boolean", default: false },
|
|
120
|
+
shell: { type: "boolean", default: false },
|
|
121
|
+
detect: { type: "boolean", default: false },
|
|
122
|
+
docker: { type: "boolean", default: true },
|
|
123
|
+
ciGhagga: { type: "boolean", default: true },
|
|
124
|
+
security: { type: "boolean", default: true },
|
|
125
|
+
timeout: { type: "number", default: 600 },
|
|
126
|
+
// Security check flags
|
|
127
|
+
minSeverity: { type: "string", default: "low" },
|
|
128
|
+
staleDays: { type: "number", default: 30 },
|
|
129
|
+
json: { type: "boolean", default: false },
|
|
130
|
+
// Plugin flags
|
|
131
|
+
codex: { type: "boolean", default: false },
|
|
132
|
+
// Skills flags
|
|
133
|
+
deep: { type: "boolean", default: false },
|
|
134
|
+
budget: { type: "number", shortFlag: "b", default: 8000 },
|
|
135
|
+
skillsDir: { type: "string", default: "" },
|
|
136
|
+
// Skill publish flags
|
|
137
|
+
author: { type: "string", default: "" },
|
|
138
|
+
repo: { type: "string", default: "" },
|
|
139
|
+
// Workflow flags
|
|
140
|
+
template: { type: "string", default: "" },
|
|
141
|
+
},
|
|
94
142
|
});
|
|
95
|
-
const subcommand = cli.input[0] ??
|
|
96
|
-
const VALID_STACKS = [
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
143
|
+
const subcommand = cli.input[0] ?? "init";
|
|
144
|
+
const VALID_STACKS = [
|
|
145
|
+
"node",
|
|
146
|
+
"python",
|
|
147
|
+
"go",
|
|
148
|
+
"rust",
|
|
149
|
+
"java-gradle",
|
|
150
|
+
"java-maven",
|
|
151
|
+
"elixir",
|
|
152
|
+
];
|
|
153
|
+
const VALID_CI = ["github", "gitlab", "woodpecker"];
|
|
154
|
+
const VALID_MEMORY = ["engram", "obsidian-brain", "memory-simple", "none"];
|
|
155
|
+
const isCI = cli.flags.batch || process.env["CI"] === "1" || process.env["CI"] === "true";
|
|
100
156
|
// When stdin doesn't support raw mode (pipes, subprocesses, CI), provide a fake
|
|
101
157
|
// stdin stream so Ink doesn't crash trying to enable raw mode on a non-TTY pipe.
|
|
102
158
|
const isTTY = process.stdin.isTTY === true;
|
|
103
159
|
const fakeStdin = new PassThrough();
|
|
104
|
-
Object.defineProperty(fakeStdin,
|
|
160
|
+
Object.defineProperty(fakeStdin, "isTTY", { value: false });
|
|
105
161
|
const inkStdin = isTTY ? process.stdin : fakeStdin;
|
|
106
162
|
switch (subcommand) {
|
|
107
|
-
case
|
|
163
|
+
case "tdd": {
|
|
164
|
+
if (cli.input[1] === "init") {
|
|
165
|
+
const { installTddHooks } = await import("./commands/tdd.js");
|
|
166
|
+
const { installed, errors } = await installTddHooks(process.cwd());
|
|
167
|
+
if (installed.length > 0) {
|
|
168
|
+
console.log(`\u2713 Installed TDD hooks: ${installed.join(", ")}`);
|
|
169
|
+
console.log(" Pre-commit hook enforces tests must pass before commit");
|
|
170
|
+
}
|
|
171
|
+
for (const err of errors) {
|
|
172
|
+
console.error(`\u2717 ${err}`);
|
|
173
|
+
}
|
|
174
|
+
process.exit(errors.length > 0 ? 1 : 0);
|
|
175
|
+
}
|
|
176
|
+
else if (cli.input[1] === "pipeline") {
|
|
177
|
+
const { installTddPipelineHook } = await import("./commands/tdd-pipeline.js");
|
|
178
|
+
const mode = cli.flags.mode === "warn"
|
|
179
|
+
? "warn"
|
|
180
|
+
: "strict";
|
|
181
|
+
const result = await installTddPipelineHook(process.cwd(), mode);
|
|
182
|
+
if (result.installed.length > 0) {
|
|
183
|
+
console.log(`\u2713 Installed TDD pipeline hook: ${result.installed.join(", ")} [${result.mode}]`);
|
|
184
|
+
console.log(` Pre-push hook enforces TDD pipeline (${result.mode} mode)`);
|
|
185
|
+
}
|
|
186
|
+
for (const skip of result.skipped) {
|
|
187
|
+
console.log(`\u26A0 ${skip}`);
|
|
188
|
+
}
|
|
189
|
+
for (const err of result.errors) {
|
|
190
|
+
console.error(`\u2717 ${err}`);
|
|
191
|
+
}
|
|
192
|
+
process.exit(result.errors.length > 0 ? 1 : 0);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.error("Usage: javi-forge tdd <command>");
|
|
196
|
+
console.error(" init Install TDD-enforcing pre-commit hook");
|
|
197
|
+
console.error(" pipeline Install TDD pipeline pre-push hook (--mode strict|warn)");
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
case "ci": {
|
|
108
203
|
// Sub-command: javi-forge ci init → install git hooks
|
|
109
|
-
if (cli.input[1] ===
|
|
110
|
-
const { installCIHooks } = await import(
|
|
204
|
+
if (cli.input[1] === "init") {
|
|
205
|
+
const { installCIHooks } = await import("./commands/ci.js");
|
|
111
206
|
const { installed, errors } = await installCIHooks(process.cwd());
|
|
112
207
|
if (installed.length > 0) {
|
|
113
|
-
console.log(`✓ Installed git hooks: ${installed.join(
|
|
114
|
-
console.log(
|
|
208
|
+
console.log(`✓ Installed git hooks: ${installed.join(", ")}`);
|
|
209
|
+
console.log(" Hooks call javi-forge ci (with npx fallback)");
|
|
115
210
|
}
|
|
116
211
|
for (const err of errors) {
|
|
117
212
|
console.error(`✗ ${err}`);
|
|
@@ -119,39 +214,290 @@ switch (subcommand) {
|
|
|
119
214
|
process.exit(errors.length > 0 ? 1 : 0);
|
|
120
215
|
break;
|
|
121
216
|
}
|
|
122
|
-
const ciMode = cli.flags.detect
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
217
|
+
const ciMode = cli.flags.detect
|
|
218
|
+
? "detect"
|
|
219
|
+
: cli.flags.shell
|
|
220
|
+
? "shell"
|
|
221
|
+
: cli.flags.quick
|
|
222
|
+
? "quick"
|
|
223
|
+
: "full";
|
|
126
224
|
render(React.createElement(CIContextProvider, { isCI: true },
|
|
127
225
|
React.createElement(CI, { projectDir: process.cwd(), mode: ciMode, noDocker: !cli.flags.docker, noGhagga: !cli.flags.ciGhagga, noSecurity: !cli.flags.security, timeout: cli.flags.timeout })), { stdin: inkStdin });
|
|
128
226
|
break;
|
|
129
227
|
}
|
|
130
|
-
case
|
|
228
|
+
case "doctor": {
|
|
131
229
|
render(React.createElement(CIContextProvider, { isCI: isCI },
|
|
132
230
|
React.createElement(Doctor, null)), { stdin: inkStdin });
|
|
133
231
|
break;
|
|
134
232
|
}
|
|
135
|
-
case
|
|
233
|
+
case "analyze": {
|
|
136
234
|
render(React.createElement(CIContextProvider, { isCI: isCI },
|
|
137
235
|
React.createElement(AnalyzeUI, { dryRun: cli.flags.dryRun })), { stdin: inkStdin });
|
|
138
236
|
break;
|
|
139
237
|
}
|
|
140
|
-
case
|
|
238
|
+
case "workflow": {
|
|
239
|
+
const workflowAction = cli.input[1];
|
|
240
|
+
const VALID_WORKFLOW_ACTIONS = ["show", "validate", "list"];
|
|
241
|
+
if (!workflowAction || !VALID_WORKFLOW_ACTIONS.includes(workflowAction)) {
|
|
242
|
+
console.error("Usage: javi-forge workflow <show|validate|list>");
|
|
243
|
+
console.error(" show Render a workflow graph as ASCII");
|
|
244
|
+
console.error(" validate Validate project state against a workflow graph");
|
|
245
|
+
console.error(" list List available workflows and built-in templates");
|
|
246
|
+
console.error("");
|
|
247
|
+
console.error(" Options:");
|
|
248
|
+
console.error(" --template <name> Use a built-in template (ci-pipeline, release, feature-flow)");
|
|
249
|
+
console.error(" <file> Path to a .dot or .mermaid file");
|
|
250
|
+
process.exit(1);
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
const { runWorkflowShow, runWorkflowValidate, runWorkflowList } = await import("./commands/workflow.js");
|
|
254
|
+
const workflowTarget = cli.input[2];
|
|
255
|
+
const workflowTemplate = cli.flags.template || undefined;
|
|
256
|
+
const onStep = (step) => {
|
|
257
|
+
const icon = step.status === "done"
|
|
258
|
+
? "\u2713"
|
|
259
|
+
: step.status === "error"
|
|
260
|
+
? "\u2717"
|
|
261
|
+
: "\u25CB";
|
|
262
|
+
console.log(`${icon} ${step.label}`);
|
|
263
|
+
if (step.detail)
|
|
264
|
+
console.log(` ${step.detail}`);
|
|
265
|
+
};
|
|
266
|
+
try {
|
|
267
|
+
if (workflowAction === "show") {
|
|
268
|
+
const output = await runWorkflowShow(process.cwd(), onStep, {
|
|
269
|
+
target: workflowTarget,
|
|
270
|
+
template: workflowTemplate,
|
|
271
|
+
});
|
|
272
|
+
if (output)
|
|
273
|
+
console.log("\n" + output);
|
|
274
|
+
else
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
else if (workflowAction === "validate") {
|
|
278
|
+
const output = await runWorkflowValidate(process.cwd(), onStep, {
|
|
279
|
+
target: workflowTarget,
|
|
280
|
+
template: workflowTemplate,
|
|
281
|
+
});
|
|
282
|
+
if (output)
|
|
283
|
+
console.log("\n" + output);
|
|
284
|
+
else
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
const output = await runWorkflowList(process.cwd(), onStep);
|
|
289
|
+
console.log("\n" + output);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
console.error(`\u2717 ${e instanceof Error ? e.message : String(e)}`);
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
case "llms-txt": {
|
|
141
299
|
render(React.createElement(CIContextProvider, { isCI: isCI },
|
|
142
300
|
React.createElement(LlmsTxt, { projectDir: process.cwd(), dryRun: cli.flags.dryRun })), { stdin: inkStdin });
|
|
143
301
|
break;
|
|
144
302
|
}
|
|
145
|
-
case
|
|
303
|
+
case "plugin": {
|
|
146
304
|
const pluginAction = cli.input[1];
|
|
147
|
-
const VALID_PLUGIN_ACTIONS = [
|
|
148
|
-
|
|
305
|
+
const VALID_PLUGIN_ACTIONS = [
|
|
306
|
+
"add",
|
|
307
|
+
"remove",
|
|
308
|
+
"list",
|
|
309
|
+
"search",
|
|
310
|
+
"validate",
|
|
311
|
+
"sync",
|
|
312
|
+
"export",
|
|
313
|
+
"import",
|
|
314
|
+
"export-skills",
|
|
315
|
+
];
|
|
316
|
+
const action = pluginAction && VALID_PLUGIN_ACTIONS.includes(pluginAction)
|
|
317
|
+
? pluginAction
|
|
318
|
+
: "list";
|
|
149
319
|
const target = cli.input[2];
|
|
150
320
|
render(React.createElement(CIContextProvider, { isCI: isCI },
|
|
151
|
-
React.createElement(Plugin, { action: action, target: target, dryRun: cli.flags.dryRun })), { stdin: inkStdin });
|
|
321
|
+
React.createElement(Plugin, { action: action, target: target, dryRun: cli.flags.dryRun, codex: cli.flags.codex })), { stdin: inkStdin });
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case "skills": {
|
|
325
|
+
const skillsAction = cli.input[1];
|
|
326
|
+
const VALID_SKILLS_ACTIONS = [
|
|
327
|
+
"doctor",
|
|
328
|
+
"budget",
|
|
329
|
+
"score",
|
|
330
|
+
"benchmark",
|
|
331
|
+
"auto",
|
|
332
|
+
"auto-install",
|
|
333
|
+
];
|
|
334
|
+
if (!skillsAction || !VALID_SKILLS_ACTIONS.includes(skillsAction)) {
|
|
335
|
+
console.error("Usage: javi-forge skills <doctor|budget|score|benchmark|auto>");
|
|
336
|
+
console.error(" doctor Show skills health report (add --deep for conflict detection)");
|
|
337
|
+
console.error(" budget Show token cost of loaded skills (add -b N for custom budget)");
|
|
338
|
+
console.error(" score Score a skill on quality dimensions (0-100)");
|
|
339
|
+
console.error(" benchmark Benchmark a skill with structural quality checks");
|
|
340
|
+
console.error(" auto Auto-detect project stack and suggest/install matching AI skills");
|
|
341
|
+
console.error(" auto-install Alias for auto");
|
|
342
|
+
process.exit(1);
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
// Auto / auto-install: render interactive Ink UI
|
|
346
|
+
if (skillsAction === "auto" || skillsAction === "auto-install") {
|
|
347
|
+
render(React.createElement(CIContextProvider, { isCI: isCI },
|
|
348
|
+
React.createElement(AutoSkills, { projectDir: process.cwd(), skillsDir: cli.flags.skillsDir || undefined, dryRun: cli.flags.dryRun })), { stdin: inkStdin });
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
// Score and benchmark are non-interactive CLI commands
|
|
352
|
+
if (skillsAction === "score" || skillsAction === "benchmark") {
|
|
353
|
+
const targetSkill = cli.input[2];
|
|
354
|
+
if (!targetSkill) {
|
|
355
|
+
console.error(`Usage: javi-forge skills ${skillsAction} <skill-name>`);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
const skillsDir = cli.flags.skillsDir ||
|
|
360
|
+
path.join(process.env["HOME"] ?? "~", ".claude", "skills");
|
|
361
|
+
const skillPath = path.join(skillsDir, targetSkill, "SKILL.md");
|
|
362
|
+
if (skillsAction === "score") {
|
|
363
|
+
const { scoreSkill } = await import("./commands/skills.js");
|
|
364
|
+
const result = await scoreSkill(skillPath, cli.flags.budget);
|
|
365
|
+
if (!result) {
|
|
366
|
+
console.error(`\u2717 Skill not found: ${skillPath}`);
|
|
367
|
+
process.exit(1);
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
console.log(`\nSkill: ${result.skillName}`);
|
|
371
|
+
console.log(` Completeness: ${result.completeness}/100`);
|
|
372
|
+
console.log(` Clarity: ${result.clarity}/100`);
|
|
373
|
+
console.log(` Testability: ${result.testability}/100`);
|
|
374
|
+
console.log(` Token Efficiency: ${result.tokenEfficiency}/100`);
|
|
375
|
+
console.log(` Safety: ${result.safety}/100`);
|
|
376
|
+
console.log(` Agent Readiness: ${result.agentReadiness}/100`);
|
|
377
|
+
console.log(` ─────────────────────────`);
|
|
378
|
+
console.log(` Overall: ${result.overall}/100`);
|
|
379
|
+
console.log(` Grade: ${result.grade}`);
|
|
380
|
+
console.log(` Threshold: ${result.threshold}`);
|
|
381
|
+
console.log(` Status: ${result.passing ? "\u2713 PASSING" : "\u2717 FAILING"}`);
|
|
382
|
+
process.exit(result.passing ? 0 : 1);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
const { benchmarkSkill } = await import("./commands/skills.js");
|
|
386
|
+
const result = await benchmarkSkill(skillPath);
|
|
387
|
+
if (!result) {
|
|
388
|
+
console.error(`\u2717 Skill not found: ${skillPath}`);
|
|
389
|
+
process.exit(1);
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
console.log(`\nBenchmark: ${result.skillName}`);
|
|
393
|
+
for (const check of result.checks) {
|
|
394
|
+
const icon = check.passed ? "\u2713" : "\u2717";
|
|
395
|
+
console.log(` ${icon} ${check.name}${check.detail ? ` — ${check.detail}` : ""}`);
|
|
396
|
+
}
|
|
397
|
+
console.log(`\n Pass rate: ${result.passRate}%`);
|
|
398
|
+
process.exit(result.passRate >= 50 ? 0 : 1);
|
|
399
|
+
}
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
const skillsMode = skillsAction;
|
|
403
|
+
render(React.createElement(CIContextProvider, { isCI: isCI },
|
|
404
|
+
React.createElement(Skills, { mode: skillsMode, budget: cli.flags.budget, deep: cli.flags.deep, skillsDir: cli.flags.skillsDir || undefined })), { stdin: inkStdin });
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
case "skill": {
|
|
408
|
+
const skillAction = cli.input[1];
|
|
409
|
+
if (skillAction !== "publish") {
|
|
410
|
+
console.error("Usage: javi-forge skill <publish>");
|
|
411
|
+
console.error(" publish Package a skill directory for marketplace distribution");
|
|
412
|
+
process.exit(1);
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
const targetDir = cli.input[2] ?? process.cwd();
|
|
416
|
+
const { publishSkill } = await import("./lib/skill-publish.js");
|
|
417
|
+
const result = await publishSkill({
|
|
418
|
+
skillDir: path.resolve(targetDir),
|
|
419
|
+
author: cli.flags.author || undefined,
|
|
420
|
+
repository: cli.flags.repo || undefined,
|
|
421
|
+
dryRun: cli.flags.dryRun,
|
|
422
|
+
});
|
|
423
|
+
if (result.success) {
|
|
424
|
+
console.log(`\u2713 Published: ${result.manifest?.name}@${result.manifest?.version}`);
|
|
425
|
+
console.log(` plugin.json: ${result.pluginJsonPath}`);
|
|
426
|
+
if (result.manifest?.tags?.length) {
|
|
427
|
+
console.log(` tags: ${result.manifest.tags.join(", ")}`);
|
|
428
|
+
}
|
|
429
|
+
if (cli.flags.dryRun) {
|
|
430
|
+
console.log(" (dry-run: no files written)");
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
console.error(`\u2717 ${result.error}`);
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
case "security": {
|
|
440
|
+
const securityAction = cli.input[1];
|
|
441
|
+
const VALID_SECURITY_ACTIONS = ["baseline", "check", "update", "allowlist"];
|
|
442
|
+
if (!securityAction || !VALID_SECURITY_ACTIONS.includes(securityAction)) {
|
|
443
|
+
console.error("Usage: javi-forge security <baseline|check|update|allowlist>");
|
|
444
|
+
console.error(" baseline Create security baseline from current audit findings");
|
|
445
|
+
console.error(" check Check for regressions against baseline (exits non-zero if found)");
|
|
446
|
+
console.error(" update Re-snapshot baseline (acknowledge current vulns)");
|
|
447
|
+
console.error(" allowlist Add all current findings to the allowlist (suppress in future checks)");
|
|
448
|
+
console.error("");
|
|
449
|
+
console.error(" Options (check mode):");
|
|
450
|
+
console.error(" --min-severity <level> Only fail on regressions >= level (critical|high|moderate|low|info)");
|
|
451
|
+
console.error(" --stale-days <N> Warn if baseline older than N days (default: 30)");
|
|
452
|
+
console.error(" --json Output result as JSON (for CI integration)");
|
|
453
|
+
process.exit(1);
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
const { runSecurity } = await import("./commands/security.js");
|
|
457
|
+
const mode = securityAction;
|
|
458
|
+
const rawMinSev = cli.flags["minSeverity"];
|
|
459
|
+
const validSeverities = [
|
|
460
|
+
"critical",
|
|
461
|
+
"high",
|
|
462
|
+
"moderate",
|
|
463
|
+
"low",
|
|
464
|
+
"info",
|
|
465
|
+
];
|
|
466
|
+
const checkOptions = {
|
|
467
|
+
minSeverity: (rawMinSev && validSeverities.includes(rawMinSev)
|
|
468
|
+
? rawMinSev
|
|
469
|
+
: "low"),
|
|
470
|
+
staleDays: cli.flags["staleDays"],
|
|
471
|
+
};
|
|
472
|
+
const jsonOutput = !!cli.flags["json"];
|
|
473
|
+
try {
|
|
474
|
+
const result = await runSecurity(mode, process.cwd(), (step) => {
|
|
475
|
+
if (jsonOutput)
|
|
476
|
+
return; // suppress step output in JSON mode
|
|
477
|
+
const icon = step.status === "done"
|
|
478
|
+
? "\u2713"
|
|
479
|
+
: step.status === "error"
|
|
480
|
+
? "\u2717"
|
|
481
|
+
: step.status === "skipped"
|
|
482
|
+
? "-"
|
|
483
|
+
: "\u25CB";
|
|
484
|
+
console.log(`${icon} ${step.label}`);
|
|
485
|
+
if (step.detail)
|
|
486
|
+
console.log(` ${step.detail}`);
|
|
487
|
+
}, checkOptions);
|
|
488
|
+
if (jsonOutput && result) {
|
|
489
|
+
console.log(JSON.stringify(result, null, 2));
|
|
490
|
+
}
|
|
491
|
+
if (mode === "check" && result && result.filteredRegressions.length > 0) {
|
|
492
|
+
process.exit(1);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
process.exit(1);
|
|
497
|
+
}
|
|
152
498
|
break;
|
|
153
499
|
}
|
|
154
|
-
case
|
|
500
|
+
case "init":
|
|
155
501
|
default: {
|
|
156
502
|
const presetStack = VALID_STACKS.includes(cli.flags.stack)
|
|
157
503
|
? cli.flags.stack
|
|
@@ -164,7 +510,7 @@ switch (subcommand) {
|
|
|
164
510
|
: undefined;
|
|
165
511
|
const presetName = cli.flags.projectName || undefined;
|
|
166
512
|
render(React.createElement(CIContextProvider, { isCI: isCI },
|
|
167
|
-
React.createElement(App, { dryRun: cli.flags.dryRun, presetStack: presetStack, presetCI: presetCI, presetMemory: presetMemory, presetName: presetName, presetGhagga: cli.flags.ghagga, presetMock: cli.flags.mock ?? false })), { stdin: inkStdin });
|
|
513
|
+
React.createElement(App, { dryRun: cli.flags.dryRun, presetStack: presetStack, presetCI: presetCI, presetMemory: presetMemory, presetName: presetName, presetGhagga: cli.flags.ghagga, presetMock: cli.flags.mock ?? false, presetLocalAi: cli.flags.localAi ?? false })), { stdin: inkStdin });
|
|
168
514
|
break;
|
|
169
515
|
}
|
|
170
516
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { AgentSkillsManifest, AggregatedSkillsManifest, InstalledPlugin, PluginManifest } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Convert a javi-forge PluginManifest to an Agent Skills spec manifest.
|
|
4
|
+
*/
|
|
5
|
+
export declare function pluginToAgentSkills(manifest: PluginManifest, source?: string): AgentSkillsManifest;
|
|
6
|
+
/**
|
|
7
|
+
* Convert an Agent Skills spec manifest to a javi-forge PluginManifest.
|
|
8
|
+
*/
|
|
9
|
+
export declare function agentSkillsToPlugin(agentManifest: AgentSkillsManifest): PluginManifest;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a skills.json file in the given plugin directory from its plugin.json.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateAgentSkillsManifest(pluginDir: string, source?: string): Promise<{
|
|
14
|
+
success: boolean;
|
|
15
|
+
path?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Export an installed plugin to Agent Skills format.
|
|
20
|
+
* Looks up the plugin by name in the global plugins directory.
|
|
21
|
+
*/
|
|
22
|
+
export declare function exportPluginAsAgentSkills(name: string): Promise<{
|
|
23
|
+
success: boolean;
|
|
24
|
+
path?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Import an Agent Skills spec package from a directory.
|
|
29
|
+
* Reads skills.json, converts to plugin.json, copies to plugins dir.
|
|
30
|
+
*/
|
|
31
|
+
export declare function importAgentSkillsPackage(sourceDir: string, options?: {
|
|
32
|
+
dryRun?: boolean;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
success: boolean;
|
|
35
|
+
name?: string;
|
|
36
|
+
error?: string;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Aggregate multiple installed plugins into a single Agent Skills spec manifest.
|
|
40
|
+
* This enables the entire registry (or a project's installed plugins) to be
|
|
41
|
+
* discoverable via `npx skills add` by any of the 40+ compatible AI agents.
|
|
42
|
+
*
|
|
43
|
+
* Pure function — no I/O.
|
|
44
|
+
*/
|
|
45
|
+
export declare function aggregatePluginsToSkillsJson(plugins: InstalledPlugin[], registryName?: string, registryVersion?: string): AggregatedSkillsManifest;
|
|
46
|
+
/**
|
|
47
|
+
* Generate a project-level skills.json from all installed plugins in a project.
|
|
48
|
+
* Writes the aggregated manifest to the project's .javi-forge/ directory.
|
|
49
|
+
*/
|
|
50
|
+
export declare function generateProjectSkillsJson(projectDir: string, options?: {
|
|
51
|
+
dryRun?: boolean;
|
|
52
|
+
registryName?: string;
|
|
53
|
+
}): Promise<{
|
|
54
|
+
success: boolean;
|
|
55
|
+
path?: string;
|
|
56
|
+
skillCount: number;
|
|
57
|
+
pluginCount: number;
|
|
58
|
+
error?: string;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Generate a skills.json from all globally installed plugins.
|
|
62
|
+
* Writes to the global plugins directory.
|
|
63
|
+
*/
|
|
64
|
+
export declare function generateGlobalSkillsJson(options?: {
|
|
65
|
+
dryRun?: boolean;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
success: boolean;
|
|
68
|
+
path?: string;
|
|
69
|
+
skillCount: number;
|
|
70
|
+
pluginCount: number;
|
|
71
|
+
error?: string;
|
|
72
|
+
}>;
|
|
73
|
+
//# sourceMappingURL=agent-skills.d.ts.map
|