agentplane 0.3.13 → 0.3.15

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/assets/RUNNER.md +1 -1
  2. package/assets/agents/ORCHESTRATOR.json +1 -1
  3. package/assets/agents/SKILL_EXTRACTOR.json +31 -0
  4. package/assets/framework.manifest.json +7 -0
  5. package/assets/policy/incidents.md +5 -3
  6. package/assets/policy/workflow.branch_pr.md +10 -5
  7. package/dist/.build-manifest.json +280 -180
  8. package/dist/cli/output.d.ts +29 -0
  9. package/dist/cli/output.d.ts.map +1 -1
  10. package/dist/cli/output.js +33 -0
  11. package/dist/cli/run-cli/command-catalog/core.d.ts.map +1 -1
  12. package/dist/cli/run-cli/command-catalog/core.js +29 -87
  13. package/dist/cli/run-cli/command-catalog/lifecycle.d.ts.map +1 -1
  14. package/dist/cli/run-cli/command-catalog/lifecycle.js +4 -12
  15. package/dist/cli/run-cli/command-catalog/project.d.ts +1 -1
  16. package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -1
  17. package/dist/cli/run-cli/command-catalog/project.js +16 -38
  18. package/dist/cli/run-cli/command-catalog/shared.d.ts +9 -6
  19. package/dist/cli/run-cli/command-catalog/shared.d.ts.map +1 -1
  20. package/dist/cli/run-cli/command-catalog/shared.js +23 -6
  21. package/dist/cli/run-cli/command-catalog/task.d.ts.map +1 -1
  22. package/dist/cli/run-cli/command-catalog/task.js +6 -18
  23. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  24. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  25. package/dist/cli/run-cli/commands/init/recipes.d.ts.map +1 -1
  26. package/dist/cli/run-cli/commands/init/recipes.js +1 -0
  27. package/dist/cli/run-cli.js +1 -1
  28. package/dist/cli/run-cli.test-helpers.d.ts +1 -74
  29. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  30. package/dist/cli/run-cli.test-helpers.js +1 -769
  31. package/dist/commands/branch/cleanup-merged.d.ts.map +1 -1
  32. package/dist/commands/branch/cleanup-merged.js +5 -9
  33. package/dist/commands/branch/work-start.command.d.ts.map +1 -1
  34. package/dist/commands/branch/work-start.command.js +1 -0
  35. package/dist/commands/commit.spec.d.ts.map +1 -1
  36. package/dist/commands/commit.spec.js +2 -0
  37. package/dist/commands/doctor/branch-pr.d.ts +1 -1
  38. package/dist/commands/doctor/branch-pr.d.ts.map +1 -1
  39. package/dist/commands/doctor/branch-pr.js +5 -2
  40. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  41. package/dist/commands/guard/impl/commands.js +4 -1
  42. package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
  43. package/dist/commands/guard/impl/comment-commit.js +2 -1
  44. package/dist/commands/guard/impl/env.d.ts +6 -0
  45. package/dist/commands/guard/impl/env.d.ts.map +1 -1
  46. package/dist/commands/guard/impl/env.js +41 -0
  47. package/dist/commands/pr/internal/auto-commit.d.ts.map +1 -1
  48. package/dist/commands/pr/internal/auto-commit.js +2 -1
  49. package/dist/commands/pr/internal/sync-branch.d.ts +36 -0
  50. package/dist/commands/pr/internal/sync-branch.d.ts.map +1 -0
  51. package/dist/commands/pr/internal/sync-branch.js +113 -0
  52. package/dist/commands/pr/internal/sync-github.d.ts +28 -0
  53. package/dist/commands/pr/internal/sync-github.d.ts.map +1 -0
  54. package/dist/commands/pr/internal/sync-github.js +178 -0
  55. package/dist/commands/pr/internal/sync-model.d.ts +36 -0
  56. package/dist/commands/pr/internal/sync-model.d.ts.map +1 -0
  57. package/dist/commands/pr/internal/sync-model.js +1 -0
  58. package/dist/commands/pr/internal/sync-open-step.d.ts +10 -0
  59. package/dist/commands/pr/internal/sync-open-step.d.ts.map +1 -0
  60. package/dist/commands/pr/internal/sync-open-step.js +128 -0
  61. package/dist/commands/pr/internal/sync-support.d.ts +7 -0
  62. package/dist/commands/pr/internal/sync-support.d.ts.map +1 -0
  63. package/dist/commands/pr/internal/sync-support.js +29 -0
  64. package/dist/commands/pr/internal/sync-update-step.d.ts +6 -0
  65. package/dist/commands/pr/internal/sync-update-step.d.ts.map +1 -0
  66. package/dist/commands/pr/internal/sync-update-step.js +68 -0
  67. package/dist/commands/pr/internal/sync.d.ts +2 -6
  68. package/dist/commands/pr/internal/sync.d.ts.map +1 -1
  69. package/dist/commands/pr/internal/sync.js +83 -529
  70. package/dist/commands/pr/open.d.ts.map +1 -1
  71. package/dist/commands/pr/open.js +25 -8
  72. package/dist/commands/pr/pr.command.d.ts.map +1 -1
  73. package/dist/commands/pr/pr.command.js +7 -2
  74. package/dist/commands/recipes/impl/apply.d.ts +1 -1
  75. package/dist/commands/recipes/impl/apply.d.ts.map +1 -1
  76. package/dist/commands/recipes/impl/apply.js +1 -2
  77. package/dist/commands/recipes/impl/commands/active.d.ts.map +1 -1
  78. package/dist/commands/recipes/impl/commands/active.js +6 -5
  79. package/dist/commands/recipes/impl/commands/add.d.ts +1 -0
  80. package/dist/commands/recipes/impl/commands/add.d.ts.map +1 -1
  81. package/dist/commands/recipes/impl/commands/add.js +32 -27
  82. package/dist/commands/recipes/impl/commands/detach.d.ts.map +1 -1
  83. package/dist/commands/recipes/impl/commands/detach.js +35 -21
  84. package/dist/commands/recipes/impl/commands/disable.d.ts.map +1 -1
  85. package/dist/commands/recipes/impl/commands/disable.js +5 -3
  86. package/dist/commands/recipes/impl/commands/enable.d.ts.map +1 -1
  87. package/dist/commands/recipes/impl/commands/enable.js +5 -3
  88. package/dist/commands/recipes/impl/commands/explain.d.ts.map +1 -1
  89. package/dist/commands/recipes/impl/commands/explain.js +57 -47
  90. package/dist/commands/recipes/impl/commands/info.d.ts.map +1 -1
  91. package/dist/commands/recipes/impl/commands/info.js +25 -21
  92. package/dist/commands/recipes/impl/commands/install.d.ts +1 -1
  93. package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
  94. package/dist/commands/recipes/impl/commands/install.js +3 -13
  95. package/dist/commands/recipes/impl/commands/list-remote.d.ts.map +1 -1
  96. package/dist/commands/recipes/impl/commands/list-remote.js +2 -3
  97. package/dist/commands/recipes/impl/commands/list.d.ts.map +1 -1
  98. package/dist/commands/recipes/impl/commands/list.js +7 -6
  99. package/dist/commands/recipes/impl/commands/remove.d.ts.map +1 -1
  100. package/dist/commands/recipes/impl/commands/remove.js +12 -7
  101. package/dist/commands/recipes/impl/commands/update.d.ts.map +1 -1
  102. package/dist/commands/recipes/impl/commands/update.js +38 -24
  103. package/dist/commands/recipes/impl/index.d.ts +1 -1
  104. package/dist/commands/recipes/impl/index.d.ts.map +1 -1
  105. package/dist/commands/recipes/impl/installed-recipes.d.ts +1 -1
  106. package/dist/commands/recipes/impl/installed-recipes.d.ts.map +1 -1
  107. package/dist/commands/recipes/impl/installed-recipes.js +1 -2
  108. package/dist/commands/recipes/impl/mutation-transaction.d.ts +7 -0
  109. package/dist/commands/recipes/impl/mutation-transaction.d.ts.map +1 -0
  110. package/dist/commands/recipes/impl/mutation-transaction.js +47 -0
  111. package/dist/commands/recipes/impl/overlay-project.d.ts +19 -3
  112. package/dist/commands/recipes/impl/overlay-project.d.ts.map +1 -1
  113. package/dist/commands/recipes/impl/overlay-project.js +76 -38
  114. package/dist/commands/recipes/impl/paths.d.ts +0 -3
  115. package/dist/commands/recipes/impl/paths.d.ts.map +1 -1
  116. package/dist/commands/recipes/impl/paths.js +0 -3
  117. package/dist/commands/recipes/impl/project-installed-recipes.d.ts +4 -1
  118. package/dist/commands/recipes/impl/project-installed-recipes.d.ts.map +1 -1
  119. package/dist/commands/recipes/impl/project-installed-recipes.js +6 -4
  120. package/dist/commands/recipes/impl/project-recipe-state.d.ts +1 -1
  121. package/dist/commands/recipes/impl/project-recipe-state.d.ts.map +1 -1
  122. package/dist/commands/recipes/impl/project-registry.d.ts +5 -1
  123. package/dist/commands/recipes/impl/project-registry.d.ts.map +1 -1
  124. package/dist/commands/recipes/impl/project-registry.js +34 -14
  125. package/dist/commands/recipes/impl/resolver.d.ts +1 -1
  126. package/dist/commands/recipes/impl/resolver.d.ts.map +1 -1
  127. package/dist/commands/recipes/impl/resolver.js +1 -1
  128. package/dist/commands/recipes/impl/types.d.ts +1 -1
  129. package/dist/commands/recipes/impl/types.d.ts.map +1 -1
  130. package/dist/commands/recipes/impl/version.d.ts +5 -0
  131. package/dist/commands/recipes/impl/version.d.ts.map +1 -0
  132. package/dist/commands/recipes/impl/version.js +9 -0
  133. package/dist/commands/recipes.d.ts +5 -4
  134. package/dist/commands/recipes.d.ts.map +1 -1
  135. package/dist/commands/recipes.js +3 -3
  136. package/dist/commands/release/apply.command.d.ts +1 -1
  137. package/dist/commands/release/apply.command.d.ts.map +1 -1
  138. package/dist/commands/release/apply.command.js +15 -379
  139. package/dist/commands/release/apply.mutation.d.ts +1 -0
  140. package/dist/commands/release/apply.mutation.d.ts.map +1 -1
  141. package/dist/commands/release/apply.mutation.js +24 -1
  142. package/dist/commands/release/apply.pipeline.d.ts +22 -0
  143. package/dist/commands/release/apply.pipeline.d.ts.map +1 -0
  144. package/dist/commands/release/apply.pipeline.js +371 -0
  145. package/dist/commands/release/apply.preflight.d.ts +2 -0
  146. package/dist/commands/release/apply.preflight.d.ts.map +1 -1
  147. package/dist/commands/release/apply.preflight.js +13 -4
  148. package/dist/commands/release/apply.types.d.ts +27 -0
  149. package/dist/commands/release/apply.types.d.ts.map +1 -1
  150. package/dist/commands/release.test-helpers.d.ts +4 -0
  151. package/dist/commands/release.test-helpers.d.ts.map +1 -1
  152. package/dist/commands/release.test-helpers.js +7 -0
  153. package/dist/commands/shared/reconcile-check.d.ts.map +1 -1
  154. package/dist/commands/shared/reconcile-check.js +2 -2
  155. package/dist/commands/shared/task-backend.d.ts +6 -1
  156. package/dist/commands/shared/task-backend.d.ts.map +1 -1
  157. package/dist/commands/shared/task-backend.js +34 -2
  158. package/dist/commands/shared/task-mutation.d.ts.map +1 -1
  159. package/dist/commands/shared/task-mutation.js +4 -4
  160. package/dist/commands/shared/task-store/intents.d.ts +34 -0
  161. package/dist/commands/shared/task-store/intents.d.ts.map +1 -0
  162. package/dist/commands/shared/task-store/intents.js +265 -0
  163. package/dist/commands/shared/task-store/readme.d.ts +28 -0
  164. package/dist/commands/shared/task-store/readme.d.ts.map +1 -0
  165. package/dist/commands/shared/task-store/readme.js +125 -0
  166. package/dist/commands/shared/task-store/store.d.ts +26 -0
  167. package/dist/commands/shared/task-store/store.d.ts.map +1 -0
  168. package/dist/commands/shared/task-store/store.js +105 -0
  169. package/dist/commands/shared/task-store/types.d.ts +94 -0
  170. package/dist/commands/shared/task-store/types.d.ts.map +1 -0
  171. package/dist/commands/shared/task-store/types.js +1 -0
  172. package/dist/commands/shared/task-store.d.ts +3 -109
  173. package/dist/commands/shared/task-store.d.ts.map +1 -1
  174. package/dist/commands/shared/task-store.js +2 -493
  175. package/dist/commands/task/block.d.ts.map +1 -1
  176. package/dist/commands/task/block.js +7 -2
  177. package/dist/commands/task/comment.d.ts.map +1 -1
  178. package/dist/commands/task/comment.js +7 -2
  179. package/dist/commands/task/finish-shared.d.ts.map +1 -1
  180. package/dist/commands/task/finish-shared.js +3 -3
  181. package/dist/commands/task/finish.d.ts.map +1 -1
  182. package/dist/commands/task/finish.js +102 -15
  183. package/dist/commands/task/hosted-close.command.d.ts.map +1 -1
  184. package/dist/commands/task/hosted-close.command.js +23 -2
  185. package/dist/commands/task/hosted-merge-sync.d.ts.map +1 -1
  186. package/dist/commands/task/hosted-merge-sync.js +9 -4
  187. package/dist/commands/task/list.run.d.ts.map +1 -1
  188. package/dist/commands/task/list.run.js +14 -4
  189. package/dist/commands/task/new.command.d.ts.map +1 -1
  190. package/dist/commands/task/new.command.js +16 -2
  191. package/dist/commands/task/new.js +2 -2
  192. package/dist/commands/task/show.d.ts.map +1 -1
  193. package/dist/commands/task/show.js +3 -3
  194. package/dist/commands/task/update.d.ts.map +1 -1
  195. package/dist/commands/task/update.js +11 -3
  196. package/dist/runner/adapters/codex.d.ts.map +1 -1
  197. package/dist/runner/adapters/codex.js +3 -33
  198. package/dist/runner/adapters/custom.d.ts.map +1 -1
  199. package/dist/runner/adapters/custom.js +3 -30
  200. package/dist/runner/adapters/runtime-shared.d.ts +14 -0
  201. package/dist/runner/adapters/runtime-shared.d.ts.map +1 -0
  202. package/dist/runner/adapters/runtime-shared.js +36 -0
  203. package/dist/runner/context/base-prompt-sources.d.ts +30 -0
  204. package/dist/runner/context/base-prompt-sources.d.ts.map +1 -0
  205. package/dist/runner/context/base-prompt-sources.js +144 -0
  206. package/dist/runner/context/base-prompts.d.ts +3 -22
  207. package/dist/runner/context/base-prompts.d.ts.map +1 -1
  208. package/dist/runner/context/base-prompts.js +6 -450
  209. package/dist/runner/context/overlay-prompt-blocks.d.ts +7 -0
  210. package/dist/runner/context/overlay-prompt-blocks.d.ts.map +1 -0
  211. package/dist/runner/context/overlay-prompt-blocks.js +72 -0
  212. package/dist/runner/context/prompt-block-shared.d.ts +54 -0
  213. package/dist/runner/context/prompt-block-shared.d.ts.map +1 -0
  214. package/dist/runner/context/prompt-block-shared.js +106 -0
  215. package/dist/runner/context/recipe-context.d.ts +2 -1
  216. package/dist/runner/context/recipe-context.d.ts.map +1 -1
  217. package/dist/runner/context/recipe-context.js +2 -1
  218. package/dist/runner/context/recipe-prompt-blocks.d.ts +6 -0
  219. package/dist/runner/context/recipe-prompt-blocks.d.ts.map +1 -0
  220. package/dist/runner/context/recipe-prompt-blocks.js +143 -0
  221. package/dist/runner/usecases/scenario-materialize-task.js +2 -2
  222. package/dist/runner/usecases/task-run-inspect.js +2 -2
  223. package/dist/runner/usecases/task-run-lifecycle-shared.js +2 -2
  224. package/dist/runner/usecases/task-run.d.ts.map +1 -1
  225. package/dist/runner/usecases/task-run.js +4 -2
  226. package/dist/runtime/capabilities/recipe.d.ts +1 -1
  227. package/dist/runtime/capabilities/recipe.d.ts.map +1 -1
  228. package/dist/runtime/execution-context.d.ts +63 -0
  229. package/dist/runtime/execution-context.d.ts.map +1 -0
  230. package/dist/{usecases/context/resolve-context.js → runtime/execution-context.js} +23 -26
  231. package/dist/runtime/incidents/advice-strategy.d.ts +15 -0
  232. package/dist/runtime/incidents/advice-strategy.d.ts.map +1 -0
  233. package/dist/runtime/incidents/advice-strategy.js +54 -0
  234. package/dist/runtime/incidents/plan-strategy.d.ts +9 -0
  235. package/dist/runtime/incidents/plan-strategy.d.ts.map +1 -0
  236. package/dist/runtime/incidents/plan-strategy.js +205 -0
  237. package/dist/runtime/incidents/registry-strategy.d.ts +6 -0
  238. package/dist/runtime/incidents/registry-strategy.d.ts.map +1 -0
  239. package/dist/runtime/incidents/registry-strategy.js +280 -0
  240. package/dist/runtime/incidents/resolve.d.ts +3 -25
  241. package/dist/runtime/incidents/resolve.d.ts.map +1 -1
  242. package/dist/runtime/incidents/resolve.js +3 -683
  243. package/dist/runtime/incidents/shared.d.ts +34 -0
  244. package/dist/runtime/incidents/shared.d.ts.map +1 -0
  245. package/dist/runtime/incidents/shared.js +171 -0
  246. package/dist/testing/cli-harness/recipe-archives.d.ts +28 -0
  247. package/dist/testing/cli-harness/recipe-archives.d.ts.map +1 -0
  248. package/dist/testing/cli-harness/recipe-archives.js +374 -0
  249. package/dist/testing/cli-harness/stdio.d.ts +26 -0
  250. package/dist/testing/cli-harness/stdio.d.ts.map +1 -0
  251. package/dist/testing/cli-harness/stdio.js +84 -0
  252. package/dist/testing/cli-harness.d.ts +25 -0
  253. package/dist/testing/cli-harness.d.ts.map +1 -0
  254. package/dist/testing/cli-harness.js +313 -0
  255. package/dist/testing/index.d.ts +2 -0
  256. package/dist/testing/index.d.ts.map +1 -0
  257. package/dist/testing/index.js +1 -0
  258. package/package.json +7 -4
  259. package/dist/commands/recipes/impl/manifest.d.ts +0 -4
  260. package/dist/commands/recipes/impl/manifest.d.ts.map +0 -1
  261. package/dist/commands/recipes/impl/manifest.js +0 -7
  262. package/dist/commands/recipes/impl/normalize.d.ts +0 -8
  263. package/dist/commands/recipes/impl/normalize.d.ts.map +0 -1
  264. package/dist/commands/recipes/impl/normalize.js +0 -54
  265. package/dist/commands/recipes/impl/scenario.d.ts +0 -16
  266. package/dist/commands/recipes/impl/scenario.d.ts.map +0 -1
  267. package/dist/commands/recipes/impl/scenario.js +0 -262
  268. package/dist/recipes/bundled-recipes.d.ts +0 -17
  269. package/dist/recipes/bundled-recipes.d.ts.map +0 -1
  270. package/dist/recipes/bundled-recipes.js +0 -15
  271. package/dist/usecases/context/resolve-context.d.ts +0 -68
  272. package/dist/usecases/context/resolve-context.d.ts.map +0 -1
  273. package/dist/usecases/task/task-list-usecase.d.ts +0 -9
  274. package/dist/usecases/task/task-list-usecase.d.ts.map +0 -1
  275. package/dist/usecases/task/task-list-usecase.js +0 -17
  276. package/dist/usecases/task/task-new-usecase.d.ts +0 -9
  277. package/dist/usecases/task/task-new-usecase.d.ts.map +0 -1
  278. package/dist/usecases/task/task-new-usecase.js +0 -17
@@ -1,769 +1 @@
1
- import { execFile } from "node:child_process";
2
- import { createHash } from "node:crypto";
3
- import { access, cp, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import { fileURLToPath } from "node:url";
7
- import { promisify } from "node:util";
8
- import { gzipSync } from "node:zlib";
9
- import { afterAll, afterEach, beforeAll, beforeEach, expect, vi } from "vitest";
10
- import { defaultConfig } from "@agentplaneorg/core";
11
- import { runCli } from "./run-cli.js";
12
- const execFileAsync = promisify(execFile);
13
- let agentplaneHome = null;
14
- const testRoots = new Set();
15
- const originalAgentplaneHome = process.env.AGENTPLANE_HOME;
16
- const originalNoUpdateCheck = process.env.AGENTPLANE_NO_UPDATE_CHECK;
17
- const originalGitAuthorName = process.env.GIT_AUTHOR_NAME;
18
- const originalGitAuthorEmail = process.env.GIT_AUTHOR_EMAIL;
19
- const originalGitCommitterName = process.env.GIT_COMMITTER_NAME;
20
- const originalGitCommitterEmail = process.env.GIT_COMMITTER_EMAIL;
21
- const originalHookRunner = process.env.AGENTPLANE_HOOK_RUNNER;
22
- const originalStdoutWrite = process.stdout.write.bind(process.stdout);
23
- const originalStderrWrite = process.stderr.write.bind(process.stderr);
24
- let stdioSilenceDepth = 0;
25
- const recipeArchiveCache = new Map();
26
- let gitTemplateRoot = null;
27
- let gitTemplatePromise = null;
28
- async function ensureGitTemplateRoot() {
29
- if (gitTemplateRoot)
30
- return gitTemplateRoot;
31
- gitTemplatePromise ??= (async () => {
32
- const root = await mkdtemp(path.join(os.tmpdir(), "agentplane-git-template-"));
33
- await execFileAsync("git", ["init", "-q"], { cwd: root, env: cleanGitEnv() });
34
- // Tests must not rely on global git config. Configure author identity locally
35
- // so any helper that creates commits works in CI.
36
- await execFileAsync("git", ["config", "user.email", "agentplane-test@example.com"], {
37
- cwd: root,
38
- env: cleanGitEnv(),
39
- });
40
- await execFileAsync("git", ["config", "user.name", "agentplane-test"], {
41
- cwd: root,
42
- env: cleanGitEnv(),
43
- });
44
- return root;
45
- })();
46
- gitTemplateRoot = await gitTemplatePromise;
47
- return gitTemplateRoot;
48
- }
49
- async function copyDirContents(src, dest) {
50
- const entries = await readdir(src, { withFileTypes: true });
51
- await Promise.all(entries.map((entry) => cp(path.join(src, entry.name), path.join(dest, entry.name), { recursive: true })));
52
- }
53
- export function registerAgentplaneHome() {
54
- beforeAll(async () => {
55
- agentplaneHome = await mkdtemp(path.join(os.tmpdir(), "agentplane-home-"));
56
- process.env.AGENTPLANE_HOME = agentplaneHome;
57
- process.env.AGENTPLANE_NO_UPDATE_CHECK = "1";
58
- process.env.AGENTPLANE_HOOK_RUNNER ??= path.join(process.cwd(), "packages", "agentplane", "bin", "agentplane.js");
59
- // Keep tests hermetic: never rely on global git config for commit authorship.
60
- process.env.GIT_AUTHOR_NAME ??= "agentplane-test";
61
- process.env.GIT_AUTHOR_EMAIL ??= "agentplane-test@example.com";
62
- process.env.GIT_COMMITTER_NAME ??= "agentplane-test";
63
- process.env.GIT_COMMITTER_EMAIL ??= "agentplane-test@example.com";
64
- });
65
- afterAll(async () => {
66
- if (agentplaneHome) {
67
- await rm(agentplaneHome, { recursive: true, force: true });
68
- }
69
- if (originalAgentplaneHome === undefined) {
70
- delete process.env.AGENTPLANE_HOME;
71
- }
72
- else {
73
- process.env.AGENTPLANE_HOME = originalAgentplaneHome;
74
- }
75
- if (originalNoUpdateCheck === undefined) {
76
- delete process.env.AGENTPLANE_NO_UPDATE_CHECK;
77
- }
78
- else {
79
- process.env.AGENTPLANE_NO_UPDATE_CHECK = originalNoUpdateCheck;
80
- }
81
- if (originalGitAuthorName === undefined)
82
- delete process.env.GIT_AUTHOR_NAME;
83
- else
84
- process.env.GIT_AUTHOR_NAME = originalGitAuthorName;
85
- if (originalGitAuthorEmail === undefined)
86
- delete process.env.GIT_AUTHOR_EMAIL;
87
- else
88
- process.env.GIT_AUTHOR_EMAIL = originalGitAuthorEmail;
89
- if (originalGitCommitterName === undefined)
90
- delete process.env.GIT_COMMITTER_NAME;
91
- else
92
- process.env.GIT_COMMITTER_NAME = originalGitCommitterName;
93
- if (originalGitCommitterEmail === undefined)
94
- delete process.env.GIT_COMMITTER_EMAIL;
95
- else
96
- process.env.GIT_COMMITTER_EMAIL = originalGitCommitterEmail;
97
- if (originalHookRunner === undefined)
98
- delete process.env.AGENTPLANE_HOOK_RUNNER;
99
- else
100
- process.env.AGENTPLANE_HOOK_RUNNER = originalHookRunner;
101
- });
102
- afterEach(async () => {
103
- const roots = [...testRoots];
104
- testRoots.clear();
105
- await Promise.all(roots.map(async (root) => {
106
- await rm(path.join(root, ".agentplane", ".upgrade"), { recursive: true, force: true });
107
- await rm(path.join(root, ".agentplane", ".release"), { recursive: true, force: true });
108
- }));
109
- });
110
- }
111
- export function installRunCliIntegrationHarness() {
112
- registerAgentplaneHome();
113
- let restoreStdIO = null;
114
- beforeEach(() => {
115
- restoreStdIO = silenceStdIO();
116
- });
117
- afterEach(() => {
118
- restoreStdIO?.();
119
- restoreStdIO = null;
120
- });
121
- }
122
- export function getAgentplaneHome() {
123
- return agentplaneHome;
124
- }
125
- export function captureStdIO() {
126
- let stdout = "";
127
- let stderr = "";
128
- const origStdoutWrite = process.stdout.write.bind(process.stdout);
129
- const origStderrWrite = process.stderr.write.bind(process.stderr);
130
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
- process.stdout.write = (chunk) => {
132
- stdout += String(chunk);
133
- return true;
134
- };
135
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
- process.stderr.write = (chunk) => {
137
- stderr += String(chunk);
138
- return true;
139
- };
140
- return {
141
- get stdout() {
142
- return stdout;
143
- },
144
- get stderr() {
145
- return stderr;
146
- },
147
- restore() {
148
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
149
- process.stdout.write = origStdoutWrite;
150
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
151
- process.stderr.write = origStderrWrite;
152
- },
153
- };
154
- }
155
- export function splitOutputLines(text) {
156
- return text
157
- .trim()
158
- .split(/\r?\n/)
159
- .map((line) => line.trimEnd())
160
- .filter(Boolean);
161
- }
162
- export function parseAgentJsonEnvelope(stdout) {
163
- return JSON.parse(stdout);
164
- }
165
- export function expectAgentJsonEnvelope(payload, opts) {
166
- expect(payload.schema_version).toBe(1);
167
- expect(payload.mode).toBe("agent_json_v1");
168
- expect(payload.command).toBe(opts.command);
169
- expect(payload.ok).toBe(opts.ok);
170
- expect(payload.exit_code).toBe(opts.exitCode);
171
- expect(Object.keys(payload)).toEqual(opts.hasData
172
- ? ["schema_version", "mode", "command", "ok", "exit_code", "stdout", "stderr", "data"]
173
- : ["schema_version", "mode", "command", "ok", "exit_code", "stdout", "stderr"]);
174
- expect(Object.hasOwn(payload, "data")).toBe(opts.hasData ?? false);
175
- }
176
- export function silenceStdIO() {
177
- if (stdioSilenceDepth === 0) {
178
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
- process.stdout.write = () => true;
180
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
- process.stderr.write = () => true;
182
- }
183
- stdioSilenceDepth += 1;
184
- return () => {
185
- stdioSilenceDepth -= 1;
186
- if (stdioSilenceDepth <= 0) {
187
- stdioSilenceDepth = 0;
188
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
189
- process.stdout.write = originalStdoutWrite;
190
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
191
- process.stderr.write = originalStderrWrite;
192
- }
193
- };
194
- }
195
- export function stubTaskBackend(overrides = {}) {
196
- return {
197
- id: "local",
198
- capabilities: {
199
- canonical_source: "local",
200
- projection: "canonical",
201
- projection_read_mode: "fallback",
202
- reads_from_projection_by_default: false,
203
- writes_task_readmes: true,
204
- supports_task_revisions: true,
205
- supports_revision_guarded_writes: true,
206
- may_access_network_on_read: false,
207
- may_access_network_on_write: false,
208
- supports_projection_refresh: false,
209
- supports_push_sync: false,
210
- supports_snapshot_export: false,
211
- },
212
- listTasks: vi.fn().mockResolvedValue([]),
213
- getTask: vi.fn().mockResolvedValue(null),
214
- writeTask: vi.fn().mockImplementation(() => Promise.resolve()),
215
- ...overrides,
216
- };
217
- }
218
- export async function runCliSilent(args) {
219
- const io = captureStdIO();
220
- try {
221
- return await runCli(args);
222
- }
223
- finally {
224
- io.restore();
225
- }
226
- }
227
- export async function mkGitRepoRoot() {
228
- const template = await ensureGitTemplateRoot();
229
- const root = await mkdtemp(path.join(os.tmpdir(), "agentplane-cli-test-"));
230
- await copyDirContents(template, root);
231
- testRoots.add(root);
232
- return root;
233
- }
234
- export async function mkTempDir() {
235
- const root = await mkdtemp(path.join(os.tmpdir(), "agentplane-cli-test-"));
236
- testRoots.add(root);
237
- return root;
238
- }
239
- export async function writeDefaultConfig(root) {
240
- const agentplaneDir = path.join(root, ".agentplane");
241
- await mkdir(agentplaneDir, { recursive: true });
242
- const configPath = path.join(agentplaneDir, "config.json");
243
- await writeFile(configPath, JSON.stringify(defaultConfig(), null, 2), "utf8");
244
- }
245
- export async function writeAndConfigureRoot() {
246
- const root = await mkGitRepoRoot();
247
- await writeDefaultConfig(root);
248
- return root;
249
- }
250
- export async function approveTaskPlan(root, taskId) {
251
- await runCliSilent([
252
- "task",
253
- "plan",
254
- "set",
255
- taskId,
256
- "--text",
257
- "1) Do the work\n2) Verify the work",
258
- "--updated-by",
259
- "ORCHESTRATOR",
260
- "--root",
261
- root,
262
- ]);
263
- await runCliSilent([
264
- "task",
265
- "plan",
266
- "approve",
267
- taskId,
268
- "--by",
269
- "USER",
270
- "--note",
271
- "OK",
272
- "--root",
273
- root,
274
- ]);
275
- }
276
- export async function recordVerificationOk(root, taskId) {
277
- await runCliSilent([
278
- "task",
279
- "doc",
280
- "set",
281
- taskId,
282
- "--section",
283
- "Verify Steps",
284
- "--text",
285
- "Run verify for this task. Expected: verification records successfully.",
286
- "--root",
287
- root,
288
- ]);
289
- await runCliSilent([
290
- "verify",
291
- taskId,
292
- "--ok",
293
- "--by",
294
- "REVIEWER",
295
- "--note",
296
- "Ok to integrate",
297
- "--quiet",
298
- "--root",
299
- root,
300
- ]);
301
- }
302
- export async function writeConfig(root, config) {
303
- const agentplaneDir = path.join(root, ".agentplane");
304
- await mkdir(agentplaneDir, { recursive: true });
305
- const configPath = path.join(agentplaneDir, "config.json");
306
- await writeFile(configPath, JSON.stringify(config, null, 2), "utf8");
307
- }
308
- export async function resetAgentplaneHomeRecipes() {
309
- if (!agentplaneHome)
310
- return;
311
- await rm(path.join(agentplaneHome, "recipes-store"), { recursive: true, force: true });
312
- await rm(path.join(agentplaneHome, "recipes.json"), { force: true });
313
- await rm(path.join(agentplaneHome, "recipes-index.json"), { force: true });
314
- }
315
- export async function createRecipeArchive(opts) {
316
- const normalizedTags = opts?.tags ? [...opts.tags].toSorted() : undefined;
317
- const cacheKey = JSON.stringify({
318
- id: opts?.id ?? "viewer",
319
- version: opts?.version ?? "1.2.3",
320
- name: opts?.name ?? "Viewer",
321
- summary: opts?.summary ?? "Preview task artifacts",
322
- description: opts?.description ?? "Provides a local viewer for task artifacts.",
323
- tags: normalizedTags,
324
- format: opts?.format ?? "tar",
325
- wrapDir: opts?.wrapDir ?? false,
326
- });
327
- const cached = recipeArchiveCache.get(cacheKey);
328
- if (cached)
329
- return cached;
330
- const baseDir = await mkdtemp(path.join(os.tmpdir(), "agentplane-recipe-"));
331
- const recipeDir = path.join(baseDir, opts?.wrapDir ? "bundle" : "recipe");
332
- await mkdir(recipeDir, { recursive: true });
333
- const manifest = {
334
- schema_version: "1",
335
- id: opts?.id ?? "viewer",
336
- version: opts?.version ?? "1.2.3",
337
- name: opts?.name ?? "Viewer",
338
- summary: opts?.summary ?? "Preview task artifacts",
339
- description: opts?.description ?? "Provides a local viewer for task artifacts.",
340
- compatibility: {
341
- min_agentplane_version: "0.3.5",
342
- manifest_api_version: "1",
343
- scenario_api_version: "1",
344
- runtime_api_version: "1",
345
- platforms: ["darwin", "linux"],
346
- repo_types: ["generic"],
347
- },
348
- skills: [
349
- {
350
- id: "RECIPE_SKILL",
351
- summary: "Recipe analysis skill",
352
- file: "skills/analysis.md",
353
- },
354
- ],
355
- agents: [
356
- {
357
- id: "RECIPE_AGENT",
358
- display_name: "Recipe Agent",
359
- role: "executor",
360
- summary: "Recipe agent",
361
- skills: ["RECIPE_SKILL"],
362
- tools: ["RECIPE_TOOL"],
363
- file: "agents/recipe.md",
364
- },
365
- ],
366
- tools: [
367
- { id: "RECIPE_TOOL", summary: "Recipe tool", runtime: "node", entrypoint: "tools/run.js" },
368
- ],
369
- scenarios: [
370
- {
371
- id: "RECIPE_SCENARIO",
372
- name: "Recipe Scenario",
373
- summary: "Recipe scenario",
374
- use_when: ["Task artifacts need local preview"],
375
- required_inputs: ["task_id"],
376
- outputs: ["report"],
377
- permissions: ["filesystem-write"],
378
- artifacts: ["artifact.txt"],
379
- agents_involved: ["RECIPE_AGENT"],
380
- skills_used: ["RECIPE_SKILL"],
381
- tools_used: ["RECIPE_TOOL"],
382
- run_profile: {
383
- mode: "analysis",
384
- sandbox: "workspace-write",
385
- writes_artifacts_to: ["logs/", "reports/"],
386
- },
387
- file: "scenarios/recipe-scenario.json",
388
- },
389
- ],
390
- };
391
- if (normalizedTags) {
392
- manifest.tags = normalizedTags;
393
- }
394
- await writeFile(path.join(recipeDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
395
- const agentsDir = path.join(recipeDir, "agents");
396
- await mkdir(agentsDir, { recursive: true });
397
- await writeFile(path.join(agentsDir, "recipe.md"), [
398
- "# Recipe Agent",
399
- "",
400
- "Role: executor",
401
- "",
402
- "Instructions:",
403
- "- Use recipe local policy.",
404
- "- Materialize the declared scenario artifacts.",
405
- ].join("\n"), "utf8");
406
- const skillsDir = path.join(recipeDir, "skills");
407
- await mkdir(skillsDir, { recursive: true });
408
- await writeFile(path.join(skillsDir, "analysis.md"), [
409
- "# Recipe Skill",
410
- "",
411
- "- Inspect the generated bundle before acting.",
412
- "- Keep recipe-owned artifacts inside the declared output paths.",
413
- ].join("\n"), "utf8");
414
- const toolsDir = path.join(recipeDir, "tools");
415
- await mkdir(toolsDir, { recursive: true });
416
- await writeFile(path.join(toolsDir, "run.js"), [
417
- 'const fs = require("node:fs");',
418
- 'fs.writeFileSync(process.env.AGENTPLANE_RUN_DIR + "/artifact.txt", "ok");',
419
- ].join("\n"), "utf8");
420
- const scenariosDir = path.join(recipeDir, "scenarios");
421
- await mkdir(scenariosDir, { recursive: true });
422
- await writeFile(path.join(scenariosDir, "recipe-scenario.json"), JSON.stringify({
423
- schema_version: "1",
424
- id: "RECIPE_SCENARIO",
425
- summary: "Recipe scenario",
426
- goal: "Preview installed tasks.",
427
- task_template: {
428
- title: "Recipe scenario task",
429
- description: "Materialize a task from the recipe scenario.",
430
- owner: "CODER",
431
- priority: "med",
432
- tags: ["code", "recipes"],
433
- verify: ["bunx vitest run packages/agentplane/src/cli/run-cli.scenario.test.ts"],
434
- doc: {
435
- summary: "Recipe-backed task execution.",
436
- scope: "Run the scenario without task materialization heuristics.",
437
- plan: "1. Materialize the task. 2. Execute the shared runner.",
438
- verify_steps: "1. Run scenario execution tests.",
439
- rollback_plan: "Revert the generated task and runner artifacts.",
440
- },
441
- },
442
- inputs: [{ name: "task_id", type: "string" }],
443
- outputs: [{ name: "report", type: "html" }],
444
- steps: [{ tool: "RECIPE_TOOL" }],
445
- }, null, 2), "utf8");
446
- const format = opts?.format ?? "tar";
447
- const archivePath = format === "zip" ? path.join(baseDir, "recipe.zip") : path.join(baseDir, "recipe.tar.gz");
448
- if (format === "zip") {
449
- await (opts?.wrapDir
450
- ? execFileAsync("zip", ["-qr", archivePath, path.basename(recipeDir)], { cwd: baseDir })
451
- : execFileAsync("zip", ["-qr", archivePath, "."], { cwd: recipeDir }));
452
- }
453
- else {
454
- await (opts?.wrapDir
455
- ? execFileAsync("tar", ["-czf", archivePath, "-C", baseDir, path.basename(recipeDir)])
456
- : execFileAsync("tar", ["-czf", archivePath, "-C", recipeDir, "."]));
457
- }
458
- const payload = { archivePath, manifest };
459
- recipeArchiveCache.set(cacheKey, payload);
460
- return payload;
461
- }
462
- export async function createRecipeArchiveWithManifest(opts) {
463
- const baseDir = await mkdtemp(path.join(os.tmpdir(), "agentplane-recipe-bad-"));
464
- const recipeDir = path.join(baseDir, opts.wrapDir ? "bundle" : "recipe");
465
- await mkdir(recipeDir, { recursive: true });
466
- await writeFile(path.join(recipeDir, "manifest.json"), JSON.stringify(opts.manifest, null, 2));
467
- if (opts.files) {
468
- for (const [relPath, content] of Object.entries(opts.files)) {
469
- const fullPath = path.join(recipeDir, relPath);
470
- await mkdir(path.dirname(fullPath), { recursive: true });
471
- await writeFile(fullPath, content, "utf8");
472
- }
473
- }
474
- const format = opts.format ?? "tar";
475
- const archivePath = format === "zip" ? path.join(baseDir, "recipe.zip") : path.join(baseDir, "recipe.tar.gz");
476
- if (format === "zip") {
477
- await (opts.wrapDir
478
- ? execFileAsync("zip", ["-qr", archivePath, path.basename(recipeDir)], { cwd: baseDir })
479
- : execFileAsync("zip", ["-qr", archivePath, "."], { cwd: recipeDir }));
480
- }
481
- else {
482
- await (opts.wrapDir
483
- ? execFileAsync("tar", ["-czf", archivePath, "-C", baseDir, path.basename(recipeDir)])
484
- : execFileAsync("tar", ["-czf", archivePath, "-C", recipeDir, "."]));
485
- }
486
- return archivePath;
487
- }
488
- export async function createUnsafeRecipeArchive(opts) {
489
- const baseDir = await mkdtemp(path.join(os.tmpdir(), "agentplane-recipe-unsafe-"));
490
- const recipeDir = path.join(baseDir, "recipe");
491
- await mkdir(recipeDir, { recursive: true });
492
- const manifest = {
493
- schema_version: "1",
494
- id: "unsafe",
495
- version: "0.0.1",
496
- name: "Unsafe",
497
- summary: "Unsafe recipe",
498
- description: "Used for archive validation tests.",
499
- skills: [
500
- {
501
- id: "RECIPE_SKILL",
502
- summary: "Recipe skill",
503
- file: "skills/recipe.md",
504
- },
505
- ],
506
- agents: [
507
- {
508
- id: "RECIPE_AGENT",
509
- display_name: "Recipe Agent",
510
- role: "executor",
511
- summary: "Recipe agent",
512
- skills: ["RECIPE_SKILL"],
513
- tools: ["RECIPE_TOOL"],
514
- file: "agents/recipe.md",
515
- },
516
- ],
517
- tools: [
518
- { id: "RECIPE_TOOL", summary: "Recipe tool", runtime: "bash", entrypoint: "tools/run.sh" },
519
- ],
520
- scenarios: [
521
- {
522
- id: "RECIPE_SCENARIO",
523
- name: "Recipe Scenario",
524
- summary: "Recipe scenario",
525
- use_when: ["Unsafe validation fixture"],
526
- required_inputs: [],
527
- outputs: [],
528
- permissions: [],
529
- artifacts: [],
530
- agents_involved: ["RECIPE_AGENT"],
531
- skills_used: ["RECIPE_SKILL"],
532
- tools_used: ["RECIPE_TOOL"],
533
- run_profile: { mode: "analysis" },
534
- file: "scenarios/recipe-scenario.json",
535
- },
536
- ],
537
- };
538
- await writeFile(path.join(recipeDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
539
- const agentsDir = path.join(recipeDir, "agents");
540
- await mkdir(agentsDir, { recursive: true });
541
- await writeFile(path.join(agentsDir, "recipe.md"), "# Recipe Agent\n\nFollow the unsafe archive validation path.\n", "utf8");
542
- const skillsDir = path.join(recipeDir, "skills");
543
- await mkdir(skillsDir, { recursive: true });
544
- await writeFile(path.join(skillsDir, "recipe.md"), "# Recipe Skill\n\nInspect archive contents before materialization.\n", "utf8");
545
- const toolsDir = path.join(recipeDir, "tools");
546
- await mkdir(toolsDir, { recursive: true });
547
- await writeFile(path.join(toolsDir, "run.sh"), "#!/usr/bin/env bash\n", "utf8");
548
- const scenariosDir = path.join(recipeDir, "scenarios");
549
- await mkdir(scenariosDir, { recursive: true });
550
- await writeFile(path.join(scenariosDir, "recipe-scenario.json"), JSON.stringify({
551
- schema_version: "1",
552
- id: "RECIPE_SCENARIO",
553
- summary: "Recipe scenario",
554
- goal: "Exercise unsafe archive validation.",
555
- task_template: {
556
- title: "Unsafe archive task",
557
- description: "Validate unsafe archive handling.",
558
- owner: "CODER",
559
- },
560
- inputs: [],
561
- outputs: [],
562
- steps: [{ tool: "RECIPE_TOOL" }],
563
- }, null, 2), "utf8");
564
- const entryPath = opts.entryPath ?? "../evil.txt";
565
- await writeFile(path.join(baseDir, "evil.txt"), "evil", "utf8");
566
- const archivePath = opts.format === "zip" ? path.join(baseDir, "unsafe.zip") : path.join(baseDir, "unsafe.tar.gz");
567
- if (opts.format === "zip") {
568
- await execFileAsync("zip", ["-qr", archivePath, ".", entryPath], { cwd: recipeDir });
569
- return archivePath;
570
- }
571
- // Build a deterministic tar.gz containing a traversal entry, without relying on system tar behavior.
572
- // This keeps the archive validation tests stable across GNU tar / bsdtar.
573
- const tar = buildTar([
574
- {
575
- name: "./manifest.json",
576
- data: Buffer.from(JSON.stringify(manifest, null, 2) + "\n", "utf8"),
577
- },
578
- { name: entryPath, data: Buffer.from("evil\n", "utf8") },
579
- ]);
580
- const gz = gzipSync(tar);
581
- await writeFile(archivePath, gz);
582
- return archivePath;
583
- }
584
- function buildTar(entries) {
585
- const out = [];
586
- for (const ent of entries) {
587
- const header = tarHeader({
588
- name: ent.name,
589
- size: ent.data.length,
590
- mtime: 0,
591
- typeflag: "0",
592
- });
593
- out.push(header, ent.data, zeroPadTo512(ent.data.length));
594
- }
595
- // Two empty blocks mark end-of-archive.
596
- out.push(Buffer.alloc(1024, 0));
597
- return Buffer.concat(out);
598
- }
599
- function zeroPadTo512(n) {
600
- const rem = n % 512;
601
- if (rem === 0)
602
- return Buffer.alloc(0);
603
- return Buffer.alloc(512 - rem, 0);
604
- }
605
- function tarHeader(opts) {
606
- const buf = Buffer.alloc(512, 0);
607
- writeTarString(buf, 0, 100, opts.name);
608
- writeTarOctal(buf, 100, 8, 0o644); // mode
609
- writeTarOctal(buf, 108, 8, 0); // uid
610
- writeTarOctal(buf, 116, 8, 0); // gid
611
- writeTarOctal(buf, 124, 12, opts.size);
612
- writeTarOctal(buf, 136, 12, opts.mtime);
613
- // checksum field must be treated as spaces for calculation
614
- buf.fill(0x20, 148, 156);
615
- writeTarString(buf, 156, 1, opts.typeflag);
616
- writeTarString(buf, 257, 6, "ustar"); // magic
617
- writeTarString(buf, 263, 2, "00"); // version
618
- const sum = buf.reduce((acc, b) => acc + b, 0);
619
- writeTarChecksum(buf, sum);
620
- return buf;
621
- }
622
- function writeTarString(buf, offset, length, value) {
623
- const b = Buffer.from(value, "utf8");
624
- b.copy(buf, offset, 0, Math.min(length, b.length));
625
- }
626
- function writeTarOctal(buf, offset, length, value) {
627
- const raw = Math.max(0, value).toString(8);
628
- const padded = raw.padStart(length - 1, "0") + "\0";
629
- writeTarString(buf, offset, length, padded);
630
- }
631
- function writeTarChecksum(buf, sum) {
632
- // 6 digits, NUL, space (common convention).
633
- const raw = Math.max(0, sum).toString(8).padStart(6, "0");
634
- writeTarString(buf, 148, 8, `${raw}\0 `);
635
- }
636
- export async function createUpgradeBundle(files) {
637
- const manifestUrl = new URL("../../assets/framework.manifest.json", import.meta.url);
638
- const manifestText = typeof files["framework.manifest.json"] === "string"
639
- ? files["framework.manifest.json"]
640
- : await readFile(fileURLToPath(manifestUrl), "utf8");
641
- const manifest = JSON.parse(manifestText);
642
- const normalizedFiles = {};
643
- for (const [relPath, content] of Object.entries(files)) {
644
- // Tests historically authored bundles using workspace-destination paths. Upgrade now reads
645
- // upstream files via manifest source_path (package assets layout).
646
- const mapped = relPath.startsWith(".agentplane/agents/")
647
- ? relPath.replace(/^\.agentplane\/agents\//, "agents/")
648
- : relPath;
649
- normalizedFiles[mapped] = content;
650
- }
651
- // Ensure the bundle always carries its manifest at the root.
652
- normalizedFiles["framework.manifest.json"] ??= manifestText;
653
- if (manifest.schema_version === 1 && Array.isArray(manifest.files)) {
654
- for (const entry of manifest.files) {
655
- if (!entry?.required)
656
- continue;
657
- const sourceRel = (entry.source_path ?? entry.path ?? "").trim();
658
- if (!sourceRel)
659
- continue;
660
- if (normalizedFiles[sourceRel] !== undefined)
661
- continue;
662
- if (entry.type === "json")
663
- normalizedFiles[sourceRel] = "{}\n";
664
- else if (sourceRel.endsWith(".md"))
665
- normalizedFiles[sourceRel] = "# AGENTS\n";
666
- else
667
- normalizedFiles[sourceRel] = "\n";
668
- }
669
- }
670
- const baseDir = await mkdtemp(path.join(os.tmpdir(), "agentplane-upgrade-bundle-"));
671
- const bundleDir = path.join(baseDir, "bundle");
672
- await mkdir(bundleDir, { recursive: true });
673
- for (const [relPath, content] of Object.entries(normalizedFiles)) {
674
- const fullPath = path.join(bundleDir, relPath);
675
- await mkdir(path.dirname(fullPath), { recursive: true });
676
- await writeFile(fullPath, content, "utf8");
677
- }
678
- const bundlePath = path.join(baseDir, "agentplane-upgrade.tar.gz");
679
- await execFileAsync("tar", ["-czf", bundlePath, "-C", bundleDir, "."]);
680
- const checksum = createHash("sha256")
681
- .update(await readFile(bundlePath))
682
- .digest("hex");
683
- const checksumPath = `${bundlePath}.sha256`;
684
- await writeFile(checksumPath, `${checksum} agentplane-upgrade.tar.gz\n`, "utf8");
685
- return { bundlePath, checksumPath };
686
- }
687
- export async function mkGitRepoRootWithBranch(branch) {
688
- const root = await mkGitRepoRoot();
689
- await execFileAsync("git", ["checkout", "-b", branch], { cwd: root, env: cleanGitEnv() });
690
- return root;
691
- }
692
- export async function configureGitUser(root) {
693
- await execFileAsync("git", ["config", "user.email", "test@example.com"], {
694
- cwd: root,
695
- env: cleanGitEnv(),
696
- });
697
- await execFileAsync("git", ["config", "user.name", "Test User"], {
698
- cwd: root,
699
- env: cleanGitEnv(),
700
- });
701
- }
702
- export function cleanGitEnv() {
703
- const env = { ...process.env };
704
- delete env.GIT_DIR;
705
- delete env.GIT_WORK_TREE;
706
- delete env.GIT_COMMON_DIR;
707
- delete env.GIT_INDEX_FILE;
708
- delete env.GIT_OBJECT_DIRECTORY;
709
- delete env.GIT_ALTERNATE_OBJECT_DIRECTORIES;
710
- env.GIT_CONFIG_GLOBAL = "/dev/null";
711
- env.GIT_CONFIG_SYSTEM = "/dev/null";
712
- env.GIT_TERMINAL_PROMPT = "0";
713
- env.GIT_AUTHOR_NAME = env.GIT_AUTHOR_NAME ?? "Agentplane Test";
714
- env.GIT_AUTHOR_EMAIL = env.GIT_AUTHOR_EMAIL ?? "agentplane-test@example.com";
715
- env.GIT_COMMITTER_NAME = env.GIT_COMMITTER_NAME ?? "Agentplane Test";
716
- env.GIT_COMMITTER_EMAIL = env.GIT_COMMITTER_EMAIL ?? "agentplane-test@example.com";
717
- return env;
718
- }
719
- export async function pathExists(filePath) {
720
- try {
721
- await access(filePath);
722
- return true;
723
- }
724
- catch {
725
- return false;
726
- }
727
- }
728
- export async function gitBranchExists(root, branch) {
729
- try {
730
- await execFileAsync("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
731
- cwd: root,
732
- env: cleanGitEnv(),
733
- });
734
- return true;
735
- }
736
- catch (err) {
737
- const code = err?.code;
738
- if (code === 1)
739
- return false;
740
- throw err;
741
- }
742
- }
743
- export async function commitAll(root, message) {
744
- await execFileAsync("git", ["add", "."], { cwd: root, env: cleanGitEnv() });
745
- await execFileAsync("git", ["commit", "--no-verify", "-m", message], {
746
- cwd: root,
747
- env: cleanGitEnv(),
748
- });
749
- }
750
- export async function commitPathsIfChanged(root, paths, message) {
751
- await execFileAsync("git", ["add", "--", ...paths], { cwd: root, env: cleanGitEnv() });
752
- const { stdout } = await execFileAsync("git", ["diff", "--cached", "--name-only", "--", ...paths], {
753
- cwd: root,
754
- env: cleanGitEnv(),
755
- });
756
- if (!stdout.trim())
757
- return false;
758
- await execFileAsync("git", ["commit", "--no-verify", "-m", message], {
759
- cwd: root,
760
- env: cleanGitEnv(),
761
- });
762
- return true;
763
- }
764
- export async function stageGitignoreIfPresent(root) {
765
- const gitignorePath = path.join(root, ".gitignore");
766
- if (!(await pathExists(gitignorePath)))
767
- return;
768
- await execFileAsync("git", ["add", ".gitignore"], { cwd: root, env: cleanGitEnv() });
769
- }
1
+ export * from "../testing/index.js";