agentplane 0.2.12 → 0.2.13

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 (43) hide show
  1. package/dist/cli/command-guide.js +1 -1
  2. package/dist/cli/run-cli/command-catalog.js +4 -4
  3. package/dist/cli/run-cli/commands/config.d.ts.map +1 -1
  4. package/dist/cli/run-cli/commands/config.js +17 -38
  5. package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
  6. package/dist/cli/run-cli/commands/core.js +100 -71
  7. package/dist/cli/run-cli/commands/ide.d.ts.map +1 -1
  8. package/dist/cli/run-cli/commands/ide.js +3 -9
  9. package/dist/cli/run-cli/commands/init/write-gitignore.d.ts +2 -1
  10. package/dist/cli/run-cli/commands/init/write-gitignore.d.ts.map +1 -1
  11. package/dist/cli/run-cli/commands/init/write-gitignore.js +19 -6
  12. package/dist/cli/run-cli/commands/init.js +5 -2
  13. package/dist/cli/run-cli/commands/wrap-command.d.ts +6 -0
  14. package/dist/cli/run-cli/commands/wrap-command.d.ts.map +1 -0
  15. package/dist/cli/run-cli/commands/wrap-command.js +17 -0
  16. package/dist/cli/run-cli.d.ts.map +1 -1
  17. package/dist/cli/run-cli.js +5 -3
  18. package/dist/commands/doctor.command.d.ts +2 -7
  19. package/dist/commands/doctor.command.d.ts.map +1 -1
  20. package/dist/commands/doctor.command.js +2 -137
  21. package/dist/commands/doctor.run.d.ts +4 -0
  22. package/dist/commands/doctor.run.d.ts.map +1 -0
  23. package/dist/commands/doctor.run.js +174 -0
  24. package/dist/commands/doctor.spec.d.ts +7 -0
  25. package/dist/commands/doctor.spec.d.ts.map +1 -0
  26. package/dist/commands/doctor.spec.js +20 -0
  27. package/dist/commands/recipes/install.command.d.ts +2 -11
  28. package/dist/commands/recipes/install.command.d.ts.map +1 -1
  29. package/dist/commands/recipes/install.command.js +2 -161
  30. package/dist/commands/recipes/install.run.d.ts +4 -0
  31. package/dist/commands/recipes/install.run.d.ts.map +1 -0
  32. package/dist/commands/recipes/install.run.js +23 -0
  33. package/dist/commands/recipes/install.spec.d.ts +11 -0
  34. package/dist/commands/recipes/install.spec.d.ts.map +1 -0
  35. package/dist/commands/recipes/install.spec.js +140 -0
  36. package/dist/commands/shared/git-context.d.ts +3 -0
  37. package/dist/commands/shared/git-context.d.ts.map +1 -1
  38. package/dist/commands/shared/git-context.js +10 -0
  39. package/dist/commands/task/finish.d.ts.map +1 -1
  40. package/dist/commands/task/finish.js +34 -2
  41. package/dist/commands/upgrade.d.ts.map +1 -1
  42. package/dist/commands/upgrade.js +23 -2
  43. package/package.json +1 -1
@@ -1,137 +1,2 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { resolveProject } from "@agentplaneorg/core";
4
- import { warnMessage, successMessage } from "../cli/output.js";
5
- import { loadCommandContext } from "./shared/task-backend.js";
6
- export const doctorSpec = {
7
- id: ["doctor"],
8
- group: "Quality",
9
- summary: "Check structural invariants of an agentplane workspace (and optionally apply safe fixes).",
10
- options: [{ kind: "boolean", name: "fix", default: false, description: "Apply safe fixes." }],
11
- examples: [
12
- { cmd: "agentplane doctor", why: "Check layering and workspace invariants." },
13
- { cmd: "agentplane doctor --fix", why: "Apply safe-only fixes (idempotent)." },
14
- ],
15
- parse: (raw) => ({ fix: raw.opts.fix === true }),
16
- };
17
- async function listTsFiles(rootDir) {
18
- const out = [];
19
- async function walk(absDir) {
20
- const entries = await fs.readdir(absDir, { withFileTypes: true });
21
- for (const ent of entries) {
22
- if (ent.name.startsWith("."))
23
- continue;
24
- if (ent.name === "__snapshots__")
25
- continue;
26
- if (ent.name === "node_modules")
27
- continue;
28
- const abs = path.join(absDir, ent.name);
29
- if (ent.isDirectory()) {
30
- await walk(abs);
31
- continue;
32
- }
33
- if (ent.isFile() && ent.name.endsWith(".ts")) {
34
- out.push({ absPath: abs, relPath: path.relative(rootDir, abs) });
35
- }
36
- }
37
- }
38
- await walk(rootDir);
39
- return out;
40
- }
41
- function extractImports(source) {
42
- const imports = [];
43
- const re = /^\s*import\s+(?:type\s+)?(?:[^"']*?\s+from\s+)?["']([^"']+)["']\s*;?/gm;
44
- for (const match of source.matchAll(re)) {
45
- imports.push(match[1] ?? "");
46
- }
47
- return imports.filter(Boolean);
48
- }
49
- async function checkLayering(repoRoot) {
50
- const problems = [];
51
- const agentplaneSrcRoot = path.join(repoRoot, "packages", "agentplane", "src");
52
- const cliRoot = path.join(agentplaneSrcRoot, "cli");
53
- const cliFiles = await listTsFiles(cliRoot);
54
- for (const f of cliFiles) {
55
- const src = await fs.readFile(f.absPath, "utf8");
56
- const imports = extractImports(src);
57
- const hits = imports.filter((s) => s.includes("/adapters/") ||
58
- s.includes("../adapters") ||
59
- s.includes("../../adapters") ||
60
- s.includes("../../../adapters"));
61
- if (hits.length > 0) {
62
- problems.push(`${f.relPath} imports adapters directly: ${hits.join(", ")}`);
63
- }
64
- }
65
- const roots = [path.join(agentplaneSrcRoot, "usecases"), path.join(agentplaneSrcRoot, "ports")];
66
- const banned = [
67
- "node:fs",
68
- "node:fs/promises",
69
- "fs",
70
- "fs/promises",
71
- "node:path",
72
- "path",
73
- "simple-git",
74
- "isomorphic-git",
75
- ];
76
- for (const root of roots) {
77
- const files = await listTsFiles(root);
78
- for (const f of files) {
79
- const src = await fs.readFile(f.absPath, "utf8");
80
- const imports = extractImports(src);
81
- const hits = imports.filter((s) => banned.some((b) => s === b || s.startsWith(`${b}/`)));
82
- if (hits.length > 0) {
83
- problems.push(`${f.relPath} imports banned modules: ${hits.join(", ")}`);
84
- }
85
- }
86
- }
87
- return problems;
88
- }
89
- async function safeFixGitignore(repoRoot) {
90
- const gitignorePath = path.join(repoRoot, ".gitignore");
91
- let existing = "";
92
- try {
93
- existing = await fs.readFile(gitignorePath, "utf8");
94
- }
95
- catch {
96
- // If .gitignore doesn't exist, do not create it implicitly (keep doctor safe).
97
- return { changed: false, note: "Skip: .gitignore not found." };
98
- }
99
- const lines = existing.split(/\r?\n/);
100
- const entry = ".agentplane/.upgrade/";
101
- if (lines.some((l) => l.trim() === entry)) {
102
- return { changed: false, note: "OK: .gitignore already ignores .agentplane/.upgrade/." };
103
- }
104
- const next = `${existing.trimEnd()}\n${entry}\n`;
105
- await fs.writeFile(gitignorePath, next, "utf8");
106
- return { changed: true, note: "Fixed: added .agentplane/.upgrade/ to .gitignore." };
107
- }
108
- async function safeFixTaskIndex(repoRoot) {
109
- try {
110
- // Best-effort: rebuilding the index is a side-effect of listing tasks for the local backend.
111
- const ctx = await loadCommandContext({ cwd: repoRoot, rootOverride: null });
112
- await ctx.taskBackend.listTasks();
113
- return { changed: true, note: "OK: rebuilt tasks index cache (best-effort)." };
114
- }
115
- catch {
116
- return { changed: false, note: "Skip: could not rebuild tasks index cache." };
117
- }
118
- }
119
- export const runDoctor = async (ctx, p) => {
120
- const resolved = await resolveProject({ cwd: ctx.cwd, rootOverride: ctx.rootOverride ?? null });
121
- const repoRoot = resolved.gitRoot;
122
- const problems = await checkLayering(repoRoot);
123
- if (problems.length > 0) {
124
- console.error(warnMessage(`doctor found ${problems.length} problem(s):`));
125
- for (const prob of problems)
126
- console.error(`- ${prob}`);
127
- return 1;
128
- }
129
- if (p.fix) {
130
- const fix = await safeFixGitignore(repoRoot);
131
- console.log(successMessage("doctor fix", undefined, fix.note));
132
- const idx = await safeFixTaskIndex(repoRoot);
133
- console.log(successMessage("doctor fix", undefined, idx.note));
134
- }
135
- console.log(successMessage("doctor", undefined, "OK"));
136
- return 0;
137
- };
1
+ export { doctorSpec } from "./doctor.spec.js";
2
+ export { runDoctor } from "./doctor.run.js";
@@ -0,0 +1,4 @@
1
+ import type { CommandHandler } from "../cli/spec/spec.js";
2
+ import type { DoctorParsed } from "./doctor.spec.js";
3
+ export declare const runDoctor: CommandHandler<DoctorParsed>;
4
+ //# sourceMappingURL=doctor.run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.run.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAoKrD,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,YAAY,CAuBlD,CAAC"}
@@ -0,0 +1,174 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { resolveProject } from "@agentplaneorg/core";
4
+ import { warnMessage, successMessage } from "../cli/output.js";
5
+ import { loadCommandContext } from "./shared/task-backend.js";
6
+ async function listTsFiles(rootDir) {
7
+ const out = [];
8
+ async function walk(absDir) {
9
+ const entries = await fs.readdir(absDir, { withFileTypes: true });
10
+ for (const ent of entries) {
11
+ if (ent.name.startsWith("."))
12
+ continue;
13
+ if (ent.name === "__snapshots__")
14
+ continue;
15
+ if (ent.name === "node_modules")
16
+ continue;
17
+ const abs = path.join(absDir, ent.name);
18
+ if (ent.isDirectory()) {
19
+ await walk(abs);
20
+ continue;
21
+ }
22
+ if (ent.isFile() && ent.name.endsWith(".ts")) {
23
+ out.push({ absPath: abs, relPath: path.relative(rootDir, abs) });
24
+ }
25
+ }
26
+ }
27
+ await walk(rootDir);
28
+ return out;
29
+ }
30
+ function extractImports(source) {
31
+ const imports = [];
32
+ const re = /^\s*import\s+(?:type\s+)?(?:[^"']*?\s+from\s+)?["']([^"']+)["']\s*;?/gm;
33
+ for (const match of source.matchAll(re)) {
34
+ imports.push(match[1] ?? "");
35
+ }
36
+ return imports.filter(Boolean);
37
+ }
38
+ async function pathExists(absPath) {
39
+ try {
40
+ await fs.access(absPath);
41
+ return true;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ async function isDirectory(absPath) {
48
+ try {
49
+ const st = await fs.stat(absPath);
50
+ return st.isDirectory();
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ async function checkWorkspace(repoRoot) {
57
+ const problems = [];
58
+ const requiredFiles = [
59
+ path.join(repoRoot, "AGENTS.md"),
60
+ path.join(repoRoot, ".agentplane", "config.json"),
61
+ ];
62
+ for (const filePath of requiredFiles) {
63
+ if (!(await pathExists(filePath))) {
64
+ problems.push(`Missing required file: ${path.relative(repoRoot, filePath)}`);
65
+ }
66
+ }
67
+ const agentsDir = path.join(repoRoot, ".agentplane", "agents");
68
+ if (!(await isDirectory(agentsDir))) {
69
+ problems.push("Missing required directory: .agentplane/agents");
70
+ return problems;
71
+ }
72
+ const entries = await fs.readdir(agentsDir);
73
+ const hasJson = entries.some((name) => name.endsWith(".json"));
74
+ if (!hasJson) {
75
+ problems.push("No agent profiles found in .agentplane/agents (*.json expected).");
76
+ }
77
+ return problems;
78
+ }
79
+ async function checkLayering(repoRoot) {
80
+ const problems = [];
81
+ const agentplaneSrcRoot = path.join(repoRoot, "packages", "agentplane", "src");
82
+ if (!(await isDirectory(agentplaneSrcRoot))) {
83
+ problems.push("Dev source checks requested but packages/agentplane/src was not found in this workspace.");
84
+ return problems;
85
+ }
86
+ const cliRoot = path.join(agentplaneSrcRoot, "cli");
87
+ const cliFiles = await listTsFiles(cliRoot);
88
+ for (const f of cliFiles) {
89
+ const src = await fs.readFile(f.absPath, "utf8");
90
+ const imports = extractImports(src);
91
+ const hits = imports.filter((s) => s.includes("/adapters/") ||
92
+ s.includes("../adapters") ||
93
+ s.includes("../../adapters") ||
94
+ s.includes("../../../adapters"));
95
+ if (hits.length > 0) {
96
+ problems.push(`${f.relPath} imports adapters directly: ${hits.join(", ")}`);
97
+ }
98
+ }
99
+ const roots = [path.join(agentplaneSrcRoot, "usecases"), path.join(agentplaneSrcRoot, "ports")];
100
+ const banned = [
101
+ "node:fs",
102
+ "node:fs/promises",
103
+ "fs",
104
+ "fs/promises",
105
+ "node:path",
106
+ "path",
107
+ "simple-git",
108
+ "isomorphic-git",
109
+ ];
110
+ for (const root of roots) {
111
+ const files = await listTsFiles(root);
112
+ for (const f of files) {
113
+ const src = await fs.readFile(f.absPath, "utf8");
114
+ const imports = extractImports(src);
115
+ const hits = imports.filter((s) => banned.some((b) => s === b || s.startsWith(`${b}/`)));
116
+ if (hits.length > 0) {
117
+ problems.push(`${f.relPath} imports banned modules: ${hits.join(", ")}`);
118
+ }
119
+ }
120
+ }
121
+ return problems;
122
+ }
123
+ async function safeFixGitignore(repoRoot) {
124
+ const gitignorePath = path.join(repoRoot, ".gitignore");
125
+ let existing = "";
126
+ try {
127
+ existing = await fs.readFile(gitignorePath, "utf8");
128
+ }
129
+ catch {
130
+ // If .gitignore doesn't exist, do not create it implicitly (keep doctor safe).
131
+ return { changed: false, note: "Skip: .gitignore not found." };
132
+ }
133
+ const lines = existing.split(/\r?\n/);
134
+ const entry = ".agentplane/.upgrade/";
135
+ if (lines.some((l) => l.trim() === entry)) {
136
+ return { changed: false, note: "OK: .gitignore already ignores .agentplane/.upgrade/." };
137
+ }
138
+ const next = `${existing.trimEnd()}\n${entry}\n`;
139
+ await fs.writeFile(gitignorePath, next, "utf8");
140
+ return { changed: true, note: "Fixed: added .agentplane/.upgrade/ to .gitignore." };
141
+ }
142
+ async function safeFixTaskIndex(repoRoot) {
143
+ try {
144
+ // Best-effort: rebuilding the index is a side-effect of listing tasks for the local backend.
145
+ const ctx = await loadCommandContext({ cwd: repoRoot, rootOverride: null });
146
+ await ctx.taskBackend.listTasks();
147
+ return { changed: true, note: "OK: rebuilt tasks index cache (best-effort)." };
148
+ }
149
+ catch {
150
+ return { changed: false, note: "Skip: could not rebuild tasks index cache." };
151
+ }
152
+ }
153
+ export const runDoctor = async (ctx, p) => {
154
+ const resolved = await resolveProject({ cwd: ctx.cwd, rootOverride: ctx.rootOverride ?? null });
155
+ const repoRoot = resolved.gitRoot;
156
+ const problems = await checkWorkspace(repoRoot);
157
+ if (p.dev) {
158
+ problems.push(...(await checkLayering(repoRoot)));
159
+ }
160
+ if (problems.length > 0) {
161
+ console.error(warnMessage(`doctor found ${problems.length} problem(s):`));
162
+ for (const prob of problems)
163
+ console.error(`- ${prob}`);
164
+ return 1;
165
+ }
166
+ if (p.fix) {
167
+ const fix = await safeFixGitignore(repoRoot);
168
+ console.log(successMessage("doctor fix", undefined, fix.note));
169
+ const idx = await safeFixTaskIndex(repoRoot);
170
+ console.log(successMessage("doctor fix", undefined, idx.note));
171
+ }
172
+ console.log(successMessage("doctor", undefined, "OK"));
173
+ return 0;
174
+ };
@@ -0,0 +1,7 @@
1
+ import type { CommandSpec } from "../cli/spec/spec.js";
2
+ export type DoctorParsed = {
3
+ fix: boolean;
4
+ dev: boolean;
5
+ };
6
+ export declare const doctorSpec: CommandSpec<DoctorParsed>;
7
+ //# sourceMappingURL=doctor.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.spec.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CAoBhD,CAAC"}
@@ -0,0 +1,20 @@
1
+ export const doctorSpec = {
2
+ id: ["doctor"],
3
+ group: "Quality",
4
+ summary: "Check workspace invariants for a normal agentplane installation (with optional dev source checks).",
5
+ options: [
6
+ { kind: "boolean", name: "fix", default: false, description: "Apply safe fixes." },
7
+ {
8
+ kind: "boolean",
9
+ name: "dev",
10
+ default: false,
11
+ description: "Run monorepo source-layer checks (requires packages/agentplane/src).",
12
+ },
13
+ ],
14
+ examples: [
15
+ { cmd: "agentplane doctor", why: "Check installed workspace invariants." },
16
+ { cmd: "agentplane doctor --dev", why: "Also run monorepo source-layer checks." },
17
+ { cmd: "agentplane doctor --fix", why: "Apply safe-only fixes (idempotent)." },
18
+ ],
19
+ parse: (raw) => ({ fix: raw.opts.fix === true, dev: raw.opts.dev === true }),
20
+ };
@@ -1,12 +1,3 @@
1
- import type { CommandHandler, CommandSpec } from "../../cli/spec/spec.js";
2
- import { type RecipeConflictMode, type RecipeInstallSource } from "../recipes.js";
3
- export type RecipesInstallParsed = {
4
- source: RecipeInstallSource;
5
- index?: string;
6
- refresh: boolean;
7
- yes: boolean;
8
- onConflict: RecipeConflictMode;
9
- };
10
- export declare const recipesInstallSpec: CommandSpec<RecipesInstallParsed>;
11
- export declare const runRecipesInstall: CommandHandler<RecipesInstallParsed>;
1
+ export { recipesInstallSpec, type RecipesInstallParsed } from "./install.spec.js";
2
+ export { runRecipesInstall } from "./install.run.js";
12
3
  //# sourceMappingURL=install.command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"install.command.d.ts","sourceRoot":"","sources":["../../../src/commands/recipes/install.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAc,MAAM,wBAAwB,CAAC;AAItF,OAAO,EAAoB,KAAK,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpG,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,UAAU,EAAE,kBAAkB,CAAC;CAChC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,oBAAoB,CA4IhE,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,oBAAoB,CAkB7D,CAAC"}
1
+ {"version":3,"file":"install.command.d.ts","sourceRoot":"","sources":["../../../src/commands/recipes/install.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1,161 +1,2 @@
1
- import { usageError } from "../../cli/spec/errors.js";
2
- import { CliError } from "../../shared/errors.js";
3
- import { cmdRecipeInstall } from "../recipes.js";
4
- export const recipesInstallSpec = {
5
- id: ["recipes", "install"],
6
- group: "Recipes",
7
- summary: "Install a recipe from remote index, local archive, or URL.",
8
- synopsis: [
9
- "agentplane recipes install <id|path|url> [--index <path|url>] [--refresh] [--yes] [--on-conflict <fail|rename|overwrite>]",
10
- "agentplane recipes install --name <id> [--index <path|url>] [--refresh] [--yes] [--on-conflict <fail|rename|overwrite>]",
11
- "agentplane recipes install --path <path> [--yes] [--on-conflict <fail|rename|overwrite>]",
12
- "agentplane recipes install --url <url> [--yes] [--on-conflict <fail|rename|overwrite>]",
13
- ],
14
- args: [
15
- {
16
- name: "source",
17
- required: false,
18
- valueHint: "<id|path|url>",
19
- description: "Auto mode: URL if http(s); else PATH if file exists; else NAME (remote index id).",
20
- },
21
- ],
22
- options: [
23
- {
24
- kind: "string",
25
- name: "name",
26
- valueHint: "<id>",
27
- description: "Install from remote index by recipe id.",
28
- },
29
- {
30
- kind: "string",
31
- name: "path",
32
- valueHint: "<path>",
33
- description: "Install from local recipe archive path.",
34
- },
35
- {
36
- kind: "string",
37
- name: "url",
38
- valueHint: "<url>",
39
- description: "Install from recipe archive URL.",
40
- },
41
- {
42
- kind: "string",
43
- name: "index",
44
- valueHint: "<path|url>",
45
- description: "Override recipes index location (used when installing by name / auto-name).",
46
- },
47
- {
48
- kind: "boolean",
49
- name: "refresh",
50
- default: false,
51
- description: "Refresh remote index cache before installing.",
52
- },
53
- {
54
- kind: "boolean",
55
- name: "yes",
56
- default: false,
57
- description: "Auto-approve network prompts when allowed by config.",
58
- },
59
- {
60
- kind: "string",
61
- name: "on-conflict",
62
- valueHint: "<fail|rename|overwrite>",
63
- choices: ["fail", "rename", "overwrite"],
64
- default: "fail",
65
- description: "How to handle conflicts when applying recipe agents.",
66
- },
67
- ],
68
- validateRaw: (raw) => {
69
- const explicit = [raw.opts.name, raw.opts.path, raw.opts.url].filter(Boolean).length;
70
- const hasPositional = Boolean(raw.args.source);
71
- if (explicit + (hasPositional ? 1 : 0) !== 1) {
72
- throw usageError({
73
- spec: recipesInstallSpec,
74
- message: "Exactly one source is required: <source> OR --name OR --path OR --url",
75
- command: "recipes install",
76
- });
77
- }
78
- },
79
- parse: (raw) => {
80
- const onConflict = (raw.opts["on-conflict"] ?? "fail");
81
- const refresh = raw.opts.refresh === true;
82
- const yes = raw.opts.yes === true;
83
- const index = typeof raw.opts.index === "string" ? raw.opts.index : undefined;
84
- if (raw.opts.name) {
85
- return {
86
- source: { type: "name", value: raw.opts.name },
87
- index,
88
- refresh,
89
- yes,
90
- onConflict,
91
- };
92
- }
93
- if (raw.opts.path) {
94
- return {
95
- source: { type: "path", value: raw.opts.path },
96
- index,
97
- refresh,
98
- yes,
99
- onConflict,
100
- };
101
- }
102
- if (raw.opts.url) {
103
- return {
104
- source: { type: "url", value: raw.opts.url },
105
- index,
106
- refresh,
107
- yes,
108
- onConflict,
109
- };
110
- }
111
- const source = raw.args.source;
112
- if (typeof source !== "string") {
113
- throw usageError({
114
- spec: recipesInstallSpec,
115
- command: "recipes install",
116
- message: "Missing source argument",
117
- });
118
- }
119
- return {
120
- source: { type: "auto", value: source },
121
- index,
122
- refresh,
123
- yes,
124
- onConflict,
125
- };
126
- },
127
- examples: [
128
- { cmd: "agentplane recipes install viewer", why: "Auto: install by id from remote index." },
129
- {
130
- cmd: "agentplane recipes install --name viewer --refresh",
131
- why: "Install by id, forcing index refresh.",
132
- },
133
- {
134
- cmd: "agentplane recipes install viewer --on-conflict overwrite",
135
- why: "Apply recipe agents, overwriting conflicts.",
136
- },
137
- ],
138
- notes: [
139
- "Auto mode resolution matches v0.1.9: URL if http(s); else PATH if file exists; else NAME (remote index).",
140
- "Network operations may require approval; use --yes to auto-approve when allowed by config.",
141
- ],
142
- };
143
- export const runRecipesInstall = (ctx, p) => (async () => {
144
- try {
145
- return await cmdRecipeInstall({
146
- cwd: ctx.cwd,
147
- rootOverride: ctx.rootOverride,
148
- ...p,
149
- });
150
- }
151
- catch (err) {
152
- if (err instanceof CliError && err.code === "E_USAGE") {
153
- throw usageError({
154
- spec: recipesInstallSpec,
155
- command: "recipes install",
156
- message: err.message,
157
- });
158
- }
159
- throw err;
160
- }
161
- })();
1
+ export { recipesInstallSpec } from "./install.spec.js";
2
+ export { runRecipesInstall } from "./install.run.js";
@@ -0,0 +1,4 @@
1
+ import type { CommandHandler } from "../../cli/spec/spec.js";
2
+ import type { RecipesInstallParsed } from "./install.spec.js";
3
+ export declare const runRecipesInstall: CommandHandler<RecipesInstallParsed>;
4
+ //# sourceMappingURL=install.run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.run.d.ts","sourceRoot":"","sources":["../../../src/commands/recipes/install.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAKzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,oBAAoB,CAkB7D,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { usageError } from "../../cli/spec/errors.js";
2
+ import { CliError } from "../../shared/errors.js";
3
+ import { cmdRecipeInstall } from "../recipes.js";
4
+ import { recipesInstallSpec } from "./install.spec.js";
5
+ export const runRecipesInstall = (ctx, p) => (async () => {
6
+ try {
7
+ return await cmdRecipeInstall({
8
+ cwd: ctx.cwd,
9
+ rootOverride: ctx.rootOverride,
10
+ ...p,
11
+ });
12
+ }
13
+ catch (err) {
14
+ if (err instanceof CliError && err.code === "E_USAGE") {
15
+ throw usageError({
16
+ spec: recipesInstallSpec,
17
+ command: "recipes install",
18
+ message: err.message,
19
+ });
20
+ }
21
+ throw err;
22
+ }
23
+ })();
@@ -0,0 +1,11 @@
1
+ import type { CommandSpec } from "../../cli/spec/spec.js";
2
+ import type { RecipeConflictMode, RecipeInstallSource } from "../recipes.js";
3
+ export type RecipesInstallParsed = {
4
+ source: RecipeInstallSource;
5
+ index?: string;
6
+ refresh: boolean;
7
+ yes: boolean;
8
+ onConflict: RecipeConflictMode;
9
+ };
10
+ export declare const recipesInstallSpec: CommandSpec<RecipesInstallParsed>;
11
+ //# sourceMappingURL=install.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.spec.d.ts","sourceRoot":"","sources":["../../../src/commands/recipes/install.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE7E,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,UAAU,EAAE,kBAAkB,CAAC;CAChC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,oBAAoB,CA4IhE,CAAC"}