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
@@ -0,0 +1,124 @@
1
+ import { Box, Text, useApp, useInput } from "ink";
2
+ import Spinner from "ink-spinner";
3
+ import React, { useCallback, useEffect, useState } from "react";
4
+ import { autoInstallSkills } from "../lib/auto-skill-install.js";
5
+ import { useCIMode } from "./CIContext.js";
6
+ import Header from "./Header.js";
7
+ import { theme } from "./theme.js";
8
+ export default function AutoSkills({ projectDir, skillsDir, dryRun, }) {
9
+ const { exit } = useApp();
10
+ const isCI = useCIMode();
11
+ const [result, setResult] = useState(null);
12
+ const [error, setError] = useState(null);
13
+ const [loading, setLoading] = useState(true);
14
+ const runDetection = useCallback(() => {
15
+ setLoading(true);
16
+ setResult(null);
17
+ setError(null);
18
+ autoInstallSkills({
19
+ projectDir,
20
+ skillsSourceDir: skillsDir,
21
+ skillsTargetDir: skillsDir,
22
+ dryRun: dryRun ?? false,
23
+ })
24
+ .then((r) => {
25
+ setResult(r);
26
+ setLoading(false);
27
+ })
28
+ .catch((e) => {
29
+ setError(String(e));
30
+ setLoading(false);
31
+ });
32
+ }, [projectDir, skillsDir, dryRun]);
33
+ useEffect(() => {
34
+ runDetection();
35
+ }, [runDetection]);
36
+ // Auto-exit in CI mode once loading finishes
37
+ useEffect(() => {
38
+ if (isCI && !loading) {
39
+ const t = setTimeout(() => exit(), 100);
40
+ return () => clearTimeout(t);
41
+ }
42
+ return undefined;
43
+ }, [isCI, loading, exit]);
44
+ useInput((input, key) => {
45
+ if (input.toLowerCase() === "r")
46
+ runDetection();
47
+ if (input.toLowerCase() === "q" || key.return || key.escape)
48
+ exit();
49
+ }, { isActive: !isCI });
50
+ const totalSkills = result
51
+ ? result.installed.length + result.skipped.length + result.notFound.length
52
+ : 0;
53
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
54
+ React.createElement(Header, { subtitle: "skills auto", dryRun: dryRun }),
55
+ loading && (React.createElement(Text, { color: theme.warning },
56
+ React.createElement(Spinner, { type: "dots" }),
57
+ " Detecting project stack...")),
58
+ error && (React.createElement(Text, { color: theme.error },
59
+ "\u2717",
60
+ " Error: ",
61
+ error)),
62
+ result && (React.createElement(Box, { flexDirection: "column" },
63
+ React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
64
+ React.createElement(Text, { bold: true, color: theme.primary },
65
+ " ",
66
+ "Stack Detection"),
67
+ React.createElement(Box, { marginLeft: 2 },
68
+ React.createElement(Text, { color: result.detection.stack ? theme.success : theme.warning },
69
+ result.detection.stack ? "\u2713" : "\u2717",
70
+ " ",
71
+ result.detection.stack
72
+ ? `Detected: ${result.detection.stack}`
73
+ : "No stack detected"))),
74
+ result.detection.signals.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
75
+ React.createElement(Text, { bold: true, color: theme.primary },
76
+ " ",
77
+ "Signals (",
78
+ result.detection.signals.length,
79
+ ")"),
80
+ result.detection.signals.map((s, i) => (React.createElement(Box, { key: `signal-${i}`, marginLeft: 4 },
81
+ React.createElement(Text, { color: theme.muted }, s.signal),
82
+ React.createElement(Text, { dimColor: true, color: theme.muted },
83
+ " ",
84
+ "via ",
85
+ s.source),
86
+ React.createElement(Text, { color: theme.accent },
87
+ " \u2192 ",
88
+ s.skills.join(", "))))))),
89
+ React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
90
+ React.createElement(Text, { bold: true, color: theme.primary },
91
+ " ",
92
+ "Skills (",
93
+ totalSkills,
94
+ ")"),
95
+ result.installed.length > 0 && (React.createElement(Box, { flexDirection: "column" }, result.installed.map((name) => (React.createElement(Box, { key: name, marginLeft: 4 },
96
+ React.createElement(Text, { color: theme.success },
97
+ dryRun ? "\u25CB" : "\u2713",
98
+ " ",
99
+ name),
100
+ React.createElement(Text, { dimColor: true, color: theme.muted },
101
+ " ",
102
+ dryRun ? "would install" : "installed")))))),
103
+ result.skipped.length > 0 && (React.createElement(Box, { flexDirection: "column" }, result.skipped.map((name) => (React.createElement(Box, { key: name, marginLeft: 4 },
104
+ React.createElement(Text, { color: theme.muted },
105
+ "\u2500",
106
+ " ",
107
+ name),
108
+ React.createElement(Text, { dimColor: true, color: theme.muted },
109
+ " ",
110
+ "already present")))))),
111
+ result.notFound.length > 0 && (React.createElement(Box, { flexDirection: "column" }, result.notFound.map((name) => (React.createElement(Box, { key: name, marginLeft: 4 },
112
+ React.createElement(Text, { color: theme.warning },
113
+ "?",
114
+ " ",
115
+ name),
116
+ React.createElement(Text, { dimColor: true, color: theme.warning },
117
+ " ",
118
+ "not found in source")))))),
119
+ totalSkills === 0 && (React.createElement(Box, { marginLeft: 4 },
120
+ React.createElement(Text, { color: theme.muted }, "No skills recommended for this stack")))))),
121
+ !loading && (React.createElement(Box, { marginTop: 1 },
122
+ React.createElement(Text, { color: theme.muted, dimColor: true }, "Press r to re-scan, q to quit")))));
123
+ }
124
+ //# sourceMappingURL=AutoSkills.js.map
package/dist/ui/CI.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
- import type { CIOptions } from '../commands/ci.js';
1
+ import React from "react";
2
+ import type { CIOptions } from "../commands/ci.js";
3
3
  interface CIProps extends CIOptions {
4
4
  /** Called when CI finishes (success or failure) */
5
5
  onDone?: (success: boolean) => void;
package/dist/ui/CI.js CHANGED
@@ -1,18 +1,18 @@
1
- import React, { useEffect, useState, useRef } from 'react';
2
- import { Box, Text, useApp } from 'ink';
3
- import Spinner from 'ink-spinner';
4
- import { runCI } from '../commands/ci.js';
5
- import Header from './Header.js';
6
- import { theme } from './theme.js';
1
+ import { Box, Text, useApp } from "ink";
2
+ import Spinner from "ink-spinner";
3
+ import React, { useEffect, useRef, useState } from "react";
4
+ import { runCI } from "../commands/ci.js";
5
+ import Header from "./Header.js";
6
+ import { theme } from "./theme.js";
7
7
  // =============================================================================
8
8
  // Icons
9
9
  // =============================================================================
10
10
  const STATUS_ICON = {
11
- pending: '',
12
- running: '',
13
- done: '',
14
- error: '',
15
- skipped: '',
11
+ pending: "",
12
+ running: "",
13
+ done: "",
14
+ error: "",
15
+ skipped: "",
16
16
  };
17
17
  const STATUS_COLOR = {
18
18
  pending: theme.muted,
@@ -35,8 +35,8 @@ export default function CI(props) {
35
35
  return;
36
36
  started.current = true;
37
37
  const onStep = (step) => {
38
- setSteps(prev => {
39
- const idx = prev.findIndex(s => s.id === step.id);
38
+ setSteps((prev) => {
39
+ const idx = prev.findIndex((s) => s.id === step.id);
40
40
  if (idx >= 0) {
41
41
  const next = [...prev];
42
42
  next[idx] = step;
@@ -64,28 +64,26 @@ export default function CI(props) {
64
64
  });
65
65
  // eslint-disable-next-line react-hooks/exhaustive-deps
66
66
  }, []);
67
- const mode = props.mode ?? 'full';
68
- const subtitle = mode === 'quick' ? 'ci — quick' : mode === 'shell' ? 'ci — shell' : 'ci';
67
+ const mode = props.mode ?? "full";
68
+ const subtitle = mode === "quick" ? "ci — quick" : mode === "shell" ? "ci — shell" : "ci";
69
69
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
70
70
  React.createElement(Header, { subtitle: subtitle }),
71
- React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, steps.map(step => (React.createElement(Box, { key: step.id },
71
+ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, steps.map((step) => (React.createElement(Box, { key: step.id },
72
72
  React.createElement(Text, { color: STATUS_COLOR[step.status] },
73
- step.status === 'running'
74
- ? React.createElement(Spinner, { type: "dots" })
75
- : `${STATUS_ICON[step.status]} `,
73
+ step.status === "running" ? (React.createElement(Spinner, { type: "dots" })) : (`${STATUS_ICON[step.status]} `),
76
74
  step.label,
77
- step.detail
78
- ? React.createElement(Text, { color: theme.muted, dimColor: true },
79
- ' ',
80
- step.detail)
81
- : null))))),
75
+ step.detail ? (React.createElement(Text, { color: theme.muted, dimColor: true },
76
+ " ",
77
+ step.detail)) : null))))),
82
78
  done && success === true && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
83
79
  React.createElement(Text, { color: theme.success, bold: true }, "\u2713 CI passed \u2014 safe to push!"))),
84
80
  done && success === false && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
85
81
  React.createElement(Text, { color: theme.error, bold: true }, "\u2717 CI failed \u2014 fix the issues above before pushing."),
86
- React.createElement(Text, { color: theme.muted, dimColor: true }, " To skip: git push --no-verify"))),
82
+ React.createElement(Text, { color: theme.muted, dimColor: true },
83
+ " ",
84
+ "To skip: git push --no-verify"))),
87
85
  !done && steps.length === 0 && (React.createElement(Text, { color: theme.warning },
88
86
  React.createElement(Spinner, { type: "dots" }),
89
- ' Starting CI...'))));
87
+ " Starting CI..."))));
90
88
  }
91
89
  //# sourceMappingURL=CI.js.map
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React from "react";
2
2
  export declare function useCIMode(): boolean;
3
3
  interface Props {
4
4
  isCI: boolean;
@@ -1,9 +1,10 @@
1
- import React, { createContext, useContext } from 'react';
1
+ import React from "react";
2
+ import { createContext, useContext } from "react";
2
3
  const CIContext = createContext({ isCI: false });
3
4
  export function useCIMode() {
4
5
  return useContext(CIContext).isCI;
5
6
  }
6
7
  export function CIProvider({ isCI, children }) {
7
- return (React.createElement(CIContext.Provider, { value: { isCI } }, children));
8
+ return React.createElement(CIContext.Provider, { value: { isCI } }, children);
8
9
  }
9
10
  //# sourceMappingURL=CIContext.js.map
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
- import type { CIProvider } from '../types/index.js';
1
+ import React from "react";
2
+ import type { CIProvider } from "../types/index.js";
3
3
  interface Props {
4
4
  onConfirm: (provider: CIProvider) => void;
5
5
  }
@@ -1,11 +1,19 @@
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 CI_PROVIDERS = [
6
- { id: 'github', label: 'GitHub Actions', description: 'GitHub CI/CD with reusable workflows' },
7
- { id: 'gitlab', label: 'GitLab CI', description: 'GitLab CI/CD pipelines' },
8
- { id: 'woodpecker', label: 'Woodpecker CI', description: 'Container-native CI for Gitea/Forgejo' },
6
+ {
7
+ id: "github",
8
+ label: "GitHub Actions",
9
+ description: "GitHub CI/CD with reusable workflows",
10
+ },
11
+ { id: "gitlab", label: "GitLab CI", description: "GitLab CI/CD pipelines" },
12
+ {
13
+ id: "woodpecker",
14
+ label: "Woodpecker CI",
15
+ description: "Container-native CI for Gitea/Forgejo",
16
+ },
9
17
  ];
10
18
  export default function CISelector({ onConfirm }) {
11
19
  const isCI = useCIMode();
@@ -18,9 +26,9 @@ export default function CISelector({ onConfirm }) {
18
26
  }, [isCI]); // eslint-disable-line react-hooks/exhaustive-deps
19
27
  useInput((_, key) => {
20
28
  if (key.upArrow)
21
- setCursor(c => Math.max(0, c - 1));
29
+ setCursor((c) => Math.max(0, c - 1));
22
30
  if (key.downArrow)
23
- setCursor(c => Math.min(CI_PROVIDERS.length - 1, c + 1));
31
+ setCursor((c) => Math.min(CI_PROVIDERS.length - 1, c + 1));
24
32
  if (key.return) {
25
33
  onConfirm(CI_PROVIDERS[cursor].id);
26
34
  }
@@ -28,18 +36,18 @@ export default function CISelector({ onConfirm }) {
28
36
  return (React.createElement(Box, { flexDirection: "column" },
29
37
  React.createElement(Text, { bold: true }, "Select CI provider:"),
30
38
  React.createElement(Box, { marginTop: 1, flexDirection: "column", borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, borderColor: theme.muted, paddingLeft: 1 }, CI_PROVIDERS.map((p, i) => (React.createElement(Box, { key: p.id },
31
- React.createElement(Text, { color: i === cursor ? theme.primary : 'white' },
32
- i === cursor ? '\u25b6 ' : ' ',
33
- i === cursor ? '\u25c9' : '\u25cb',
39
+ React.createElement(Text, { color: i === cursor ? theme.primary : "white" },
40
+ i === cursor ? "\u25b6 " : " ",
41
+ i === cursor ? "\u25c9" : "\u25cb",
34
42
  " ",
35
43
  p.label),
36
44
  React.createElement(Text, { color: theme.muted, dimColor: true },
37
- " ",
45
+ " ",
38
46
  p.description))))),
39
47
  React.createElement(Box, { marginTop: 1, gap: 2 },
40
48
  React.createElement(Text, { color: theme.primary }, CI_PROVIDERS[cursor].label),
41
49
  React.createElement(Text, { color: theme.muted, dimColor: true },
42
- '\u2191\u2193',
43
- " navigate Enter confirm"))));
50
+ "\u2191\u2193",
51
+ " navigate Enter confirm"))));
44
52
  }
45
53
  //# sourceMappingURL=CISelector.js.map
@@ -1,3 +1,3 @@
1
- import React from 'react';
1
+ import React from "react";
2
2
  export default function Doctor(): React.JSX.Element;
3
3
  //# sourceMappingURL=Doctor.d.ts.map
package/dist/ui/Doctor.js CHANGED
@@ -1,14 +1,14 @@
1
- import React, { useEffect, useState, useCallback } from 'react';
2
- import { Box, Text, useApp, useInput } from 'ink';
3
- import Spinner from 'ink-spinner';
4
- import { runDoctor } from '../commands/doctor.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 { runDoctor } from "../commands/doctor.js";
5
+ import { useCIMode } from "./CIContext.js";
6
+ import Header from "./Header.js";
7
+ import { theme } from "./theme.js";
8
8
  const STATUS_ICON = {
9
- ok: '\u2713',
10
- fail: '\u2717',
11
- skip: '\u2013',
9
+ ok: "\u2713",
10
+ fail: "\u2717",
11
+ skip: "\u2013",
12
12
  };
13
13
  const STATUS_COLOR = {
14
14
  ok: theme.success,
@@ -26,10 +26,18 @@ export default function Doctor() {
26
26
  setResult(null);
27
27
  setError(null);
28
28
  runDoctor()
29
- .then(r => { setResult(r); setLoading(false); })
30
- .catch(e => { setError(String(e)); setLoading(false); });
29
+ .then((r) => {
30
+ setResult(r);
31
+ setLoading(false);
32
+ })
33
+ .catch((e) => {
34
+ setError(String(e));
35
+ setLoading(false);
36
+ });
31
37
  }, []);
32
- useEffect(() => { runCheck(); }, [runCheck]);
38
+ useEffect(() => {
39
+ runCheck();
40
+ }, [runCheck]);
33
41
  // Auto-exit in CI mode once loading finishes
34
42
  useEffect(() => {
35
43
  if (isCI && !loading) {
@@ -39,22 +47,22 @@ export default function Doctor() {
39
47
  return undefined;
40
48
  }, [isCI, loading, exit]);
41
49
  useInput((input, key) => {
42
- if (input.toLowerCase() === 'r')
50
+ if (input.toLowerCase() === "r")
43
51
  runCheck();
44
- if (input.toLowerCase() === 'q' || key.return || key.escape)
52
+ if (input.toLowerCase() === "q" || key.return || key.escape)
45
53
  exit();
46
54
  }, { isActive: !isCI });
47
55
  // Compute health score
48
- const allChecks = result?.sections.flatMap(s => s.checks) ?? [];
49
- const passed = allChecks.filter(c => c.status === 'ok').length;
50
- const total = allChecks.filter(c => c.status !== 'skip').length;
56
+ const allChecks = result?.sections.flatMap((s) => s.checks) ?? [];
57
+ const passed = allChecks.filter((c) => c.status === "ok").length;
58
+ const total = allChecks.filter((c) => c.status !== "skip").length;
51
59
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
52
60
  React.createElement(Header, { subtitle: "doctor" }),
53
61
  loading && (React.createElement(Text, { color: theme.warning },
54
62
  React.createElement(Spinner, { type: "dots" }),
55
- ' Running checks...')),
63
+ " Running checks...")),
56
64
  error && (React.createElement(Text, { color: theme.error },
57
- '\u2717',
65
+ "\u2717",
58
66
  " Error: ",
59
67
  error)),
60
68
  result && (React.createElement(Box, { flexDirection: "column" },
@@ -66,22 +74,20 @@ export default function Doctor() {
66
74
  total,
67
75
  " checks passed")),
68
76
  result.sections.map((section, si) => {
69
- const sectionHasFail = section.checks.some(c => c.status === 'fail');
77
+ const sectionHasFail = section.checks.some((c) => c.status === "fail");
70
78
  return (React.createElement(Box, { key: `section-${si}`, flexDirection: "column", marginBottom: 1 },
71
79
  React.createElement(Text, { bold: true, color: sectionHasFail ? theme.warning : theme.success },
72
- ' ',
80
+ " ",
73
81
  section.title),
74
82
  section.checks.map((check, ci) => (React.createElement(Box, { key: `section-${si}-check-${ci}-${check.label}` },
75
83
  React.createElement(Text, { color: STATUS_COLOR[check.status] },
76
- ' ',
84
+ " ",
77
85
  STATUS_ICON[check.status],
78
- ' ',
86
+ " ",
79
87
  check.label,
80
- check.detail
81
- ? React.createElement(Text, { color: theme.muted, dimColor: true },
82
- ' ',
83
- check.detail)
84
- : null))))));
88
+ check.detail ? (React.createElement(Text, { color: theme.muted, dimColor: true },
89
+ " ",
90
+ check.detail)) : null))))));
85
91
  }))),
86
92
  !loading && (React.createElement(Box, { marginTop: 1 },
87
93
  React.createElement(Text, { color: theme.muted, dimColor: true }, "Press r to refresh, q to quit")))));
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React from "react";
2
2
  interface Props {
3
3
  subtitle?: string;
4
4
  dryRun?: boolean;
package/dist/ui/Header.js CHANGED
@@ -1,30 +1,30 @@
1
- import React from 'react';
2
- import { Box, Text } from 'ink';
3
- import { theme } from './theme.js';
4
- const TITLE = '\u2726 javi-forge Project scaffolding';
1
+ import { Box, Text } from "ink";
2
+ import React from "react";
3
+ import { theme } from "./theme.js";
4
+ const TITLE = "\u2726 javi-forge Project scaffolding";
5
5
  // Fixed inner width (characters between the box walls)
6
6
  const BOX_WIDTH = 41;
7
7
  function pad(content) {
8
8
  const len = [...content].length; // unicode-safe length
9
9
  const spaces = BOX_WIDTH - len;
10
- return content + ' '.repeat(Math.max(0, spaces));
10
+ return content + " ".repeat(Math.max(0, spaces));
11
11
  }
12
12
  export default function Header({ subtitle, dryRun }) {
13
- const top = '\u256d' + '\u2500'.repeat(BOX_WIDTH) + '\u256e';
14
- const bottom = '\u2570' + '\u2500'.repeat(BOX_WIDTH) + '\u256f';
15
- const titleLine = pad(' ' + TITLE + ' ');
16
- const subLine = subtitle ? pad(' ' + subtitle + ' ') : null;
13
+ const top = "\u256d" + "\u2500".repeat(BOX_WIDTH) + "\u256e";
14
+ const bottom = "\u2570" + "\u2500".repeat(BOX_WIDTH) + "\u256f";
15
+ const titleLine = pad(" " + TITLE + " ");
16
+ const subLine = subtitle ? pad(" " + subtitle + " ") : null;
17
17
  return (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
18
18
  React.createElement(Text, { color: theme.muted }, top),
19
19
  React.createElement(Box, null,
20
- React.createElement(Text, { color: theme.muted }, '\u2502'),
20
+ React.createElement(Text, { color: theme.muted }, "\u2502"),
21
21
  React.createElement(Text, { bold: true, color: theme.primary }, titleLine),
22
- React.createElement(Text, { color: theme.muted }, '\u2502')),
22
+ React.createElement(Text, { color: theme.muted }, "\u2502")),
23
23
  subLine && (React.createElement(Box, null,
24
- React.createElement(Text, { color: theme.muted }, '\u2502'),
24
+ React.createElement(Text, { color: theme.muted }, "\u2502"),
25
25
  React.createElement(Text, { color: theme.muted }, subLine),
26
- React.createElement(Text, { color: theme.muted }, '\u2502'))),
26
+ React.createElement(Text, { color: theme.muted }, "\u2502"))),
27
27
  React.createElement(Text, { color: theme.muted }, bottom),
28
- dryRun && (React.createElement(Text, { color: theme.warning }, " [DRY RUN]"))));
28
+ dryRun && React.createElement(Text, { color: theme.warning }, " [DRY RUN]")));
29
29
  }
30
30
  //# sourceMappingURL=Header.js.map
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import type { HookProfile } from "../types/index.js";
3
+ interface Props {
4
+ onConfirm: (profile: HookProfile) => void;
5
+ presetProfile?: HookProfile;
6
+ }
7
+ export default function HookProfileSelector({ onConfirm, presetProfile, }: Props): React.JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=HookProfileSelector.d.ts.map
@@ -0,0 +1,54 @@
1
+ import { Box, Text, useInput } from "ink";
2
+ import React, { useEffect, useState } from "react";
3
+ import { HOOK_PROFILES } from "../constants.js";
4
+ import { useCIMode } from "./CIContext.js";
5
+ import { theme } from "./theme.js";
6
+ const PROFILES = Object.entries(HOOK_PROFILES).map(([id, meta]) => ({
7
+ id,
8
+ label: meta.label,
9
+ description: meta.description,
10
+ }));
11
+ // Default selection index (standard = index 1)
12
+ const DEFAULT_INDEX = 1;
13
+ export default function HookProfileSelector({ onConfirm, presetProfile, }) {
14
+ const isCI = useCIMode();
15
+ const [cursor, setCursor] = useState(() => {
16
+ if (presetProfile) {
17
+ const idx = PROFILES.findIndex((p) => p.id === presetProfile);
18
+ return idx >= 0 ? idx : DEFAULT_INDEX;
19
+ }
20
+ return DEFAULT_INDEX;
21
+ });
22
+ // Auto-confirm in CI mode with default
23
+ useEffect(() => {
24
+ if (isCI) {
25
+ onConfirm(PROFILES[cursor].id);
26
+ }
27
+ }, [isCI]); // eslint-disable-line react-hooks/exhaustive-deps
28
+ useInput((_, key) => {
29
+ if (key.upArrow)
30
+ setCursor((c) => Math.max(0, c - 1));
31
+ if (key.downArrow)
32
+ setCursor((c) => Math.min(PROFILES.length - 1, c + 1));
33
+ if (key.return) {
34
+ onConfirm(PROFILES[cursor].id);
35
+ }
36
+ }, { isActive: !isCI });
37
+ return (React.createElement(Box, { flexDirection: "column" },
38
+ React.createElement(Text, { bold: true }, "Select hook reliability profile:"),
39
+ React.createElement(Box, { marginTop: 1, flexDirection: "column", borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, borderColor: theme.muted, paddingLeft: 1 }, PROFILES.map((p, i) => (React.createElement(Box, { key: p.id },
40
+ React.createElement(Text, { color: i === cursor ? theme.primary : "white" },
41
+ i === cursor ? "\u25b6 " : " ",
42
+ i === cursor ? "\u25c9" : "\u25cb",
43
+ " ",
44
+ p.label),
45
+ React.createElement(Text, { color: theme.muted, dimColor: true },
46
+ " ",
47
+ p.description))))),
48
+ React.createElement(Box, { marginTop: 1, gap: 2 },
49
+ React.createElement(Text, { color: theme.primary }, PROFILES[cursor].label),
50
+ React.createElement(Text, { color: theme.muted, dimColor: true },
51
+ "\u2191\u2193",
52
+ " navigate Enter confirm"))));
53
+ }
54
+ //# sourceMappingURL=HookProfileSelector.js.map
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React from "react";
2
2
  interface Props {
3
3
  projectDir: string;
4
4
  dryRun: boolean;
@@ -1,17 +1,19 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text } from 'ink';
3
- import Spinner from 'ink-spinner';
4
- import { generateLlmsTxt } from '../commands/llmstxt.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 { generateLlmsTxt } from "../commands/llmstxt.js";
5
+ import { theme } from "./theme.js";
6
6
  const STATUS_ICON = {
7
- done: '\u2713', error: '\u2717', skipped: '\u2013',
7
+ done: "\u2713",
8
+ error: "\u2717",
9
+ skipped: "\u2013",
8
10
  };
9
11
  export default function LlmsTxt({ projectDir, dryRun }) {
10
12
  const [steps, setSteps] = useState([]);
11
13
  const [done, setDone] = useState(false);
12
14
  const onStep = (step) => {
13
- setSteps(prev => {
14
- const idx = prev.findIndex(s => s.id === step.id);
15
+ setSteps((prev) => {
16
+ const idx = prev.findIndex((s) => s.id === step.id);
15
17
  if (idx >= 0) {
16
18
  const n = [...prev];
17
19
  n[idx] = step;
@@ -22,7 +24,12 @@ export default function LlmsTxt({ projectDir, dryRun }) {
22
24
  };
23
25
  useEffect(() => {
24
26
  generateLlmsTxt(projectDir, dryRun, onStep)
25
- .catch(e => onStep({ id: 'fatal', label: 'Error', status: 'error', detail: String(e) }))
27
+ .catch((e) => onStep({
28
+ id: "fatal",
29
+ label: "Error",
30
+ status: "error",
31
+ detail: String(e),
32
+ }))
26
33
  .finally(() => setDone(true));
27
34
  }, [projectDir, dryRun]);
28
35
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
@@ -30,19 +37,21 @@ export default function LlmsTxt({ projectDir, dryRun }) {
30
37
  React.createElement(Text, { bold: true, color: theme.primary }, "javi-forge"),
31
38
  React.createElement(Text, null, " llms-txt"),
32
39
  dryRun && React.createElement(Text, { color: theme.warning }, " (dry-run)")),
33
- steps.map(s => (React.createElement(Box, { key: s.id, marginLeft: 2 }, s.status === 'running'
34
- ? React.createElement(Text, { color: theme.warning },
35
- React.createElement(Spinner, { type: "dots" }),
40
+ steps.map((s) => (React.createElement(Box, { key: s.id, marginLeft: 2 }, s.status === "running" ? (React.createElement(Text, { color: theme.warning },
41
+ React.createElement(Spinner, { type: "dots" }),
42
+ " ",
43
+ s.label)) : (React.createElement(Text, { color: s.status === "done"
44
+ ? theme.success
45
+ : s.status === "error"
46
+ ? theme.error
47
+ : theme.muted },
48
+ STATUS_ICON[s.status],
49
+ " ",
50
+ s.label,
51
+ s.detail ? (React.createElement(Text, { color: theme.muted, dimColor: true },
36
52
  " ",
37
- s.label)
38
- : React.createElement(Text, { color: s.status === 'done' ? theme.success : s.status === 'error' ? theme.error : theme.muted },
39
- STATUS_ICON[s.status],
40
- " ",
41
- s.label,
42
- s.detail ? React.createElement(Text, { color: theme.muted, dimColor: true },
43
- " ",
44
- s.detail) : null)))),
45
- done && React.createElement(Box, { marginTop: 1 },
46
- React.createElement(Text, { color: theme.muted }, "Done."))));
53
+ s.detail)) : null))))),
54
+ done && (React.createElement(Box, { marginTop: 1 },
55
+ React.createElement(Text, { color: theme.muted }, "Done.")))));
47
56
  }
48
57
  //# sourceMappingURL=LlmsTxt.js.map
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
- import type { MemoryOption } from '../types/index.js';
1
+ import React from "react";
2
+ import type { MemoryOption } from "../types/index.js";
3
3
  interface Props {
4
4
  onConfirm: (memory: MemoryOption) => void;
5
5
  }