javi-forge 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.md +191 -3
  2. package/ci-local/hooks/pre-push +17 -13
  3. package/dist/commands/analyze.d.ts +1 -1
  4. package/dist/commands/analyze.js +15 -15
  5. package/dist/commands/atlassian-mcp.d.ts +42 -0
  6. package/dist/commands/atlassian-mcp.js +98 -0
  7. package/dist/commands/ci.d.ts +3 -3
  8. package/dist/commands/ci.js +185 -147
  9. package/dist/commands/crash-recovery.d.ts +34 -0
  10. package/dist/commands/crash-recovery.js +123 -0
  11. package/dist/commands/doctor.d.ts +2 -2
  12. package/dist/commands/doctor.js +113 -61
  13. package/dist/commands/harness-audit.d.ts +35 -0
  14. package/dist/commands/harness-audit.js +277 -0
  15. package/dist/commands/init.d.ts +1 -1
  16. package/dist/commands/init.js +415 -118
  17. package/dist/commands/llmstxt.d.ts +1 -1
  18. package/dist/commands/llmstxt.js +36 -34
  19. package/dist/commands/parallel-batch.d.ts +42 -0
  20. package/dist/commands/parallel-batch.js +90 -0
  21. package/dist/commands/plugin.d.ts +26 -1
  22. package/dist/commands/plugin.js +138 -24
  23. package/dist/commands/secret-scanner.d.ts +30 -0
  24. package/dist/commands/secret-scanner.js +272 -0
  25. package/dist/commands/security-analysis.d.ts +74 -0
  26. package/dist/commands/security-analysis.js +487 -0
  27. package/dist/commands/security.d.ts +31 -0
  28. package/dist/commands/security.js +445 -0
  29. package/dist/commands/skill-scanner.d.ts +63 -0
  30. package/dist/commands/skill-scanner.js +383 -0
  31. package/dist/commands/skills.d.ts +139 -0
  32. package/dist/commands/skills.js +895 -0
  33. package/dist/commands/supply-chain.d.ts +23 -0
  34. package/dist/commands/supply-chain.js +126 -0
  35. package/dist/commands/tdd-pipeline.d.ts +17 -0
  36. package/dist/commands/tdd-pipeline.js +144 -0
  37. package/dist/commands/tdd.d.ts +21 -0
  38. package/dist/commands/tdd.js +120 -0
  39. package/dist/commands/team-presets.d.ts +53 -0
  40. package/dist/commands/team-presets.js +201 -0
  41. package/dist/commands/workflow.d.ts +23 -0
  42. package/dist/commands/workflow.js +114 -0
  43. package/dist/constants.d.ts +21 -0
  44. package/dist/constants.js +208 -37
  45. package/dist/index.js +400 -54
  46. package/dist/lib/agent-skills.d.ts +73 -0
  47. package/dist/lib/agent-skills.js +260 -0
  48. package/dist/lib/auto-skill-install.d.ts +37 -0
  49. package/dist/lib/auto-skill-install.js +92 -0
  50. package/dist/lib/auto-wire.d.ts +20 -0
  51. package/dist/lib/auto-wire.js +240 -0
  52. package/dist/lib/claudemd.d.ts +20 -0
  53. package/dist/lib/claudemd.js +222 -0
  54. package/dist/lib/codex-export.d.ts +16 -0
  55. package/dist/lib/codex-export.js +109 -0
  56. package/dist/lib/common.d.ts +1 -1
  57. package/dist/lib/common.js +52 -44
  58. package/dist/lib/context.d.ts +27 -0
  59. package/dist/lib/context.js +204 -0
  60. package/dist/lib/docker.d.ts +1 -1
  61. package/dist/lib/docker.js +141 -112
  62. package/dist/lib/frontmatter.d.ts +1 -1
  63. package/dist/lib/frontmatter.js +29 -15
  64. package/dist/lib/plugin.d.ts +19 -1
  65. package/dist/lib/plugin.js +174 -47
  66. package/dist/lib/skill-publish.d.ts +40 -0
  67. package/dist/lib/skill-publish.js +146 -0
  68. package/dist/lib/stack-detector.d.ts +38 -0
  69. package/dist/lib/stack-detector.js +207 -0
  70. package/dist/lib/template.d.ts +16 -1
  71. package/dist/lib/template.js +46 -17
  72. package/dist/lib/workflow/discovery.d.ts +19 -0
  73. package/dist/lib/workflow/discovery.js +68 -0
  74. package/dist/lib/workflow/index.d.ts +5 -0
  75. package/dist/lib/workflow/index.js +5 -0
  76. package/dist/lib/workflow/parser.d.ts +16 -0
  77. package/dist/lib/workflow/parser.js +198 -0
  78. package/dist/lib/workflow/renderer.d.ts +9 -0
  79. package/dist/lib/workflow/renderer.js +152 -0
  80. package/dist/lib/workflow/validator.d.ts +10 -0
  81. package/dist/lib/workflow/validator.js +189 -0
  82. package/dist/tasks/index.d.ts +4 -0
  83. package/dist/tasks/index.js +4 -0
  84. package/dist/tasks/scaffold-tasks.d.ts +3 -0
  85. package/dist/tasks/scaffold-tasks.js +14 -0
  86. package/dist/tasks/task-id.d.ts +30 -0
  87. package/dist/tasks/task-id.js +55 -0
  88. package/dist/tasks/task-tracker.d.ts +15 -0
  89. package/dist/tasks/task-tracker.js +81 -0
  90. package/dist/types/index.d.ts +252 -5
  91. package/dist/types/index.js +11 -1
  92. package/dist/ui/AnalyzeUI.d.ts +1 -1
  93. package/dist/ui/AnalyzeUI.js +38 -39
  94. package/dist/ui/App.d.ts +5 -3
  95. package/dist/ui/App.js +92 -46
  96. package/dist/ui/AutoSkills.d.ts +9 -0
  97. package/dist/ui/AutoSkills.js +124 -0
  98. package/dist/ui/CI.d.ts +2 -2
  99. package/dist/ui/CI.js +24 -26
  100. package/dist/ui/CIContext.d.ts +1 -1
  101. package/dist/ui/CIContext.js +3 -2
  102. package/dist/ui/CISelector.d.ts +2 -2
  103. package/dist/ui/CISelector.js +23 -15
  104. package/dist/ui/Doctor.d.ts +1 -1
  105. package/dist/ui/Doctor.js +35 -29
  106. package/dist/ui/Header.d.ts +1 -1
  107. package/dist/ui/Header.js +14 -14
  108. package/dist/ui/HookProfileSelector.d.ts +9 -0
  109. package/dist/ui/HookProfileSelector.js +54 -0
  110. package/dist/ui/LlmsTxt.d.ts +1 -1
  111. package/dist/ui/LlmsTxt.js +31 -22
  112. package/dist/ui/MemorySelector.d.ts +2 -2
  113. package/dist/ui/MemorySelector.js +28 -16
  114. package/dist/ui/NameInput.d.ts +1 -1
  115. package/dist/ui/NameInput.js +21 -21
  116. package/dist/ui/OptionSelector.d.ts +8 -2
  117. package/dist/ui/OptionSelector.js +83 -26
  118. package/dist/ui/Plugin.d.ts +4 -3
  119. package/dist/ui/Plugin.js +89 -29
  120. package/dist/ui/Progress.d.ts +3 -3
  121. package/dist/ui/Progress.js +23 -22
  122. package/dist/ui/Skills.d.ts +11 -0
  123. package/dist/ui/Skills.js +148 -0
  124. package/dist/ui/StackSelector.d.ts +2 -2
  125. package/dist/ui/StackSelector.js +26 -16
  126. package/dist/ui/Summary.d.ts +3 -3
  127. package/dist/ui/Summary.js +60 -50
  128. package/dist/ui/Welcome.d.ts +1 -1
  129. package/dist/ui/Welcome.js +15 -16
  130. package/dist/ui/theme.d.ts +1 -1
  131. package/dist/ui/theme.js +6 -6
  132. package/package.json +9 -6
  133. package/templates/common/atlassian/mcp-atlassian-snippet.json +16 -0
  134. package/templates/common/repoforge/mcp-repoforge-snippet.json +11 -0
  135. package/templates/common/repoforge/repoforge.yaml +34 -0
  136. package/templates/github/deploy-docker-zero-downtime.yml +140 -0
  137. package/templates/github/repoforge-graph.yml +45 -0
  138. package/templates/gitlab/deploy-docker-zero-downtime.yml +57 -0
  139. package/templates/local-ai/.env.example +17 -0
  140. package/templates/local-ai/docker-compose.yml +95 -0
  141. package/templates/security-hooks/claude-settings-security.json +30 -0
  142. package/templates/security-hooks/commit-msg-signing +29 -0
  143. package/templates/security-hooks/pre-commit-permissions +74 -0
  144. package/templates/security-hooks/pre-commit-secrets +74 -0
  145. package/templates/security-hooks/pre-push-branch-protection +62 -0
  146. package/templates/security-hooks/pre-push-deps +83 -0
  147. package/templates/security-hooks/pre-push-signing +67 -0
  148. package/templates/woodpecker/deploy-docker-zero-downtime.yml +50 -0
  149. package/templates/workflows/ci-pipeline.dot +15 -0
  150. package/templates/workflows/feature-flow.dot +21 -0
  151. package/templates/workflows/release.dot +16 -0
  152. package/dist/__integration__/helpers.d.ts +0 -20
  153. package/dist/__integration__/helpers.d.ts.map +0 -1
  154. package/dist/__integration__/helpers.js +0 -31
  155. package/dist/__integration__/helpers.js.map +0 -1
  156. package/dist/commands/analyze.d.ts.map +0 -1
  157. package/dist/commands/analyze.js.map +0 -1
  158. package/dist/commands/ci.d.ts.map +0 -1
  159. package/dist/commands/ci.js.map +0 -1
  160. package/dist/commands/doctor.d.ts.map +0 -1
  161. package/dist/commands/doctor.js.map +0 -1
  162. package/dist/commands/init.d.ts.map +0 -1
  163. package/dist/commands/init.js.map +0 -1
  164. package/dist/commands/llmstxt.d.ts.map +0 -1
  165. package/dist/commands/llmstxt.js.map +0 -1
  166. package/dist/commands/plugin.d.ts.map +0 -1
  167. package/dist/commands/plugin.js.map +0 -1
  168. package/dist/constants.d.ts.map +0 -1
  169. package/dist/constants.js.map +0 -1
  170. package/dist/index.d.ts.map +0 -1
  171. package/dist/index.js.map +0 -1
  172. package/dist/lib/common.d.ts.map +0 -1
  173. package/dist/lib/common.js.map +0 -1
  174. package/dist/lib/docker.d.ts.map +0 -1
  175. package/dist/lib/docker.js.map +0 -1
  176. package/dist/lib/frontmatter.d.ts.map +0 -1
  177. package/dist/lib/frontmatter.js.map +0 -1
  178. package/dist/lib/plugin.d.ts.map +0 -1
  179. package/dist/lib/plugin.js.map +0 -1
  180. package/dist/lib/template.d.ts.map +0 -1
  181. package/dist/lib/template.js.map +0 -1
  182. package/dist/types/index.d.ts.map +0 -1
  183. package/dist/types/index.js.map +0 -1
  184. package/dist/ui/AnalyzeUI.d.ts.map +0 -1
  185. package/dist/ui/AnalyzeUI.js.map +0 -1
  186. package/dist/ui/App.d.ts.map +0 -1
  187. package/dist/ui/App.js.map +0 -1
  188. package/dist/ui/CI.d.ts.map +0 -1
  189. package/dist/ui/CI.js.map +0 -1
  190. package/dist/ui/CIContext.d.ts.map +0 -1
  191. package/dist/ui/CIContext.js.map +0 -1
  192. package/dist/ui/CISelector.d.ts.map +0 -1
  193. package/dist/ui/CISelector.js.map +0 -1
  194. package/dist/ui/Doctor.d.ts.map +0 -1
  195. package/dist/ui/Doctor.js.map +0 -1
  196. package/dist/ui/Header.d.ts.map +0 -1
  197. package/dist/ui/Header.js.map +0 -1
  198. package/dist/ui/LlmsTxt.d.ts.map +0 -1
  199. package/dist/ui/LlmsTxt.js.map +0 -1
  200. package/dist/ui/MemorySelector.d.ts.map +0 -1
  201. package/dist/ui/MemorySelector.js.map +0 -1
  202. package/dist/ui/NameInput.d.ts.map +0 -1
  203. package/dist/ui/NameInput.js.map +0 -1
  204. package/dist/ui/OptionSelector.d.ts.map +0 -1
  205. package/dist/ui/OptionSelector.js.map +0 -1
  206. package/dist/ui/Plugin.d.ts.map +0 -1
  207. package/dist/ui/Plugin.js.map +0 -1
  208. package/dist/ui/Progress.d.ts.map +0 -1
  209. package/dist/ui/Progress.js.map +0 -1
  210. package/dist/ui/StackSelector.d.ts.map +0 -1
  211. package/dist/ui/StackSelector.js.map +0 -1
  212. package/dist/ui/Summary.d.ts.map +0 -1
  213. package/dist/ui/Summary.js.map +0 -1
  214. package/dist/ui/Welcome.d.ts.map +0 -1
  215. package/dist/ui/Welcome.js.map +0 -1
  216. package/dist/ui/theme.d.ts.map +0 -1
  217. package/dist/ui/theme.js.map +0 -1
@@ -0,0 +1,222 @@
1
+ import { STACK_CLAUDEMD_MAP } from "../constants.js";
2
+ // =============================================================================
3
+ // Architecture pattern mapping (signal → patterns)
4
+ // =============================================================================
5
+ const ARCHITECTURE_PATTERNS = {
6
+ "react-19": [
7
+ "Container-Presentational pattern",
8
+ "Atomic Design for component hierarchy",
9
+ ],
10
+ "nextjs-15": [
11
+ "App Router file-based routing",
12
+ "Server Components by default, client opt-in",
13
+ ],
14
+ "django-drf": ["ViewSets + Serializers", "URL namespace per app"],
15
+ "zustand-5": [
16
+ "Slice pattern for store modules",
17
+ "Selectors for derived state",
18
+ ],
19
+ typescript: [
20
+ "Strict mode enabled",
21
+ "Prefer interfaces for public APIs, types for unions",
22
+ ],
23
+ "tailwind-4": ["Utility-first CSS", "Use `cn()` for conditional classes"],
24
+ "zod-4": [
25
+ "Schema-first validation",
26
+ "Infer types from schemas with `z.infer`",
27
+ ],
28
+ playwright: [
29
+ "Page Object Model for E2E tests",
30
+ "Locator-based selectors over CSS",
31
+ ],
32
+ pytest: ["Fixtures for setup/teardown", "Parametrize for data-driven tests"],
33
+ "ai-sdk-5": [
34
+ "Streaming responses by default",
35
+ "Tool calling with structured schemas",
36
+ ],
37
+ };
38
+ // =============================================================================
39
+ // Plugin instruction mapping (detected skill → plugin hints)
40
+ // =============================================================================
41
+ const PLUGIN_INSTRUCTIONS = {
42
+ "react-19": "Check `~/.claude/plugins/` for merge-checks and mermaid diagram support",
43
+ "nextjs-15": "Check `~/.claude/plugins/` for merge-checks and mermaid diagram support",
44
+ typescript: "Run `javi-forge skills doctor` to validate skill compatibility",
45
+ playwright: "Use `javi-forge tdd` for test-driven development workflow",
46
+ };
47
+ // =============================================================================
48
+ // Internal helpers
49
+ // =============================================================================
50
+ function getStackClaudeMd(stack) {
51
+ return STACK_CLAUDEMD_MAP[stack] ?? STACK_CLAUDEMD_MAP["default"];
52
+ }
53
+ export function buildClaudeMd(projectName, stack, entry, contextDir, modules) {
54
+ const lines = [];
55
+ lines.push(`# ${projectName}`);
56
+ lines.push("");
57
+ // Stack section
58
+ lines.push("## Stack");
59
+ lines.push(`- **Language/Runtime**: ${stack}`);
60
+ lines.push(`- **Conventions**: ${entry.conventions}`);
61
+ lines.push(`- **Testing**: ${entry.testFramework}`);
62
+ lines.push("");
63
+ // Skills section
64
+ if (entry.skills.length > 0) {
65
+ lines.push("## Recommended Skills");
66
+ for (const skill of entry.skills) {
67
+ lines.push(`- Load \`~/.claude/skills/${skill}/SKILL.md\` before writing code`);
68
+ }
69
+ lines.push("");
70
+ }
71
+ // Project Context section (conditional on contextDir)
72
+ if (contextDir) {
73
+ lines.push("## Project Context");
74
+ lines.push("- See `.context/INDEX.md` for file structure and entry points");
75
+ lines.push("- See `.context/summary.md` for project overview");
76
+ lines.push("");
77
+ }
78
+ // Conventions section
79
+ lines.push("## Conventions");
80
+ lines.push(`- ${entry.conventions}`);
81
+ lines.push("- Scaffolded with javi-forge — see `.javi-forge/manifest.json` for config");
82
+ lines.push("");
83
+ // Modules section
84
+ if (modules.length > 0) {
85
+ lines.push("## Modules");
86
+ lines.push(`- ${modules.join(", ")}`);
87
+ lines.push("");
88
+ }
89
+ return lines.join("\n");
90
+ }
91
+ /**
92
+ * Build an enriched CLAUDE.md using detected skills, architecture patterns,
93
+ * and plugin instructions. Pure function — no I/O.
94
+ */
95
+ export function buildSmartClaudeMd(projectName, stack, entry, detectedSkills, contextDir, modules) {
96
+ const lines = [];
97
+ // Merge: detected skills + static entry skills (deduplicated)
98
+ const allSkills = [...new Set([...detectedSkills, ...entry.skills])].sort();
99
+ lines.push(`# ${projectName}`);
100
+ lines.push("");
101
+ // Stack section
102
+ lines.push("## Stack");
103
+ lines.push(`- **Language/Runtime**: ${stack}`);
104
+ lines.push(`- **Conventions**: ${entry.conventions}`);
105
+ lines.push(`- **Testing**: ${entry.testFramework}`);
106
+ lines.push("");
107
+ // Skills section — auto-detected + static
108
+ if (allSkills.length > 0) {
109
+ lines.push("## Skills (auto-detected)");
110
+ lines.push("");
111
+ lines.push("Load these skill files BEFORE writing code in this project:");
112
+ lines.push("");
113
+ for (const skill of allSkills) {
114
+ lines.push(`- \`~/.claude/skills/${skill}/SKILL.md\``);
115
+ }
116
+ lines.push("");
117
+ }
118
+ // Architecture patterns section — derived from detected skills
119
+ const patterns = [];
120
+ for (const skill of allSkills) {
121
+ const skillPatterns = ARCHITECTURE_PATTERNS[skill];
122
+ if (skillPatterns) {
123
+ patterns.push(...skillPatterns);
124
+ }
125
+ }
126
+ if (patterns.length > 0) {
127
+ lines.push("## Architecture Patterns");
128
+ lines.push("");
129
+ for (const pattern of patterns) {
130
+ lines.push(`- ${pattern}`);
131
+ }
132
+ lines.push("");
133
+ }
134
+ // Project Context section
135
+ if (contextDir) {
136
+ lines.push("## Project Context");
137
+ lines.push("- See `.context/INDEX.md` for file structure and entry points");
138
+ lines.push("- See `.context/summary.md` for project overview");
139
+ lines.push("");
140
+ }
141
+ // Conventions section
142
+ lines.push("## Conventions");
143
+ lines.push(`- ${entry.conventions}`);
144
+ lines.push("- Scaffolded with javi-forge — see `.javi-forge/manifest.json` for config");
145
+ lines.push("");
146
+ // Plugin instructions — derived from detected skills
147
+ const pluginHints = new Set();
148
+ for (const skill of allSkills) {
149
+ const hint = PLUGIN_INSTRUCTIONS[skill];
150
+ if (hint)
151
+ pluginHints.add(hint);
152
+ }
153
+ if (pluginHints.size > 0) {
154
+ lines.push("## Plugins");
155
+ lines.push("");
156
+ for (const hint of pluginHints) {
157
+ lines.push(`- ${hint}`);
158
+ }
159
+ lines.push("");
160
+ }
161
+ // Modules section
162
+ if (modules.length > 0) {
163
+ lines.push("## Modules");
164
+ lines.push(`- ${modules.join(", ")}`);
165
+ lines.push("");
166
+ }
167
+ return lines.join("\n");
168
+ }
169
+ // =============================================================================
170
+ // Public API
171
+ // =============================================================================
172
+ /**
173
+ * Generate CLAUDE.md content from InitOptions metadata.
174
+ * Pure function — does NOT perform filesystem I/O.
175
+ */
176
+ export function generateClaudeMd(options) {
177
+ const { projectName, stack, contextDir } = options;
178
+ const entry = getStackClaudeMd(stack);
179
+ // Collect enabled modules for listing
180
+ const modules = [];
181
+ if (options.aiSync)
182
+ modules.push("ai-sync");
183
+ if (options.sdd)
184
+ modules.push("sdd");
185
+ if (options.ghagga)
186
+ modules.push("ghagga");
187
+ if (options.mock)
188
+ modules.push("mock");
189
+ if (options.contextDir)
190
+ modules.push("context");
191
+ if (options.claudeMd)
192
+ modules.push("claude-md");
193
+ return buildClaudeMd(projectName, stack, entry, contextDir, modules);
194
+ }
195
+ /**
196
+ * Generate a project-aware CLAUDE.md using stack detection results.
197
+ * Falls back to static generation if no detection result provided.
198
+ * Pure function — does NOT perform filesystem I/O.
199
+ */
200
+ export function generateSmartClaudeMd(options, detection) {
201
+ // Fallback: no detection result, use static generation
202
+ if (!detection || detection.recommendedSkills.length === 0) {
203
+ return generateClaudeMd(options);
204
+ }
205
+ const { projectName, stack, contextDir } = options;
206
+ const entry = getStackClaudeMd(stack);
207
+ const modules = [];
208
+ if (options.aiSync)
209
+ modules.push("ai-sync");
210
+ if (options.sdd)
211
+ modules.push("sdd");
212
+ if (options.ghagga)
213
+ modules.push("ghagga");
214
+ if (options.mock)
215
+ modules.push("mock");
216
+ if (options.contextDir)
217
+ modules.push("context");
218
+ if (options.claudeMd)
219
+ modules.push("claude-md");
220
+ return buildSmartClaudeMd(projectName, stack, entry, detection.recommendedSkills, contextDir, modules);
221
+ }
222
+ //# sourceMappingURL=claudemd.js.map
@@ -0,0 +1,16 @@
1
+ import type { CodexExportResult, CodexTomlEntry } from "../types/index.js";
2
+ /**
3
+ * Convert a SKILL.md raw content string into a CodexTomlEntry.
4
+ * Returns null if the file has no valid frontmatter or missing name.
5
+ */
6
+ export declare function skillToCodexToml(raw: string): CodexTomlEntry | null;
7
+ /**
8
+ * Serialize a CodexTomlEntry to a TOML string.
9
+ */
10
+ export declare function serializeToml(entry: CodexTomlEntry): string;
11
+ /**
12
+ * Export an installed plugin's SKILL.md files as Codex-compatible TOML subagent files.
13
+ * Writes .toml files to a `codex/` subdirectory inside the plugin directory.
14
+ */
15
+ export declare function exportPluginAsCodexToml(name: string): Promise<CodexExportResult>;
16
+ //# sourceMappingURL=codex-export.d.ts.map
@@ -0,0 +1,109 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import { PLUGIN_MANIFEST_FILE, PLUGINS_DIR } from "../constants.js";
4
+ import { parseFrontmatter } from "./frontmatter.js";
5
+ const DEFAULT_MODEL = "o4-mini";
6
+ // ── Conversion ─────────────────────────────────────────────────────────────
7
+ /**
8
+ * Convert a SKILL.md raw content string into a CodexTomlEntry.
9
+ * Returns null if the file has no valid frontmatter or missing name.
10
+ */
11
+ export function skillToCodexToml(raw) {
12
+ const parsed = parseFrontmatter(raw);
13
+ if (!parsed)
14
+ return null;
15
+ const name = parsed.data["name"];
16
+ if (typeof name !== "string" || !name)
17
+ return null;
18
+ const description = typeof parsed.data["description"] === "string"
19
+ ? parsed.data["description"]
20
+ : "";
21
+ const model = typeof parsed.data["model"] === "string"
22
+ ? parsed.data["model"]
23
+ : DEFAULT_MODEL;
24
+ const instructions = description
25
+ ? `${description}\n\n${parsed.content}`.trim()
26
+ : parsed.content.trim();
27
+ return { name, model, instructions };
28
+ }
29
+ /**
30
+ * Serialize a CodexTomlEntry to a TOML string.
31
+ */
32
+ export function serializeToml(entry) {
33
+ const escapedName = escapeTomlString(entry.name);
34
+ const escapedModel = escapeTomlString(entry.model);
35
+ const escapedInstructions = escapeTomlMultiline(entry.instructions);
36
+ return [
37
+ `name = "${escapedName}"`,
38
+ `model = "${escapedModel}"`,
39
+ `instructions = """`,
40
+ escapedInstructions,
41
+ `"""`,
42
+ "",
43
+ ].join("\n");
44
+ }
45
+ // ── File I/O ───────────────────────────────────────────────────────────────
46
+ /**
47
+ * Export an installed plugin's SKILL.md files as Codex-compatible TOML subagent files.
48
+ * Writes .toml files to a `codex/` subdirectory inside the plugin directory.
49
+ */
50
+ export async function exportPluginAsCodexToml(name) {
51
+ const pluginDir = path.join(PLUGINS_DIR, name);
52
+ if (!(await fs.pathExists(pluginDir))) {
53
+ return { success: false, error: `plugin "${name}" is not installed` };
54
+ }
55
+ // Read plugin manifest for skill list
56
+ const manifestPath = path.join(pluginDir, PLUGIN_MANIFEST_FILE);
57
+ if (!(await fs.pathExists(manifestPath))) {
58
+ return { success: false, error: "plugin.json not found" };
59
+ }
60
+ let manifest;
61
+ try {
62
+ manifest = (await fs.readJson(manifestPath));
63
+ }
64
+ catch {
65
+ return { success: false, error: "invalid plugin.json" };
66
+ }
67
+ const skillNames = manifest.skills ?? [];
68
+ if (skillNames.length === 0) {
69
+ return { success: false, error: "plugin has no skills to export" };
70
+ }
71
+ const codexDir = path.join(pluginDir, "codex");
72
+ await fs.ensureDir(codexDir);
73
+ const writtenFiles = [];
74
+ for (const skillName of skillNames) {
75
+ const skillPath = path.join(pluginDir, "skills", skillName, "SKILL.md");
76
+ if (!(await fs.pathExists(skillPath)))
77
+ continue;
78
+ let raw;
79
+ try {
80
+ raw = await fs.readFile(skillPath, "utf-8");
81
+ }
82
+ catch {
83
+ continue;
84
+ }
85
+ const entry = skillToCodexToml(raw);
86
+ if (!entry)
87
+ continue;
88
+ const toml = serializeToml(entry);
89
+ const outPath = path.join(codexDir, `${skillName}.toml`);
90
+ await fs.writeFile(outPath, toml, "utf-8");
91
+ writtenFiles.push(outPath);
92
+ }
93
+ if (writtenFiles.length === 0) {
94
+ return { success: false, error: "no skills with valid frontmatter found" };
95
+ }
96
+ return { success: true, files: writtenFiles };
97
+ }
98
+ // ── Helpers ────────────────────────────────────────────────────────────────
99
+ function escapeTomlString(value) {
100
+ return value
101
+ .replace(/\\/g, "\\\\")
102
+ .replace(/"/g, '\\"')
103
+ .replace(/\n/g, "\\n");
104
+ }
105
+ function escapeTomlMultiline(value) {
106
+ // In TOML multiline basic strings ("""), only backslashes and triple quotes need escaping
107
+ return value.replace(/\\/g, "\\\\").replace(/"""/g, '"\\""');
108
+ }
109
+ //# sourceMappingURL=codex-export.js.map
@@ -1,4 +1,4 @@
1
- import type { Stack, StackDetection } from '../types/index.js';
1
+ import type { Stack, StackDetection } from "../types/index.js";
2
2
  /**
3
3
  * Detect the project stack by looking for well-known build/config files.
4
4
  * Returns the first match (precedence order matters).
@@ -1,5 +1,5 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
3
  /**
4
4
  * Detect the project stack by looking for well-known build/config files.
5
5
  * Returns the first match (precedence order matters).
@@ -7,70 +7,78 @@ import path from 'path';
7
7
  export async function detectStack(projectDir) {
8
8
  const exists = async (file) => fs.pathExists(path.join(projectDir, file));
9
9
  // Java — Gradle
10
- if (await exists('build.gradle') || await exists('build.gradle.kts')) {
10
+ if ((await exists("build.gradle")) || (await exists("build.gradle.kts"))) {
11
11
  const javaVersion = await detectJavaVersion(projectDir);
12
- return { stackType: 'java-gradle', buildTool: 'gradle', javaVersion };
12
+ return { stackType: "java-gradle", buildTool: "gradle", javaVersion };
13
13
  }
14
14
  // Java — Maven
15
- if (await exists('pom.xml')) {
15
+ if (await exists("pom.xml")) {
16
16
  const javaVersion = await detectJavaVersion(projectDir);
17
- return { stackType: 'java-maven', buildTool: 'maven', javaVersion };
17
+ return { stackType: "java-maven", buildTool: "maven", javaVersion };
18
18
  }
19
19
  // Node.js
20
- if (await exists('package.json')) {
21
- const pkg = await fs.readJson(path.join(projectDir, 'package.json')).catch(() => ({}));
22
- const buildTool = await exists('pnpm-lock.yaml') ? 'pnpm'
23
- : await exists('yarn.lock') ? 'yarn'
24
- : 'npm';
25
- return { stackType: 'node', buildTool };
20
+ if (await exists("package.json")) {
21
+ const pkg = await fs
22
+ .readJson(path.join(projectDir, "package.json"))
23
+ .catch(() => ({}));
24
+ const buildTool = (await exists("pnpm-lock.yaml"))
25
+ ? "pnpm"
26
+ : (await exists("yarn.lock"))
27
+ ? "yarn"
28
+ : "npm";
29
+ return { stackType: "node", buildTool };
26
30
  }
27
31
  // Python
28
- if (await exists('pyproject.toml') || await exists('requirements.txt') || await exists('setup.py')) {
29
- const buildTool = await exists('pyproject.toml') ? 'pyproject'
30
- : await exists('Pipfile') ? 'pipenv'
31
- : 'pip';
32
- return { stackType: 'python', buildTool };
32
+ if ((await exists("pyproject.toml")) ||
33
+ (await exists("requirements.txt")) ||
34
+ (await exists("setup.py"))) {
35
+ const buildTool = (await exists("pyproject.toml"))
36
+ ? "pyproject"
37
+ : (await exists("Pipfile"))
38
+ ? "pipenv"
39
+ : "pip";
40
+ return { stackType: "python", buildTool };
33
41
  }
34
42
  // Go
35
- if (await exists('go.mod')) {
36
- return { stackType: 'go', buildTool: 'go' };
43
+ if (await exists("go.mod")) {
44
+ return { stackType: "go", buildTool: "go" };
37
45
  }
38
46
  // Rust
39
- if (await exists('Cargo.toml')) {
40
- return { stackType: 'rust', buildTool: 'cargo' };
47
+ if (await exists("Cargo.toml")) {
48
+ return { stackType: "rust", buildTool: "cargo" };
41
49
  }
42
50
  // Elixir
43
- if (await exists('mix.exs')) {
44
- return { stackType: 'elixir', buildTool: 'mix' };
51
+ if (await exists("mix.exs")) {
52
+ return { stackType: "elixir", buildTool: "mix" };
45
53
  }
46
54
  return null;
47
55
  }
48
56
  /** Try to detect Java version from gradle or maven config */
49
57
  async function detectJavaVersion(projectDir) {
50
58
  // Try build.gradle
51
- const gradleFile = path.join(projectDir, 'build.gradle');
59
+ const gradleFile = path.join(projectDir, "build.gradle");
52
60
  if (await fs.pathExists(gradleFile)) {
53
- const content = await fs.readFile(gradleFile, 'utf-8');
54
- const match = content.match(/sourceCompatibility\s*=\s*['"]?(\d+)/)
55
- || content.match(/JavaVersion\.VERSION_(\d+)/);
61
+ const content = await fs.readFile(gradleFile, "utf-8");
62
+ const match = content.match(/sourceCompatibility\s*=\s*['"]?(\d+)/) ||
63
+ content.match(/JavaVersion\.VERSION_(\d+)/);
56
64
  if (match)
57
65
  return match[1];
58
66
  }
59
67
  // Try build.gradle.kts
60
- const ktsFile = path.join(projectDir, 'build.gradle.kts');
68
+ const ktsFile = path.join(projectDir, "build.gradle.kts");
61
69
  if (await fs.pathExists(ktsFile)) {
62
- const content = await fs.readFile(ktsFile, 'utf-8');
63
- const match = content.match(/jvmTarget\s*=\s*['"](\d+)['"]/)
64
- || content.match(/JavaVersion\.VERSION_(\d+)/);
70
+ const content = await fs.readFile(ktsFile, "utf-8");
71
+ const match = content.match(/jvmTarget\s*=\s*['"](\d+)['"]/) ||
72
+ content.match(/JavaVersion\.VERSION_(\d+)/);
65
73
  if (match)
66
74
  return match[1];
67
75
  }
68
76
  // Try pom.xml
69
- const pomFile = path.join(projectDir, 'pom.xml');
77
+ const pomFile = path.join(projectDir, "pom.xml");
70
78
  if (await fs.pathExists(pomFile)) {
71
- const content = await fs.readFile(pomFile, 'utf-8');
72
- const match = content.match(/<java\.version>(\d+)</)
73
- || content.match(/<maven\.compiler\.source>(\d+)</);
79
+ const content = await fs.readFile(pomFile, "utf-8");
80
+ const match = content.match(/<java\.version>(\d+)</) ||
81
+ content.match(/<maven\.compiler\.source>(\d+)</);
74
82
  if (match)
75
83
  return match[1];
76
84
  }
@@ -90,22 +98,22 @@ export async function ensureDirExists(dirPath) {
90
98
  }
91
99
  /** Check if a directory is a git repository */
92
100
  export async function isGitRepo(dir) {
93
- return fs.pathExists(path.join(dir, '.git'));
101
+ return fs.pathExists(path.join(dir, ".git"));
94
102
  }
95
103
  /** Resolve the forge assets root (the package root directory) */
96
104
  export function getForgeRoot() {
97
105
  // When running from dist/, go up one level
98
106
  const thisDir = path.dirname(new URL(import.meta.url).pathname);
99
- return path.resolve(thisDir, '..');
107
+ return path.resolve(thisDir, "..");
100
108
  }
101
109
  /** Stack display names */
102
110
  export const STACK_LABELS = {
103
- 'node': 'Node.js / TypeScript',
104
- 'python': 'Python',
105
- 'go': 'Go',
106
- 'rust': 'Rust',
107
- 'java-gradle': 'Java (Gradle)',
108
- 'java-maven': 'Java (Maven)',
109
- 'elixir': 'Elixir',
111
+ node: "Node.js / TypeScript",
112
+ python: "Python",
113
+ go: "Go",
114
+ rust: "Rust",
115
+ "java-gradle": "Java (Gradle)",
116
+ "java-maven": "Java (Maven)",
117
+ elixir: "Elixir",
110
118
  };
111
119
  //# sourceMappingURL=common.js.map
@@ -0,0 +1,27 @@
1
+ import type { InitOptions, StackContextEntry } from "../types/index.js";
2
+ export declare function buildIndexMd(projectName: string, stackCtx: StackContextEntry, ciProvider: string, memory: string): string;
3
+ export declare function buildSummaryMd(projectName: string, stack: string, ciProvider: string, memory: string, modules: string[], dependencies?: string[]): string;
4
+ /**
5
+ * Detect top-level dependencies from project manifest files.
6
+ * Returns up to 10 dependency names (key deps only, not devDeps).
7
+ */
8
+ export declare function detectDependencies(projectDir: string, stack: string): Promise<string[]>;
9
+ /**
10
+ * Generate .context/ directory content from InitOptions metadata.
11
+ * Pure function — does NOT perform filesystem I/O.
12
+ */
13
+ export declare function generateContextDir(options: InitOptions): Promise<{
14
+ index: string;
15
+ summary: string;
16
+ }>;
17
+ /**
18
+ * Refresh .context/ directory from the forge manifest and live project state.
19
+ * Reads manifest, detects current dependencies, regenerates INDEX.md + summary.md.
20
+ * Returns null if the project is not forge-managed or has no .context/ dir.
21
+ */
22
+ export declare function refreshContextDir(projectDir: string): Promise<{
23
+ index: string;
24
+ summary: string;
25
+ updated: boolean;
26
+ } | null>;
27
+ //# sourceMappingURL=context.d.ts.map