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,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: _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: _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;
package/dist/ui/Skills.js CHANGED
@@ -1,10 +1,10 @@
1
- import React, { useEffect, useState, useCallback } from 'react';
2
- import { Box, Text, useApp, useInput } from 'ink';
3
- import Spinner from 'ink-spinner';
4
- import { runSkillsDoctor } from '../commands/skills.js';
5
- import Header from './Header.js';
6
- import { theme } from './theme.js';
7
- import { useCIMode } from './CIContext.js';
1
+ import { Box, Text, useApp, useInput } from "ink";
2
+ import Spinner from "ink-spinner";
3
+ import React, { useCallback, useEffect, useState } from "react";
4
+ import { runSkillsDoctor } from "../commands/skills.js";
5
+ import { useCIMode } from "./CIContext.js";
6
+ import Header from "./Header.js";
7
+ import { theme } from "./theme.js";
8
8
  export default function Skills({ mode, budget, deep, skillsDir }) {
9
9
  const { exit } = useApp();
10
10
  const isCI = useCIMode();
@@ -16,10 +16,18 @@ export default function Skills({ mode, budget, deep, skillsDir }) {
16
16
  setResult(null);
17
17
  setError(null);
18
18
  runSkillsDoctor({ mode, budget, deep, skillsDir })
19
- .then(r => { setResult(r); setLoading(false); })
20
- .catch(e => { setError(String(e)); setLoading(false); });
19
+ .then((r) => {
20
+ setResult(r);
21
+ setLoading(false);
22
+ })
23
+ .catch((e) => {
24
+ setError(String(e));
25
+ setLoading(false);
26
+ });
21
27
  }, [mode, budget, deep, skillsDir]);
22
- useEffect(() => { runCheck(); }, [runCheck]);
28
+ useEffect(() => {
29
+ runCheck();
30
+ }, [runCheck]);
23
31
  // Auto-exit in CI mode once loading finishes
24
32
  useEffect(() => {
25
33
  if (isCI && !loading) {
@@ -29,75 +37,96 @@ export default function Skills({ mode, budget, deep, skillsDir }) {
29
37
  return undefined;
30
38
  }, [isCI, loading, exit]);
31
39
  useInput((input, key) => {
32
- if (input.toLowerCase() === 'r')
40
+ if (input.toLowerCase() === "r")
33
41
  runCheck();
34
- if (input.toLowerCase() === 'q' || key.return || key.escape)
42
+ if (input.toLowerCase() === "q" || key.return || key.escape)
35
43
  exit();
36
44
  }, { isActive: !isCI });
37
- const subtitle = mode === 'budget' ? 'skills budget' : 'skills doctor';
45
+ const subtitle = mode === "budget" ? "skills budget" : "skills doctor";
38
46
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
39
47
  React.createElement(Header, { subtitle: subtitle }),
40
48
  loading && (React.createElement(Text, { color: theme.warning },
41
49
  React.createElement(Spinner, { type: "dots" }),
42
- ' Running skills analysis...')),
50
+ " Running skills analysis...")),
43
51
  error && (React.createElement(Text, { color: theme.error },
44
- '\u2717',
52
+ "\u2717",
45
53
  " Error: ",
46
54
  error)),
47
55
  result && (React.createElement(Box, { flexDirection: "column" },
48
56
  React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
49
57
  React.createElement(Text, { bold: true, color: result.budget.overBudget ? theme.warning : theme.success },
50
- ' ',
58
+ " ",
51
59
  "Context Budget"),
52
60
  React.createElement(Box, { marginLeft: 2 },
53
61
  React.createElement(Text, { color: result.budget.overBudget ? theme.warning : theme.success },
54
- result.budget.overBudget ? '\u2717' : '\u2713',
55
- ' ',
62
+ result.budget.overBudget ? "\u2717" : "\u2713",
63
+ " ",
56
64
  result.budget.totalTokens,
57
65
  " / ",
58
66
  result.budget.budget,
59
67
  " tokens",
60
- result.budget.overBudget ? ' (OVER BUDGET)' : '')),
61
- result.budget.entries.map(entry => (React.createElement(Box, { key: entry.skillName, marginLeft: 4 },
68
+ result.budget.overBudget ? " (OVER BUDGET)" : "")),
69
+ result.budget.entries.map((entry) => (React.createElement(Box, { key: entry.skillName, marginLeft: 4 },
62
70
  React.createElement(Text, { color: theme.muted }, entry.skillName),
63
71
  React.createElement(Text, { color: theme.muted, dimColor: true },
64
- ' ',
72
+ " ",
65
73
  "~",
66
74
  entry.tokens,
67
75
  " tokens")))),
68
76
  result.budget.suggestions.map((s, i) => (React.createElement(Box, { key: `suggestion-${i}`, marginLeft: 4 },
69
77
  React.createElement(Text, { color: theme.warning },
70
- '! ',
78
+ "! ",
71
79
  s))))),
80
+ result.budget.optimizations.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
81
+ React.createElement(Text, { bold: true, color: theme.warning },
82
+ " ",
83
+ "Optimization Suggestions"),
84
+ result.budget.optimizations.map((opt, i) => (React.createElement(Box, { key: `opt-${i}`, flexDirection: "column", marginLeft: 4 },
85
+ React.createElement(Text, { color: opt.meetsbudget ? theme.success : theme.warning },
86
+ opt.meetsbudget ? "\u2713" : "~",
87
+ " Disable [",
88
+ opt.disableSkills.join(", "),
89
+ "] ",
90
+ "\u2192",
91
+ " save ~",
92
+ opt.tokensSaved,
93
+ " tokens (",
94
+ opt.remainingTokens,
95
+ " remaining)")))))),
72
96
  result.conflicts.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
73
97
  React.createElement(Text, { bold: true, color: theme.error },
74
- ' ',
98
+ " ",
75
99
  "Conflicts (",
76
100
  result.conflicts.length,
77
101
  ")"),
78
102
  result.conflicts.map((c, i) => (React.createElement(Box, { key: `conflict-${i}`, flexDirection: "column", marginLeft: 4 },
79
103
  React.createElement(Text, { color: theme.error },
80
- '\u2717',
104
+ "\u2717",
81
105
  " ",
82
106
  c.ruleA.skillName,
83
107
  " vs ",
84
- c.ruleB.skillName),
108
+ c.ruleB.skillName,
109
+ React.createElement(Text, { color: theme.muted, dimColor: true },
110
+ " ",
111
+ "[",
112
+ c.kind,
113
+ "]")),
85
114
  React.createElement(Text, { color: theme.muted, dimColor: true },
86
- ' ',
115
+ " ",
87
116
  c.reason)))))),
88
117
  deep && result.conflicts.length === 0 && (React.createElement(Box, { marginLeft: 2, marginBottom: 1 },
89
118
  React.createElement(Text, { color: theme.success },
90
- '\u2713',
119
+ "\u2713",
91
120
  " No rule conflicts detected"))),
92
121
  result.duplicates.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
93
122
  React.createElement(Text, { bold: true, color: theme.warning },
94
- ' ',
123
+ " ",
95
124
  "Potential Duplicates (",
96
125
  result.duplicates.length,
97
126
  ")"),
98
127
  result.duplicates.map((d, i) => (React.createElement(Box, { key: `dup-${i}`, flexDirection: "column", marginLeft: 4 },
99
128
  React.createElement(Text, { color: theme.warning },
100
- '~',
129
+ "~",
101
130
  " ",
102
131
  d.skillA,
103
132
  " + ",
@@ -106,12 +135,12 @@ export default function Skills({ mode, budget, deep, skillsDir }) {
106
135
  d.similarity,
107
136
  "% overlap)"),
108
137
  React.createElement(Text, { color: theme.muted, dimColor: true },
109
- ' ',
138
+ " ",
110
139
  "shared: ",
111
- d.sharedTriggers.slice(0, 3).join(', '))))))),
140
+ d.sharedTriggers.slice(0, 3).join(", "))))))),
112
141
  deep && result.duplicates.length === 0 && (React.createElement(Box, { marginLeft: 2, marginBottom: 1 },
113
142
  React.createElement(Text, { color: theme.success },
114
- '\u2713',
143
+ "\u2713",
115
144
  " No duplicate skills detected"))))),
116
145
  !loading && (React.createElement(Box, { marginTop: 1 },
117
146
  React.createElement(Text, { color: theme.muted, dimColor: true }, "Press r to refresh, q to quit")))));
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
- import type { Stack } from '../types/index.js';
1
+ import React from "react";
2
+ import type { Stack } from "../types/index.js";
3
3
  interface Props {
4
4
  projectDir: string;
5
5
  onConfirm: (stack: Stack) => void;