agentplane 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.md +103 -75
  2. package/assets/AGENTS.md +4 -2
  3. package/bin/dist-guard.js +13 -3
  4. package/bin/runtime-watch.d.ts +1 -0
  5. package/bin/runtime-watch.js +22 -5
  6. package/bin/stale-dist-policy.js +9 -2
  7. package/dist/.build-manifest.json +220 -790
  8. package/dist/adapters/task-backend/task-backend-adapter.d.ts +1 -1
  9. package/dist/adapters/task-backend/task-backend-adapter.d.ts.map +1 -1
  10. package/dist/adapters/task-backend/task-backend-adapter.js +5 -2
  11. package/dist/backends/task-backend/local-backend.d.ts +13 -0
  12. package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
  13. package/dist/backends/task-backend/local-backend.js +17 -0
  14. package/dist/backends/task-backend/redmine-backend.d.ts +18 -0
  15. package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
  16. package/dist/backends/task-backend/redmine-backend.js +35 -25
  17. package/dist/backends/task-backend/shared/types.d.ts +20 -0
  18. package/dist/backends/task-backend/shared/types.d.ts.map +1 -1
  19. package/dist/backends/task-backend/shared.d.ts +1 -1
  20. package/dist/backends/task-backend/shared.d.ts.map +1 -1
  21. package/dist/backends/task-backend.d.ts +1 -1
  22. package/dist/backends/task-backend.d.ts.map +1 -1
  23. package/dist/backends/task-backend.test-helpers.d.ts +4 -0
  24. package/dist/backends/task-backend.test-helpers.d.ts.map +1 -0
  25. package/dist/backends/task-backend.test-helpers.js +33 -0
  26. package/dist/cli/bootstrap-guide.d.ts.map +1 -1
  27. package/dist/cli/bootstrap-guide.js +1 -0
  28. package/dist/cli/command-guide.d.ts.map +1 -1
  29. package/dist/cli/command-guide.js +3 -2
  30. package/dist/cli/reason-codes.d.ts.map +1 -1
  31. package/dist/cli/reason-codes.js +30 -0
  32. package/dist/cli/run-cli/command-catalog/core.d.ts +3 -0
  33. package/dist/cli/run-cli/command-catalog/core.d.ts.map +1 -0
  34. package/dist/cli/run-cli/command-catalog/core.js +137 -0
  35. package/dist/cli/run-cli/command-catalog/lifecycle.d.ts +3 -0
  36. package/dist/cli/run-cli/command-catalog/lifecycle.d.ts.map +1 -0
  37. package/dist/cli/run-cli/command-catalog/lifecycle.js +52 -0
  38. package/dist/cli/run-cli/command-catalog/project.d.ts +3 -0
  39. package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -0
  40. package/dist/cli/run-cli/command-catalog/project.js +78 -0
  41. package/dist/cli/run-cli/command-catalog/shared.d.ts +19 -0
  42. package/dist/cli/run-cli/command-catalog/shared.d.ts.map +1 -0
  43. package/dist/cli/run-cli/command-catalog/shared.js +9 -0
  44. package/dist/cli/run-cli/command-catalog/task.d.ts +3 -0
  45. package/dist/cli/run-cli/command-catalog/task.d.ts.map +1 -0
  46. package/dist/cli/run-cli/command-catalog/task.js +85 -0
  47. package/dist/cli/run-cli/command-catalog.d.ts +3 -18
  48. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  49. package/dist/cli/run-cli/command-catalog.js +8 -337
  50. package/dist/cli/run-cli/commands/ide.d.ts.map +1 -1
  51. package/dist/cli/run-cli/commands/ide.js +64 -2
  52. package/dist/cli/run-cli/commands/init/ui.d.ts.map +1 -1
  53. package/dist/cli/run-cli/commands/init/ui.js +33 -13
  54. package/dist/cli/run-cli.core.pr-flow.test-helpers.d.ts +3 -0
  55. package/dist/cli/run-cli.core.pr-flow.test-helpers.d.ts.map +1 -0
  56. package/dist/cli/run-cli.core.pr-flow.test-helpers.js +41 -0
  57. package/dist/cli/run-cli.core.tasks.test-helpers.d.ts +2 -0
  58. package/dist/cli/run-cli.core.tasks.test-helpers.d.ts.map +1 -0
  59. package/dist/cli/run-cli.core.tasks.test-helpers.js +6 -0
  60. package/dist/cli/run-cli.test-helpers.d.ts +3 -0
  61. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  62. package/dist/cli/run-cli.test-helpers.js +138 -6
  63. package/dist/commands/commit.spec.d.ts.map +1 -1
  64. package/dist/commands/commit.spec.js +2 -2
  65. package/dist/commands/doctor/runtime.d.ts.map +1 -1
  66. package/dist/commands/doctor/runtime.js +3 -6
  67. package/dist/commands/doctor/workspace.d.ts +4 -1
  68. package/dist/commands/doctor/workspace.d.ts.map +1 -1
  69. package/dist/commands/doctor/workspace.js +87 -4
  70. package/dist/commands/doctor.run.d.ts.map +1 -1
  71. package/dist/commands/doctor.run.js +8 -1
  72. package/dist/commands/guard/commit.command.js +1 -1
  73. package/dist/commands/guard/impl/allow.d.ts +5 -0
  74. package/dist/commands/guard/impl/allow.d.ts.map +1 -1
  75. package/dist/commands/guard/impl/allow.js +15 -10
  76. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  77. package/dist/commands/guard/impl/commands.js +137 -18
  78. package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
  79. package/dist/commands/guard/impl/comment-commit.js +2 -0
  80. package/dist/commands/hooks/index.d.ts.map +1 -1
  81. package/dist/commands/hooks/index.js +8 -35
  82. package/dist/commands/recipes/impl/apply.d.ts +4 -0
  83. package/dist/commands/recipes/impl/apply.d.ts.map +1 -1
  84. package/dist/commands/recipes/impl/apply.js +34 -0
  85. package/dist/commands/recipes/impl/commands/explain.d.ts.map +1 -1
  86. package/dist/commands/recipes/impl/commands/explain.js +70 -11
  87. package/dist/commands/recipes/impl/commands/info.d.ts.map +1 -1
  88. package/dist/commands/recipes/impl/commands/info.js +24 -12
  89. package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
  90. package/dist/commands/recipes/impl/commands/install.js +32 -36
  91. package/dist/commands/recipes/impl/commands/list.d.ts.map +1 -1
  92. package/dist/commands/recipes/impl/commands/list.js +7 -4
  93. package/dist/commands/recipes/impl/commands/remove.d.ts.map +1 -1
  94. package/dist/commands/recipes/impl/commands/remove.js +9 -11
  95. package/dist/commands/recipes/impl/constants.d.ts +2 -0
  96. package/dist/commands/recipes/impl/constants.d.ts.map +1 -1
  97. package/dist/commands/recipes/impl/constants.js +2 -0
  98. package/dist/commands/recipes/impl/manifest.d.ts.map +1 -1
  99. package/dist/commands/recipes/impl/manifest.js +219 -23
  100. package/dist/commands/recipes/impl/normalize.d.ts +3 -0
  101. package/dist/commands/recipes/impl/normalize.d.ts.map +1 -1
  102. package/dist/commands/recipes/impl/normalize.js +28 -24
  103. package/dist/commands/recipes/impl/paths.d.ts +9 -0
  104. package/dist/commands/recipes/impl/paths.d.ts.map +1 -1
  105. package/dist/commands/recipes/impl/paths.js +10 -1
  106. package/dist/commands/recipes/impl/project-installed-recipes.d.ts +7 -0
  107. package/dist/commands/recipes/impl/project-installed-recipes.d.ts.map +1 -0
  108. package/dist/commands/recipes/impl/project-installed-recipes.js +102 -0
  109. package/dist/commands/recipes/impl/resolver.d.ts +20 -0
  110. package/dist/commands/recipes/impl/resolver.d.ts.map +1 -0
  111. package/dist/commands/recipes/impl/resolver.js +220 -0
  112. package/dist/commands/recipes/impl/scenario.d.ts.map +1 -1
  113. package/dist/commands/recipes/impl/scenario.js +40 -11
  114. package/dist/commands/recipes/impl/types.d.ts +145 -16
  115. package/dist/commands/recipes/impl/types.d.ts.map +1 -1
  116. package/dist/commands/recipes/install.spec.d.ts.map +1 -1
  117. package/dist/commands/recipes/install.spec.js +3 -2
  118. package/dist/commands/recipes.d.ts +6 -4
  119. package/dist/commands/recipes.d.ts.map +1 -1
  120. package/dist/commands/recipes.js +5 -3
  121. package/dist/commands/recipes.test-helpers.d.ts +185 -0
  122. package/dist/commands/recipes.test-helpers.d.ts.map +1 -0
  123. package/dist/commands/recipes.test-helpers.js +339 -0
  124. package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
  125. package/dist/commands/scenario/impl/commands.js +192 -336
  126. package/dist/commands/scenario/info.command.d.ts.map +1 -1
  127. package/dist/commands/scenario/info.command.js +7 -2
  128. package/dist/commands/scenario/list.command.js +2 -2
  129. package/dist/commands/scenario/run.command.d.ts.map +1 -1
  130. package/dist/commands/scenario/run.command.js +7 -2
  131. package/dist/commands/shared/git-context.d.ts +1 -0
  132. package/dist/commands/shared/git-context.d.ts.map +1 -1
  133. package/dist/commands/shared/git-context.js +4 -0
  134. package/dist/commands/shared/reconcile-check.d.ts.map +1 -1
  135. package/dist/commands/shared/reconcile-check.js +77 -2
  136. package/dist/commands/shared/task-backend.d.ts +5 -0
  137. package/dist/commands/shared/task-backend.d.ts.map +1 -1
  138. package/dist/commands/shared/task-backend.js +24 -0
  139. package/dist/commands/shared/task-store.d.ts +32 -1
  140. package/dist/commands/shared/task-store.d.ts.map +1 -1
  141. package/dist/commands/shared/task-store.js +166 -42
  142. package/dist/commands/task/block.d.ts.map +1 -1
  143. package/dist/commands/task/block.js +46 -29
  144. package/dist/commands/task/close-duplicate.d.ts.map +1 -1
  145. package/dist/commands/task/close-duplicate.js +12 -37
  146. package/dist/commands/task/close-noop.d.ts.map +1 -1
  147. package/dist/commands/task/close-noop.js +12 -30
  148. package/dist/commands/task/close-shared.d.ts +14 -0
  149. package/dist/commands/task/close-shared.d.ts.map +1 -0
  150. package/dist/commands/task/close-shared.js +76 -0
  151. package/dist/commands/task/comment.d.ts.map +1 -1
  152. package/dist/commands/task/comment.js +35 -17
  153. package/dist/commands/task/doc-set.command.d.ts +2 -1
  154. package/dist/commands/task/doc-set.command.d.ts.map +1 -1
  155. package/dist/commands/task/doc-set.command.js +36 -4
  156. package/dist/commands/task/doc-template.d.ts.map +1 -1
  157. package/dist/commands/task/doc-template.js +2 -7
  158. package/dist/commands/task/doc.command.js +1 -1
  159. package/dist/commands/task/doc.d.ts +2 -1
  160. package/dist/commands/task/doc.d.ts.map +1 -1
  161. package/dist/commands/task/doc.js +123 -71
  162. package/dist/commands/task/export.d.ts.map +1 -1
  163. package/dist/commands/task/export.js +4 -4
  164. package/dist/commands/task/finish.d.ts.map +1 -1
  165. package/dist/commands/task/finish.js +141 -78
  166. package/dist/commands/task/migrate-doc.d.ts.map +1 -1
  167. package/dist/commands/task/migrate-doc.js +15 -11
  168. package/dist/commands/task/plan-set.command.js +1 -1
  169. package/dist/commands/task/plan.command.d.ts +8 -0
  170. package/dist/commands/task/plan.command.d.ts.map +1 -0
  171. package/dist/commands/task/plan.command.js +37 -0
  172. package/dist/commands/task/plan.d.ts.map +1 -1
  173. package/dist/commands/task/plan.js +190 -93
  174. package/dist/commands/task/set-status.command.d.ts.map +1 -1
  175. package/dist/commands/task/set-status.command.js +1 -1
  176. package/dist/commands/task/set-status.d.ts.map +1 -1
  177. package/dist/commands/task/set-status.js +40 -3
  178. package/dist/commands/task/shared/docs.d.ts +1 -0
  179. package/dist/commands/task/shared/docs.d.ts.map +1 -1
  180. package/dist/commands/task/shared/docs.js +7 -0
  181. package/dist/commands/task/shared/transitions.d.ts +0 -2
  182. package/dist/commands/task/shared/transitions.d.ts.map +1 -1
  183. package/dist/commands/task/shared/transitions.js +0 -6
  184. package/dist/commands/task/shared.d.ts +2 -2
  185. package/dist/commands/task/shared.d.ts.map +1 -1
  186. package/dist/commands/task/shared.js +2 -2
  187. package/dist/commands/task/start.d.ts.map +1 -1
  188. package/dist/commands/task/start.js +88 -63
  189. package/dist/commands/task/task.command.d.ts +8 -0
  190. package/dist/commands/task/task.command.d.ts.map +1 -0
  191. package/dist/commands/task/task.command.js +71 -0
  192. package/dist/commands/task/verify-command-shared.d.ts +16 -0
  193. package/dist/commands/task/verify-command-shared.d.ts.map +1 -0
  194. package/dist/commands/task/verify-command-shared.js +53 -0
  195. package/dist/commands/task/verify-ok.command.d.ts +2 -6
  196. package/dist/commands/task/verify-ok.command.d.ts.map +1 -1
  197. package/dist/commands/task/verify-ok.command.js +8 -50
  198. package/dist/commands/task/verify-record.d.ts.map +1 -1
  199. package/dist/commands/task/verify-record.js +119 -140
  200. package/dist/commands/task/verify-rework.command.d.ts +2 -6
  201. package/dist/commands/task/verify-rework.command.d.ts.map +1 -1
  202. package/dist/commands/task/verify-rework.command.js +8 -50
  203. package/dist/commands/verify.spec.d.ts.map +1 -1
  204. package/dist/commands/verify.spec.js +3 -12
  205. package/dist/policy/rules/allowlist.d.ts.map +1 -1
  206. package/dist/policy/rules/allowlist.js +13 -4
  207. package/dist/policy/rules/protected-paths.d.ts.map +1 -1
  208. package/dist/policy/rules/protected-paths.js +6 -1
  209. package/dist/ports/task-backend-port.d.ts +1 -1
  210. package/dist/ports/task-backend-port.d.ts.map +1 -1
  211. package/dist/shared/agent-emoji.d.ts.map +1 -1
  212. package/dist/shared/protected-paths.d.ts +7 -0
  213. package/dist/shared/protected-paths.d.ts.map +1 -1
  214. package/dist/shared/protected-paths.js +26 -10
  215. package/dist/shared/repo-cli-version.d.ts.map +1 -1
  216. package/dist/shared/repo-cli-version.js +9 -3
  217. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  import { cp, mkdir, mkdtemp, rm } from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
- import { defaultConfig, loadConfig } from "@agentplaneorg/core";
4
+ import { defaultConfig, loadConfig, resolveProject } from "@agentplaneorg/core";
5
5
  import { extractArchive } from "../../../../cli/archive.js";
6
6
  import { sha256File } from "../../../../cli/checksum.js";
7
7
  import { mapCoreError } from "../../../../cli/error-map.js";
@@ -12,26 +12,27 @@ import { getBundledRecipeEntry, resolveBundledRecipeSourcePath, } from "../../..
12
12
  import { CliError } from "../../../../shared/errors.js";
13
13
  import { ensureNetworkApproved } from "../../../shared/network-approval.js";
14
14
  import { resolvePathFallback } from "../../../shared/path.js";
15
- import { applyRecipeAgents, applyRecipeScenarios, moveRecipeDir } from "../apply.js";
15
+ import { moveRecipeDir, validateRecipeAssets } from "../apply.js";
16
16
  import { resolveRecipeRoot } from "../archive.js";
17
- import { DEFAULT_RECIPES_INDEX_URL } from "../constants.js";
17
+ import { DEFAULT_RECIPES_INDEX_URL, RECIPE_RUNS_DIR_NAME } from "../constants.js";
18
18
  import { loadRecipesRemoteIndex, willFetchRemoteRecipesIndex } from "../index.js";
19
- import { readInstalledRecipesFile, writeInstalledRecipesFile } from "../installed-recipes.js";
20
19
  import { readRecipeManifest } from "../manifest.js";
21
20
  import { normalizeRecipeTags } from "../normalize.js";
22
- import { resolveGlobalRecipesDir, resolveInstalledRecipeDir, resolveInstalledRecipesPath, resolveRecipesIndexCachePath, } from "../paths.js";
23
- import { maybeResolveProject } from "../project.js";
21
+ import { writeRecipeInstallMetadata } from "../project-installed-recipes.js";
22
+ import { resolveProjectInstalledRecipeDir, resolveProjectRecipeInstallMetaPath, resolveProjectRecipesDir, resolveRecipesIndexCachePath, } from "../paths.js";
24
23
  function isHttpUrl(value) {
25
24
  return value.startsWith("http://") || value.startsWith("https://");
26
25
  }
27
26
  export async function cmdRecipeInstall(opts) {
27
+ void opts.onConflict;
28
28
  try {
29
- const project = await maybeResolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride });
29
+ const project = await resolveProject({
30
+ cwd: opts.cwd,
31
+ rootOverride: opts.rootOverride ?? null,
32
+ });
30
33
  let config = defaultConfig();
31
- if (project) {
32
- const loaded = await loadConfig(project.agentplaneDir);
33
- config = loaded.config;
34
- }
34
+ const loaded = await loadConfig(project.agentplaneDir);
35
+ config = loaded.config;
35
36
  let networkApproved = false;
36
37
  const ensureApproved = async (reason) => {
37
38
  if (networkApproved)
@@ -173,7 +174,8 @@ export async function cmdRecipeInstall(opts) {
173
174
  const manifest = await readRecipeManifest(path.join(recipeRoot, "manifest.json"));
174
175
  const resolvedTags = manifest.tags && manifest.tags.length > 0 ? manifest.tags : normalizeRecipeTags(indexTags);
175
176
  const manifestWithTags = resolvedTags.length > 0 ? { ...manifest, tags: resolvedTags } : manifest;
176
- const installDir = resolveInstalledRecipeDir(manifestWithTags);
177
+ await validateRecipeAssets({ manifest: manifestWithTags, recipeDir: recipeRoot });
178
+ const installDir = resolveProjectInstalledRecipeDir(project, manifestWithTags.id);
177
179
  const installKind = await getPathKind(installDir);
178
180
  if (installKind && installKind !== "dir") {
179
181
  throw new CliError({
@@ -183,21 +185,31 @@ export async function cmdRecipeInstall(opts) {
183
185
  });
184
186
  }
185
187
  const hadExisting = Boolean(installKind);
188
+ const existingRunsDir = path.join(installDir, RECIPE_RUNS_DIR_NAME);
189
+ const preservedRunsDir = path.join(tempRoot, RECIPE_RUNS_DIR_NAME);
186
190
  if (installKind) {
191
+ if ((await getPathKind(existingRunsDir)) === "dir") {
192
+ await cp(existingRunsDir, preservedRunsDir, { recursive: true });
193
+ }
187
194
  await rm(installDir, { recursive: true, force: true });
188
195
  }
189
- await mkdir(resolveGlobalRecipesDir(), { recursive: true });
196
+ await mkdir(resolveProjectRecipesDir(project), { recursive: true });
190
197
  await moveRecipeDir({ from: recipeRoot, to: installDir });
191
198
  try {
192
- if (project) {
193
- await applyRecipeAgents({
194
- manifest: manifestWithTags,
195
- recipeDir: installDir,
196
- agentplaneDir: project.agentplaneDir,
197
- onConflict: opts.onConflict,
199
+ if ((await getPathKind(preservedRunsDir)) === "dir") {
200
+ await cp(preservedRunsDir, path.join(installDir, RECIPE_RUNS_DIR_NAME), {
201
+ recursive: true,
198
202
  });
199
203
  }
200
- await applyRecipeScenarios({ manifest: manifestWithTags, recipeDir: installDir });
204
+ await writeRecipeInstallMetadata(resolveProjectRecipeInstallMetaPath(project, manifestWithTags.id), {
205
+ schema_version: 1,
206
+ id: manifestWithTags.id,
207
+ version: manifestWithTags.version,
208
+ source: sourceLabel,
209
+ installed_at: new Date().toISOString(),
210
+ tags: resolvedTags,
211
+ install_mode: "project-local",
212
+ });
201
213
  }
202
214
  catch (err) {
203
215
  if (!hadExisting) {
@@ -205,22 +217,6 @@ export async function cmdRecipeInstall(opts) {
205
217
  }
206
218
  throw err;
207
219
  }
208
- const recipesPath = resolveInstalledRecipesPath();
209
- const installed = await readInstalledRecipesFile(recipesPath);
210
- const updated = installed.recipes.filter((entry) => entry.id !== manifestWithTags.id);
211
- updated.push({
212
- id: manifestWithTags.id,
213
- version: manifestWithTags.version,
214
- source: sourceLabel,
215
- installed_at: new Date().toISOString(),
216
- tags: resolvedTags,
217
- manifest: manifestWithTags,
218
- });
219
- await writeInstalledRecipesFile(recipesPath, {
220
- schema_version: 1,
221
- updated_at: installed.updated_at,
222
- recipes: updated,
223
- });
224
220
  process.stdout.write(`Installed recipe ${manifestWithTags.id}@${manifestWithTags.version}\n`);
225
221
  return 0;
226
222
  }
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../../src/commands/recipes/impl/commands/list.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,eAAe,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CA+ClB"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../../src/commands/recipes/impl/commands/list.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,eAAe,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkDlB"}
@@ -1,13 +1,16 @@
1
+ import { resolveProject } from "@agentplaneorg/core";
1
2
  import { mapCoreError } from "../../../../cli/error-map.js";
2
3
  import { emptyStateMessage } from "../../../../cli/output.js";
3
4
  import { CliError } from "../../../../shared/errors.js";
4
- import { readInstalledRecipesFile } from "../installed-recipes.js";
5
- import { resolveInstalledRecipesPath } from "../paths.js";
5
+ import { readProjectInstalledRecipes } from "../project-installed-recipes.js";
6
6
  export async function cmdRecipeListParsed(opts) {
7
7
  const flags = opts.flags;
8
8
  try {
9
- const filePath = resolveInstalledRecipesPath();
10
- const installed = await readInstalledRecipesFile(filePath);
9
+ const resolved = await resolveProject({
10
+ cwd: opts.cwd,
11
+ rootOverride: opts.rootOverride ?? null,
12
+ });
13
+ const installed = await readProjectInstalledRecipes(resolved);
11
14
  let recipes = installed.recipes;
12
15
  if (flags.tag) {
13
16
  const needle = flags.tag.toLowerCase();
@@ -1 +1 @@
1
- {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../../../src/commands/recipes/impl/commands/remove.ts"],"names":[],"mappings":"AAUA,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;CACZ,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BlB"}
1
+ {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../../../src/commands/recipes/impl/commands/remove.ts"],"names":[],"mappings":"AAYA,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;CACZ,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBlB"}
@@ -1,14 +1,18 @@
1
+ import { resolveProject } from "@agentplaneorg/core";
1
2
  import { rm } from "node:fs/promises";
2
3
  import { mapCoreError } from "../../../../cli/error-map.js";
3
4
  import { exitCodeForError } from "../../../../cli/exit-codes.js";
4
5
  import { successMessage } from "../../../../cli/output.js";
5
6
  import { CliError } from "../../../../shared/errors.js";
6
- import { readInstalledRecipesFile, writeInstalledRecipesFile } from "../installed-recipes.js";
7
- import { resolveInstalledRecipeDir, resolveInstalledRecipesPath } from "../paths.js";
7
+ import { readProjectInstalledRecipes } from "../project-installed-recipes.js";
8
+ import { resolveProjectInstalledRecipeDir } from "../paths.js";
8
9
  export async function cmdRecipeRemoveParsed(opts) {
9
10
  try {
10
- const recipesPath = resolveInstalledRecipesPath();
11
- const installed = await readInstalledRecipesFile(recipesPath);
11
+ const resolved = await resolveProject({
12
+ cwd: opts.cwd,
13
+ rootOverride: opts.rootOverride ?? null,
14
+ });
15
+ const installed = await readProjectInstalledRecipes(resolved);
12
16
  const entry = installed.recipes.find((recipe) => recipe.id === opts.id);
13
17
  if (!entry) {
14
18
  throw new CliError({
@@ -17,14 +21,8 @@ export async function cmdRecipeRemoveParsed(opts) {
17
21
  message: `Recipe not installed: ${opts.id}`,
18
22
  });
19
23
  }
20
- const recipeDir = resolveInstalledRecipeDir(entry);
24
+ const recipeDir = resolveProjectInstalledRecipeDir(resolved, entry.id);
21
25
  await rm(recipeDir, { recursive: true, force: true });
22
- const updated = installed.recipes.filter((recipe) => recipe.id !== opts.id);
23
- await writeInstalledRecipesFile(recipesPath, {
24
- schema_version: 1,
25
- updated_at: installed.updated_at,
26
- recipes: updated,
27
- });
28
26
  process.stdout.write(`${successMessage("removed recipe", `${entry.id}@${entry.version}`)}\n`);
29
27
  return 0;
30
28
  }
@@ -2,6 +2,8 @@ export declare const INSTALLED_RECIPES_NAME = "recipes.json";
2
2
  export declare const RECIPES_DIR_NAME = "recipes";
3
3
  export declare const RECIPES_SCENARIOS_DIR_NAME = "scenarios";
4
4
  export declare const RECIPES_SCENARIOS_INDEX_NAME = "scenarios.json";
5
+ export declare const RECIPE_INSTALL_META_NAME = ".install.json";
6
+ export declare const RECIPE_RUNS_DIR_NAME = "runs";
5
7
  export declare const AGENTPLANE_HOME_ENV = "AGENTPLANE_HOME";
6
8
  export declare const GLOBAL_RECIPES_DIR_NAME = "recipes";
7
9
  export declare const PROJECT_RECIPES_CACHE_DIR_NAME = "recipes-cache";
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,iBAAiB,CAAC;AACrD,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAC1C,eAAO,MAAM,0BAA0B,cAAc,CAAC;AACtD,eAAO,MAAM,4BAA4B,mBAAmB,CAAC;AAE7D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD,eAAO,MAAM,uBAAuB,YAAY,CAAC;AACjD,eAAO,MAAM,8BAA8B,kBAAkB,CAAC;AAE9D,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAC9D,eAAO,MAAM,6BAA6B,2BAA2B,CAAC;AAEtE,eAAO,MAAM,yBAAyB,uFACgD,CAAC;AAEvF,eAAO,MAAM,6BAA6B,yCAAyC,CAAC;AAEpF,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAI5D,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,iBAAiB,CAAC;AACrD,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAC1C,eAAO,MAAM,0BAA0B,cAAc,CAAC;AACtD,eAAO,MAAM,4BAA4B,mBAAmB,CAAC;AAC7D,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAE3C,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD,eAAO,MAAM,uBAAuB,YAAY,CAAC;AACjD,eAAO,MAAM,8BAA8B,kBAAkB,CAAC;AAE9D,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAC9D,eAAO,MAAM,6BAA6B,2BAA2B,CAAC;AAEtE,eAAO,MAAM,yBAAyB,uFACgD,CAAC;AAEvF,eAAO,MAAM,6BAA6B,yCAAyC,CAAC;AAEpF,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAI5D,CAAC"}
@@ -2,6 +2,8 @@ export const INSTALLED_RECIPES_NAME = "recipes.json";
2
2
  export const RECIPES_DIR_NAME = "recipes";
3
3
  export const RECIPES_SCENARIOS_DIR_NAME = "scenarios";
4
4
  export const RECIPES_SCENARIOS_INDEX_NAME = "scenarios.json";
5
+ export const RECIPE_INSTALL_META_NAME = ".install.json";
6
+ export const RECIPE_RUNS_DIR_NAME = "runs";
5
7
  export const AGENTPLANE_HOME_ENV = "AGENTPLANE_HOME";
6
8
  export const GLOBAL_RECIPES_DIR_NAME = "recipes";
7
9
  export const PROJECT_RECIPES_CACHE_DIR_NAME = "recipes-cache";
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/manifest.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAgCnE;AAED,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAGtF"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/manifest.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAGV,cAAc,EAKf,MAAM,YAAY,CAAC;AAyPpB,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAsDnE;AAED,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAGtF"}
@@ -1,40 +1,236 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { invalidFieldMessage, requiredFieldMessage } from "../../../cli/output.js";
3
3
  import { isRecord } from "../../../shared/guards.js";
4
- import { normalizeRecipeId, normalizeRecipeTags } from "./normalize.js";
4
+ import { dedupeStrings } from "../../../shared/strings.js";
5
+ import { normalizeAgentId, normalizeRecipeId, normalizeRecipeRelativePath, normalizeRecipeTags, normalizeScenarioId, normalizeSkillId, normalizeToolId, } from "./normalize.js";
6
+ function normalizeRequiredString(raw, field) {
7
+ if (typeof raw !== "string")
8
+ throw new Error(invalidFieldMessage(field, "string"));
9
+ const value = raw.trim();
10
+ if (!value)
11
+ throw new Error(requiredFieldMessage(field));
12
+ return value;
13
+ }
14
+ function normalizeOptionalString(raw, field) {
15
+ if (raw === undefined)
16
+ return undefined;
17
+ if (typeof raw !== "string")
18
+ throw new Error(invalidFieldMessage(field, "string"));
19
+ const value = raw.trim();
20
+ return value || undefined;
21
+ }
22
+ function normalizeStringList(raw, field, opts) {
23
+ if (!Array.isArray(raw))
24
+ throw new Error(invalidFieldMessage(field, "string[]"));
25
+ const values = raw.map((value) => {
26
+ if (typeof value !== "string")
27
+ throw new Error(invalidFieldMessage(field, "string[]"));
28
+ const trimmed = value.trim();
29
+ if (!trimmed)
30
+ throw new Error(invalidFieldMessage(field, "string[]"));
31
+ return trimmed;
32
+ });
33
+ const deduped = dedupeStrings(values);
34
+ if ((opts?.minLength ?? 0) > 0 && deduped.length < (opts?.minLength ?? 0)) {
35
+ throw new Error(invalidFieldMessage(field, `string[${opts?.minLength ?? 0}+]`));
36
+ }
37
+ return deduped;
38
+ }
39
+ function normalizeOptionalStringList(raw, field) {
40
+ if (raw === undefined)
41
+ return undefined;
42
+ return normalizeStringList(raw, field);
43
+ }
44
+ function normalizeBoolean(raw, field) {
45
+ if (raw === undefined)
46
+ return undefined;
47
+ if (typeof raw !== "boolean")
48
+ throw new Error(invalidFieldMessage(field, "boolean"));
49
+ return raw;
50
+ }
51
+ function normalizeNumber(raw, field) {
52
+ if (raw === undefined)
53
+ return undefined;
54
+ if (typeof raw !== "number" || Number.isNaN(raw)) {
55
+ throw new Error(invalidFieldMessage(field, "number"));
56
+ }
57
+ return raw;
58
+ }
59
+ function normalizeCompatibility(raw) {
60
+ if (raw === undefined)
61
+ return undefined;
62
+ if (!isRecord(raw))
63
+ throw new Error(invalidFieldMessage("manifest.compatibility", "object"));
64
+ return {
65
+ min_agentplane_version: normalizeOptionalString(raw.min_agentplane_version, "manifest.compatibility.min_agentplane_version"),
66
+ manifest_api_version: normalizeOptionalString(raw.manifest_api_version, "manifest.compatibility.manifest_api_version"),
67
+ scenario_api_version: normalizeOptionalString(raw.scenario_api_version, "manifest.compatibility.scenario_api_version"),
68
+ runtime_api_version: normalizeOptionalString(raw.runtime_api_version, "manifest.compatibility.runtime_api_version"),
69
+ platforms: normalizeOptionalStringList(raw.platforms, "manifest.compatibility.platforms"),
70
+ repo_types: normalizeOptionalStringList(raw.repo_types, "manifest.compatibility.repo_types"),
71
+ };
72
+ }
73
+ function normalizeRunProfile(raw, field) {
74
+ if (!isRecord(raw))
75
+ throw new Error(invalidFieldMessage(field, "object"));
76
+ return {
77
+ mode: normalizeRequiredString(raw.mode, `${field}.mode`),
78
+ sandbox: normalizeOptionalString(raw.sandbox, `${field}.sandbox`),
79
+ network: normalizeBoolean(raw.network, `${field}.network`),
80
+ requires_human_approval: normalizeBoolean(raw.requires_human_approval, `${field}.requires_human_approval`),
81
+ writes_artifacts_to: normalizeOptionalStringList(raw.writes_artifacts_to, `${field}.writes_artifacts_to`),
82
+ expected_exit_contract: normalizeOptionalString(raw.expected_exit_contract, `${field}.expected_exit_contract`),
83
+ };
84
+ }
85
+ function normalizeSkills(raw) {
86
+ if (raw === undefined)
87
+ return undefined;
88
+ if (!Array.isArray(raw))
89
+ throw new Error(invalidFieldMessage("manifest.skills", "array"));
90
+ return raw.map((entry, index) => {
91
+ if (!isRecord(entry))
92
+ throw new Error(invalidFieldMessage(`manifest.skills[${index}]`, "object"));
93
+ return {
94
+ id: normalizeSkillId(normalizeRequiredString(entry.id, `manifest.skills[${index}].id`)),
95
+ summary: normalizeRequiredString(entry.summary, `manifest.skills[${index}].summary`),
96
+ kind: normalizeRequiredString(entry.kind, `manifest.skills[${index}].kind`),
97
+ file: normalizeRecipeRelativePath(`manifest.skills[${index}].file`, normalizeRequiredString(entry.file, `manifest.skills[${index}].file`)),
98
+ };
99
+ });
100
+ }
101
+ function normalizeTools(raw) {
102
+ if (raw === undefined)
103
+ return undefined;
104
+ if (!Array.isArray(raw))
105
+ throw new Error(invalidFieldMessage("manifest.tools", "array"));
106
+ return raw.map((entry, index) => {
107
+ if (!isRecord(entry))
108
+ throw new Error(invalidFieldMessage(`manifest.tools[${index}]`, "object"));
109
+ const runtime = normalizeRequiredString(entry.runtime, `manifest.tools[${index}].runtime`);
110
+ if (runtime !== "node" && runtime !== "bash") {
111
+ throw new Error(invalidFieldMessage(`manifest.tools[${index}].runtime`, '"node" | "bash"'));
112
+ }
113
+ return {
114
+ id: normalizeToolId(normalizeRequiredString(entry.id, `manifest.tools[${index}].id`)),
115
+ summary: normalizeRequiredString(entry.summary, `manifest.tools[${index}].summary`),
116
+ runtime,
117
+ entrypoint: normalizeRecipeRelativePath(`manifest.tools[${index}].entrypoint`, normalizeRequiredString(entry.entrypoint, `manifest.tools[${index}].entrypoint`)),
118
+ permissions: normalizeOptionalStringList(entry.permissions, `manifest.tools[${index}].permissions`),
119
+ timeout_ms: normalizeNumber(entry.timeout_ms, `manifest.tools[${index}].timeout_ms`),
120
+ cwd_policy: normalizeOptionalString(entry.cwd_policy, `manifest.tools[${index}].cwd_policy`),
121
+ };
122
+ });
123
+ }
124
+ function normalizeAgents(raw) {
125
+ if (raw === undefined)
126
+ return undefined;
127
+ if (!Array.isArray(raw))
128
+ throw new Error(invalidFieldMessage("manifest.agents", "array"));
129
+ return raw.map((entry, index) => {
130
+ if (!isRecord(entry))
131
+ throw new Error(invalidFieldMessage(`manifest.agents[${index}]`, "object"));
132
+ return {
133
+ id: normalizeAgentId(normalizeRequiredString(entry.id, `manifest.agents[${index}].id`)),
134
+ display_name: normalizeRequiredString(entry.display_name, `manifest.agents[${index}].display_name`),
135
+ role: normalizeRequiredString(entry.role, `manifest.agents[${index}].role`),
136
+ summary: normalizeRequiredString(entry.summary, `manifest.agents[${index}].summary`),
137
+ skills: normalizeOptionalStringList(entry.skills, `manifest.agents[${index}].skills`),
138
+ tools: normalizeOptionalStringList(entry.tools, `manifest.agents[${index}].tools`),
139
+ file: normalizeRecipeRelativePath(`manifest.agents[${index}].file`, normalizeRequiredString(entry.file, `manifest.agents[${index}].file`)),
140
+ };
141
+ });
142
+ }
143
+ function normalizeScenarios(raw) {
144
+ if (!Array.isArray(raw) || raw.length === 0) {
145
+ throw new Error(invalidFieldMessage("manifest.scenarios", "non-empty array"));
146
+ }
147
+ return raw.map((entry, index) => {
148
+ if (!isRecord(entry)) {
149
+ throw new Error(invalidFieldMessage(`manifest.scenarios[${index}]`, "object"));
150
+ }
151
+ return {
152
+ id: normalizeScenarioId(normalizeRequiredString(entry.id, `manifest.scenarios[${index}].id`)),
153
+ name: normalizeRequiredString(entry.name, `manifest.scenarios[${index}].name`),
154
+ summary: normalizeRequiredString(entry.summary, `manifest.scenarios[${index}].summary`),
155
+ description: normalizeOptionalString(entry.description, `manifest.scenarios[${index}].description`),
156
+ use_when: normalizeStringList(entry.use_when, `manifest.scenarios[${index}].use_when`, {
157
+ minLength: 1,
158
+ }),
159
+ avoid_when: normalizeOptionalStringList(entry.avoid_when, `manifest.scenarios[${index}].avoid_when`),
160
+ required_inputs: normalizeStringList(entry.required_inputs, `manifest.scenarios[${index}].required_inputs`),
161
+ outputs: normalizeStringList(entry.outputs, `manifest.scenarios[${index}].outputs`),
162
+ permissions: normalizeStringList(Array.isArray(entry.permissions) ? entry.permissions : [], `manifest.scenarios[${index}].permissions`),
163
+ artifacts: normalizeStringList(Array.isArray(entry.artifacts) ? entry.artifacts : [], `manifest.scenarios[${index}].artifacts`),
164
+ agents_involved: normalizeStringList(entry.agents_involved, `manifest.scenarios[${index}].agents_involved`, { minLength: 1 }),
165
+ skills_used: normalizeStringList(Array.isArray(entry.skills_used) ? entry.skills_used : [], `manifest.scenarios[${index}].skills_used`),
166
+ tools_used: normalizeStringList(Array.isArray(entry.tools_used) ? entry.tools_used : [], `manifest.scenarios[${index}].tools_used`),
167
+ run_profile: normalizeRunProfile(entry.run_profile, `manifest.scenarios[${index}].run_profile`),
168
+ file: normalizeRecipeRelativePath(`manifest.scenarios[${index}].file`, normalizeRequiredString(entry.file, `manifest.scenarios[${index}].file`)),
169
+ };
170
+ });
171
+ }
172
+ function assertUniqueIds(field, items) {
173
+ const seen = new Set();
174
+ for (const item of items) {
175
+ if (seen.has(item.id)) {
176
+ throw new Error(invalidFieldMessage(field, `unique ids (duplicate: ${item.id})`));
177
+ }
178
+ seen.add(item.id);
179
+ }
180
+ }
181
+ function assertKnownReferences(field, refs, known) {
182
+ if (!refs || refs.length === 0)
183
+ return;
184
+ const missing = refs.filter((ref) => !known.has(ref));
185
+ if (missing.length > 0) {
186
+ throw new Error(invalidFieldMessage(field, `known ids (missing: ${missing.join(", ")})`));
187
+ }
188
+ }
5
189
  export function validateRecipeManifest(raw) {
6
190
  if (!isRecord(raw))
7
191
  throw new Error(invalidFieldMessage("manifest", "object"));
8
192
  if (raw.schema_version !== "1")
9
193
  throw new Error(invalidFieldMessage("manifest.schema_version", '"1"'));
10
- if (typeof raw.id !== "string")
11
- throw new Error(invalidFieldMessage("manifest.id", "string"));
12
- if (typeof raw.version !== "string")
13
- throw new Error(invalidFieldMessage("manifest.version", "string"));
14
- if (typeof raw.name !== "string")
15
- throw new Error(invalidFieldMessage("manifest.name", "string"));
16
- if (typeof raw.summary !== "string")
17
- throw new Error(invalidFieldMessage("manifest.summary", "string"));
18
- if (typeof raw.description !== "string")
19
- throw new Error(invalidFieldMessage("manifest.description", "string"));
20
- const id = normalizeRecipeId(raw.id);
21
- const version = raw.version.trim();
22
- if (!version)
23
- throw new Error(requiredFieldMessage("manifest.version"));
194
+ const id = normalizeRecipeId(normalizeRequiredString(raw.id, "manifest.id"));
195
+ const version = normalizeRequiredString(raw.version, "manifest.version");
24
196
  const tags = normalizeRecipeTags(raw.tags);
197
+ const compatibility = normalizeCompatibility(raw.compatibility);
198
+ const skills = normalizeSkills(raw.skills);
199
+ const tools = normalizeTools(raw.tools);
200
+ const agents = normalizeAgents(raw.agents);
201
+ const scenarios = normalizeScenarios(raw.scenarios);
202
+ if (skills)
203
+ assertUniqueIds("manifest.skills", skills);
204
+ if (tools)
205
+ assertUniqueIds("manifest.tools", tools);
206
+ if (agents)
207
+ assertUniqueIds("manifest.agents", agents);
208
+ assertUniqueIds("manifest.scenarios", scenarios);
209
+ const skillIds = new Set((skills ?? []).map((skill) => skill.id));
210
+ const toolIds = new Set((tools ?? []).map((tool) => tool.id));
211
+ const agentIds = new Set((agents ?? []).map((agent) => agent.id));
212
+ for (const [index, agent] of (agents ?? []).entries()) {
213
+ assertKnownReferences(`manifest.agents[${index}].skills`, agent.skills, skillIds);
214
+ assertKnownReferences(`manifest.agents[${index}].tools`, agent.tools, toolIds);
215
+ }
216
+ for (const [index, scenario] of scenarios.entries()) {
217
+ assertKnownReferences(`manifest.scenarios[${index}].agents_involved`, scenario.agents_involved, agentIds);
218
+ assertKnownReferences(`manifest.scenarios[${index}].skills_used`, scenario.skills_used, skillIds);
219
+ assertKnownReferences(`manifest.scenarios[${index}].tools_used`, scenario.tools_used, toolIds);
220
+ }
25
221
  return {
26
222
  schema_version: "1",
27
223
  id,
28
224
  version,
29
- name: raw.name.trim(),
30
- summary: raw.summary.trim(),
31
- description: raw.description.trim(),
225
+ name: normalizeRequiredString(raw.name, "manifest.name"),
226
+ summary: normalizeRequiredString(raw.summary, "manifest.summary"),
227
+ description: normalizeRequiredString(raw.description, "manifest.description"),
32
228
  tags: tags.length > 0 ? tags : undefined,
33
- agents: Array.isArray(raw.agents) ? raw.agents : undefined,
34
- tools: Array.isArray(raw.tools) ? raw.tools : undefined,
35
- scenarios: Array.isArray(raw.scenarios)
36
- ? raw.scenarios
37
- : undefined,
229
+ compatibility,
230
+ skills,
231
+ agents,
232
+ tools,
233
+ scenarios,
38
234
  };
39
235
  }
40
236
  export async function readRecipeManifest(manifestPath) {
@@ -1,5 +1,8 @@
1
1
  export declare function normalizeRecipeId(value: string): string;
2
2
  export declare function normalizeAgentId(value: string): string;
3
+ export declare function normalizeSkillId(value: string): string;
4
+ export declare function normalizeToolId(value: string): string;
3
5
  export declare function normalizeScenarioId(value: string): string;
4
6
  export declare function normalizeRecipeTags(value: unknown): string[];
7
+ export declare function normalizeRecipeRelativePath(field: string, value: string): string;
5
8
  //# sourceMappingURL=normalize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/normalize.ts"],"names":[],"mappings":"AAOA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUvD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUzD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAQ5D"}
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/normalize.ts"],"names":[],"mappings":"AAmBA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAQ5D;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAWhF"}
@@ -1,40 +1,31 @@
1
1
  import { invalidPathMessage, requiredFieldMessage, invalidFieldMessage, } from "../../../cli/output.js";
2
2
  import { dedupeStrings } from "../../../shared/strings.js";
3
- export function normalizeRecipeId(value) {
3
+ function normalizeScopedId(field, value) {
4
4
  const trimmed = value.trim();
5
5
  if (!trimmed)
6
- throw new Error(requiredFieldMessage("manifest.id"));
6
+ throw new Error(requiredFieldMessage(field));
7
7
  if (trimmed.includes("/") || trimmed.includes("\\")) {
8
- throw new Error(invalidPathMessage("manifest.id", "must not contain path separators"));
8
+ throw new Error(invalidPathMessage(field, "must not contain path separators"));
9
9
  }
10
10
  if (trimmed === "." || trimmed === "..") {
11
- throw new Error(invalidPathMessage("manifest.id", "must not be '.' or '..'"));
11
+ throw new Error(invalidPathMessage(field, "must not be '.' or '..'"));
12
12
  }
13
13
  return trimmed;
14
14
  }
15
+ export function normalizeRecipeId(value) {
16
+ return normalizeScopedId("manifest.id", value);
17
+ }
15
18
  export function normalizeAgentId(value) {
16
- const trimmed = value.trim();
17
- if (!trimmed)
18
- throw new Error(requiredFieldMessage("agent.id"));
19
- if (trimmed.includes("/") || trimmed.includes("\\")) {
20
- throw new Error(invalidPathMessage("agent.id", "must not contain path separators"));
21
- }
22
- if (trimmed === "." || trimmed === "..") {
23
- throw new Error(invalidPathMessage("agent.id", "must not be '.' or '..'"));
24
- }
25
- return trimmed;
19
+ return normalizeScopedId("agent.id", value);
20
+ }
21
+ export function normalizeSkillId(value) {
22
+ return normalizeScopedId("skill.id", value);
23
+ }
24
+ export function normalizeToolId(value) {
25
+ return normalizeScopedId("tool.id", value);
26
26
  }
27
27
  export function normalizeScenarioId(value) {
28
- const trimmed = value.trim();
29
- if (!trimmed)
30
- throw new Error(requiredFieldMessage("scenario.id"));
31
- if (trimmed.includes("/") || trimmed.includes("\\")) {
32
- throw new Error(invalidPathMessage("scenario.id", "must not contain path separators"));
33
- }
34
- if (trimmed === "." || trimmed === "..") {
35
- throw new Error(invalidPathMessage("scenario.id", "must not be '.' or '..'"));
36
- }
37
- return trimmed;
28
+ return normalizeScopedId("scenario.id", value);
38
29
  }
39
30
  export function normalizeRecipeTags(value) {
40
31
  if (value === undefined)
@@ -48,3 +39,16 @@ export function normalizeRecipeTags(value) {
48
39
  });
49
40
  return dedupeStrings(tags);
50
41
  }
42
+ export function normalizeRecipeRelativePath(field, value) {
43
+ const trimmed = value.trim().replaceAll("\\", "/");
44
+ if (!trimmed)
45
+ throw new Error(requiredFieldMessage(field));
46
+ if (trimmed.startsWith("/")) {
47
+ throw new Error(invalidPathMessage(field, "must be relative"));
48
+ }
49
+ const segments = trimmed.split("/");
50
+ if (segments.some((segment) => !segment || segment === "." || segment === "..")) {
51
+ throw new Error(invalidPathMessage(field, "must stay within the recipe root"));
52
+ }
53
+ return trimmed;
54
+ }
@@ -7,6 +7,15 @@ export declare function resolveInstalledRecipeDir(entry: {
7
7
  id: string;
8
8
  version: string;
9
9
  }): string;
10
+ export declare function resolveProjectRecipesDir(resolved: {
11
+ agentplaneDir: string;
12
+ }): string;
13
+ export declare function resolveProjectInstalledRecipeDir(resolved: {
14
+ agentplaneDir: string;
15
+ }, recipeId: string): string;
16
+ export declare function resolveProjectRecipeInstallMetaPath(resolved: {
17
+ agentplaneDir: string;
18
+ }, recipeId: string): string;
10
19
  export declare function resolveProjectRecipesCacheDir(resolved: {
11
20
  agentplaneDir: string;
12
21
  }): string;
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/paths.ts"],"names":[],"mappings":"AAYA,wBAAgB,qBAAqB,IAAI,MAAM,CAI9C;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAErD;AAED,wBAAgB,+BAA+B,IAAI,MAAM,CAExD;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAExF;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEzF"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/paths.ts"],"names":[],"mappings":"AAcA,wBAAgB,qBAAqB,IAAI,MAAM,CAI9C;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAErD;AAED,wBAAgB,+BAA+B,IAAI,MAAM,CAExD;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAExF;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEpF;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,EACnC,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,EACnC,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEzF"}
@@ -1,6 +1,6 @@
1
1
  import os from "node:os";
2
2
  import path from "node:path";
3
- import { AGENTPLANE_HOME_ENV, GLOBAL_RECIPES_DIR_NAME, INSTALLED_RECIPES_NAME, PROJECT_RECIPES_CACHE_DIR_NAME, RECIPES_REMOTE_INDEX_NAME, RECIPES_REMOTE_INDEX_SIG_NAME, } from "./constants.js";
3
+ import { AGENTPLANE_HOME_ENV, GLOBAL_RECIPES_DIR_NAME, INSTALLED_RECIPES_NAME, RECIPE_INSTALL_META_NAME, PROJECT_RECIPES_CACHE_DIR_NAME, RECIPES_DIR_NAME, RECIPES_REMOTE_INDEX_NAME, RECIPES_REMOTE_INDEX_SIG_NAME, } from "./constants.js";
4
4
  export function resolveAgentplaneHome() {
5
5
  const overridden = process.env[AGENTPLANE_HOME_ENV]?.trim();
6
6
  if (overridden)
@@ -22,6 +22,15 @@ export function resolveRecipesIndexCacheSigPath() {
22
22
  export function resolveInstalledRecipeDir(entry) {
23
23
  return path.join(resolveGlobalRecipesDir(), entry.id, entry.version);
24
24
  }
25
+ export function resolveProjectRecipesDir(resolved) {
26
+ return path.join(resolved.agentplaneDir, RECIPES_DIR_NAME);
27
+ }
28
+ export function resolveProjectInstalledRecipeDir(resolved, recipeId) {
29
+ return path.join(resolveProjectRecipesDir(resolved), recipeId);
30
+ }
31
+ export function resolveProjectRecipeInstallMetaPath(resolved, recipeId) {
32
+ return path.join(resolveProjectInstalledRecipeDir(resolved, recipeId), RECIPE_INSTALL_META_NAME);
33
+ }
25
34
  export function resolveProjectRecipesCacheDir(resolved) {
26
35
  return path.join(resolved.agentplaneDir, PROJECT_RECIPES_CACHE_DIR_NAME);
27
36
  }
@@ -0,0 +1,7 @@
1
+ import type { InstalledRecipesFile, RecipeInstallMetadata } from "./types.js";
2
+ export declare function readRecipeInstallMetadata(filePath: string): Promise<RecipeInstallMetadata | null>;
3
+ export declare function writeRecipeInstallMetadata(filePath: string, metadata: RecipeInstallMetadata): Promise<void>;
4
+ export declare function readProjectInstalledRecipes(opts: {
5
+ agentplaneDir: string;
6
+ }): Promise<InstalledRecipesFile>;
7
+ //# sourceMappingURL=project-installed-recipes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-installed-recipes.d.ts","sourceRoot":"","sources":["../../../../src/commands/recipes/impl/project-installed-recipes.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAwB,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAuCpG,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CASvC;AAED,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IACtD,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0DhC"}