@outputai/cli 0.1.3 → 0.1.4-dev.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 (278) hide show
  1. package/dist/api/generated/api.d.ts +820 -0
  2. package/dist/api/generated/api.js +226 -0
  3. package/dist/api/http_client.d.ts +27 -0
  4. package/dist/api/http_client.js +71 -0
  5. package/dist/api/orval_post_process.d.ts +11 -0
  6. package/dist/api/orval_post_process.js +46 -0
  7. package/dist/api/parser.d.ts +17 -0
  8. package/dist/api/parser.js +68 -0
  9. package/dist/assets/config/costs.yml +309 -0
  10. package/dist/assets/docker/docker-compose-dev.yml +146 -0
  11. package/dist/commands/credentials/edit.d.ts +10 -0
  12. package/dist/commands/credentials/edit.js +67 -0
  13. package/dist/commands/credentials/edit.spec.d.ts +1 -0
  14. package/dist/commands/credentials/edit.spec.js +73 -0
  15. package/dist/commands/credentials/get.d.ts +13 -0
  16. package/dist/commands/credentials/get.js +46 -0
  17. package/dist/commands/credentials/get.spec.d.ts +1 -0
  18. package/dist/commands/credentials/get.spec.js +74 -0
  19. package/dist/commands/credentials/init.d.ts +11 -0
  20. package/dist/commands/credentials/init.js +45 -0
  21. package/dist/commands/credentials/init.spec.d.ts +1 -0
  22. package/dist/commands/credentials/init.spec.js +68 -0
  23. package/dist/commands/credentials/show.d.ts +10 -0
  24. package/dist/commands/credentials/show.js +33 -0
  25. package/dist/commands/credentials/show.spec.d.ts +1 -0
  26. package/dist/commands/credentials/show.spec.js +57 -0
  27. package/dist/commands/dev/eject.d.ts +11 -0
  28. package/dist/commands/dev/eject.js +58 -0
  29. package/dist/commands/dev/eject.spec.d.ts +1 -0
  30. package/dist/commands/dev/eject.spec.js +109 -0
  31. package/dist/commands/dev/index.d.ts +14 -0
  32. package/dist/commands/dev/index.js +173 -0
  33. package/dist/commands/dev/index.spec.d.ts +1 -0
  34. package/dist/commands/dev/index.spec.js +239 -0
  35. package/dist/commands/init.d.ts +12 -0
  36. package/dist/commands/init.js +37 -0
  37. package/dist/commands/init.spec.d.ts +1 -0
  38. package/dist/commands/init.spec.js +100 -0
  39. package/dist/commands/update.d.ts +14 -0
  40. package/dist/commands/update.js +120 -0
  41. package/dist/commands/update.spec.d.ts +1 -0
  42. package/dist/commands/update.spec.js +178 -0
  43. package/dist/commands/workflow/cost.d.ts +16 -0
  44. package/dist/commands/workflow/cost.js +71 -0
  45. package/dist/commands/workflow/cost.spec.d.ts +1 -0
  46. package/dist/commands/workflow/cost.spec.js +47 -0
  47. package/dist/commands/workflow/dataset/generate.d.ts +22 -0
  48. package/dist/commands/workflow/dataset/generate.js +143 -0
  49. package/dist/commands/workflow/dataset/list.d.ts +12 -0
  50. package/dist/commands/workflow/dataset/list.js +87 -0
  51. package/dist/commands/workflow/debug.d.ts +16 -0
  52. package/dist/commands/workflow/debug.js +60 -0
  53. package/dist/commands/workflow/debug.spec.d.ts +1 -0
  54. package/dist/commands/workflow/debug.spec.js +34 -0
  55. package/dist/commands/workflow/generate.d.ts +17 -0
  56. package/dist/commands/workflow/generate.js +85 -0
  57. package/dist/commands/workflow/generate.spec.d.ts +1 -0
  58. package/dist/commands/workflow/generate.spec.js +115 -0
  59. package/dist/commands/workflow/list.d.ts +22 -0
  60. package/dist/commands/workflow/list.js +152 -0
  61. package/dist/commands/workflow/list.spec.d.ts +1 -0
  62. package/dist/commands/workflow/list.spec.js +99 -0
  63. package/dist/commands/workflow/plan.d.ts +12 -0
  64. package/dist/commands/workflow/plan.js +66 -0
  65. package/dist/commands/workflow/plan.spec.d.ts +1 -0
  66. package/dist/commands/workflow/plan.spec.js +341 -0
  67. package/dist/commands/workflow/reset.d.ts +14 -0
  68. package/dist/commands/workflow/reset.js +51 -0
  69. package/dist/commands/workflow/result.d.ts +13 -0
  70. package/dist/commands/workflow/result.js +46 -0
  71. package/dist/commands/workflow/result.spec.d.ts +1 -0
  72. package/dist/commands/workflow/result.spec.js +23 -0
  73. package/dist/commands/workflow/run.d.ts +16 -0
  74. package/dist/commands/workflow/run.js +97 -0
  75. package/dist/commands/workflow/run.spec.d.ts +1 -0
  76. package/dist/commands/workflow/run.spec.js +110 -0
  77. package/dist/commands/workflow/runs/list.d.ts +14 -0
  78. package/dist/commands/workflow/runs/list.js +104 -0
  79. package/dist/commands/workflow/start.d.ts +15 -0
  80. package/dist/commands/workflow/start.js +62 -0
  81. package/dist/commands/workflow/start.spec.d.ts +1 -0
  82. package/dist/commands/workflow/start.spec.js +28 -0
  83. package/dist/commands/workflow/status.d.ts +13 -0
  84. package/dist/commands/workflow/status.js +57 -0
  85. package/dist/commands/workflow/status.spec.d.ts +1 -0
  86. package/dist/commands/workflow/status.spec.js +33 -0
  87. package/dist/commands/workflow/stop.d.ts +10 -0
  88. package/dist/commands/workflow/stop.js +31 -0
  89. package/dist/commands/workflow/stop.spec.d.ts +1 -0
  90. package/dist/commands/workflow/stop.spec.js +17 -0
  91. package/dist/commands/workflow/terminate.d.ts +13 -0
  92. package/dist/commands/workflow/terminate.js +39 -0
  93. package/dist/commands/workflow/test_eval.d.ts +20 -0
  94. package/dist/commands/workflow/test_eval.js +151 -0
  95. package/dist/config.d.ts +47 -0
  96. package/dist/config.js +47 -0
  97. package/dist/generated/framework_version.json +3 -0
  98. package/dist/hooks/init.d.ts +3 -0
  99. package/dist/hooks/init.js +30 -0
  100. package/dist/hooks/init.spec.d.ts +1 -0
  101. package/dist/hooks/init.spec.js +54 -0
  102. package/dist/index.d.ts +1 -0
  103. package/dist/index.js +1 -0
  104. package/dist/index.spec.d.ts +1 -0
  105. package/dist/index.spec.js +6 -0
  106. package/dist/services/claude_client.d.ts +30 -0
  107. package/dist/services/claude_client.integration.test.d.ts +1 -0
  108. package/dist/services/claude_client.integration.test.js +43 -0
  109. package/dist/services/claude_client.js +215 -0
  110. package/dist/services/claude_client.spec.d.ts +1 -0
  111. package/dist/services/claude_client.spec.js +145 -0
  112. package/dist/services/coding_agents.d.ts +36 -0
  113. package/dist/services/coding_agents.js +236 -0
  114. package/dist/services/coding_agents.spec.d.ts +1 -0
  115. package/dist/services/coding_agents.spec.js +256 -0
  116. package/dist/services/copy_assets.spec.d.ts +1 -0
  117. package/dist/services/copy_assets.spec.js +22 -0
  118. package/dist/services/cost_calculator.d.ts +18 -0
  119. package/dist/services/cost_calculator.js +359 -0
  120. package/dist/services/cost_calculator.spec.d.ts +1 -0
  121. package/dist/services/cost_calculator.spec.js +540 -0
  122. package/dist/services/credentials_service.d.ts +12 -0
  123. package/dist/services/credentials_service.integration.test.d.ts +1 -0
  124. package/dist/services/credentials_service.integration.test.js +66 -0
  125. package/dist/services/credentials_service.js +64 -0
  126. package/dist/services/credentials_service.spec.d.ts +1 -0
  127. package/dist/services/credentials_service.spec.js +106 -0
  128. package/dist/services/datasets.d.ts +20 -0
  129. package/dist/services/datasets.js +132 -0
  130. package/dist/services/docker.d.ts +39 -0
  131. package/dist/services/docker.js +160 -0
  132. package/dist/services/docker.spec.d.ts +1 -0
  133. package/dist/services/docker.spec.js +124 -0
  134. package/dist/services/env_configurator.d.ts +15 -0
  135. package/dist/services/env_configurator.js +163 -0
  136. package/dist/services/env_configurator.spec.d.ts +1 -0
  137. package/dist/services/env_configurator.spec.js +192 -0
  138. package/dist/services/generate_plan_name@v1.prompt +24 -0
  139. package/dist/services/messages.d.ts +9 -0
  140. package/dist/services/messages.js +338 -0
  141. package/dist/services/messages.spec.d.ts +1 -0
  142. package/dist/services/messages.spec.js +55 -0
  143. package/dist/services/npm_update_service.d.ts +6 -0
  144. package/dist/services/npm_update_service.js +87 -0
  145. package/dist/services/npm_update_service.spec.d.ts +1 -0
  146. package/dist/services/npm_update_service.spec.js +104 -0
  147. package/dist/services/project_scaffold.d.ts +31 -0
  148. package/dist/services/project_scaffold.js +212 -0
  149. package/dist/services/project_scaffold.spec.d.ts +1 -0
  150. package/dist/services/project_scaffold.spec.js +122 -0
  151. package/dist/services/s3_trace_downloader.d.ts +12 -0
  152. package/dist/services/s3_trace_downloader.js +57 -0
  153. package/dist/services/template_processor.d.ts +14 -0
  154. package/dist/services/template_processor.js +57 -0
  155. package/dist/services/trace_reader.d.ts +16 -0
  156. package/dist/services/trace_reader.js +57 -0
  157. package/dist/services/trace_reader.spec.d.ts +1 -0
  158. package/dist/services/trace_reader.spec.js +78 -0
  159. package/dist/services/version_check.d.ts +6 -0
  160. package/dist/services/version_check.js +52 -0
  161. package/dist/services/version_check.spec.d.ts +1 -0
  162. package/dist/services/version_check.spec.js +106 -0
  163. package/dist/services/workflow_builder.d.ts +16 -0
  164. package/dist/services/workflow_builder.js +86 -0
  165. package/dist/services/workflow_builder.spec.d.ts +1 -0
  166. package/dist/services/workflow_builder.spec.js +165 -0
  167. package/dist/services/workflow_generator.d.ts +5 -0
  168. package/dist/services/workflow_generator.js +40 -0
  169. package/dist/services/workflow_generator.spec.d.ts +1 -0
  170. package/dist/services/workflow_generator.spec.js +77 -0
  171. package/dist/services/workflow_planner.d.ts +15 -0
  172. package/dist/services/workflow_planner.js +48 -0
  173. package/dist/services/workflow_planner.spec.d.ts +1 -0
  174. package/dist/services/workflow_planner.spec.js +122 -0
  175. package/dist/services/workflow_runs.d.ts +14 -0
  176. package/dist/services/workflow_runs.js +25 -0
  177. package/dist/templates/agent_instructions/CLAUDE.md.template +19 -0
  178. package/dist/templates/agent_instructions/dotclaude/settings.json.template +29 -0
  179. package/dist/templates/project/.env.example.template +9 -0
  180. package/dist/templates/project/.gitignore.template +35 -0
  181. package/dist/templates/project/README.md.template +100 -0
  182. package/dist/templates/project/config/costs.yml.template +29 -0
  183. package/dist/templates/project/package.json.template +25 -0
  184. package/dist/templates/project/src/clients/jina.ts.template +30 -0
  185. package/dist/templates/project/src/shared/utils/string.ts.template +3 -0
  186. package/dist/templates/project/src/shared/utils/url.ts.template +15 -0
  187. package/dist/templates/project/src/workflows/blog_evaluator/evaluators.ts.template +23 -0
  188. package/dist/templates/project/src/workflows/blog_evaluator/prompts/signal_noise@v1.prompt.template +26 -0
  189. package/dist/templates/project/src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json.template +3 -0
  190. package/dist/templates/project/src/workflows/blog_evaluator/steps.ts.template +27 -0
  191. package/dist/templates/project/src/workflows/blog_evaluator/types.ts.template +30 -0
  192. package/dist/templates/project/src/workflows/blog_evaluator/utils.ts.template +15 -0
  193. package/dist/templates/project/src/workflows/blog_evaluator/workflow.ts.template +27 -0
  194. package/dist/templates/project/tsconfig.json.template +20 -0
  195. package/dist/templates/workflow/README.md.template +216 -0
  196. package/dist/templates/workflow/evaluators.ts.template +21 -0
  197. package/dist/templates/workflow/prompts/example@v1.prompt.template +15 -0
  198. package/dist/templates/workflow/scenarios/test_input.json.template +3 -0
  199. package/dist/templates/workflow/steps.ts.template +20 -0
  200. package/dist/templates/workflow/types.ts.template +13 -0
  201. package/dist/templates/workflow/workflow.ts.template +23 -0
  202. package/dist/test_helpers/mocks.d.ts +38 -0
  203. package/dist/test_helpers/mocks.js +77 -0
  204. package/dist/types/cost.d.ts +149 -0
  205. package/dist/types/cost.js +6 -0
  206. package/dist/types/domain.d.ts +20 -0
  207. package/dist/types/domain.js +4 -0
  208. package/dist/types/errors.d.ts +68 -0
  209. package/dist/types/errors.js +100 -0
  210. package/dist/types/errors.spec.d.ts +1 -0
  211. package/dist/types/errors.spec.js +18 -0
  212. package/dist/types/generator.d.ts +26 -0
  213. package/dist/types/generator.js +1 -0
  214. package/dist/types/trace.d.ts +161 -0
  215. package/dist/types/trace.js +18 -0
  216. package/dist/utils/claude.d.ts +5 -0
  217. package/dist/utils/claude.js +19 -0
  218. package/dist/utils/claude.spec.d.ts +1 -0
  219. package/dist/utils/claude.spec.js +119 -0
  220. package/dist/utils/constants.d.ts +5 -0
  221. package/dist/utils/constants.js +4 -0
  222. package/dist/utils/cost_formatter.d.ts +5 -0
  223. package/dist/utils/cost_formatter.js +218 -0
  224. package/dist/utils/date_formatter.d.ts +23 -0
  225. package/dist/utils/date_formatter.js +49 -0
  226. package/dist/utils/env_loader.d.ts +1 -0
  227. package/dist/utils/env_loader.js +22 -0
  228. package/dist/utils/env_loader.spec.d.ts +1 -0
  229. package/dist/utils/env_loader.spec.js +43 -0
  230. package/dist/utils/error_handler.d.ts +8 -0
  231. package/dist/utils/error_handler.js +71 -0
  232. package/dist/utils/error_utils.d.ts +24 -0
  233. package/dist/utils/error_utils.js +87 -0
  234. package/dist/utils/file_system.d.ts +3 -0
  235. package/dist/utils/file_system.js +33 -0
  236. package/dist/utils/format_workflow_result.d.ts +5 -0
  237. package/dist/utils/format_workflow_result.js +18 -0
  238. package/dist/utils/format_workflow_result.spec.d.ts +1 -0
  239. package/dist/utils/format_workflow_result.spec.js +81 -0
  240. package/dist/utils/framework_version.d.ts +4 -0
  241. package/dist/utils/framework_version.js +4 -0
  242. package/dist/utils/framework_version.spec.d.ts +1 -0
  243. package/dist/utils/framework_version.spec.js +13 -0
  244. package/dist/utils/header_utils.d.ts +12 -0
  245. package/dist/utils/header_utils.js +29 -0
  246. package/dist/utils/header_utils.spec.d.ts +1 -0
  247. package/dist/utils/header_utils.spec.js +52 -0
  248. package/dist/utils/input_parser.d.ts +1 -0
  249. package/dist/utils/input_parser.js +19 -0
  250. package/dist/utils/output_formatter.d.ts +2 -0
  251. package/dist/utils/output_formatter.js +11 -0
  252. package/dist/utils/paths.d.ts +25 -0
  253. package/dist/utils/paths.js +36 -0
  254. package/dist/utils/process.d.ts +4 -0
  255. package/dist/utils/process.js +50 -0
  256. package/dist/utils/resolve_input.d.ts +1 -0
  257. package/dist/utils/resolve_input.js +22 -0
  258. package/dist/utils/scenario_resolver.d.ts +9 -0
  259. package/dist/utils/scenario_resolver.js +93 -0
  260. package/dist/utils/scenario_resolver.spec.d.ts +1 -0
  261. package/dist/utils/scenario_resolver.spec.js +214 -0
  262. package/dist/utils/secret_sanitizer.d.ts +1 -0
  263. package/dist/utils/secret_sanitizer.js +29 -0
  264. package/dist/utils/sleep.d.ts +5 -0
  265. package/dist/utils/sleep.js +5 -0
  266. package/dist/utils/template.d.ts +9 -0
  267. package/dist/utils/template.js +30 -0
  268. package/dist/utils/template.spec.d.ts +1 -0
  269. package/dist/utils/template.spec.js +77 -0
  270. package/dist/utils/trace_extractor.d.ts +27 -0
  271. package/dist/utils/trace_extractor.js +53 -0
  272. package/dist/utils/trace_formatter.d.ts +11 -0
  273. package/dist/utils/trace_formatter.js +402 -0
  274. package/dist/utils/validation.d.ts +13 -0
  275. package/dist/utils/validation.js +25 -0
  276. package/dist/utils/validation.spec.d.ts +1 -0
  277. package/dist/utils/validation.spec.js +140 -0
  278. package/package.json +4 -4
@@ -0,0 +1,52 @@
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { fetchLatestVersion, isOutdated } from '#services/npm_update_service.js';
4
+ const CACHE_TTL_MS = 4 * 60 * 60 * 1000; // 4 hours
5
+ const CACHE_FILENAME = 'version_check.json';
6
+ async function readCache(cacheDir, currentVersion) {
7
+ try {
8
+ const raw = await readFile(join(cacheDir, CACHE_FILENAME), 'utf-8');
9
+ const cache = JSON.parse(raw);
10
+ if (Date.now() - cache.timestamp > CACHE_TTL_MS) {
11
+ return null;
12
+ }
13
+ if (cache.result.currentVersion !== currentVersion) {
14
+ return null;
15
+ }
16
+ return cache.result;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ async function writeCache(cacheDir, result) {
23
+ try {
24
+ await mkdir(cacheDir, { recursive: true });
25
+ const cache = { timestamp: Date.now(), result };
26
+ await writeFile(join(cacheDir, CACHE_FILENAME), JSON.stringify(cache));
27
+ }
28
+ catch {
29
+ // Silently ignore cache write failures
30
+ }
31
+ }
32
+ export async function checkForUpdate(currentVersion, cacheDir) {
33
+ if (cacheDir) {
34
+ const cached = await readCache(cacheDir, currentVersion);
35
+ if (cached) {
36
+ return cached;
37
+ }
38
+ }
39
+ const latestVersion = await fetchLatestVersion();
40
+ if (!latestVersion) {
41
+ return { updateAvailable: false, currentVersion, latestVersion: currentVersion };
42
+ }
43
+ const result = {
44
+ updateAvailable: isOutdated(currentVersion, latestVersion),
45
+ currentVersion,
46
+ latestVersion
47
+ };
48
+ if (cacheDir) {
49
+ await writeCache(cacheDir, result);
50
+ }
51
+ return result;
52
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,106 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { checkForUpdate } from './version_check.js';
3
+ import { fetchLatestVersion, isOutdated } from '#services/npm_update_service.js';
4
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
5
+ vi.mock('#services/npm_update_service.js', () => ({
6
+ fetchLatestVersion: vi.fn(),
7
+ isOutdated: vi.fn()
8
+ }));
9
+ vi.mock('node:fs/promises', () => ({
10
+ readFile: vi.fn(),
11
+ writeFile: vi.fn(),
12
+ mkdir: vi.fn()
13
+ }));
14
+ describe('version_check', () => {
15
+ beforeEach(() => {
16
+ vi.clearAllMocks();
17
+ });
18
+ describe('checkForUpdate', () => {
19
+ it('should return updateAvailable true when outdated', async () => {
20
+ vi.mocked(fetchLatestVersion).mockResolvedValue('1.0.0');
21
+ vi.mocked(isOutdated).mockReturnValue(true);
22
+ const result = await checkForUpdate('0.8.4');
23
+ expect(result).toEqual({
24
+ updateAvailable: true,
25
+ currentVersion: '0.8.4',
26
+ latestVersion: '1.0.0'
27
+ });
28
+ });
29
+ it('should return updateAvailable false when up to date', async () => {
30
+ vi.mocked(fetchLatestVersion).mockResolvedValue('0.8.4');
31
+ vi.mocked(isOutdated).mockReturnValue(false);
32
+ const result = await checkForUpdate('0.8.4');
33
+ expect(result).toEqual({
34
+ updateAvailable: false,
35
+ currentVersion: '0.8.4',
36
+ latestVersion: '0.8.4'
37
+ });
38
+ });
39
+ it('should return updateAvailable false when fetch fails', async () => {
40
+ vi.mocked(fetchLatestVersion).mockResolvedValue(null);
41
+ const result = await checkForUpdate('0.8.4');
42
+ expect(result.updateAvailable).toBe(false);
43
+ expect(isOutdated).not.toHaveBeenCalled();
44
+ });
45
+ });
46
+ describe('caching', () => {
47
+ const cacheDir = '/tmp/test-cache';
48
+ it('should return cached result when cache is fresh', async () => {
49
+ const cached = {
50
+ timestamp: Date.now() - 1000,
51
+ result: { updateAvailable: true, currentVersion: '0.8.4', latestVersion: '1.0.0' }
52
+ };
53
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify(cached));
54
+ const result = await checkForUpdate('0.8.4', cacheDir);
55
+ expect(result).toEqual(cached.result);
56
+ expect(fetchLatestVersion).not.toHaveBeenCalled();
57
+ });
58
+ it('should fetch fresh result when cache is expired', async () => {
59
+ const cached = {
60
+ timestamp: Date.now() - (5 * 60 * 60 * 1000), // 5 hours ago
61
+ result: { updateAvailable: true, currentVersion: '0.8.4', latestVersion: '0.9.0' }
62
+ };
63
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify(cached));
64
+ vi.mocked(fetchLatestVersion).mockResolvedValue('1.0.0');
65
+ vi.mocked(isOutdated).mockReturnValue(true);
66
+ const result = await checkForUpdate('0.8.4', cacheDir);
67
+ expect(fetchLatestVersion).toHaveBeenCalled();
68
+ expect(result.latestVersion).toBe('1.0.0');
69
+ });
70
+ it('should fetch fresh result when cached version differs from current', async () => {
71
+ const cached = {
72
+ timestamp: Date.now() - 1000,
73
+ result: { updateAvailable: true, currentVersion: '0.8.4', latestVersion: '1.0.0' }
74
+ };
75
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify(cached));
76
+ vi.mocked(fetchLatestVersion).mockResolvedValue('1.0.0');
77
+ vi.mocked(isOutdated).mockReturnValue(true);
78
+ const result = await checkForUpdate('0.9.0', cacheDir);
79
+ expect(fetchLatestVersion).toHaveBeenCalled();
80
+ expect(result.currentVersion).toBe('0.9.0');
81
+ });
82
+ it('should fetch fresh result when cache file is missing', async () => {
83
+ vi.mocked(readFile).mockRejectedValue(new Error('ENOENT'));
84
+ vi.mocked(fetchLatestVersion).mockResolvedValue('1.0.0');
85
+ vi.mocked(isOutdated).mockReturnValue(true);
86
+ const result = await checkForUpdate('0.8.4', cacheDir);
87
+ expect(fetchLatestVersion).toHaveBeenCalled();
88
+ expect(result.latestVersion).toBe('1.0.0');
89
+ });
90
+ it('should write cache after fresh fetch', async () => {
91
+ vi.mocked(readFile).mockRejectedValue(new Error('ENOENT'));
92
+ vi.mocked(fetchLatestVersion).mockResolvedValue('1.0.0');
93
+ vi.mocked(isOutdated).mockReturnValue(true);
94
+ await checkForUpdate('0.8.4', cacheDir);
95
+ expect(mkdir).toHaveBeenCalledWith(cacheDir, { recursive: true });
96
+ expect(writeFile).toHaveBeenCalled();
97
+ });
98
+ it('should skip caching when no cacheDir provided', async () => {
99
+ vi.mocked(fetchLatestVersion).mockResolvedValue('1.0.0');
100
+ vi.mocked(isOutdated).mockReturnValue(true);
101
+ await checkForUpdate('0.8.4');
102
+ expect(readFile).not.toHaveBeenCalled();
103
+ expect(writeFile).not.toHaveBeenCalled();
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Build a workflow from a plan file using the /outputai:build_workflow slash command
3
+ * @param planFilePath - Absolute path to the plan file
4
+ * @param workflowDir - Absolute path to the workflow directory
5
+ * @param workflowName - Name of the workflow
6
+ * @param additionalInstructions - Optional additional instructions
7
+ * @returns Implementation output from claude-code
8
+ */
9
+ export declare function buildWorkflow(planFilePath: string, workflowDir: string, workflowName: string, additionalInstructions?: string): Promise<string>;
10
+ /**
11
+ * Interactive loop for refining workflow implementation
12
+ * Similar to the plan modification loop pattern
13
+ * @param originalOutput - Initial implementation output from claude-code
14
+ * @returns Final accepted output
15
+ */
16
+ export declare function buildWorkflowInteractiveLoop(originalOutput: string): Promise<string>;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Workflow builder service for implementing workflows from plan files
3
+ */
4
+ import { BUILD_COMMAND_OPTIONS, invokeBuildWorkflow as invokeBuildWorkflowFromClient, replyToClaude } from './claude_client.js';
5
+ import { input } from '@inquirer/prompts';
6
+ import { ux } from '@oclif/core';
7
+ import fs from 'node:fs/promises';
8
+ import path from 'node:path';
9
+ import { getErrorMessage } from '#utils/error_utils.js';
10
+ const ACCEPT_KEY = 'ACCEPT';
11
+ const SEPARATOR_LINE = '─'.repeat(80);
12
+ function displayImplementationOutput(output, message) {
13
+ ux.stdout('\n');
14
+ ux.stdout(ux.colorize('green', message));
15
+ ux.stdout('\n');
16
+ ux.stdout(ux.colorize('dim', SEPARATOR_LINE));
17
+ ux.stdout(output);
18
+ ux.stdout(ux.colorize('dim', SEPARATOR_LINE));
19
+ ux.stdout('\n');
20
+ }
21
+ async function promptForModification() {
22
+ return input({
23
+ message: `Review the implementation. Type "${ACCEPT_KEY}" to accept, or describe modifications:`,
24
+ default: ACCEPT_KEY
25
+ });
26
+ }
27
+ function isAcceptCommand(modification) {
28
+ return modification.trim().toUpperCase() === ACCEPT_KEY;
29
+ }
30
+ function isEmpty(modification) {
31
+ return modification.trim() === '';
32
+ }
33
+ /**
34
+ * Build a workflow from a plan file using the /outputai:build_workflow slash command
35
+ * @param planFilePath - Absolute path to the plan file
36
+ * @param workflowDir - Absolute path to the workflow directory
37
+ * @param workflowName - Name of the workflow
38
+ * @param additionalInstructions - Optional additional instructions
39
+ * @returns Implementation output from claude-code
40
+ */
41
+ export async function buildWorkflow(planFilePath, workflowDir, workflowName, additionalInstructions) {
42
+ try {
43
+ await fs.access(planFilePath);
44
+ }
45
+ catch {
46
+ throw new Error(`Plan file not found: ${planFilePath}`);
47
+ }
48
+ await fs.mkdir(workflowDir, { recursive: true });
49
+ const absolutePlanPath = path.resolve(planFilePath);
50
+ const absoluteWorkflowDir = path.resolve(workflowDir);
51
+ return invokeBuildWorkflowFromClient(absolutePlanPath, absoluteWorkflowDir, workflowName, additionalInstructions);
52
+ }
53
+ async function processModification(modification, currentOutput) {
54
+ if (isEmpty(modification)) {
55
+ ux.stdout(ux.colorize('yellow', 'Please provide modification instructions or type ACCEPT to continue.'));
56
+ return currentOutput;
57
+ }
58
+ try {
59
+ const updatedOutput = await replyToClaude(modification, BUILD_COMMAND_OPTIONS);
60
+ displayImplementationOutput(updatedOutput, '✓ Implementation updated!');
61
+ return updatedOutput;
62
+ }
63
+ catch (error) {
64
+ ux.error(`Failed to apply modifications: ${getErrorMessage(error)}`);
65
+ ux.stdout('Continuing with previous version...\n');
66
+ return currentOutput;
67
+ }
68
+ }
69
+ async function interactiveRefinementLoop(currentOutput) {
70
+ const modification = await promptForModification();
71
+ if (isAcceptCommand(modification)) {
72
+ return currentOutput;
73
+ }
74
+ const updatedOutput = await processModification(modification, currentOutput);
75
+ return interactiveRefinementLoop(updatedOutput);
76
+ }
77
+ /**
78
+ * Interactive loop for refining workflow implementation
79
+ * Similar to the plan modification loop pattern
80
+ * @param originalOutput - Initial implementation output from claude-code
81
+ * @returns Final accepted output
82
+ */
83
+ export async function buildWorkflowInteractiveLoop(originalOutput) {
84
+ displayImplementationOutput(originalOutput, '✓ Workflow implementation complete!');
85
+ return interactiveRefinementLoop(originalOutput);
86
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,165 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { buildWorkflow, buildWorkflowInteractiveLoop } from './workflow_builder.js';
3
+ import { BUILD_COMMAND_OPTIONS, invokeBuildWorkflow, replyToClaude } from './claude_client.js';
4
+ import { input } from '@inquirer/prompts';
5
+ import { ux } from '@oclif/core';
6
+ import fs from 'node:fs/promises';
7
+ vi.mock('./claude_client.js');
8
+ vi.mock('@inquirer/prompts');
9
+ vi.mock('@oclif/core', () => ({
10
+ ux: {
11
+ stdout: vi.fn(),
12
+ error: vi.fn(),
13
+ colorize: vi.fn((_color, text) => text)
14
+ }
15
+ }));
16
+ vi.mock('node:fs/promises');
17
+ describe('workflow-builder service', () => {
18
+ beforeEach(() => {
19
+ vi.clearAllMocks();
20
+ });
21
+ describe('buildWorkflow', () => {
22
+ it('should build workflow from plan file', async () => {
23
+ vi.mocked(fs.access).mockResolvedValue(undefined);
24
+ vi.mocked(fs.mkdir).mockResolvedValue(undefined);
25
+ vi.mocked(invokeBuildWorkflow).mockResolvedValue('Implementation complete!');
26
+ const result = await buildWorkflow('/path/to/plan.md', '/path/to/workflows/test_workflow', 'test_workflow');
27
+ expect(fs.access).toHaveBeenCalledWith('/path/to/plan.md');
28
+ expect(fs.mkdir).toHaveBeenCalledWith(expect.stringContaining('test_workflow'), { recursive: true });
29
+ expect(invokeBuildWorkflow).toHaveBeenCalledWith(expect.stringContaining('plan.md'), expect.stringContaining('test_workflow'), 'test_workflow', undefined);
30
+ expect(result).toBe('Implementation complete!');
31
+ });
32
+ it('should pass additional instructions to claude-code', async () => {
33
+ vi.mocked(fs.access).mockResolvedValue(undefined);
34
+ vi.mocked(fs.mkdir).mockResolvedValue(undefined);
35
+ vi.mocked(invokeBuildWorkflow).mockResolvedValue('Done!');
36
+ await buildWorkflow('/plan.md', '/workflows', 'test', 'Use TypeScript only');
37
+ expect(invokeBuildWorkflow).toHaveBeenCalledWith(expect.any(String), expect.any(String), 'test', 'Use TypeScript only');
38
+ });
39
+ it('should throw error if plan file does not exist', async () => {
40
+ vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT'));
41
+ await expect(buildWorkflow('/nonexistent/plan.md', '/workflows', 'test')).rejects.toThrow('Plan file not found: /nonexistent/plan.md');
42
+ expect(fs.mkdir).not.toHaveBeenCalled();
43
+ expect(invokeBuildWorkflow).not.toHaveBeenCalled();
44
+ });
45
+ it('should create workflow directory if it does not exist', async () => {
46
+ vi.mocked(fs.access).mockResolvedValue(undefined);
47
+ vi.mocked(fs.mkdir).mockResolvedValue(undefined);
48
+ vi.mocked(invokeBuildWorkflow).mockResolvedValue('Done');
49
+ await buildWorkflow('/plan.md', '/new/workflows/test', 'test');
50
+ expect(fs.mkdir).toHaveBeenCalledWith(expect.stringContaining('test'), { recursive: true });
51
+ });
52
+ it('should resolve paths to absolute paths', async () => {
53
+ vi.mocked(fs.access).mockResolvedValue(undefined);
54
+ vi.mocked(fs.mkdir).mockResolvedValue(undefined);
55
+ vi.mocked(invokeBuildWorkflow).mockResolvedValue('Done');
56
+ await buildWorkflow('relative/plan.md', 'relative/workflows', 'test');
57
+ // Should call with absolute paths
58
+ const calls = vi.mocked(invokeBuildWorkflow).mock.calls[0];
59
+ expect(calls[0]).toMatch(/^[/\\]/); // Absolute path starts with / or \
60
+ expect(calls[1]).toMatch(/^[/\\]/); // Absolute path starts with / or \
61
+ });
62
+ it('should propagate errors from claude-code invocation', async () => {
63
+ vi.mocked(fs.access).mockResolvedValue(undefined);
64
+ vi.mocked(fs.mkdir).mockResolvedValue(undefined);
65
+ vi.mocked(invokeBuildWorkflow).mockRejectedValue(new Error('Claude API timeout'));
66
+ await expect(buildWorkflow('/plan.md', '/workflows', 'test')).rejects.toThrow('Claude API timeout');
67
+ });
68
+ it('should handle directory creation failures', async () => {
69
+ vi.mocked(fs.access).mockResolvedValue(undefined);
70
+ vi.mocked(fs.mkdir).mockRejectedValue(new Error('Permission denied'));
71
+ await expect(buildWorkflow('/plan.md', '/workflows', 'test')).rejects.toThrow('Permission denied');
72
+ });
73
+ });
74
+ describe('buildWorkflowInteractiveLoop', () => {
75
+ it('should accept implementation immediately when user types ACCEPT', async () => {
76
+ vi.mocked(input).mockResolvedValue('ACCEPT');
77
+ const result = await buildWorkflowInteractiveLoop('Initial implementation');
78
+ expect(result).toBe('Initial implementation');
79
+ expect(input).toHaveBeenCalledOnce();
80
+ expect(replyToClaude).not.toHaveBeenCalled();
81
+ });
82
+ it('should accept implementation when user types lowercase accept', async () => {
83
+ vi.mocked(input).mockResolvedValue('accept');
84
+ const result = await buildWorkflowInteractiveLoop('Initial implementation');
85
+ expect(result).toBe('Initial implementation');
86
+ expect(replyToClaude).not.toHaveBeenCalled();
87
+ });
88
+ it('should accept implementation with extra whitespace', async () => {
89
+ vi.mocked(input).mockResolvedValue(' ACCEPT ');
90
+ const result = await buildWorkflowInteractiveLoop('Initial implementation');
91
+ expect(result).toBe('Initial implementation');
92
+ expect(replyToClaude).not.toHaveBeenCalled();
93
+ });
94
+ it('should apply modifications and return updated implementation', async () => {
95
+ vi.mocked(input)
96
+ .mockResolvedValueOnce('Add error handling')
97
+ .mockResolvedValueOnce('ACCEPT');
98
+ vi.mocked(replyToClaude).mockResolvedValue('Updated implementation with error handling');
99
+ const result = await buildWorkflowInteractiveLoop('Initial implementation');
100
+ expect(replyToClaude).toHaveBeenCalledWith('Add error handling', BUILD_COMMAND_OPTIONS);
101
+ expect(result).toBe('Updated implementation with error handling');
102
+ expect(input).toHaveBeenCalledTimes(2);
103
+ });
104
+ it('should handle multiple modification rounds', async () => {
105
+ vi.mocked(input)
106
+ .mockResolvedValueOnce('Add logging')
107
+ .mockResolvedValueOnce('Add validation')
108
+ .mockResolvedValueOnce('ACCEPT');
109
+ vi.mocked(replyToClaude)
110
+ .mockResolvedValueOnce('Implementation with logging')
111
+ .mockResolvedValueOnce('Implementation with logging and validation');
112
+ const result = await buildWorkflowInteractiveLoop('Initial implementation');
113
+ expect(replyToClaude).toHaveBeenCalledTimes(2);
114
+ expect(replyToClaude).toHaveBeenNthCalledWith(1, 'Add logging', BUILD_COMMAND_OPTIONS);
115
+ expect(replyToClaude).toHaveBeenNthCalledWith(2, 'Add validation', BUILD_COMMAND_OPTIONS);
116
+ expect(result).toBe('Implementation with logging and validation');
117
+ });
118
+ it('should prompt again when user provides empty input', async () => {
119
+ vi.mocked(input)
120
+ .mockResolvedValueOnce('')
121
+ .mockResolvedValueOnce(' ')
122
+ .mockResolvedValueOnce('ACCEPT');
123
+ const result = await buildWorkflowInteractiveLoop('Initial implementation');
124
+ expect(result).toBe('Initial implementation');
125
+ expect(input).toHaveBeenCalledTimes(3);
126
+ expect(ux.stdout).toHaveBeenCalledWith(expect.stringContaining('provide modification instructions'));
127
+ });
128
+ it('should display implementation output to user', async () => {
129
+ vi.mocked(input).mockResolvedValue('ACCEPT');
130
+ await buildWorkflowInteractiveLoop('Implementation summary');
131
+ expect(ux.stdout).toHaveBeenCalledWith(expect.stringContaining('Implementation summary'));
132
+ });
133
+ it('should display updated implementation after modifications', async () => {
134
+ vi.mocked(input)
135
+ .mockResolvedValueOnce('Improve performance')
136
+ .mockResolvedValueOnce('ACCEPT');
137
+ vi.mocked(replyToClaude).mockResolvedValue('Optimized implementation');
138
+ await buildWorkflowInteractiveLoop('Initial');
139
+ expect(ux.stdout).toHaveBeenCalledWith(expect.stringContaining('Optimized implementation'));
140
+ });
141
+ it('should handle errors from replyToClaude gracefully', async () => {
142
+ vi.mocked(input)
143
+ .mockResolvedValueOnce('Invalid modification')
144
+ .mockResolvedValueOnce('ACCEPT');
145
+ vi.mocked(replyToClaude).mockRejectedValue(new Error('API error'));
146
+ const result = await buildWorkflowInteractiveLoop('Original implementation');
147
+ // Should return original implementation after error
148
+ expect(result).toBe('Original implementation');
149
+ expect(ux.error).toHaveBeenCalledWith(expect.stringContaining('Failed to apply modifications'));
150
+ expect(ux.stdout).toHaveBeenCalledWith(expect.stringContaining('Continuing with previous version'));
151
+ });
152
+ it('should continue looping after handling error', async () => {
153
+ vi.mocked(input)
154
+ .mockResolvedValueOnce('Bad request')
155
+ .mockResolvedValueOnce('Good request')
156
+ .mockResolvedValueOnce('ACCEPT');
157
+ vi.mocked(replyToClaude)
158
+ .mockRejectedValueOnce(new Error('API error'))
159
+ .mockResolvedValueOnce('Fixed implementation');
160
+ const result = await buildWorkflowInteractiveLoop('Initial');
161
+ expect(result).toBe('Fixed implementation');
162
+ expect(replyToClaude).toHaveBeenCalledTimes(2);
163
+ });
164
+ });
165
+ });
@@ -0,0 +1,5 @@
1
+ import type { WorkflowGenerationConfig, WorkflowGenerationResult } from '#types/generator.js';
2
+ /**
3
+ * Generate a new workflow
4
+ */
5
+ export declare function generateWorkflow(config: WorkflowGenerationConfig): Promise<WorkflowGenerationResult>;
@@ -0,0 +1,40 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import { WorkflowExistsError } from '#types/errors.js';
3
+ import { createTargetDir, getTemplateDir } from '#utils/paths.js';
4
+ import { validateWorkflowName, validateOutputDirectory } from '#utils/validation.js';
5
+ import { prepareTemplateVariables } from '#utils/template.js';
6
+ import { getTemplateFiles, processAllTemplates } from './template_processor.js';
7
+ /**
8
+ * Validate the generation configuration
9
+ */
10
+ function validateConfig(config) {
11
+ validateWorkflowName(config.name);
12
+ validateOutputDirectory(config.outputDir);
13
+ }
14
+ /**
15
+ * Check if target directory exists and handle accordingly
16
+ */
17
+ import * as fsSync from 'node:fs';
18
+ async function checkTargetDirectory(targetDir, force) {
19
+ if (fsSync.existsSync(targetDir) && !force) {
20
+ throw new WorkflowExistsError('', targetDir);
21
+ }
22
+ }
23
+ /**
24
+ * Generate a new workflow
25
+ */
26
+ export async function generateWorkflow(config) {
27
+ validateConfig(config);
28
+ const targetDir = createTargetDir(config.outputDir, config.name);
29
+ const templatesDir = getTemplateDir('workflow');
30
+ await checkTargetDirectory(targetDir, config.force);
31
+ await fs.mkdir(targetDir, { recursive: true });
32
+ const variables = prepareTemplateVariables(config.name, config.description || '');
33
+ const templateFiles = await getTemplateFiles(templatesDir);
34
+ const filesCreated = await processAllTemplates(templateFiles, targetDir, variables);
35
+ return {
36
+ workflowName: config.name,
37
+ targetDir,
38
+ filesCreated
39
+ };
40
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,77 @@
1
+ /* eslint-disable no-restricted-syntax, init-declarations */
2
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
3
+ import * as fs from 'node:fs/promises';
4
+ import * as path from 'node:path';
5
+ import * as os from 'node:os';
6
+ import { generateWorkflow } from './workflow_generator.js';
7
+ describe('Workflow Generator', () => {
8
+ let tempDir;
9
+ beforeEach(async () => {
10
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'workflow-gen-test-'));
11
+ });
12
+ afterEach(async () => {
13
+ await fs.rm(tempDir, { recursive: true, force: true });
14
+ });
15
+ describe('skeleton generation', () => {
16
+ it('should create prompts folder with prompt template', async () => {
17
+ const result = await generateWorkflow({
18
+ name: 'testWorkflow',
19
+ description: 'Test workflow',
20
+ outputDir: tempDir,
21
+ skeleton: true,
22
+ force: false
23
+ });
24
+ const promptPath = path.join(result.targetDir, 'prompts', 'example@v1.prompt');
25
+ const promptExists = await fs.access(promptPath).then(() => true).catch(() => false);
26
+ expect(promptExists).toBe(true);
27
+ expect(result.filesCreated).toContain('prompts/example@v1.prompt');
28
+ });
29
+ it('should create scenarios folder with test_input.json', async () => {
30
+ const result = await generateWorkflow({
31
+ name: 'testWorkflow',
32
+ description: 'Test workflow',
33
+ outputDir: tempDir,
34
+ skeleton: true,
35
+ force: false
36
+ });
37
+ const scenarioPath = path.join(result.targetDir, 'scenarios', 'test_input.json');
38
+ const scenarioExists = await fs.access(scenarioPath).then(() => true).catch(() => false);
39
+ expect(scenarioExists).toBe(true);
40
+ expect(result.filesCreated).toContain('scenarios/test_input.json');
41
+ });
42
+ it('should create valid JSON in scenario file', async () => {
43
+ const result = await generateWorkflow({
44
+ name: 'testWorkflow',
45
+ description: 'Test workflow',
46
+ outputDir: tempDir,
47
+ skeleton: true,
48
+ force: false
49
+ });
50
+ const scenarioPath = path.join(result.targetDir, 'scenarios', 'test_input.json');
51
+ const content = await fs.readFile(scenarioPath, 'utf-8');
52
+ const parsed = JSON.parse(content);
53
+ expect(parsed).toHaveProperty('text');
54
+ });
55
+ it('should create all expected skeleton files', async () => {
56
+ const result = await generateWorkflow({
57
+ name: 'testWorkflow',
58
+ description: 'Test workflow',
59
+ outputDir: tempDir,
60
+ skeleton: true,
61
+ force: false
62
+ });
63
+ const expectedFiles = [
64
+ 'workflow.ts',
65
+ 'steps.ts',
66
+ 'evaluators.ts',
67
+ 'types.ts',
68
+ 'README.md',
69
+ 'prompts/example@v1.prompt',
70
+ 'scenarios/test_input.json'
71
+ ];
72
+ for (const file of expectedFiles) {
73
+ expect(result.filesCreated).toContain(file);
74
+ }
75
+ });
76
+ });
77
+ });
@@ -0,0 +1,15 @@
1
+ export declare function generatePlanName(description: string, date?: Date): Promise<string>;
2
+ /**
3
+ * Write plan content to PLAN.md file in the plans directory
4
+ * @param planName - Name of the plan (e.g., "2025_10_06_customer_order_processing")
5
+ * @param content - Plan content to write
6
+ * @param projectRoot - Root directory of the project
7
+ * @returns Full path to the created PLAN.md file
8
+ */
9
+ export declare function writePlanFile(planName: string, content: string, projectRoot: string): Promise<string>;
10
+ /**
11
+ * Update agent templates by reinitializing with force flag
12
+ * This recreates all agent configuration files, overwriting existing ones
13
+ * @param projectRoot - Root directory of the project
14
+ */
15
+ export declare function updateAgentTemplates(projectRoot: string): Promise<void>;
@@ -0,0 +1,48 @@
1
+ import { initializeAgentConfig } from './coding_agents.js';
2
+ import { generateText } from '@outputai/llm';
3
+ import fs from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { config } from '#config.js';
6
+ export async function generatePlanName(description, date = new Date()) {
7
+ const year = date.getFullYear();
8
+ const month = String(date.getMonth() + 1).padStart(2, '0');
9
+ const day = String(date.getDate()).padStart(2, '0');
10
+ const datePrefix = `${year}_${month}_${day}`;
11
+ const { text: planNameSlug } = await generateText({
12
+ prompt: 'generate_plan_name@v1',
13
+ variables: { description }
14
+ });
15
+ const cleanedName = planNameSlug
16
+ .trim()
17
+ .toLowerCase()
18
+ .replace(/[^a-z0-9_]+/g, '_')
19
+ .replace(/_+/g, '_')
20
+ .replace(/^_|_$/g, '')
21
+ .slice(0, 50);
22
+ return `${datePrefix}_${cleanedName}`;
23
+ }
24
+ /**
25
+ * Write plan content to PLAN.md file in the plans directory
26
+ * @param planName - Name of the plan (e.g., "2025_10_06_customer_order_processing")
27
+ * @param content - Plan content to write
28
+ * @param projectRoot - Root directory of the project
29
+ * @returns Full path to the created PLAN.md file
30
+ */
31
+ export async function writePlanFile(planName, content, projectRoot) {
32
+ const planDir = path.join(projectRoot, config.agentConfigDir, 'plans', planName);
33
+ const planFilePath = path.join(planDir, 'PLAN.md');
34
+ await fs.mkdir(planDir, { recursive: true });
35
+ await fs.writeFile(planFilePath, content, 'utf-8');
36
+ return planFilePath;
37
+ }
38
+ /**
39
+ * Update agent templates by reinitializing with force flag
40
+ * This recreates all agent configuration files, overwriting existing ones
41
+ * @param projectRoot - Root directory of the project
42
+ */
43
+ export async function updateAgentTemplates(projectRoot) {
44
+ await initializeAgentConfig({
45
+ projectRoot,
46
+ force: true
47
+ });
48
+ }
@@ -0,0 +1 @@
1
+ export {};