javi-forge 1.6.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/dist/commands/analyze.d.ts +1 -1
  2. package/dist/commands/analyze.js +15 -15
  3. package/dist/commands/atlassian-mcp.d.ts +42 -0
  4. package/dist/commands/atlassian-mcp.js +98 -0
  5. package/dist/commands/ci.d.ts +3 -3
  6. package/dist/commands/ci.js +185 -147
  7. package/dist/commands/crash-recovery.d.ts +34 -0
  8. package/dist/commands/crash-recovery.js +123 -0
  9. package/dist/commands/doctor.d.ts +2 -2
  10. package/dist/commands/doctor.js +113 -61
  11. package/dist/commands/harness-audit.d.ts +35 -0
  12. package/dist/commands/harness-audit.js +277 -0
  13. package/dist/commands/init.d.ts +1 -1
  14. package/dist/commands/init.js +384 -141
  15. package/dist/commands/llmstxt.d.ts +1 -1
  16. package/dist/commands/llmstxt.js +36 -34
  17. package/dist/commands/parallel-batch.d.ts +42 -0
  18. package/dist/commands/parallel-batch.js +90 -0
  19. package/dist/commands/plugin.d.ts +10 -1
  20. package/dist/commands/plugin.js +92 -47
  21. package/dist/commands/secret-scanner.d.ts +30 -0
  22. package/dist/commands/secret-scanner.js +272 -0
  23. package/dist/commands/security-analysis.d.ts +74 -0
  24. package/dist/commands/security-analysis.js +487 -0
  25. package/dist/commands/security.d.ts +11 -5
  26. package/dist/commands/security.js +216 -76
  27. package/dist/commands/skill-scanner.d.ts +63 -0
  28. package/dist/commands/skill-scanner.js +383 -0
  29. package/dist/commands/skills.d.ts +62 -5
  30. package/dist/commands/skills.js +439 -54
  31. package/dist/commands/supply-chain.d.ts +23 -0
  32. package/dist/commands/supply-chain.js +126 -0
  33. package/dist/commands/tdd-pipeline.d.ts +17 -0
  34. package/dist/commands/tdd-pipeline.js +144 -0
  35. package/dist/commands/tdd.d.ts +1 -1
  36. package/dist/commands/tdd.js +21 -18
  37. package/dist/commands/team-presets.d.ts +53 -0
  38. package/dist/commands/team-presets.js +201 -0
  39. package/dist/commands/workflow.d.ts +23 -0
  40. package/dist/commands/workflow.js +114 -0
  41. package/dist/constants.d.ts +15 -1
  42. package/dist/constants.js +161 -122
  43. package/dist/index.js +308 -98
  44. package/dist/lib/agent-skills.d.ts +36 -1
  45. package/dist/lib/agent-skills.js +168 -19
  46. package/dist/lib/auto-skill-install.d.ts +37 -0
  47. package/dist/lib/auto-skill-install.js +92 -0
  48. package/dist/lib/auto-wire.d.ts +20 -0
  49. package/dist/lib/auto-wire.js +240 -0
  50. package/dist/lib/claudemd.d.ts +13 -1
  51. package/dist/lib/claudemd.js +174 -24
  52. package/dist/lib/codex-export.d.ts +1 -1
  53. package/dist/lib/codex-export.js +29 -31
  54. package/dist/lib/common.d.ts +1 -1
  55. package/dist/lib/common.js +52 -44
  56. package/dist/lib/context.d.ts +17 -2
  57. package/dist/lib/context.js +142 -13
  58. package/dist/lib/docker.d.ts +1 -1
  59. package/dist/lib/docker.js +141 -112
  60. package/dist/lib/frontmatter.d.ts +1 -1
  61. package/dist/lib/frontmatter.js +29 -15
  62. package/dist/lib/plugin.d.ts +9 -3
  63. package/dist/lib/plugin.js +128 -69
  64. package/dist/lib/skill-publish.d.ts +40 -0
  65. package/dist/lib/skill-publish.js +146 -0
  66. package/dist/lib/stack-detector.d.ts +38 -0
  67. package/dist/lib/stack-detector.js +207 -0
  68. package/dist/lib/template.d.ts +16 -1
  69. package/dist/lib/template.js +46 -17
  70. package/dist/lib/workflow/discovery.d.ts +19 -0
  71. package/dist/lib/workflow/discovery.js +68 -0
  72. package/dist/lib/workflow/index.d.ts +5 -0
  73. package/dist/lib/workflow/index.js +5 -0
  74. package/dist/lib/workflow/parser.d.ts +16 -0
  75. package/dist/lib/workflow/parser.js +198 -0
  76. package/dist/lib/workflow/renderer.d.ts +9 -0
  77. package/dist/lib/workflow/renderer.js +152 -0
  78. package/dist/lib/workflow/validator.d.ts +10 -0
  79. package/dist/lib/workflow/validator.js +189 -0
  80. package/dist/tasks/index.d.ts +4 -0
  81. package/dist/tasks/index.js +4 -0
  82. package/dist/tasks/scaffold-tasks.d.ts +3 -0
  83. package/dist/tasks/scaffold-tasks.js +14 -0
  84. package/dist/tasks/task-id.d.ts +30 -0
  85. package/dist/tasks/task-id.js +55 -0
  86. package/dist/tasks/task-tracker.d.ts +15 -0
  87. package/dist/tasks/task-tracker.js +81 -0
  88. package/dist/types/index.d.ts +134 -6
  89. package/dist/types/index.js +11 -1
  90. package/dist/ui/AnalyzeUI.d.ts +1 -1
  91. package/dist/ui/AnalyzeUI.js +38 -39
  92. package/dist/ui/App.d.ts +5 -3
  93. package/dist/ui/App.js +86 -46
  94. package/dist/ui/AutoSkills.d.ts +9 -0
  95. package/dist/ui/AutoSkills.js +124 -0
  96. package/dist/ui/CI.d.ts +2 -2
  97. package/dist/ui/CI.js +24 -26
  98. package/dist/ui/CIContext.d.ts +1 -1
  99. package/dist/ui/CIContext.js +3 -2
  100. package/dist/ui/CISelector.d.ts +2 -2
  101. package/dist/ui/CISelector.js +23 -15
  102. package/dist/ui/Doctor.d.ts +1 -1
  103. package/dist/ui/Doctor.js +35 -29
  104. package/dist/ui/Header.d.ts +1 -1
  105. package/dist/ui/Header.js +14 -14
  106. package/dist/ui/HookProfileSelector.d.ts +9 -0
  107. package/dist/ui/HookProfileSelector.js +54 -0
  108. package/dist/ui/LlmsTxt.d.ts +1 -1
  109. package/dist/ui/LlmsTxt.js +31 -22
  110. package/dist/ui/MemorySelector.d.ts +2 -2
  111. package/dist/ui/MemorySelector.js +28 -16
  112. package/dist/ui/NameInput.d.ts +1 -1
  113. package/dist/ui/NameInput.js +21 -21
  114. package/dist/ui/OptionSelector.d.ts +6 -2
  115. package/dist/ui/OptionSelector.js +83 -32
  116. package/dist/ui/Plugin.d.ts +4 -3
  117. package/dist/ui/Plugin.js +78 -35
  118. package/dist/ui/Progress.d.ts +3 -3
  119. package/dist/ui/Progress.js +23 -22
  120. package/dist/ui/Skills.d.ts +2 -2
  121. package/dist/ui/Skills.js +61 -32
  122. package/dist/ui/StackSelector.d.ts +2 -2
  123. package/dist/ui/StackSelector.js +26 -16
  124. package/dist/ui/Summary.d.ts +3 -3
  125. package/dist/ui/Summary.js +60 -50
  126. package/dist/ui/Welcome.d.ts +1 -1
  127. package/dist/ui/Welcome.js +15 -16
  128. package/dist/ui/theme.d.ts +1 -1
  129. package/dist/ui/theme.js +6 -6
  130. package/package.json +9 -6
  131. package/templates/common/atlassian/mcp-atlassian-snippet.json +16 -0
  132. package/templates/common/repoforge/mcp-repoforge-snippet.json +11 -0
  133. package/templates/common/repoforge/repoforge.yaml +34 -0
  134. package/templates/github/deploy-docker-zero-downtime.yml +140 -0
  135. package/templates/github/repoforge-graph.yml +45 -0
  136. package/templates/gitlab/deploy-docker-zero-downtime.yml +57 -0
  137. package/templates/local-ai/.env.example +17 -0
  138. package/templates/local-ai/docker-compose.yml +95 -0
  139. package/templates/security-hooks/claude-settings-security.json +30 -0
  140. package/templates/security-hooks/commit-msg-signing +29 -0
  141. package/templates/security-hooks/pre-commit-permissions +74 -0
  142. package/templates/security-hooks/pre-commit-secrets +74 -0
  143. package/templates/security-hooks/pre-push-branch-protection +62 -0
  144. package/templates/security-hooks/pre-push-deps +83 -0
  145. package/templates/security-hooks/pre-push-signing +67 -0
  146. package/templates/woodpecker/deploy-docker-zero-downtime.yml +50 -0
  147. package/templates/workflows/ci-pipeline.dot +15 -0
  148. package/templates/workflows/feature-flow.dot +21 -0
  149. package/templates/workflows/release.dot +16 -0
  150. package/dist/__integration__/helpers.d.ts +0 -20
  151. package/dist/__integration__/helpers.d.ts.map +0 -1
  152. package/dist/__integration__/helpers.js +0 -31
  153. package/dist/__integration__/helpers.js.map +0 -1
  154. package/dist/commands/analyze.d.ts.map +0 -1
  155. package/dist/commands/analyze.js.map +0 -1
  156. package/dist/commands/ci.d.ts.map +0 -1
  157. package/dist/commands/ci.js.map +0 -1
  158. package/dist/commands/doctor.d.ts.map +0 -1
  159. package/dist/commands/doctor.js.map +0 -1
  160. package/dist/commands/init.d.ts.map +0 -1
  161. package/dist/commands/init.js.map +0 -1
  162. package/dist/commands/llmstxt.d.ts.map +0 -1
  163. package/dist/commands/llmstxt.js.map +0 -1
  164. package/dist/commands/plugin.d.ts.map +0 -1
  165. package/dist/commands/plugin.js.map +0 -1
  166. package/dist/commands/security.d.ts.map +0 -1
  167. package/dist/commands/security.js.map +0 -1
  168. package/dist/commands/skills.d.ts.map +0 -1
  169. package/dist/commands/skills.js.map +0 -1
  170. package/dist/commands/tdd.d.ts.map +0 -1
  171. package/dist/commands/tdd.js.map +0 -1
  172. package/dist/constants.d.ts.map +0 -1
  173. package/dist/constants.js.map +0 -1
  174. package/dist/index.d.ts.map +0 -1
  175. package/dist/index.js.map +0 -1
  176. package/dist/lib/agent-skills.d.ts.map +0 -1
  177. package/dist/lib/agent-skills.js.map +0 -1
  178. package/dist/lib/claudemd.d.ts.map +0 -1
  179. package/dist/lib/claudemd.js.map +0 -1
  180. package/dist/lib/codex-export.d.ts.map +0 -1
  181. package/dist/lib/codex-export.js.map +0 -1
  182. package/dist/lib/common.d.ts.map +0 -1
  183. package/dist/lib/common.js.map +0 -1
  184. package/dist/lib/context.d.ts.map +0 -1
  185. package/dist/lib/context.js.map +0 -1
  186. package/dist/lib/docker.d.ts.map +0 -1
  187. package/dist/lib/docker.js.map +0 -1
  188. package/dist/lib/frontmatter.d.ts.map +0 -1
  189. package/dist/lib/frontmatter.js.map +0 -1
  190. package/dist/lib/plugin.d.ts.map +0 -1
  191. package/dist/lib/plugin.js.map +0 -1
  192. package/dist/lib/template.d.ts.map +0 -1
  193. package/dist/lib/template.js.map +0 -1
  194. package/dist/types/index.d.ts.map +0 -1
  195. package/dist/types/index.js.map +0 -1
  196. package/dist/ui/AnalyzeUI.d.ts.map +0 -1
  197. package/dist/ui/AnalyzeUI.js.map +0 -1
  198. package/dist/ui/App.d.ts.map +0 -1
  199. package/dist/ui/App.js.map +0 -1
  200. package/dist/ui/CI.d.ts.map +0 -1
  201. package/dist/ui/CI.js.map +0 -1
  202. package/dist/ui/CIContext.d.ts.map +0 -1
  203. package/dist/ui/CIContext.js.map +0 -1
  204. package/dist/ui/CISelector.d.ts.map +0 -1
  205. package/dist/ui/CISelector.js.map +0 -1
  206. package/dist/ui/Doctor.d.ts.map +0 -1
  207. package/dist/ui/Doctor.js.map +0 -1
  208. package/dist/ui/Header.d.ts.map +0 -1
  209. package/dist/ui/Header.js.map +0 -1
  210. package/dist/ui/LlmsTxt.d.ts.map +0 -1
  211. package/dist/ui/LlmsTxt.js.map +0 -1
  212. package/dist/ui/MemorySelector.d.ts.map +0 -1
  213. package/dist/ui/MemorySelector.js.map +0 -1
  214. package/dist/ui/NameInput.d.ts.map +0 -1
  215. package/dist/ui/NameInput.js.map +0 -1
  216. package/dist/ui/OptionSelector.d.ts.map +0 -1
  217. package/dist/ui/OptionSelector.js.map +0 -1
  218. package/dist/ui/Plugin.d.ts.map +0 -1
  219. package/dist/ui/Plugin.js.map +0 -1
  220. package/dist/ui/Progress.d.ts.map +0 -1
  221. package/dist/ui/Progress.js.map +0 -1
  222. package/dist/ui/Skills.d.ts.map +0 -1
  223. package/dist/ui/Skills.js.map +0 -1
  224. package/dist/ui/StackSelector.d.ts.map +0 -1
  225. package/dist/ui/StackSelector.js.map +0 -1
  226. package/dist/ui/Summary.d.ts.map +0 -1
  227. package/dist/ui/Summary.js.map +0 -1
  228. package/dist/ui/Welcome.d.ts.map +0 -1
  229. package/dist/ui/Welcome.js.map +0 -1
  230. package/dist/ui/theme.d.ts.map +0 -1
  231. package/dist/ui/theme.js.map +0 -1
@@ -1,12 +1,24 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text, useInput } from 'ink';
3
- import { theme } from './theme.js';
4
- import { useCIMode } from './CIContext.js';
1
+ import { Box, Text, useInput } from "ink";
2
+ import React, { useEffect, useState } from "react";
3
+ import { useCIMode } from "./CIContext.js";
4
+ import { theme } from "./theme.js";
5
5
  const MEMORY_OPTIONS = [
6
- { id: 'engram', label: 'Engram', description: 'SQLite-backed persistent memory with MCP' },
7
- { id: 'obsidian-brain', label: 'Obsidian Brain', description: 'Obsidian vault with Kanban + Dataview' },
8
- { id: 'memory-simple', label: 'Simple Memory', description: 'File-based .project/ memory' },
9
- { id: 'none', label: 'None', description: 'Skip memory module' },
6
+ {
7
+ id: "engram",
8
+ label: "Engram",
9
+ description: "SQLite-backed persistent memory with MCP",
10
+ },
11
+ {
12
+ id: "obsidian-brain",
13
+ label: "Obsidian Brain",
14
+ description: "Obsidian vault with Kanban + Dataview",
15
+ },
16
+ {
17
+ id: "memory-simple",
18
+ label: "Simple Memory",
19
+ description: "File-based .project/ memory",
20
+ },
21
+ { id: "none", label: "None", description: "Skip memory module" },
10
22
  ];
11
23
  export default function MemorySelector({ onConfirm }) {
12
24
  const isCI = useCIMode();
@@ -19,9 +31,9 @@ export default function MemorySelector({ onConfirm }) {
19
31
  }, [isCI]); // eslint-disable-line react-hooks/exhaustive-deps
20
32
  useInput((_, key) => {
21
33
  if (key.upArrow)
22
- setCursor(c => Math.max(0, c - 1));
34
+ setCursor((c) => Math.max(0, c - 1));
23
35
  if (key.downArrow)
24
- setCursor(c => Math.min(MEMORY_OPTIONS.length - 1, c + 1));
36
+ setCursor((c) => Math.min(MEMORY_OPTIONS.length - 1, c + 1));
25
37
  if (key.return) {
26
38
  onConfirm(MEMORY_OPTIONS[cursor].id);
27
39
  }
@@ -29,18 +41,18 @@ export default function MemorySelector({ onConfirm }) {
29
41
  return (React.createElement(Box, { flexDirection: "column" },
30
42
  React.createElement(Text, { bold: true }, "Select memory module:"),
31
43
  React.createElement(Box, { marginTop: 1, flexDirection: "column", borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, borderColor: theme.muted, paddingLeft: 1 }, MEMORY_OPTIONS.map((m, i) => (React.createElement(Box, { key: m.id },
32
- React.createElement(Text, { color: i === cursor ? theme.primary : 'white' },
33
- i === cursor ? '\u25b6 ' : ' ',
34
- i === cursor ? '\u25c9' : '\u25cb',
44
+ React.createElement(Text, { color: i === cursor ? theme.primary : "white" },
45
+ i === cursor ? "\u25b6 " : " ",
46
+ i === cursor ? "\u25c9" : "\u25cb",
35
47
  " ",
36
48
  m.label),
37
49
  React.createElement(Text, { color: theme.muted, dimColor: true },
38
- " ",
50
+ " ",
39
51
  m.description))))),
40
52
  React.createElement(Box, { marginTop: 1, gap: 2 },
41
53
  React.createElement(Text, { color: theme.primary }, MEMORY_OPTIONS[cursor].label),
42
54
  React.createElement(Text, { color: theme.muted, dimColor: true },
43
- '\u2191\u2193',
44
- " navigate Enter confirm"))));
55
+ "\u2191\u2193",
56
+ " navigate Enter confirm"))));
45
57
  }
46
58
  //# sourceMappingURL=MemorySelector.js.map
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React from "react";
2
2
  interface Props {
3
3
  defaultName: string;
4
4
  onConfirm: (name: string, dir: string) => void;
@@ -1,20 +1,20 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text, useInput } from 'ink';
3
- import path from 'path';
4
- import { theme } from './theme.js';
5
- import { useCIMode } from './CIContext.js';
1
+ import { Box, Text, useInput } from "ink";
2
+ import path from "path";
3
+ import React, { useEffect, useState } from "react";
4
+ import { useCIMode } from "./CIContext.js";
5
+ import { theme } from "./theme.js";
6
6
  const VALID_NAME = /^[a-z0-9][a-z0-9._-]*$/;
7
7
  function validateName(name) {
8
8
  if (name.length === 0)
9
- return 'Name cannot be empty';
9
+ return "Name cannot be empty";
10
10
  if (name !== name.toLowerCase())
11
- return 'Use lowercase only';
12
- if (name.includes(' '))
13
- return 'No spaces allowed (use - or _)';
11
+ return "Use lowercase only";
12
+ if (name.includes(" "))
13
+ return "No spaces allowed (use - or _)";
14
14
  if (!VALID_NAME.test(name))
15
- return 'Use lowercase letters, numbers, hyphens, dots';
15
+ return "Use lowercase letters, numbers, hyphens, dots";
16
16
  if (name.length > 60)
17
- return 'Name too long (max 60 chars)';
17
+ return "Name too long (max 60 chars)";
18
18
  return null;
19
19
  }
20
20
  export default function NameInput({ defaultName, onConfirm }) {
@@ -23,7 +23,7 @@ export default function NameInput({ defaultName, onConfirm }) {
23
23
  const trimmed = value.trim();
24
24
  const error = validateName(trimmed);
25
25
  const isValid = error === null && trimmed.length > 0;
26
- const targetDir = path.resolve(process.cwd(), trimmed || '.');
26
+ const targetDir = path.resolve(process.cwd(), trimmed || ".");
27
27
  // Auto-confirm in CI mode
28
28
  useEffect(() => {
29
29
  if (isCI && isValid) {
@@ -35,33 +35,33 @@ export default function NameInput({ defaultName, onConfirm }) {
35
35
  onConfirm(trimmed, targetDir);
36
36
  }
37
37
  else if (key.backspace || key.delete) {
38
- setValue(v => v.slice(0, -1));
38
+ setValue((v) => v.slice(0, -1));
39
39
  }
40
40
  else if (input && !key.ctrl && !key.meta) {
41
- setValue(v => v + input);
41
+ setValue((v) => v + input);
42
42
  }
43
43
  }, { isActive: !isCI });
44
44
  return (React.createElement(Box, { flexDirection: "column" },
45
45
  React.createElement(Text, { bold: true }, "Project name:"),
46
46
  React.createElement(Box, { marginTop: 1 },
47
47
  React.createElement(Text, { color: isValid ? theme.primary : theme.error },
48
- '\u25b6',
48
+ "\u25b6",
49
49
  " "),
50
50
  React.createElement(Text, null, value),
51
- React.createElement(Text, { color: theme.muted }, '\u2588')),
51
+ React.createElement(Text, { color: theme.muted }, "\u2588")),
52
52
  trimmed.length > 0 && error && (React.createElement(Box, { marginTop: 1 },
53
53
  React.createElement(Text, { color: theme.error },
54
- " ",
55
- '\u2717',
54
+ " ",
55
+ "\u2717",
56
56
  " ",
57
57
  error))),
58
58
  isValid && (React.createElement(Box, { marginTop: 1 },
59
59
  React.createElement(Text, { color: theme.success },
60
- " ",
61
- '\u2713',
60
+ " ",
61
+ "\u2713",
62
62
  " Valid name"))),
63
63
  React.createElement(Box, { marginTop: 1 },
64
- React.createElement(Text, { color: theme.muted }, " Will create: "),
64
+ React.createElement(Text, { color: theme.muted }, " Will create: "),
65
65
  React.createElement(Text, { color: isValid ? theme.primary : theme.muted, dimColor: !isValid }, targetDir)),
66
66
  React.createElement(Box, { marginTop: 1 },
67
67
  React.createElement(Text, { color: theme.muted, dimColor: true }, "Enter to confirm"))));
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React from "react";
2
2
  interface Props {
3
3
  onConfirm: (selected: {
4
4
  aiSync: boolean;
@@ -6,9 +6,13 @@ interface Props {
6
6
  contextDir: boolean;
7
7
  claudeMd: boolean;
8
8
  ghagga: boolean;
9
+ securityHooks: boolean;
10
+ codeGraph: boolean;
11
+ localAi: boolean;
9
12
  }) => void;
10
13
  presetGhagga?: boolean;
14
+ presetLocalAi?: boolean;
11
15
  }
12
- export default function OptionSelector({ onConfirm, presetGhagga }: Props): React.JSX.Element;
16
+ export default function OptionSelector({ onConfirm, presetGhagga, presetLocalAi, }: Props): React.JSX.Element;
13
17
  export {};
14
18
  //# sourceMappingURL=OptionSelector.d.ts.map
@@ -1,43 +1,91 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text, useInput } from 'ink';
3
- import { theme } from './theme.js';
4
- import { useCIMode } from './CIContext.js';
1
+ import { Box, Text, useInput } from "ink";
2
+ import React, { useEffect, useState } from "react";
3
+ import { useCIMode } from "./CIContext.js";
4
+ import { theme } from "./theme.js";
5
5
  const OPTIONS = [
6
- { id: 'aiSync', label: 'AI Config Sync', description: 'Sync AI config via javi-ai', default: true },
7
- { id: 'sdd', label: 'SDD (openspec/)', description: 'Spec-Driven Development workflow', default: true },
8
- { id: 'contextDir', label: '.context/ Directory', description: 'AI-ready project context files', default: true },
9
- { id: 'claudeMd', label: 'CLAUDE.md', description: 'AI agent project instructions', default: true },
10
- { id: 'ghagga', label: 'GHAGGA Review', description: 'Multi-agent AI code review system', default: false },
6
+ {
7
+ id: "aiSync",
8
+ label: "AI Config Sync",
9
+ description: "Sync AI config via javi-ai",
10
+ default: true,
11
+ },
12
+ {
13
+ id: "sdd",
14
+ label: "SDD (openspec/)",
15
+ description: "Spec-Driven Development workflow",
16
+ default: true,
17
+ },
18
+ {
19
+ id: "contextDir",
20
+ label: ".context/ Directory",
21
+ description: "AI-ready project context files",
22
+ default: true,
23
+ },
24
+ {
25
+ id: "claudeMd",
26
+ label: "CLAUDE.md",
27
+ description: "AI agent project instructions",
28
+ default: true,
29
+ },
30
+ {
31
+ id: "ghagga",
32
+ label: "GHAGGA Review",
33
+ description: "Multi-agent AI code review system",
34
+ default: false,
35
+ },
36
+ {
37
+ id: "securityHooks",
38
+ label: "Security Hooks",
39
+ description: "6-layer git hooks + runtime AI guardrails",
40
+ default: true,
41
+ },
42
+ {
43
+ id: "codeGraph",
44
+ label: "Code Graph",
45
+ description: "RepoForge call-graph scaffolding + CI",
46
+ default: false,
47
+ },
48
+ {
49
+ id: "localAi",
50
+ label: "Local AI Stack",
51
+ description: "Ollama + Open WebUI + n8n + Supabase (Docker)",
52
+ default: false,
53
+ },
11
54
  ];
12
- export default function OptionSelector({ onConfirm, presetGhagga = false }) {
55
+ export default function OptionSelector({ onConfirm, presetGhagga = false, presetLocalAi = false, }) {
13
56
  const isCI = useCIMode();
14
57
  const [cursor, setCursor] = useState(0);
15
58
  const [selected, setSelected] = useState(() => {
16
- const defaults = new Set(OPTIONS.filter(o => o.default).map(o => o.id));
59
+ const defaults = new Set(OPTIONS.filter((o) => o.default).map((o) => o.id));
17
60
  if (presetGhagga)
18
- defaults.add('ghagga');
61
+ defaults.add("ghagga");
62
+ if (presetLocalAi)
63
+ defaults.add("localAi");
19
64
  return defaults;
20
65
  });
21
66
  // Auto-confirm in CI mode with defaults
22
67
  useEffect(() => {
23
68
  if (isCI) {
24
69
  onConfirm({
25
- aiSync: selected.has('aiSync'),
26
- sdd: selected.has('sdd'),
27
- contextDir: selected.has('contextDir'),
28
- claudeMd: selected.has('claudeMd'),
29
- ghagga: selected.has('ghagga'),
70
+ aiSync: selected.has("aiSync"),
71
+ sdd: selected.has("sdd"),
72
+ contextDir: selected.has("contextDir"),
73
+ claudeMd: selected.has("claudeMd"),
74
+ ghagga: selected.has("ghagga"),
75
+ securityHooks: selected.has("securityHooks"),
76
+ codeGraph: selected.has("codeGraph"),
77
+ localAi: selected.has("localAi"),
30
78
  });
31
79
  }
32
80
  }, [isCI]); // eslint-disable-line react-hooks/exhaustive-deps
33
81
  useInput((input, key) => {
34
82
  if (key.upArrow)
35
- setCursor(c => Math.max(0, c - 1));
83
+ setCursor((c) => Math.max(0, c - 1));
36
84
  if (key.downArrow)
37
- setCursor(c => Math.min(OPTIONS.length - 1, c + 1));
38
- if (input === ' ') {
85
+ setCursor((c) => Math.min(OPTIONS.length - 1, c + 1));
86
+ if (input === " ") {
39
87
  const opt = OPTIONS[cursor].id;
40
- setSelected(prev => {
88
+ setSelected((prev) => {
41
89
  const next = new Set(prev);
42
90
  next.has(opt) ? next.delete(opt) : next.add(opt);
43
91
  return next;
@@ -45,31 +93,34 @@ export default function OptionSelector({ onConfirm, presetGhagga = false }) {
45
93
  }
46
94
  if (key.return) {
47
95
  onConfirm({
48
- aiSync: selected.has('aiSync'),
49
- sdd: selected.has('sdd'),
50
- contextDir: selected.has('contextDir'),
51
- claudeMd: selected.has('claudeMd'),
52
- ghagga: selected.has('ghagga'),
96
+ aiSync: selected.has("aiSync"),
97
+ sdd: selected.has("sdd"),
98
+ contextDir: selected.has("contextDir"),
99
+ claudeMd: selected.has("claudeMd"),
100
+ ghagga: selected.has("ghagga"),
101
+ securityHooks: selected.has("securityHooks"),
102
+ codeGraph: selected.has("codeGraph"),
103
+ localAi: selected.has("localAi"),
53
104
  });
54
105
  }
55
106
  }, { isActive: !isCI });
56
107
  return (React.createElement(Box, { flexDirection: "column" },
57
108
  React.createElement(Text, { bold: true }, "Select additional options:"),
58
109
  React.createElement(Box, { marginTop: 1, flexDirection: "column", borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, borderColor: theme.muted, paddingLeft: 1 }, OPTIONS.map((opt, i) => (React.createElement(Box, { key: opt.id },
59
- React.createElement(Text, { color: i === cursor ? theme.primary : 'white' },
60
- i === cursor ? '\u25b6 ' : ' ',
61
- selected.has(opt.id) ? '\u25c9' : '\u25cb',
110
+ React.createElement(Text, { color: i === cursor ? theme.primary : "white" },
111
+ i === cursor ? "\u25b6 " : " ",
112
+ selected.has(opt.id) ? "\u25c9" : "\u25cb",
62
113
  " ",
63
114
  opt.label),
64
115
  React.createElement(Text, { color: theme.muted, dimColor: true },
65
- " ",
116
+ " ",
66
117
  opt.description))))),
67
118
  React.createElement(Box, { marginTop: 1, gap: 2 },
68
119
  React.createElement(Text, { color: selected.size > 0 ? theme.accent : theme.muted },
69
120
  selected.size,
70
121
  " selected"),
71
122
  React.createElement(Text, { color: theme.muted, dimColor: true },
72
- '\u2191\u2193',
73
- " navigate Space toggle Enter confirm"))));
123
+ "\u2191\u2193",
124
+ " navigate Space toggle Enter confirm"))));
74
125
  }
75
126
  //# sourceMappingURL=OptionSelector.js.map
@@ -1,9 +1,10 @@
1
- import React from 'react';
1
+ import React from "react";
2
2
  interface PluginProps {
3
- action: 'add' | 'remove' | 'list' | 'search' | 'validate' | 'sync' | 'export' | 'import';
3
+ action: "add" | "remove" | "list" | "search" | "validate" | "sync" | "export" | "import" | "export-skills";
4
4
  target?: string;
5
5
  dryRun: boolean;
6
+ codex?: boolean;
6
7
  }
7
- export default function Plugin({ action, target, dryRun }: PluginProps): React.JSX.Element;
8
+ export default function Plugin({ action, target, dryRun, codex, }: PluginProps): React.JSX.Element;
8
9
  export {};
9
10
  //# sourceMappingURL=Plugin.d.ts.map
package/dist/ui/Plugin.js CHANGED
@@ -1,13 +1,13 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text } from 'ink';
3
- import Spinner from 'ink-spinner';
4
- import { runPluginAdd, runPluginRemove, runPluginList, runPluginSearch, runPluginValidate, runPluginSync, runPluginExport, runPluginImport } from '../commands/plugin.js';
5
- import { theme } from './theme.js';
1
+ import { Box, Text } from "ink";
2
+ import Spinner from "ink-spinner";
3
+ import React, { useEffect, useState } from "react";
4
+ import { runPluginAdd, runPluginExport, runPluginExportCodex, runPluginExportGlobalSkillsJson, runPluginExportSkillsJson, runPluginImport, runPluginList, runPluginRemove, runPluginSearch, runPluginSync, runPluginValidate, } from "../commands/plugin.js";
5
+ import { theme } from "./theme.js";
6
6
  const STATUS_ICON = {
7
- pending: '\u25cb',
8
- done: '\u2713',
9
- error: '\u2717',
10
- skipped: '\u2013',
7
+ pending: "\u25cb",
8
+ done: "\u2713",
9
+ error: "\u2717",
10
+ skipped: "\u2013",
11
11
  };
12
12
  const STATUS_COLOR = {
13
13
  pending: theme.muted,
@@ -16,12 +16,12 @@ const STATUS_COLOR = {
16
16
  error: theme.error,
17
17
  skipped: theme.muted,
18
18
  };
19
- export default function Plugin({ action, target, dryRun }) {
19
+ export default function Plugin({ action, target, dryRun, codex = false, }) {
20
20
  const [steps, setSteps] = useState([]);
21
21
  const [done, setDone] = useState(false);
22
22
  const onStep = (step) => {
23
- setSteps(prev => {
24
- const idx = prev.findIndex(s => s.id === step.id);
23
+ setSteps((prev) => {
24
+ const idx = prev.findIndex((s) => s.id === step.id);
25
25
  if (idx >= 0) {
26
26
  const next = [...prev];
27
27
  next[idx] = step;
@@ -34,54 +34,97 @@ export default function Plugin({ action, target, dryRun }) {
34
34
  const run = async () => {
35
35
  try {
36
36
  switch (action) {
37
- case 'add':
37
+ case "add":
38
38
  if (!target) {
39
- onStep({ id: 'err', label: 'Error', status: 'error', detail: 'source required: javi-forge plugin add <org/repo>' });
39
+ onStep({
40
+ id: "err",
41
+ label: "Error",
42
+ status: "error",
43
+ detail: "source required: javi-forge plugin add <org/repo>",
44
+ });
40
45
  break;
41
46
  }
42
47
  await runPluginAdd(target, dryRun, onStep);
43
48
  break;
44
- case 'remove':
49
+ case "remove":
45
50
  if (!target) {
46
- onStep({ id: 'err', label: 'Error', status: 'error', detail: 'name required: javi-forge plugin remove <name>' });
51
+ onStep({
52
+ id: "err",
53
+ label: "Error",
54
+ status: "error",
55
+ detail: "name required: javi-forge plugin remove <name>",
56
+ });
47
57
  break;
48
58
  }
49
59
  await runPluginRemove(target, dryRun, onStep);
50
60
  break;
51
- case 'list':
61
+ case "list":
52
62
  await runPluginList(onStep);
53
63
  break;
54
- case 'search':
64
+ case "search":
55
65
  await runPluginSearch(target, onStep);
56
66
  break;
57
- case 'validate':
67
+ case "validate":
58
68
  if (!target) {
59
- onStep({ id: 'err', label: 'Error', status: 'error', detail: 'path required: javi-forge plugin validate <dir>' });
69
+ onStep({
70
+ id: "err",
71
+ label: "Error",
72
+ status: "error",
73
+ detail: "path required: javi-forge plugin validate <dir>",
74
+ });
60
75
  break;
61
76
  }
62
77
  await runPluginValidate(target, onStep);
63
78
  break;
64
- case 'sync':
79
+ case "sync":
65
80
  await runPluginSync(process.cwd(), dryRun, onStep);
66
81
  break;
67
- case 'export':
82
+ case "export":
68
83
  if (!target) {
69
- onStep({ id: 'err', label: 'Error', status: 'error', detail: 'name required: javi-forge plugin export <name>' });
84
+ onStep({
85
+ id: "err",
86
+ label: "Error",
87
+ status: "error",
88
+ detail: "name required: javi-forge plugin export <name>",
89
+ });
70
90
  break;
71
91
  }
72
- await runPluginExport(target, onStep);
92
+ if (codex) {
93
+ await runPluginExportCodex(target, onStep);
94
+ }
95
+ else {
96
+ await runPluginExport(target, onStep);
97
+ }
73
98
  break;
74
- case 'import':
99
+ case "import":
75
100
  if (!target) {
76
- onStep({ id: 'err', label: 'Error', status: 'error', detail: 'path required: javi-forge plugin import <dir>' });
101
+ onStep({
102
+ id: "err",
103
+ label: "Error",
104
+ status: "error",
105
+ detail: "path required: javi-forge plugin import <dir>",
106
+ });
77
107
  break;
78
108
  }
79
109
  await runPluginImport(target, dryRun, onStep);
80
110
  break;
111
+ case "export-skills":
112
+ if (target === "global") {
113
+ await runPluginExportGlobalSkillsJson(dryRun, onStep);
114
+ }
115
+ else {
116
+ await runPluginExportSkillsJson(target ?? process.cwd(), dryRun, onStep);
117
+ }
118
+ break;
81
119
  }
82
120
  }
83
121
  catch (e) {
84
- onStep({ id: 'fatal', label: 'Fatal error', status: 'error', detail: String(e) });
122
+ onStep({
123
+ id: "fatal",
124
+ label: "Fatal error",
125
+ status: "error",
126
+ detail: String(e),
127
+ });
85
128
  }
86
129
  setDone(true);
87
130
  };
@@ -94,19 +137,19 @@ export default function Plugin({ action, target, dryRun }) {
94
137
  " plugin ",
95
138
  action),
96
139
  dryRun && React.createElement(Text, { color: theme.warning }, " (dry-run)")),
97
- steps.map(step => (React.createElement(Box, { key: step.id, marginLeft: 2 }, step.status === 'running' ? (React.createElement(Text, { color: theme.warning },
140
+ steps.map((step) => (React.createElement(Box, { key: step.id, marginLeft: 2 }, step.status === "running" ? (React.createElement(Text, { color: theme.warning },
98
141
  React.createElement(Spinner, { type: "dots" }),
99
- ' ',
142
+ " ",
100
143
  step.label,
101
- step.detail ? React.createElement(Text, { color: theme.muted, dimColor: true },
102
- " ",
103
- step.detail) : null)) : (React.createElement(Text, { color: STATUS_COLOR[step.status] },
144
+ step.detail ? (React.createElement(Text, { color: theme.muted, dimColor: true },
145
+ " ",
146
+ step.detail)) : null)) : (React.createElement(Text, { color: STATUS_COLOR[step.status] },
104
147
  STATUS_ICON[step.status],
105
148
  " ",
106
149
  step.label,
107
- step.detail ? React.createElement(Text, { color: theme.muted, dimColor: true },
108
- " ",
109
- step.detail) : null))))),
150
+ step.detail ? (React.createElement(Text, { color: theme.muted, dimColor: true },
151
+ " ",
152
+ step.detail)) : null))))),
110
153
  done && (React.createElement(Box, { marginTop: 1 },
111
154
  React.createElement(Text, { color: theme.muted }, "Done.")))));
112
155
  }
@@ -1,11 +1,11 @@
1
- import React from 'react';
2
- import type { InitStep } from '../types/index.js';
1
+ import React from "react";
2
+ import type { InitStep } from "../types/index.js";
3
3
  interface Props {
4
4
  steps: InitStep[];
5
5
  projectName?: string;
6
6
  contextLine?: string;
7
7
  onDone?: () => void;
8
8
  }
9
- export default function Progress({ steps, projectName, contextLine, onDone }: Props): React.JSX.Element;
9
+ export default function Progress({ steps, projectName, contextLine, onDone, }: Props): React.JSX.Element;
10
10
  export {};
11
11
  //# sourceMappingURL=Progress.d.ts.map
@@ -1,12 +1,12 @@
1
- import React, { useEffect, useRef } from 'react';
2
- import { Box, Text } from 'ink';
3
- import Spinner from 'ink-spinner';
4
- import { theme } from './theme.js';
1
+ import { Box, Text } from "ink";
2
+ import Spinner from "ink-spinner";
3
+ import React, { useEffect, useRef } from "react";
4
+ import { theme } from "./theme.js";
5
5
  const STATUS_ICON = {
6
- pending: '\u25cb',
7
- done: '\u2713',
8
- error: '\u2717',
9
- skipped: '\u2013',
6
+ pending: "\u25cb",
7
+ done: "\u2713",
8
+ error: "\u2717",
9
+ skipped: "\u2013",
10
10
  };
11
11
  const STATUS_COLOR = {
12
12
  pending: theme.muted,
@@ -15,12 +15,13 @@ const STATUS_COLOR = {
15
15
  error: theme.error,
16
16
  skipped: theme.muted,
17
17
  };
18
- export default function Progress({ steps, projectName, contextLine, onDone }) {
18
+ export default function Progress({ steps, projectName, contextLine, onDone, }) {
19
19
  const doneRef = useRef(false);
20
20
  const total = steps.length;
21
- const completed = steps.filter(s => s.status === 'done' || s.status === 'skipped').length;
22
- const hasError = steps.some(s => s.status === 'error');
23
- const allFinished = total > 0 && steps.every(s => s.status === 'done' || s.status === 'error' || s.status === 'skipped');
21
+ const completed = steps.filter((s) => s.status === "done" || s.status === "skipped").length;
22
+ const hasError = steps.some((s) => s.status === "error");
23
+ const allFinished = total > 0 &&
24
+ steps.every((s) => s.status === "done" || s.status === "error" || s.status === "skipped");
24
25
  useEffect(() => {
25
26
  if (allFinished && !hasError && !doneRef.current && onDone) {
26
27
  doneRef.current = true;
@@ -32,27 +33,27 @@ export default function Progress({ steps, projectName, contextLine, onDone }) {
32
33
  return (React.createElement(Box, { flexDirection: "column" },
33
34
  React.createElement(Box, { marginBottom: 1, flexDirection: "column" },
34
35
  contextLine && (React.createElement(Text, { color: theme.muted },
35
- 'Initializing: ',
36
+ "Initializing: ",
36
37
  React.createElement(Text, { color: theme.primary }, contextLine))),
37
38
  total > 0 && (React.createElement(Text, { color: theme.muted },
38
- 'Progress: ',
39
+ "Progress: ",
39
40
  React.createElement(Text, { color: completed === total ? theme.success : theme.warning },
40
41
  completed,
41
42
  "/",
42
43
  total,
43
44
  " steps")))),
44
- React.createElement(Box, { flexDirection: "column" }, steps.map(step => (React.createElement(Box, { key: step.id, marginLeft: 2 }, step.status === 'running' ? (React.createElement(Text, { color: theme.warning },
45
+ React.createElement(Box, { flexDirection: "column" }, steps.map((step) => (React.createElement(Box, { key: step.id, marginLeft: 2 }, step.status === "running" ? (React.createElement(Text, { color: theme.warning },
45
46
  React.createElement(Spinner, { type: "dots" }),
46
- ' ',
47
+ " ",
47
48
  step.label,
48
- step.detail ? React.createElement(Text, { color: theme.muted, dimColor: true },
49
- " ",
50
- step.detail) : null)) : (React.createElement(Text, { color: STATUS_COLOR[step.status] },
49
+ step.detail ? (React.createElement(Text, { color: theme.muted, dimColor: true },
50
+ " ",
51
+ step.detail)) : null)) : (React.createElement(Text, { color: STATUS_COLOR[step.status] },
51
52
  STATUS_ICON[step.status],
52
53
  " ",
53
54
  step.label,
54
- step.detail ? React.createElement(Text, { color: theme.muted, dimColor: true },
55
- " ",
56
- step.detail) : null))))))));
55
+ step.detail ? (React.createElement(Text, { color: theme.muted, dimColor: true },
56
+ " ",
57
+ step.detail)) : null))))))));
57
58
  }
58
59
  //# sourceMappingURL=Progress.js.map
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
- import type { SkillsDoctorMode } from '../commands/skills.js';
1
+ import React from "react";
2
+ import type { SkillsDoctorMode } from "../commands/skills.js";
3
3
  interface SkillsProps {
4
4
  mode: SkillsDoctorMode;
5
5
  budget?: number;