agentic-forge 0.0.0 → 0.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 (209) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +31 -21
  3. package/dist/checkpoints/manager.d.ts +5 -0
  4. package/dist/checkpoints/manager.js +87 -0
  5. package/dist/checkpoints/manager.js.map +1 -0
  6. package/{src → dist}/claude/.claude/skills/analyze/SKILL.md +1 -1
  7. package/{src → dist}/claude/.claude/skills/create-checkpoint/SKILL.md +1 -1
  8. package/{src → dist}/claude/.claude/skills/create-log/SKILL.md +1 -1
  9. package/{src → dist}/claude/.claude/skills/fix-analyze/SKILL.md +1 -1
  10. package/{src → dist}/claude/.claude/skills/git-branch/SKILL.md +1 -1
  11. package/{src → dist}/claude/.claude/skills/git-commit/SKILL.md +1 -1
  12. package/{src → dist}/claude/.claude/skills/git-pr/SKILL.md +1 -1
  13. package/{src → dist}/claude/.claude/skills/sdlc-plan/SKILL.md +1 -1
  14. package/{src → dist}/claude/.claude/skills/sdlc-review/SKILL.md +1 -1
  15. package/{src → dist}/claude/.claude/skills/workflow-builder/SKILL.md +1 -1
  16. package/dist/cli.d.ts +3 -0
  17. package/dist/cli.js +155 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/commands/config-cmd.d.ts +2 -0
  20. package/dist/commands/config-cmd.js +30 -0
  21. package/dist/commands/config-cmd.js.map +1 -0
  22. package/{src/commands/index.ts → dist/commands/index.d.ts} +1 -10
  23. package/dist/commands/index.js +13 -0
  24. package/dist/commands/index.js.map +1 -0
  25. package/dist/commands/init.d.ts +6 -0
  26. package/dist/commands/init.js +83 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/release-notes.d.ts +5 -0
  29. package/dist/commands/release-notes.js +68 -0
  30. package/dist/commands/release-notes.js.map +1 -0
  31. package/dist/commands/resume.d.ts +5 -0
  32. package/dist/commands/resume.js +79 -0
  33. package/dist/commands/resume.js.map +1 -0
  34. package/dist/commands/run.d.ts +14 -0
  35. package/dist/commands/run.js +193 -0
  36. package/dist/commands/run.js.map +1 -0
  37. package/dist/commands/shortcuts.d.ts +2 -0
  38. package/dist/commands/shortcuts.js +11 -0
  39. package/dist/commands/shortcuts.js.map +1 -0
  40. package/dist/commands/skills-dir.d.ts +2 -0
  41. package/dist/commands/skills-dir.js +9 -0
  42. package/dist/commands/skills-dir.js.map +1 -0
  43. package/dist/commands/status.d.ts +4 -0
  44. package/dist/commands/status.js +99 -0
  45. package/dist/commands/status.js.map +1 -0
  46. package/dist/commands/update.d.ts +4 -0
  47. package/dist/commands/update.js +65 -0
  48. package/dist/commands/update.js.map +1 -0
  49. package/dist/commands/version.d.ts +3 -0
  50. package/dist/commands/version.js +26 -0
  51. package/dist/commands/version.js.map +1 -0
  52. package/dist/commands/workflows.d.ts +4 -0
  53. package/dist/commands/workflows.js +109 -0
  54. package/dist/commands/workflows.js.map +1 -0
  55. package/dist/config.d.ts +8 -0
  56. package/dist/config.js +110 -0
  57. package/dist/config.js.map +1 -0
  58. package/dist/console.d.ts +103 -0
  59. package/dist/console.js +670 -0
  60. package/dist/console.js.map +1 -0
  61. package/dist/executor.d.ts +27 -0
  62. package/dist/executor.js +236 -0
  63. package/dist/executor.js.map +1 -0
  64. package/dist/git/worktree.d.ts +23 -0
  65. package/dist/git/worktree.js +170 -0
  66. package/dist/git/worktree.js.map +1 -0
  67. package/dist/logging/logger.d.ts +27 -0
  68. package/dist/logging/logger.js +69 -0
  69. package/dist/logging/logger.js.map +1 -0
  70. package/dist/orchestrator.d.ts +44 -0
  71. package/dist/orchestrator.js +587 -0
  72. package/dist/orchestrator.js.map +1 -0
  73. package/dist/parser.d.ts +17 -0
  74. package/dist/parser.js +184 -0
  75. package/dist/parser.js.map +1 -0
  76. package/dist/progress.d.ts +29 -0
  77. package/dist/progress.js +275 -0
  78. package/dist/progress.js.map +1 -0
  79. package/dist/ralph-loop.d.ts +26 -0
  80. package/dist/ralph-loop.js +194 -0
  81. package/dist/ralph-loop.js.map +1 -0
  82. package/dist/renderer.d.ts +15 -0
  83. package/dist/renderer.js +123 -0
  84. package/dist/renderer.js.map +1 -0
  85. package/dist/runner.d.ts +84 -0
  86. package/dist/runner.js +529 -0
  87. package/dist/runner.js.map +1 -0
  88. package/dist/signal-manager.d.ts +16 -0
  89. package/dist/signal-manager.js +50 -0
  90. package/dist/signal-manager.js.map +1 -0
  91. package/dist/steps/base.d.ts +28 -0
  92. package/dist/steps/base.js +23 -0
  93. package/dist/steps/base.js.map +1 -0
  94. package/dist/steps/conditional-step.d.ts +12 -0
  95. package/dist/steps/conditional-step.js +106 -0
  96. package/dist/steps/conditional-step.js.map +1 -0
  97. package/{src/steps/index.ts → dist/steps/index.d.ts} +1 -9
  98. package/dist/steps/index.js +8 -0
  99. package/dist/steps/index.js.map +1 -0
  100. package/dist/steps/parallel-step.d.ts +11 -0
  101. package/dist/steps/parallel-step.js +166 -0
  102. package/dist/steps/parallel-step.js.map +1 -0
  103. package/dist/steps/prompt-step.d.ts +8 -0
  104. package/dist/steps/prompt-step.js +94 -0
  105. package/dist/steps/prompt-step.js.map +1 -0
  106. package/dist/steps/ralph-loop-step.d.ts +8 -0
  107. package/dist/steps/ralph-loop-step.js +132 -0
  108. package/dist/steps/ralph-loop-step.js.map +1 -0
  109. package/dist/steps/serial-step.d.ts +10 -0
  110. package/dist/steps/serial-step.js +57 -0
  111. package/dist/steps/serial-step.js.map +1 -0
  112. package/dist/types.d.ts +118 -0
  113. package/dist/types.js +10 -0
  114. package/dist/types.js.map +1 -0
  115. package/package.json +56 -2
  116. package/.gitattributes +0 -24
  117. package/.github/workflows/ci.yml +0 -70
  118. package/.markdownlint-cli2.jsonc +0 -16
  119. package/.prettierignore +0 -3
  120. package/.prettierrc +0 -6
  121. package/.vscode/agentic-forge.code-workspace +0 -26
  122. package/CHANGELOG.md +0 -100
  123. package/CLAUDE.md +0 -158
  124. package/CONTRIBUTING.md +0 -152
  125. package/biome.json +0 -21
  126. package/scripts/copy-assets.js +0 -21
  127. package/src/checkpoints/manager.ts +0 -119
  128. package/src/cli.ts +0 -182
  129. package/src/commands/config-cmd.ts +0 -28
  130. package/src/commands/init.ts +0 -96
  131. package/src/commands/release-notes.ts +0 -85
  132. package/src/commands/resume.ts +0 -103
  133. package/src/commands/run.ts +0 -234
  134. package/src/commands/shortcuts.ts +0 -11
  135. package/src/commands/skills-dir.ts +0 -11
  136. package/src/commands/status.ts +0 -112
  137. package/src/commands/update.ts +0 -64
  138. package/src/commands/version.ts +0 -27
  139. package/src/commands/workflows.ts +0 -129
  140. package/src/config.ts +0 -129
  141. package/src/console.ts +0 -790
  142. package/src/executor.ts +0 -354
  143. package/src/git/worktree.ts +0 -236
  144. package/src/logging/logger.ts +0 -95
  145. package/src/orchestrator.ts +0 -815
  146. package/src/parser.ts +0 -225
  147. package/src/progress.ts +0 -306
  148. package/src/ralph-loop.ts +0 -260
  149. package/src/renderer.ts +0 -164
  150. package/src/runner.ts +0 -634
  151. package/src/signal-manager.ts +0 -55
  152. package/src/steps/base.ts +0 -71
  153. package/src/steps/conditional-step.ts +0 -144
  154. package/src/steps/parallel-step.ts +0 -213
  155. package/src/steps/prompt-step.ts +0 -121
  156. package/src/steps/ralph-loop-step.ts +0 -186
  157. package/src/steps/serial-step.ts +0 -84
  158. package/src/types.ts +0 -141
  159. package/tests/config.test.ts +0 -219
  160. package/tests/console.test.ts +0 -506
  161. package/tests/executor.test.ts +0 -339
  162. package/tests/init.test.ts +0 -86
  163. package/tests/logger.test.ts +0 -110
  164. package/tests/parser.test.ts +0 -290
  165. package/tests/progress.test.ts +0 -345
  166. package/tests/ralph-loop.test.ts +0 -418
  167. package/tests/renderer.test.ts +0 -350
  168. package/tests/runner.test.ts +0 -497
  169. package/tests/setup.test.ts +0 -7
  170. package/tests/signal-manager.test.ts +0 -26
  171. package/tests/steps.test.ts +0 -412
  172. package/tests/worktree.test.ts +0 -411
  173. package/tsconfig.json +0 -18
  174. package/vitest.config.ts +0 -8
  175. /package/{src → dist}/agents/explorer.md +0 -0
  176. /package/{src → dist}/agents/reviewer.md +0 -0
  177. /package/{src → dist}/claude/.claude/skills/analyze/references/bug.md +0 -0
  178. /package/{src → dist}/claude/.claude/skills/analyze/references/debt.md +0 -0
  179. /package/{src → dist}/claude/.claude/skills/analyze/references/doc.md +0 -0
  180. /package/{src → dist}/claude/.claude/skills/analyze/references/security.md +0 -0
  181. /package/{src → dist}/claude/.claude/skills/analyze/references/style.md +0 -0
  182. /package/{src → dist}/claude/.claude/skills/orchestrate/SKILL.md +0 -0
  183. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/bug.md +0 -0
  184. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/chore.md +0 -0
  185. /package/{src → dist}/claude/.claude/skills/sdlc-plan/references/feature.md +0 -0
  186. /package/{src → dist}/claude/.claude/skills/workflow-builder/references/REFERENCE.md +0 -0
  187. /package/{src → dist}/claude/.claude/skills/workflow-builder/references/workflow-example.yaml +0 -0
  188. /package/{src → dist}/prompts/agentic-system.md +0 -0
  189. /package/{src → dist}/templates/analysis/bug.md.j2 +0 -0
  190. /package/{src → dist}/templates/analysis/debt.md.j2 +0 -0
  191. /package/{src → dist}/templates/analysis/doc.md.j2 +0 -0
  192. /package/{src → dist}/templates/analysis/security.md.j2 +0 -0
  193. /package/{src → dist}/templates/analysis/style.md.j2 +0 -0
  194. /package/{src → dist}/templates/analysis-summary.md.j2 +0 -0
  195. /package/{src → dist}/templates/checkpoint.md.j2 +0 -0
  196. /package/{src → dist}/templates/implementation-report.md.j2 +0 -0
  197. /package/{src → dist}/templates/memory.md.j2 +0 -0
  198. /package/{src → dist}/templates/plan-bug.md.j2 +0 -0
  199. /package/{src → dist}/templates/plan-chore.md.j2 +0 -0
  200. /package/{src → dist}/templates/plan-feature.md.j2 +0 -0
  201. /package/{src → dist}/templates/progress.json.j2 +0 -0
  202. /package/{src → dist}/templates/ralph-report.md.j2 +0 -0
  203. /package/{src → dist}/workflows/analyze-codebase-merge.yaml +0 -0
  204. /package/{src → dist}/workflows/analyze-codebase.yaml +0 -0
  205. /package/{src → dist}/workflows/analyze-single.yaml +0 -0
  206. /package/{src → dist}/workflows/demo.yaml +0 -0
  207. /package/{src → dist}/workflows/one-shot.yaml +0 -0
  208. /package/{src → dist}/workflows/plan-build-review.yaml +0 -0
  209. /package/{src → dist}/workflows/ralph-loop.yaml +0 -0
@@ -1,350 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import path from "node:path";
4
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
- import {
6
- TemplateRenderer,
7
- buildTemplateContext,
8
- extractAnalysisSteps,
9
- extractFixSteps,
10
- renderWorkflowOutput,
11
- } from "../src/renderer.js";
12
-
13
- let tempDir: string;
14
-
15
- beforeEach(() => {
16
- tempDir = path.join(
17
- tmpdir(),
18
- `renderer-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
19
- );
20
- mkdirSync(tempDir, { recursive: true });
21
- });
22
-
23
- afterEach(() => {
24
- // Best-effort cleanup
25
- try {
26
- const { rmSync } = require("node:fs");
27
- rmSync(tempDir, { recursive: true, force: true });
28
- } catch {
29
- // Ignore cleanup errors on Windows
30
- }
31
- });
32
-
33
- describe("TemplateRenderer", () => {
34
- it("initializes with default template directory", () => {
35
- const renderer = new TemplateRenderer();
36
- expect(renderer.templateDirs.length).toBe(1);
37
- expect(renderer.templateDirs[0]).toContain("templates");
38
- });
39
-
40
- it("initializes with custom template directories", () => {
41
- const templatesDir = path.join(tempDir, "templates");
42
- mkdirSync(templatesDir, { recursive: true });
43
-
44
- const renderer = new TemplateRenderer([templatesDir]);
45
- expect(renderer.templateDirs).toContain(templatesDir);
46
- });
47
-
48
- it("renders basic template string", () => {
49
- const renderer = new TemplateRenderer();
50
- const result = renderer.renderString("Hello, {{ name }}!", { name: "World" });
51
- expect(result).toBe("Hello, World!");
52
- });
53
-
54
- it("renders template with loop", () => {
55
- const renderer = new TemplateRenderer();
56
- const template = "{% for item in items %}{{ item }}{% endfor %}";
57
- const result = renderer.renderString(template, { items: ["a", "b", "c"] });
58
- expect(result).toBe("abc");
59
- });
60
-
61
- it("renders template with conditionals", () => {
62
- const renderer = new TemplateRenderer();
63
- const template = "{% if enabled %}ON{% else %}OFF{% endif %}";
64
-
65
- expect(renderer.renderString(template, { enabled: true })).toBe("ON");
66
- expect(renderer.renderString(template, { enabled: false })).toBe("OFF");
67
- });
68
-
69
- it("handles undefined variables in lenient mode (default)", () => {
70
- const renderer = new TemplateRenderer();
71
- // In nunjucks lenient mode, undefined variables render as empty string
72
- const result = renderer.renderString("{{ undefined_var }}", {});
73
- expect(result).toBe("");
74
- });
75
-
76
- it("raises error for undefined variables in strict mode", () => {
77
- const renderer = new TemplateRenderer(undefined, true);
78
- expect(() => renderer.renderString("{{ undefined_var }}", {})).toThrow();
79
- });
80
-
81
- it("handles undefined vars in loops (returns empty)", () => {
82
- const renderer = new TemplateRenderer();
83
- const result = renderer.renderString("{% for x in undefined_list %}{{ x }}{% endfor %}", {});
84
- expect(result).toBe("");
85
- });
86
-
87
- it("renders nested variable access", () => {
88
- const renderer = new TemplateRenderer();
89
- const template = "{{ user.name }} - {{ user.email }}";
90
- const context = { user: { name: "Alice", email: "alice@example.com" } };
91
- const result = renderer.renderString(template, context);
92
- expect(result).toBe("Alice - alice@example.com");
93
- });
94
-
95
- it("renders from template file", () => {
96
- const templatesDir = path.join(tempDir, "templates");
97
- mkdirSync(templatesDir, { recursive: true });
98
- writeFileSync(path.join(templatesDir, "test.j2"), "Name: {{ name }}");
99
-
100
- const renderer = new TemplateRenderer([templatesDir]);
101
- const result = renderer.render("test.j2", { name: "Test" });
102
- expect(result).toBe("Name: Test");
103
- });
104
-
105
- it("has_variables returns true for templates", () => {
106
- const renderer = new TemplateRenderer();
107
- expect(renderer.hasVariables("{{ variable }}")).toBe(true);
108
- expect(renderer.hasVariables("{% if x %}{% endif %}")).toBe(true);
109
- });
110
-
111
- it("has_variables returns false for plain text", () => {
112
- const renderer = new TemplateRenderer();
113
- expect(renderer.hasVariables("plain text")).toBe(false);
114
- expect(renderer.hasVariables("no templates here")).toBe(false);
115
- });
116
-
117
- it("adds template directory", () => {
118
- const newDir = path.join(tempDir, "new_templates");
119
- mkdirSync(newDir, { recursive: true });
120
-
121
- const renderer = new TemplateRenderer();
122
- const originalCount = renderer.templateDirs.length;
123
-
124
- renderer.addTemplateDir(newDir);
125
-
126
- expect(renderer.templateDirs.length).toBe(originalCount + 1);
127
- expect(renderer.templateDirs).toContain(newDir);
128
- });
129
-
130
- it("ignores nonexistent template directory", () => {
131
- const nonexistent = path.join(tempDir, "nonexistent");
132
- const renderer = new TemplateRenderer();
133
- const originalCount = renderer.templateDirs.length;
134
-
135
- renderer.addTemplateDir(nonexistent);
136
-
137
- expect(renderer.templateDirs.length).toBe(originalCount);
138
- });
139
-
140
- it("ignores duplicate template directory", () => {
141
- const newDir = path.join(tempDir, "templates");
142
- mkdirSync(newDir, { recursive: true });
143
-
144
- const renderer = new TemplateRenderer([newDir]);
145
- renderer.addTemplateDir(newDir);
146
-
147
- expect(renderer.templateDirs.filter((d) => d === newDir).length).toBe(1);
148
- });
149
- });
150
-
151
- describe("sandboxed environment", () => {
152
- it("allows normal operations", () => {
153
- const renderer = new TemplateRenderer();
154
-
155
- const result = renderer.renderString("{{ name | upper }}", { name: "test" });
156
- expect(result).toBe("TEST");
157
-
158
- const result2 = renderer.renderString("{{ items | length }}", { items: [1, 2, 3] });
159
- expect(result2).toBe("3");
160
- });
161
-
162
- it("allows standard filters", () => {
163
- const renderer = new TemplateRenderer();
164
-
165
- expect(renderer.renderString("{{ x | default('N/A') }}", {})).toBe("N/A");
166
- expect(renderer.renderString("{{ 'test' | capitalize }}", {})).toBe("Test");
167
- expect(renderer.renderString("{{ [1,2,3] | join('-') }}", {})).toBe("1-2-3");
168
- });
169
- });
170
-
171
- describe("extractAnalysisSteps", () => {
172
- it("extracts analysis steps from outputs", () => {
173
- const stepOutputs = {
174
- "analyze-bug": { issues: 5 },
175
- "analyze-debt": { issues: 3 },
176
- "analyze-security": { issues: 2 },
177
- "other-step": { data: "ignored" },
178
- };
179
-
180
- const result = extractAnalysisSteps(stepOutputs);
181
-
182
- expect(result["analyze-bug"]).toBeDefined();
183
- expect(result["analyze-debt"]).toBeDefined();
184
- expect(result["analyze-security"]).toBeDefined();
185
- expect(result["other-step"]).toBeUndefined();
186
- });
187
-
188
- it("filters invalid analysis types", () => {
189
- const stepOutputs = {
190
- "analyze-bug": { issues: 5 },
191
- "analyze-invalid": { issues: 3 },
192
- "analyze-and-fix-all": { container: true },
193
- };
194
-
195
- const result = extractAnalysisSteps(stepOutputs);
196
-
197
- expect(result["analyze-bug"]).toBeDefined();
198
- expect(result["analyze-invalid"]).toBeUndefined();
199
- expect(result["analyze-and-fix-all"]).toBeUndefined();
200
- });
201
-
202
- it("returns empty for empty outputs", () => {
203
- expect(extractAnalysisSteps({})).toEqual({});
204
- });
205
-
206
- it("recognizes all analysis types", () => {
207
- const stepOutputs = {
208
- "analyze-bug": { type: "bug" },
209
- "analyze-debt": { type: "debt" },
210
- "analyze-doc": { type: "doc" },
211
- "analyze-security": { type: "security" },
212
- "analyze-style": { type: "style" },
213
- };
214
-
215
- const result = extractAnalysisSteps(stepOutputs);
216
- expect(Object.keys(result).length).toBe(5);
217
- });
218
- });
219
-
220
- describe("extractFixSteps", () => {
221
- it("extracts fix steps from outputs", () => {
222
- const stepOutputs = {
223
- "fix-bugs": { fixed: 3 },
224
- "apply-fixes": { applied: 5 },
225
- "other-step": { ignored: true },
226
- };
227
-
228
- const result = extractFixSteps(stepOutputs);
229
-
230
- expect(result["fix-bugs"]).toBeDefined();
231
- expect(result["apply-fixes"]).toBeDefined();
232
- expect(result["other-step"]).toBeUndefined();
233
- });
234
-
235
- it("returns empty for empty outputs", () => {
236
- expect(extractFixSteps({})).toEqual({});
237
- });
238
- });
239
-
240
- describe("buildTemplateContext", () => {
241
- it("builds basic template context", () => {
242
- const context = buildTemplateContext(
243
- "test-workflow",
244
- "test-workflow-20260111-143000",
245
- "2026-01-11T14:30:00Z",
246
- "2026-01-11T15:00:00Z",
247
- { step1: { result: "ok" } },
248
- ["file1.py", "file2.py"],
249
- ["feature/test"],
250
- [{ number: 123, url: "https://github.com/test" }],
251
- { var: "value" },
252
- );
253
-
254
- expect(context.workflow).toEqual({
255
- name: "test-workflow",
256
- id: "test-workflow-20260111-143000",
257
- started_at: "2026-01-11T14:30:00Z",
258
- completed_at: "2026-01-11T15:00:00Z",
259
- });
260
- expect(context.workflow_id).toBe("test-workflow-20260111-143000");
261
- expect(context.steps).toEqual({ step1: { result: "ok" } });
262
- expect(context.files_changed).toEqual(["file1.py", "file2.py"]);
263
- expect(context.branches).toEqual(["feature/test"]);
264
- expect((context.pull_requests as unknown[]).length).toBe(1);
265
- expect(context.inputs).toEqual({ var: "value" });
266
- });
267
-
268
- it("extracts analysis steps into context", () => {
269
- const context = buildTemplateContext(
270
- "test",
271
- "test-123",
272
- "",
273
- null,
274
- {
275
- "analyze-bug": { issues: 5 },
276
- "other-step": { data: "ignored" },
277
- },
278
- [],
279
- [],
280
- [],
281
- {},
282
- );
283
-
284
- const analysisSteps = context.analysis_steps as Record<string, unknown>;
285
- expect(analysisSteps["analyze-bug"]).toBeDefined();
286
- expect(analysisSteps["other-step"]).toBeUndefined();
287
- });
288
-
289
- it("extracts fix steps into context", () => {
290
- const context = buildTemplateContext(
291
- "test",
292
- "test-123",
293
- "",
294
- null,
295
- {
296
- "fix-bugs": { fixed: 3 },
297
- "apply-fixes": { applied: 2 },
298
- },
299
- [],
300
- [],
301
- [],
302
- {},
303
- );
304
-
305
- const fixSteps = context.fix_steps as Record<string, unknown>;
306
- expect(fixSteps["fix-bugs"]).toBeDefined();
307
- expect(fixSteps["apply-fixes"]).toBeDefined();
308
- });
309
- });
310
-
311
- describe("renderWorkflowOutput", () => {
312
- it("creates output file from template", () => {
313
- const templatesDir = path.join(tempDir, "templates");
314
- mkdirSync(templatesDir, { recursive: true });
315
- writeFileSync(path.join(templatesDir, "output.j2"), "Workflow: {{ workflow.name }}");
316
-
317
- const outputPath = path.join(tempDir, "output", "result.md");
318
- const context = { workflow: { name: "test-workflow" } };
319
-
320
- renderWorkflowOutput("output.j2", outputPath, context, [templatesDir]);
321
-
322
- expect(existsSync(outputPath)).toBe(true);
323
- expect(readFileSync(outputPath, "utf-8")).toBe("Workflow: test-workflow");
324
- });
325
-
326
- it("creates necessary directories", () => {
327
- const templatesDir = path.join(tempDir, "templates");
328
- mkdirSync(templatesDir, { recursive: true });
329
- writeFileSync(path.join(templatesDir, "output.j2"), "Content");
330
-
331
- const outputPath = path.join(tempDir, "deep", "nested", "output.md");
332
-
333
- renderWorkflowOutput("output.j2", outputPath, {}, [templatesDir]);
334
-
335
- expect(existsSync(outputPath)).toBe(true);
336
- });
337
-
338
- it("renders with absolute template path", () => {
339
- const templateFile = path.join(tempDir, "absolute_template.j2");
340
- writeFileSync(templateFile, "Value: {{ value }}");
341
-
342
- const outputPath = path.join(tempDir, "output.md");
343
- const context = { value: "test" };
344
-
345
- renderWorkflowOutput(templateFile, outputPath, context);
346
-
347
- expect(existsSync(outputPath)).toBe(true);
348
- expect(readFileSync(outputPath, "utf-8")).toBe("Value: test");
349
- });
350
- });