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.
Files changed (322) hide show
  1. package/README.md +45 -1
  2. package/ci-local/README.md +56 -11
  3. package/ci-local/ci-local.ps1 +445 -0
  4. package/ci-local/ci-local.sh +52 -9
  5. package/ci-local/docker/python.Dockerfile +7 -0
  6. package/ci-local/hooks/commit-msg +120 -44
  7. package/ci-local/hooks/commit-msg.test.sh +286 -0
  8. package/ci-local/hooks/pre-commit +9 -1
  9. package/ci-local/hooks/pre-push +13 -2
  10. package/ci-local/install.ps1 +367 -0
  11. package/ci-local/install.sh +151 -5
  12. package/ci-local/uninstall.ps1 +85 -0
  13. package/ci-local/uninstall.sh +95 -0
  14. package/dist/cli/dispatch/ci.d.ts +12 -0
  15. package/dist/cli/dispatch/ci.js +38 -0
  16. package/dist/cli/dispatch/security.d.ts +11 -0
  17. package/dist/cli/dispatch/security.js +68 -0
  18. package/dist/cli/dispatch/simple-renderers.d.ts +16 -0
  19. package/dist/cli/dispatch/simple-renderers.js +65 -0
  20. package/dist/cli/dispatch/skill-publish.d.ts +15 -0
  21. package/dist/cli/dispatch/skill-publish.js +44 -0
  22. package/dist/cli/dispatch/skills-cmd.d.ts +13 -0
  23. package/dist/cli/dispatch/skills-cmd.js +93 -0
  24. package/dist/cli/dispatch/tdd.d.ts +11 -0
  25. package/dist/cli/dispatch/tdd.js +45 -0
  26. package/dist/cli/dispatch/types.d.ts +29 -0
  27. package/dist/cli/dispatch/types.js +2 -0
  28. package/dist/cli/dispatch/workflow.d.ts +11 -0
  29. package/dist/cli/dispatch/workflow.js +67 -0
  30. package/dist/cli/help.d.ts +127 -0
  31. package/dist/cli/help.js +138 -0
  32. package/dist/cli/runtime.d.ts +16 -0
  33. package/dist/cli/runtime.js +38 -0
  34. package/dist/cli/validators.d.ts +17 -0
  35. package/dist/cli/validators.js +35 -0
  36. package/dist/commands/analyze.d.ts +1 -1
  37. package/dist/commands/analyze.js +14 -16
  38. package/dist/commands/ci.d.ts +50 -3
  39. package/dist/commands/ci.js +537 -182
  40. package/dist/commands/doctor.d.ts +2 -2
  41. package/dist/commands/doctor.js +112 -62
  42. package/dist/commands/init/report.d.ts +13 -0
  43. package/dist/commands/init/report.js +13 -0
  44. package/dist/commands/init/steps/agent-skills.d.ts +13 -0
  45. package/dist/commands/init/steps/agent-skills.js +45 -0
  46. package/dist/commands/init/steps/ai-sync.d.ts +15 -0
  47. package/dist/commands/init/steps/ai-sync.js +62 -0
  48. package/dist/commands/init/steps/ci.d.ts +24 -0
  49. package/dist/commands/init/steps/ci.js +74 -0
  50. package/dist/commands/init/steps/claude-md.d.ts +14 -0
  51. package/dist/commands/init/steps/claude-md.js +54 -0
  52. package/dist/commands/init/steps/code-graph.d.ts +16 -0
  53. package/dist/commands/init/steps/code-graph.js +68 -0
  54. package/dist/commands/init/steps/context-dir.d.ts +13 -0
  55. package/dist/commands/init/steps/context-dir.js +47 -0
  56. package/dist/commands/init/steps/docker-deploy.d.ts +16 -0
  57. package/dist/commands/init/steps/docker-deploy.js +60 -0
  58. package/dist/commands/init/steps/ghagga.d.ts +15 -0
  59. package/dist/commands/init/steps/ghagga.js +58 -0
  60. package/dist/commands/init/steps/git.d.ts +25 -0
  61. package/dist/commands/init/steps/git.js +81 -0
  62. package/dist/commands/init/steps/gitignore.d.ts +13 -0
  63. package/dist/commands/init/steps/gitignore.js +39 -0
  64. package/dist/commands/init/steps/local-ai.d.ts +14 -0
  65. package/dist/commands/init/steps/local-ai.js +53 -0
  66. package/dist/commands/init/steps/manifest.d.ts +13 -0
  67. package/dist/commands/init/steps/manifest.js +58 -0
  68. package/dist/commands/init/steps/memory.d.ts +15 -0
  69. package/dist/commands/init/steps/memory.js +60 -0
  70. package/dist/commands/init/steps/mock.d.ts +13 -0
  71. package/dist/commands/init/steps/mock.js +64 -0
  72. package/dist/commands/init/steps/sdd.d.ts +12 -0
  73. package/dist/commands/init/steps/sdd.js +40 -0
  74. package/dist/commands/init/steps/security.d.ts +28 -0
  75. package/dist/commands/init/steps/security.js +103 -0
  76. package/dist/commands/init/types.d.ts +30 -0
  77. package/dist/commands/init/types.js +2 -0
  78. package/dist/commands/init.d.ts +5 -4
  79. package/dist/commands/init.js +42 -387
  80. package/dist/commands/llmstxt.d.ts +1 -1
  81. package/dist/commands/llmstxt.js +36 -34
  82. package/dist/commands/plugin.d.ts +10 -1
  83. package/dist/commands/plugin.js +92 -47
  84. package/dist/commands/security.d.ts +11 -5
  85. package/dist/commands/security.js +236 -77
  86. package/dist/commands/skills/analysis.d.ts +19 -0
  87. package/dist/commands/skills/analysis.js +211 -0
  88. package/dist/commands/skills/benchmark.d.ts +6 -0
  89. package/dist/commands/skills/benchmark.js +82 -0
  90. package/dist/commands/skills/constants.d.ts +10 -0
  91. package/dist/commands/skills/constants.js +27 -0
  92. package/dist/commands/skills/directives.d.ts +21 -0
  93. package/dist/commands/skills/directives.js +101 -0
  94. package/dist/commands/skills/parsing.d.ts +16 -0
  95. package/dist/commands/skills/parsing.js +108 -0
  96. package/dist/commands/skills/rules.d.ts +5 -0
  97. package/dist/commands/skills/rules.js +32 -0
  98. package/dist/commands/skills/scoring.d.ts +47 -0
  99. package/dist/commands/skills/scoring.js +306 -0
  100. package/dist/commands/skills.d.ts +8 -69
  101. package/dist/commands/skills.js +9 -490
  102. package/dist/commands/tdd-pipeline.d.ts +17 -0
  103. package/dist/commands/tdd-pipeline.js +144 -0
  104. package/dist/commands/tdd.d.ts +1 -1
  105. package/dist/commands/tdd.js +21 -18
  106. package/dist/commands/workflow.d.ts +23 -0
  107. package/dist/commands/workflow.js +116 -0
  108. package/dist/constants.d.ts +15 -1
  109. package/dist/constants.js +161 -122
  110. package/dist/index.js +43 -280
  111. package/dist/lib/agent-skills.d.ts +36 -1
  112. package/dist/lib/agent-skills.js +168 -19
  113. package/dist/lib/atlassian-mcp.d.ts +42 -0
  114. package/dist/lib/atlassian-mcp.js +98 -0
  115. package/dist/lib/auto-skill-install.d.ts +37 -0
  116. package/dist/lib/auto-skill-install.js +91 -0
  117. package/dist/lib/auto-wire.d.ts +20 -0
  118. package/dist/lib/auto-wire.js +240 -0
  119. package/dist/lib/ci-config.d.ts +65 -0
  120. package/dist/lib/ci-config.js +297 -0
  121. package/dist/lib/claudemd.d.ts +13 -1
  122. package/dist/lib/claudemd.js +174 -24
  123. package/dist/lib/codex-export.d.ts +1 -1
  124. package/dist/lib/codex-export.js +26 -32
  125. package/dist/lib/common.d.ts +1 -1
  126. package/dist/lib/common.js +52 -44
  127. package/dist/lib/context.d.ts +17 -2
  128. package/dist/lib/context.js +142 -13
  129. package/dist/lib/crash-recovery.d.ts +34 -0
  130. package/dist/lib/crash-recovery.js +123 -0
  131. package/dist/lib/docker.d.ts +23 -2
  132. package/dist/lib/docker.js +168 -131
  133. package/dist/lib/exec.d.ts +10 -0
  134. package/dist/lib/exec.js +11 -0
  135. package/dist/lib/frontmatter.d.ts +1 -1
  136. package/dist/lib/frontmatter.js +29 -15
  137. package/dist/lib/harness-audit.d.ts +35 -0
  138. package/dist/lib/harness-audit.js +277 -0
  139. package/dist/lib/parallel-batch.d.ts +42 -0
  140. package/dist/lib/parallel-batch.js +88 -0
  141. package/dist/lib/plugin.d.ts +9 -3
  142. package/dist/lib/plugin.js +127 -70
  143. package/dist/lib/secret-scanner.d.ts +30 -0
  144. package/dist/lib/secret-scanner.js +274 -0
  145. package/dist/lib/security-analysis.d.ts +74 -0
  146. package/dist/lib/security-analysis.js +489 -0
  147. package/dist/lib/skill-publish.d.ts +40 -0
  148. package/dist/lib/skill-publish.js +164 -0
  149. package/dist/lib/skill-scanner.d.ts +63 -0
  150. package/dist/lib/skill-scanner.js +383 -0
  151. package/dist/lib/stack-detector.d.ts +38 -0
  152. package/dist/lib/stack-detector.js +207 -0
  153. package/dist/lib/supply-chain.d.ts +23 -0
  154. package/dist/lib/supply-chain.js +126 -0
  155. package/dist/lib/team-presets.d.ts +53 -0
  156. package/dist/lib/team-presets.js +201 -0
  157. package/dist/lib/template.d.ts +16 -1
  158. package/dist/lib/template.js +46 -17
  159. package/dist/lib/workflow/discovery.d.ts +27 -0
  160. package/dist/lib/workflow/discovery.js +85 -0
  161. package/dist/lib/workflow/index.d.ts +5 -0
  162. package/dist/lib/workflow/index.js +5 -0
  163. package/dist/lib/workflow/parser.d.ts +16 -0
  164. package/dist/lib/workflow/parser.js +198 -0
  165. package/dist/lib/workflow/renderer.d.ts +9 -0
  166. package/dist/lib/workflow/renderer.js +152 -0
  167. package/dist/lib/workflow/validator.d.ts +10 -0
  168. package/dist/lib/workflow/validator.js +189 -0
  169. package/dist/tasks/index.d.ts +4 -0
  170. package/dist/tasks/index.js +4 -0
  171. package/dist/tasks/scaffold-tasks.d.ts +3 -0
  172. package/dist/tasks/scaffold-tasks.js +14 -0
  173. package/dist/tasks/task-id.d.ts +30 -0
  174. package/dist/tasks/task-id.js +59 -0
  175. package/dist/tasks/task-tracker.d.ts +15 -0
  176. package/dist/tasks/task-tracker.js +81 -0
  177. package/dist/types/index.d.ts +134 -6
  178. package/dist/types/index.js +11 -1
  179. package/dist/ui/AnalyzeUI.d.ts +1 -1
  180. package/dist/ui/AnalyzeUI.js +38 -39
  181. package/dist/ui/App.d.ts +5 -3
  182. package/dist/ui/App.js +86 -46
  183. package/dist/ui/AutoSkills.d.ts +9 -0
  184. package/dist/ui/AutoSkills.js +124 -0
  185. package/dist/ui/CI.d.ts +2 -2
  186. package/dist/ui/CI.js +24 -26
  187. package/dist/ui/CIContext.d.ts +1 -1
  188. package/dist/ui/CIContext.js +2 -2
  189. package/dist/ui/CISelector.d.ts +2 -2
  190. package/dist/ui/CISelector.js +23 -15
  191. package/dist/ui/Doctor.d.ts +1 -1
  192. package/dist/ui/Doctor.js +35 -29
  193. package/dist/ui/Header.d.ts +1 -1
  194. package/dist/ui/Header.js +14 -14
  195. package/dist/ui/HookProfileSelector.d.ts +9 -0
  196. package/dist/ui/HookProfileSelector.js +54 -0
  197. package/dist/ui/LlmsTxt.d.ts +1 -1
  198. package/dist/ui/LlmsTxt.js +31 -22
  199. package/dist/ui/MemorySelector.d.ts +2 -2
  200. package/dist/ui/MemorySelector.js +28 -16
  201. package/dist/ui/NameInput.d.ts +1 -1
  202. package/dist/ui/NameInput.js +21 -21
  203. package/dist/ui/OptionSelector.d.ts +6 -2
  204. package/dist/ui/OptionSelector.js +83 -32
  205. package/dist/ui/Plugin.d.ts +4 -3
  206. package/dist/ui/Plugin.js +78 -35
  207. package/dist/ui/Progress.d.ts +3 -3
  208. package/dist/ui/Progress.js +23 -22
  209. package/dist/ui/Skills.d.ts +2 -2
  210. package/dist/ui/Skills.js +61 -32
  211. package/dist/ui/StackSelector.d.ts +2 -2
  212. package/dist/ui/StackSelector.js +27 -17
  213. package/dist/ui/Summary.d.ts +3 -3
  214. package/dist/ui/Summary.js +60 -50
  215. package/dist/ui/Welcome.d.ts +1 -1
  216. package/dist/ui/Welcome.js +15 -16
  217. package/dist/ui/theme.d.ts +1 -1
  218. package/dist/ui/theme.js +6 -6
  219. package/lib/common.psm1 +167 -0
  220. package/lib/common.sh +30 -0
  221. package/package.json +46 -24
  222. package/templates/common/atlassian/mcp-atlassian-snippet.json +16 -0
  223. package/templates/common/repoforge/mcp-repoforge-snippet.json +11 -0
  224. package/templates/common/repoforge/repoforge.yaml +34 -0
  225. package/templates/github/deploy-docker-zero-downtime.yml +140 -0
  226. package/templates/github/repoforge-graph.yml +45 -0
  227. package/templates/gitlab/deploy-docker-zero-downtime.yml +57 -0
  228. package/templates/local-ai/.env.example +17 -0
  229. package/templates/local-ai/docker-compose.yml +95 -0
  230. package/templates/security-hooks/claude-settings-security.json +30 -0
  231. package/templates/security-hooks/commit-msg-signing +29 -0
  232. package/templates/security-hooks/pre-commit-permissions +74 -0
  233. package/templates/security-hooks/pre-commit-secrets +74 -0
  234. package/templates/security-hooks/pre-push-branch-protection +62 -0
  235. package/templates/security-hooks/pre-push-deps +83 -0
  236. package/templates/security-hooks/pre-push-signing +67 -0
  237. package/templates/woodpecker/deploy-docker-zero-downtime.yml +50 -0
  238. package/templates/workflows/ci-pipeline.dot +15 -0
  239. package/templates/workflows/feature-flow.dot +21 -0
  240. package/templates/workflows/release.dot +16 -0
  241. package/dist/__integration__/helpers.d.ts +0 -20
  242. package/dist/__integration__/helpers.d.ts.map +0 -1
  243. package/dist/__integration__/helpers.js +0 -31
  244. package/dist/__integration__/helpers.js.map +0 -1
  245. package/dist/commands/analyze.d.ts.map +0 -1
  246. package/dist/commands/analyze.js.map +0 -1
  247. package/dist/commands/ci.d.ts.map +0 -1
  248. package/dist/commands/ci.js.map +0 -1
  249. package/dist/commands/doctor.d.ts.map +0 -1
  250. package/dist/commands/doctor.js.map +0 -1
  251. package/dist/commands/init.d.ts.map +0 -1
  252. package/dist/commands/init.js.map +0 -1
  253. package/dist/commands/llmstxt.d.ts.map +0 -1
  254. package/dist/commands/llmstxt.js.map +0 -1
  255. package/dist/commands/plugin.d.ts.map +0 -1
  256. package/dist/commands/plugin.js.map +0 -1
  257. package/dist/commands/security.d.ts.map +0 -1
  258. package/dist/commands/security.js.map +0 -1
  259. package/dist/commands/skills.d.ts.map +0 -1
  260. package/dist/commands/skills.js.map +0 -1
  261. package/dist/commands/tdd.d.ts.map +0 -1
  262. package/dist/commands/tdd.js.map +0 -1
  263. package/dist/constants.d.ts.map +0 -1
  264. package/dist/constants.js.map +0 -1
  265. package/dist/index.d.ts.map +0 -1
  266. package/dist/index.js.map +0 -1
  267. package/dist/lib/agent-skills.d.ts.map +0 -1
  268. package/dist/lib/agent-skills.js.map +0 -1
  269. package/dist/lib/claudemd.d.ts.map +0 -1
  270. package/dist/lib/claudemd.js.map +0 -1
  271. package/dist/lib/codex-export.d.ts.map +0 -1
  272. package/dist/lib/codex-export.js.map +0 -1
  273. package/dist/lib/common.d.ts.map +0 -1
  274. package/dist/lib/common.js.map +0 -1
  275. package/dist/lib/context.d.ts.map +0 -1
  276. package/dist/lib/context.js.map +0 -1
  277. package/dist/lib/docker.d.ts.map +0 -1
  278. package/dist/lib/docker.js.map +0 -1
  279. package/dist/lib/frontmatter.d.ts.map +0 -1
  280. package/dist/lib/frontmatter.js.map +0 -1
  281. package/dist/lib/plugin.d.ts.map +0 -1
  282. package/dist/lib/plugin.js.map +0 -1
  283. package/dist/lib/template.d.ts.map +0 -1
  284. package/dist/lib/template.js.map +0 -1
  285. package/dist/types/index.d.ts.map +0 -1
  286. package/dist/types/index.js.map +0 -1
  287. package/dist/ui/AnalyzeUI.d.ts.map +0 -1
  288. package/dist/ui/AnalyzeUI.js.map +0 -1
  289. package/dist/ui/App.d.ts.map +0 -1
  290. package/dist/ui/App.js.map +0 -1
  291. package/dist/ui/CI.d.ts.map +0 -1
  292. package/dist/ui/CI.js.map +0 -1
  293. package/dist/ui/CIContext.d.ts.map +0 -1
  294. package/dist/ui/CIContext.js.map +0 -1
  295. package/dist/ui/CISelector.d.ts.map +0 -1
  296. package/dist/ui/CISelector.js.map +0 -1
  297. package/dist/ui/Doctor.d.ts.map +0 -1
  298. package/dist/ui/Doctor.js.map +0 -1
  299. package/dist/ui/Header.d.ts.map +0 -1
  300. package/dist/ui/Header.js.map +0 -1
  301. package/dist/ui/LlmsTxt.d.ts.map +0 -1
  302. package/dist/ui/LlmsTxt.js.map +0 -1
  303. package/dist/ui/MemorySelector.d.ts.map +0 -1
  304. package/dist/ui/MemorySelector.js.map +0 -1
  305. package/dist/ui/NameInput.d.ts.map +0 -1
  306. package/dist/ui/NameInput.js.map +0 -1
  307. package/dist/ui/OptionSelector.d.ts.map +0 -1
  308. package/dist/ui/OptionSelector.js.map +0 -1
  309. package/dist/ui/Plugin.d.ts.map +0 -1
  310. package/dist/ui/Plugin.js.map +0 -1
  311. package/dist/ui/Progress.d.ts.map +0 -1
  312. package/dist/ui/Progress.js.map +0 -1
  313. package/dist/ui/Skills.d.ts.map +0 -1
  314. package/dist/ui/Skills.js.map +0 -1
  315. package/dist/ui/StackSelector.d.ts.map +0 -1
  316. package/dist/ui/StackSelector.js.map +0 -1
  317. package/dist/ui/Summary.d.ts.map +0 -1
  318. package/dist/ui/Summary.js.map +0 -1
  319. package/dist/ui/Welcome.d.ts.map +0 -1
  320. package/dist/ui/Welcome.js.map +0 -1
  321. package/dist/ui/theme.d.ts.map +0 -1
  322. package/dist/ui/theme.js.map +0 -1
@@ -1,10 +1,9 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import { execFile } from 'child_process';
4
- import { promisify } from 'util';
5
- import { PLUGINS_DIR, PLUGIN_MANIFEST_FILE, PLUGIN_ASSET_DIRS, PLUGIN_REGISTRY_URL } from '../constants.js';
6
- import { generateAgentSkillsManifest } from './agent-skills.js';
7
- const execFileAsync = promisify(execFile);
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { PLUGIN_ASSET_DIRS, PLUGIN_MANIFEST_FILE, PLUGIN_REGISTRY_URL, PLUGINS_DIR, } from "../constants.js";
4
+ import { generateAgentSkillsManifest } from "./agent-skills.js";
5
+ import { autoWirePlugins } from "./auto-wire.js";
6
+ import { execFileAsync } from "./exec.js";
8
7
  const KEBAB_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
9
8
  const SEMVER_RE = /^\d+\.\d+\.\d+$/;
10
9
  // ── Validation ──────────────────────────────────────────────────────────────
@@ -15,49 +14,60 @@ export async function validatePlugin(pluginDir) {
15
14
  const errors = [];
16
15
  // Check plugin.json exists
17
16
  const manifestPath = path.join(pluginDir, PLUGIN_MANIFEST_FILE);
18
- if (!await fs.pathExists(manifestPath)) {
17
+ if (!(await fs.pathExists(manifestPath))) {
19
18
  return {
20
19
  valid: false,
21
- errors: [{ path: PLUGIN_MANIFEST_FILE, message: 'plugin.json not found' }],
20
+ errors: [
21
+ { path: PLUGIN_MANIFEST_FILE, message: "plugin.json not found" },
22
+ ],
22
23
  manifest: null,
23
24
  };
24
25
  }
25
26
  // Parse manifest
26
27
  let manifest;
27
28
  try {
28
- manifest = await fs.readJson(manifestPath);
29
+ manifest = (await fs.readJson(manifestPath));
29
30
  }
30
31
  catch {
31
32
  return {
32
33
  valid: false,
33
- errors: [{ path: PLUGIN_MANIFEST_FILE, message: 'invalid JSON' }],
34
+ errors: [{ path: PLUGIN_MANIFEST_FILE, message: "invalid JSON" }],
34
35
  manifest: null,
35
36
  };
36
37
  }
37
38
  // Validate required fields
38
- if (typeof manifest.name !== 'string' || !manifest.name) {
39
- errors.push({ path: 'name', message: 'name is required' });
39
+ if (typeof manifest.name !== "string" || !manifest.name) {
40
+ errors.push({ path: "name", message: "name is required" });
40
41
  }
41
42
  else if (!KEBAB_RE.test(manifest.name)) {
42
- errors.push({ path: 'name', message: 'name must be kebab-case' });
43
+ errors.push({ path: "name", message: "name must be kebab-case" });
43
44
  }
44
45
  else if (manifest.name.length < 2 || manifest.name.length > 60) {
45
- errors.push({ path: 'name', message: 'name must be 2-60 characters' });
46
+ errors.push({ path: "name", message: "name must be 2-60 characters" });
46
47
  }
47
- if (typeof manifest.version !== 'string' || !manifest.version) {
48
- errors.push({ path: 'version', message: 'version is required' });
48
+ if (typeof manifest.version !== "string" || !manifest.version) {
49
+ errors.push({ path: "version", message: "version is required" });
49
50
  }
50
51
  else if (!SEMVER_RE.test(manifest.version)) {
51
- errors.push({ path: 'version', message: 'version must be semver (e.g. 1.0.0)' });
52
+ errors.push({
53
+ path: "version",
54
+ message: "version must be semver (e.g. 1.0.0)",
55
+ });
52
56
  }
53
- if (typeof manifest.description !== 'string' || !manifest.description) {
54
- errors.push({ path: 'description', message: 'description is required' });
57
+ if (typeof manifest.description !== "string" || !manifest.description) {
58
+ errors.push({ path: "description", message: "description is required" });
55
59
  }
56
60
  else if (manifest.description.length < 10) {
57
- errors.push({ path: 'description', message: 'description must be at least 10 characters' });
61
+ errors.push({
62
+ path: "description",
63
+ message: "description must be at least 10 characters",
64
+ });
58
65
  }
59
66
  else if (manifest.description.length > 200) {
60
- errors.push({ path: 'description', message: 'description must be at most 200 characters' });
67
+ errors.push({
68
+ path: "description",
69
+ message: "description must be at most 200 characters",
70
+ });
61
71
  }
62
72
  // Validate asset directories actually exist when declared
63
73
  for (const assetType of PLUGIN_ASSET_DIRS) {
@@ -65,26 +75,36 @@ export async function validatePlugin(pluginDir) {
65
75
  if (!Array.isArray(declared) || declared.length === 0)
66
76
  continue;
67
77
  const assetDir = path.join(pluginDir, assetType);
68
- if (!await fs.pathExists(assetDir)) {
69
- errors.push({ path: assetType, message: `declared ${assetType}/ directory not found` });
78
+ if (!(await fs.pathExists(assetDir))) {
79
+ errors.push({
80
+ path: assetType,
81
+ message: `declared ${assetType}/ directory not found`,
82
+ });
70
83
  continue;
71
84
  }
72
85
  // Check each declared asset exists
73
86
  for (const entry of declared) {
74
87
  const entryPath = path.join(assetDir, entry);
75
- if (!await fs.pathExists(entryPath)) {
76
- errors.push({ path: `${assetType}/${entry}`, message: `declared entry not found` });
88
+ if (!(await fs.pathExists(entryPath))) {
89
+ errors.push({
90
+ path: `${assetType}/${entry}`,
91
+ message: `declared entry not found`,
92
+ });
77
93
  }
78
94
  }
79
95
  }
80
96
  // Validate tags
81
97
  if (manifest.tags && !Array.isArray(manifest.tags)) {
82
- errors.push({ path: 'tags', message: 'tags must be an array' });
98
+ errors.push({ path: "tags", message: "tags must be an array" });
83
99
  }
84
100
  else if (manifest.tags && manifest.tags.length > 10) {
85
- errors.push({ path: 'tags', message: 'max 10 tags allowed' });
101
+ errors.push({ path: "tags", message: "max 10 tags allowed" });
86
102
  }
87
- return { valid: errors.length === 0, errors, manifest: errors.length === 0 ? manifest : manifest };
103
+ return {
104
+ valid: errors.length === 0,
105
+ errors,
106
+ manifest: errors.length === 0 ? manifest : manifest,
107
+ };
88
108
  }
89
109
  // ── Installation ────────────────────────────────────────────────────────────
90
110
  /**
@@ -96,23 +116,36 @@ export async function installPlugin(source, options = {}) {
96
116
  // Normalize source to a git URL
97
117
  const gitUrl = normalizeGitUrl(source);
98
118
  if (!gitUrl) {
99
- return { success: false, error: `invalid source: ${source}. Use org/repo or a GitHub URL` };
119
+ return {
120
+ success: false,
121
+ error: `invalid source: ${source}. Use org/repo or a GitHub URL`,
122
+ };
100
123
  }
101
124
  // Clone to temp
102
- const tmpDir = path.join(PLUGINS_DIR, '.tmp', `install-${Date.now()}`);
125
+ const tmpDir = path.join(PLUGINS_DIR, ".tmp", `install-${Date.now()}`);
103
126
  try {
104
127
  if (!dryRun) {
105
128
  await fs.ensureDir(tmpDir);
106
- await execFileAsync('git', ['clone', '--depth', '1', gitUrl, tmpDir], {
129
+ await execFileAsync("git", ["clone", "--depth", "1", gitUrl, tmpDir], {
107
130
  timeout: 60_000,
108
131
  });
109
132
  }
110
133
  // Validate
111
134
  const validation = dryRun
112
- ? { valid: true, errors: [], manifest: { name: source.split('/').pop() ?? 'unknown', version: '0.0.0', description: 'dry-run placeholder' } }
135
+ ? {
136
+ valid: true,
137
+ errors: [],
138
+ manifest: {
139
+ name: source.split("/").pop() ?? "unknown",
140
+ version: "0.0.0",
141
+ description: "dry-run placeholder",
142
+ },
143
+ }
113
144
  : await validatePlugin(tmpDir);
114
145
  if (!validation.valid || !validation.manifest) {
115
- const msgs = validation.errors.map(e => ` ${e.path}: ${e.message}`).join('\n');
146
+ const msgs = validation.errors
147
+ .map((e) => ` ${e.path}: ${e.message}`)
148
+ .join("\n");
116
149
  return { success: false, error: `validation failed:\n${msgs}` };
117
150
  }
118
151
  const pluginName = validation.manifest.name;
@@ -131,7 +164,7 @@ export async function installPlugin(source, options = {}) {
131
164
  source,
132
165
  manifest: validation.manifest,
133
166
  };
134
- await fs.writeJson(path.join(destDir, '.installed.json'), installedPlugin, { spaces: 2 });
167
+ await fs.writeJson(path.join(destDir, ".installed.json"), installedPlugin, { spaces: 2 });
135
168
  // Generate Agent Skills spec manifest for cross-agent compatibility
136
169
  await generateAgentSkillsManifest(destDir, source).catch(() => { });
137
170
  }
@@ -143,7 +176,7 @@ export async function installPlugin(source, options = {}) {
143
176
  }
144
177
  finally {
145
178
  // Clean up tmp if it still exists (error path)
146
- if (!dryRun && await fs.pathExists(tmpDir)) {
179
+ if (!dryRun && (await fs.pathExists(tmpDir))) {
147
180
  await fs.remove(tmpDir).catch(() => { });
148
181
  }
149
182
  }
@@ -153,7 +186,7 @@ export async function installPlugin(source, options = {}) {
153
186
  */
154
187
  export async function removePlugin(name, options = {}) {
155
188
  const pluginDir = path.join(PLUGINS_DIR, name);
156
- if (!await fs.pathExists(pluginDir)) {
189
+ if (!(await fs.pathExists(pluginDir))) {
157
190
  return { success: false, error: `plugin "${name}" is not installed` };
158
191
  }
159
192
  if (!options.dryRun) {
@@ -166,20 +199,22 @@ export async function removePlugin(name, options = {}) {
166
199
  * List all installed plugins.
167
200
  */
168
201
  export async function listInstalledPlugins() {
169
- if (!await fs.pathExists(PLUGINS_DIR))
202
+ if (!(await fs.pathExists(PLUGINS_DIR)))
170
203
  return [];
171
204
  const entries = await fs.readdir(PLUGINS_DIR);
172
205
  const plugins = [];
173
206
  for (const entry of entries) {
174
- if (entry.startsWith('.'))
207
+ if (entry.startsWith("."))
175
208
  continue;
176
- const metaPath = path.join(PLUGINS_DIR, entry, '.installed.json');
209
+ const metaPath = path.join(PLUGINS_DIR, entry, ".installed.json");
177
210
  if (await fs.pathExists(metaPath)) {
178
211
  try {
179
- const meta = await fs.readJson(metaPath);
212
+ const meta = (await fs.readJson(metaPath));
180
213
  plugins.push(meta);
181
214
  }
182
- catch { /* skip corrupt entries */ }
215
+ catch {
216
+ /* skip corrupt entries */
217
+ }
183
218
  }
184
219
  }
185
220
  return plugins;
@@ -193,13 +228,13 @@ export async function searchRegistry(query) {
193
228
  if (!response.ok) {
194
229
  return [];
195
230
  }
196
- const registry = await response.json();
231
+ const registry = (await response.json());
197
232
  let plugins = registry.plugins ?? [];
198
233
  if (query) {
199
234
  const q = query.toLowerCase();
200
- plugins = plugins.filter(p => p.id.toLowerCase().includes(q) ||
235
+ plugins = plugins.filter((p) => p.id.toLowerCase().includes(q) ||
201
236
  p.description.toLowerCase().includes(q) ||
202
- p.tags.some(t => t.toLowerCase().includes(q)));
237
+ p.tags.some((t) => t.toLowerCase().includes(q)));
203
238
  }
204
239
  return plugins;
205
240
  }
@@ -213,47 +248,59 @@ export async function searchRegistry(query) {
213
248
  * Returns an array of plugin names (sorted alphabetically).
214
249
  */
215
250
  export async function detectProjectPlugins(projectDir) {
216
- const pluginsDir = path.join(projectDir, '.javi-forge', 'plugins');
217
- if (!await fs.pathExists(pluginsDir))
251
+ const full = await detectProjectPluginsFull(projectDir);
252
+ return full.map((p) => p.name).sort();
253
+ }
254
+ /**
255
+ * Detect installed plugins with full metadata (including manifest).
256
+ * Used by auto-wiring to read plugin capabilities.
257
+ */
258
+ export async function detectProjectPluginsFull(projectDir) {
259
+ const pluginsDir = path.join(projectDir, ".javi-forge", "plugins");
260
+ if (!(await fs.pathExists(pluginsDir)))
218
261
  return [];
219
262
  const entries = await fs.readdir(pluginsDir);
220
263
  const plugins = [];
221
264
  for (const entry of entries) {
222
- if (entry.startsWith('.'))
265
+ if (entry.startsWith("."))
223
266
  continue;
224
- const metaPath = path.join(pluginsDir, entry, '.installed.json');
267
+ const metaPath = path.join(pluginsDir, entry, ".installed.json");
225
268
  if (await fs.pathExists(metaPath)) {
226
269
  try {
227
- const meta = await fs.readJson(metaPath);
270
+ const meta = (await fs.readJson(metaPath));
228
271
  if (meta.name) {
229
- plugins.push(meta.name);
272
+ plugins.push(meta);
230
273
  }
231
274
  }
232
- catch { /* skip corrupt entries */ }
275
+ catch {
276
+ /* skip corrupt entries */
277
+ }
233
278
  }
234
279
  }
235
- return plugins.sort();
280
+ return plugins.sort((a, b) => a.name.localeCompare(b.name));
236
281
  }
237
282
  /**
238
- * Sync detected plugins into the project manifest.
239
- * Returns a report of added, removed, and unchanged plugins.
283
+ * Sync detected plugins into the project manifest and auto-wire
284
+ * their capabilities into CLAUDE.md and .claude/settings.json.
285
+ * Returns a report of added, removed, unchanged, wired, and unwired plugins.
240
286
  */
241
287
  export async function syncPlugins(projectDir, options = {}) {
242
288
  const { dryRun = false } = options;
243
- const detected = await detectProjectPlugins(projectDir);
244
- const manifestPath = path.join(projectDir, '.javi-forge', 'manifest.json');
289
+ const detectedFull = await detectProjectPluginsFull(projectDir);
290
+ const detected = detectedFull.map((p) => p.name).sort();
291
+ const manifestPath = path.join(projectDir, ".javi-forge", "manifest.json");
245
292
  let manifest;
246
293
  if (await fs.pathExists(manifestPath)) {
247
- manifest = await fs.readJson(manifestPath);
294
+ manifest = (await fs.readJson(manifestPath));
248
295
  }
249
296
  else {
250
297
  // No manifest yet — treat current plugins as empty
251
298
  manifest = {
252
- version: '0.1.0',
299
+ version: "0.1.0",
253
300
  projectName: path.basename(projectDir),
254
- stack: 'node',
255
- ciProvider: 'github',
256
- memory: 'none',
301
+ stack: "node",
302
+ ciProvider: "github",
303
+ memory: "none",
257
304
  createdAt: new Date().toISOString(),
258
305
  updatedAt: new Date().toISOString(),
259
306
  modules: [],
@@ -261,16 +308,26 @@ export async function syncPlugins(projectDir, options = {}) {
261
308
  }
262
309
  const previous = new Set(manifest.plugins ?? []);
263
310
  const current = new Set(detected);
264
- const added = detected.filter(p => !previous.has(p));
265
- const removed = [...previous].filter(p => !current.has(p));
266
- const unchanged = detected.filter(p => previous.has(p));
311
+ const added = detected.filter((p) => !previous.has(p));
312
+ const removed = [...previous].filter((p) => !current.has(p));
313
+ const unchanged = detected.filter((p) => previous.has(p));
267
314
  if (!dryRun && (added.length > 0 || removed.length > 0)) {
268
315
  manifest.plugins = detected;
269
316
  manifest.updatedAt = new Date().toISOString();
270
317
  await fs.ensureDir(path.dirname(manifestPath));
271
318
  await fs.writeJson(manifestPath, manifest, { spaces: 2 });
272
319
  }
273
- return { added, removed, unchanged };
320
+ // ── Auto-wire plugin capabilities ──────────────────────────────────
321
+ const wireResult = await autoWirePlugins(projectDir, detectedFull, {
322
+ dryRun,
323
+ });
324
+ return {
325
+ added,
326
+ removed,
327
+ unchanged,
328
+ wired: wireResult.wired,
329
+ unwired: wireResult.unwired,
330
+ };
274
331
  }
275
332
  // ── Helpers ─────────────────────────────────────────────────────────────────
276
333
  /**
@@ -279,15 +336,15 @@ export async function syncPlugins(projectDir, options = {}) {
279
336
  */
280
337
  export function normalizeGitUrl(source) {
281
338
  // Already a full URL
282
- if (source.startsWith('https://github.com/')) {
283
- return source.endsWith('.git') ? source : `${source}.git`;
339
+ if (source.startsWith("https://github.com/")) {
340
+ return source.endsWith(".git") ? source : `${source}.git`;
284
341
  }
285
342
  // github.com/org/repo
286
- if (source.startsWith('github.com/')) {
343
+ if (source.startsWith("github.com/")) {
287
344
  return `https://${source}.git`;
288
345
  }
289
346
  // org/repo shorthand
290
- const parts = source.split('/');
347
+ const parts = source.split("/");
291
348
  if (parts.length === 2 && parts[0] && parts[1]) {
292
349
  return `https://github.com/${parts[0]}/${parts[1]}.git`;
293
350
  }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Secret scanner — detects leaked credentials in source files using
3
+ * a curated regex bundle. Designed for pre-commit hooks and CI gates.
4
+ */
5
+ export interface SecretPattern {
6
+ id: string;
7
+ label: string;
8
+ pattern: RegExp;
9
+ severity: "critical" | "high" | "medium";
10
+ }
11
+ export interface SecretFinding {
12
+ patternId: string;
13
+ label: string;
14
+ severity: SecretPattern["severity"];
15
+ file: string;
16
+ line: number;
17
+ match: string;
18
+ }
19
+ export interface ScanResult {
20
+ findings: SecretFinding[];
21
+ filesScanned: number;
22
+ patternsUsed: number;
23
+ }
24
+ export declare const SECRET_PATTERNS: SecretPattern[];
25
+ export declare function maskSecret(value: string): string;
26
+ export declare function shouldScanFile(filePath: string): boolean;
27
+ export declare function shouldSkipDir(dirName: string): boolean;
28
+ export declare function scanContent(content: string, filePath: string, patterns?: SecretPattern[]): SecretFinding[];
29
+ export declare function scanDirectory(dir: string, patterns?: SecretPattern[]): Promise<ScanResult>;
30
+ //# sourceMappingURL=secret-scanner.d.ts.map
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Secret scanner — detects leaked credentials in source files using
3
+ * a curated regex bundle. Designed for pre-commit hooks and CI gates.
4
+ */
5
+ import path from "node:path";
6
+ import fs from "fs-extra";
7
+ // ── Regex Bundle ──
8
+ export const SECRET_PATTERNS = [
9
+ // AWS
10
+ {
11
+ id: "aws-access-key",
12
+ label: "AWS Access Key ID",
13
+ pattern: /\bAKIA[0-9A-Z]{16}\b/,
14
+ severity: "critical",
15
+ },
16
+ {
17
+ id: "aws-secret-key",
18
+ label: "AWS Secret Access Key",
19
+ pattern: /(?<![A-Za-z0-9/+=])[A-Za-z0-9/+=]{40}(?![A-Za-z0-9/+=])/,
20
+ severity: "critical",
21
+ },
22
+ // Google
23
+ {
24
+ id: "gcp-api-key",
25
+ label: "Google API Key",
26
+ pattern: /\bAIza[0-9A-Za-z_-]{35}\b/,
27
+ severity: "high",
28
+ },
29
+ {
30
+ id: "gcp-oauth",
31
+ label: "Google OAuth Client Secret",
32
+ pattern: /\bGOCSPX-[A-Za-z0-9_-]{28}\b/,
33
+ severity: "critical",
34
+ },
35
+ {
36
+ id: "gcp-sa-key",
37
+ label: "GCP Service Account Key",
38
+ pattern: /"type":\s*"service_account"/,
39
+ severity: "critical",
40
+ },
41
+ // GitHub
42
+ {
43
+ id: "github-pat",
44
+ label: "GitHub Personal Access Token",
45
+ pattern: /\bghp_[A-Za-z0-9]{36}\b/,
46
+ severity: "critical",
47
+ },
48
+ {
49
+ id: "github-oauth",
50
+ label: "GitHub OAuth Token",
51
+ pattern: /\bgho_[A-Za-z0-9]{36}\b/,
52
+ severity: "critical",
53
+ },
54
+ {
55
+ id: "github-app-token",
56
+ label: "GitHub App Token",
57
+ pattern: /\bghu_[A-Za-z0-9]{36}\b/,
58
+ severity: "high",
59
+ },
60
+ {
61
+ id: "github-fine-pat",
62
+ label: "GitHub Fine-Grained PAT",
63
+ pattern: /\bgithub_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59}\b/,
64
+ severity: "critical",
65
+ },
66
+ // Stripe
67
+ {
68
+ id: "stripe-secret",
69
+ label: "Stripe Secret Key",
70
+ pattern: /\bsk_live_[A-Za-z0-9]{24,}\b/,
71
+ severity: "critical",
72
+ },
73
+ {
74
+ id: "stripe-restricted",
75
+ label: "Stripe Restricted Key",
76
+ pattern: /\brk_live_[A-Za-z0-9]{24,}\b/,
77
+ severity: "critical",
78
+ },
79
+ // Slack
80
+ {
81
+ id: "slack-bot-token",
82
+ label: "Slack Bot Token",
83
+ pattern: /\bxoxb-[0-9]{10,}-[0-9]{10,}-[A-Za-z0-9]{24,}\b/,
84
+ severity: "high",
85
+ },
86
+ {
87
+ id: "slack-webhook",
88
+ label: "Slack Webhook URL",
89
+ pattern: /https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]+/,
90
+ severity: "high",
91
+ },
92
+ // Generic
93
+ {
94
+ id: "private-key",
95
+ label: "Private Key",
96
+ pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
97
+ severity: "critical",
98
+ },
99
+ {
100
+ id: "jwt-token",
101
+ label: "JSON Web Token",
102
+ pattern: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/,
103
+ severity: "high",
104
+ },
105
+ {
106
+ id: "bearer-token",
107
+ label: "Bearer Token",
108
+ pattern: /[Bb]earer\s+[A-Za-z0-9_-]{20,}/,
109
+ severity: "medium",
110
+ },
111
+ {
112
+ id: "password-assign",
113
+ label: "Password Assignment",
114
+ pattern: /(?:password|passwd|pwd|secret)\s*[:=]\s*['"][^'"]{8,}['"]/,
115
+ severity: "high",
116
+ },
117
+ // Cloud providers
118
+ {
119
+ id: "azure-connection",
120
+ label: "Azure Connection String",
121
+ pattern: /DefaultEndpointsProtocol=https;AccountName=[^;]+;AccountKey=[A-Za-z0-9+/=]+/,
122
+ severity: "critical",
123
+ },
124
+ {
125
+ id: "sendgrid-key",
126
+ label: "SendGrid API Key",
127
+ pattern: /\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}\b/,
128
+ severity: "high",
129
+ },
130
+ {
131
+ id: "twilio-key",
132
+ label: "Twilio API Key",
133
+ pattern: /\bSK[0-9a-fA-F]{32}\b/,
134
+ severity: "high",
135
+ },
136
+ // Database
137
+ {
138
+ id: "db-connection",
139
+ label: "Database Connection String",
140
+ pattern: /(?:mongodb|postgres|mysql|redis):\/\/[^:\s]+:[^@\s]+@[^\s]+/,
141
+ severity: "critical",
142
+ },
143
+ // Anthropic / OpenAI
144
+ {
145
+ id: "anthropic-key",
146
+ label: "Anthropic API Key",
147
+ pattern: /\bsk-ant-api03-[A-Za-z0-9_-]{93}\b/,
148
+ severity: "critical",
149
+ },
150
+ {
151
+ id: "openai-key",
152
+ label: "OpenAI API Key",
153
+ pattern: /\bsk-[A-Za-z0-9]{48,}\b/,
154
+ severity: "critical",
155
+ },
156
+ ];
157
+ // ── Masking ──
158
+ export function maskSecret(value) {
159
+ if (value.length <= 8)
160
+ return "****";
161
+ return `${value.slice(0, 4)}...${value.slice(-2)}`;
162
+ }
163
+ // ── File filtering ──
164
+ const BINARY_EXTENSIONS = new Set([
165
+ ".png",
166
+ ".jpg",
167
+ ".jpeg",
168
+ ".gif",
169
+ ".ico",
170
+ ".svg",
171
+ ".woff",
172
+ ".woff2",
173
+ ".ttf",
174
+ ".eot",
175
+ ".mp3",
176
+ ".mp4",
177
+ ".zip",
178
+ ".gz",
179
+ ".tar",
180
+ ".pdf",
181
+ ".exe",
182
+ ".dll",
183
+ ".so",
184
+ ".dylib",
185
+ ".o",
186
+ ".a",
187
+ ]);
188
+ const SKIP_DIRS = new Set([
189
+ "node_modules",
190
+ ".git",
191
+ "dist",
192
+ "build",
193
+ ".next",
194
+ "__pycache__",
195
+ "target",
196
+ "vendor",
197
+ ".venv",
198
+ "venv",
199
+ ".tox",
200
+ ".mypy_cache",
201
+ "coverage",
202
+ ".nyc_output",
203
+ ".cache",
204
+ ]);
205
+ export function shouldScanFile(filePath) {
206
+ const ext = path.extname(filePath).toLowerCase();
207
+ if (BINARY_EXTENSIONS.has(ext))
208
+ return false;
209
+ if (filePath.includes("lock") && (ext === ".json" || ext === ".yaml"))
210
+ return false;
211
+ return true;
212
+ }
213
+ export function shouldSkipDir(dirName) {
214
+ return SKIP_DIRS.has(dirName);
215
+ }
216
+ // ── Scanner ──
217
+ export function scanContent(content, filePath, patterns = SECRET_PATTERNS) {
218
+ const findings = [];
219
+ const lines = content.split("\n");
220
+ for (let i = 0; i < lines.length; i++) {
221
+ const line = lines[i];
222
+ if (line === undefined)
223
+ continue;
224
+ for (const pat of patterns) {
225
+ const match = pat.pattern.exec(line);
226
+ if (match) {
227
+ findings.push({
228
+ patternId: pat.id,
229
+ label: pat.label,
230
+ severity: pat.severity,
231
+ file: filePath,
232
+ line: i + 1,
233
+ match: maskSecret(match[0]),
234
+ });
235
+ }
236
+ }
237
+ }
238
+ return findings;
239
+ }
240
+ async function collectFiles(dir) {
241
+ const results = [];
242
+ const entries = await fs.readdir(dir, { withFileTypes: true });
243
+ for (const entry of entries) {
244
+ if (entry.isDirectory()) {
245
+ if (!shouldSkipDir(entry.name)) {
246
+ results.push(...(await collectFiles(path.join(dir, entry.name))));
247
+ }
248
+ }
249
+ else if (entry.isFile() && shouldScanFile(entry.name)) {
250
+ results.push(path.join(dir, entry.name));
251
+ }
252
+ }
253
+ return results;
254
+ }
255
+ export async function scanDirectory(dir, patterns = SECRET_PATTERNS) {
256
+ const files = await collectFiles(dir);
257
+ const allFindings = [];
258
+ for (const file of files) {
259
+ try {
260
+ const content = await fs.readFile(file, "utf-8");
261
+ const rel = path.relative(dir, file);
262
+ allFindings.push(...scanContent(content, rel, patterns));
263
+ }
264
+ catch {
265
+ // Skip unreadable files
266
+ }
267
+ }
268
+ return {
269
+ findings: allFindings,
270
+ filesScanned: files.length,
271
+ patternsUsed: patterns.length,
272
+ };
273
+ }
274
+ //# sourceMappingURL=secret-scanner.js.map