@actiondock/cli 2.0.9 → 2.0.10
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.
- package/README.md +17 -0
- package/dist/index.js +45803 -35787
- package/package.json +7 -7
- package/src/commands/action/create.ts +129 -0
- package/src/commands/action/index.ts +27 -0
- package/src/commands/action/list.ts +247 -0
- package/src/commands/action/run.ts +197 -0
- package/src/commands/action/show.ts +128 -0
- package/src/commands/action/sync.ts +76 -0
- package/src/commands/action/validate.ts +102 -0
- package/src/commands/action.ts +1 -633
- package/src/commands/build.ts +19 -17
- package/src/commands/config.ts +168 -69
- package/src/commands/doctor.ts +44 -42
- package/src/commands/export.ts +9 -9
- package/src/commands/index.ts +33 -1
- package/src/commands/info.ts +93 -74
- package/src/commands/init.ts +2 -2
- package/src/commands/link.ts +3 -4
- package/src/commands/mcp.ts +6 -10
- package/src/commands/playbook.ts +289 -161
- package/src/commands/profile.ts +125 -92
- package/src/commands/runs.ts +92 -49
- package/src/commands/serve.ts +3 -4
- package/src/commands/state.ts +145 -64
- package/src/commands/test.ts +1 -1
- package/src/index.ts +39 -3
- package/src/utils/bytes.ts +1 -35
- package/src/utils/filter.ts +2 -24
package/src/commands/playbook.ts
CHANGED
|
@@ -11,11 +11,19 @@ import {
|
|
|
11
11
|
loadPlaybooks,
|
|
12
12
|
loadProjectConfig,
|
|
13
13
|
resolveActionProject,
|
|
14
|
+
resolvePackageRoot,
|
|
14
15
|
resolvePlaybookProject,
|
|
15
16
|
resolveTarget,
|
|
16
17
|
} from "@actiondock/core";
|
|
17
18
|
import type { PlaybookDefinition } from "@actiondock/core";
|
|
18
19
|
import { Command } from "commander";
|
|
20
|
+
import {
|
|
21
|
+
ArgumentError,
|
|
22
|
+
CliError,
|
|
23
|
+
ExecutionError,
|
|
24
|
+
getEffectiveOptions,
|
|
25
|
+
renderResult,
|
|
26
|
+
} from "@actiondock/runtime-cli";
|
|
19
27
|
import { resolveIntent } from "../utils/filter";
|
|
20
28
|
|
|
21
29
|
export function registerPlaybookCommands(program: Command): void {
|
|
@@ -32,12 +40,17 @@ export function registerPlaybookCommands(program: Command): void {
|
|
|
32
40
|
.option("-p, --profile <name>", "Query against a specific profile")
|
|
33
41
|
.option("-s, --server <url>", "Remote server URL")
|
|
34
42
|
.option("-t, --token <token>", "Auth token for remote server")
|
|
43
|
+
.option("--fallback", "Enable fallback to full list when no items match intent")
|
|
35
44
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
36
45
|
.option("--json", "Output as JSON")
|
|
37
|
-
.
|
|
46
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
47
|
+
.action(async (patterns, rawOptions, cmd) => {
|
|
38
48
|
try {
|
|
49
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
39
50
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
40
|
-
const
|
|
51
|
+
const isMachine = Boolean(options.json || options.envelope);
|
|
52
|
+
const fallbackExplicit = options.fallback === true || (Array.isArray(process.argv) && process.argv.includes("--fallback"));
|
|
53
|
+
const shouldFallback = isMachine ? fallbackExplicit : options.fallback !== false;
|
|
41
54
|
|
|
42
55
|
const target = resolveTarget({
|
|
43
56
|
profile: options.profile,
|
|
@@ -46,31 +59,65 @@ export function registerPlaybookCommands(program: Command): void {
|
|
|
46
59
|
});
|
|
47
60
|
|
|
48
61
|
if (target.type === "remote") {
|
|
49
|
-
|
|
62
|
+
let remotePbs = await fetchRemotePlaybooks(target.serverUrl!, target.token, {
|
|
50
63
|
intent: effectiveIntent,
|
|
51
64
|
package: options.package,
|
|
52
65
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
|
|
67
|
+
let isFallback = false;
|
|
68
|
+
if (remotePbs.length === 0 && effectiveIntent && shouldFallback) {
|
|
69
|
+
remotePbs = await fetchRemotePlaybooks(target.serverUrl!, target.token, {
|
|
70
|
+
package: options.package,
|
|
71
|
+
});
|
|
72
|
+
isFallback = true;
|
|
56
73
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
console.log(` ${p.id.padEnd(24)} ${p.description} (Package: ${p.packageId})`);
|
|
65
|
-
}
|
|
74
|
+
|
|
75
|
+
if (isFallback && isMachine) {
|
|
76
|
+
renderResult(
|
|
77
|
+
{ items: remotePbs, isFallback: true, matchedCount: 0 },
|
|
78
|
+
{ json: options.json, envelope: options.envelope }
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
66
81
|
}
|
|
82
|
+
|
|
83
|
+
renderResult(remotePbs, {
|
|
84
|
+
json: options.json,
|
|
85
|
+
envelope: options.envelope,
|
|
86
|
+
humanFormatter: () => {
|
|
87
|
+
const lines = [
|
|
88
|
+
`Playbooks on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""}:\n`,
|
|
89
|
+
];
|
|
90
|
+
if (isFallback && effectiveIntent) {
|
|
91
|
+
lines.push(`(No remote playbooks matched intent '${effectiveIntent}', showing all playbooks)\n`);
|
|
92
|
+
}
|
|
93
|
+
if (remotePbs.length === 0) {
|
|
94
|
+
lines.push(" (No playbooks found)");
|
|
95
|
+
} else {
|
|
96
|
+
for (const p of remotePbs) {
|
|
97
|
+
lines.push(` ${p.id.padEnd(24)} ${p.description} (Package: ${p.packageId})`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return lines.join("\n");
|
|
101
|
+
},
|
|
102
|
+
});
|
|
67
103
|
return;
|
|
68
104
|
}
|
|
69
105
|
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
106
|
+
let targetRoot: string | null = null;
|
|
107
|
+
if (options.package) {
|
|
108
|
+
targetRoot = resolvePackageRoot(options.package);
|
|
109
|
+
if (!targetRoot) {
|
|
110
|
+
throw new ArgumentError(
|
|
111
|
+
`Package '${options.package}' not found in linked packages or path`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
targetRoot = findProjectRoot();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (targetRoot) {
|
|
119
|
+
const config = loadProjectConfig(targetRoot);
|
|
120
|
+
const playbooks = loadPlaybooks(targetRoot, config.playbooksDir);
|
|
74
121
|
const rawList = Array.from(playbooks.values()).map((p) => ({
|
|
75
122
|
id: p.id,
|
|
76
123
|
description: p.description || "",
|
|
@@ -86,116 +133,144 @@ export function registerPlaybookCommands(program: Command): void {
|
|
|
86
133
|
shouldFallback
|
|
87
134
|
);
|
|
88
135
|
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.log(`(No playbooks matched intent '${effectiveIntent}', showing all playbooks)\n`);
|
|
95
|
-
}
|
|
96
|
-
for (const p of filterRes.items) {
|
|
97
|
-
console.log(` ${p.id.padEnd(24)} ${p.description}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
// List playbooks across all linked packages
|
|
102
|
-
const linkedList = listLinkedPackages();
|
|
103
|
-
if (linkedList.length === 0) {
|
|
104
|
-
console.log("No ActionDock project in current directory, and no packages linked.");
|
|
105
|
-
console.log("Run 'ad link' inside an Action package to register it.");
|
|
136
|
+
if (filterRes.isFallback && isMachine) {
|
|
137
|
+
renderResult(
|
|
138
|
+
{ items: filterRes.items, isFallback: true, matchedCount: 0 },
|
|
139
|
+
{ json: options.json, envelope: options.envelope }
|
|
140
|
+
);
|
|
106
141
|
return;
|
|
107
142
|
}
|
|
108
143
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
144
|
+
renderResult(filterRes.items, {
|
|
145
|
+
json: options.json,
|
|
146
|
+
envelope: options.envelope,
|
|
147
|
+
humanFormatter: () => {
|
|
148
|
+
const lines = [`Playbooks in ${config.id} (${targetRoot}):\n`];
|
|
149
|
+
if (filterRes.isFallback && effectiveIntent) {
|
|
150
|
+
lines.push(`(No playbooks matched intent '${effectiveIntent}', showing all playbooks)\n`);
|
|
151
|
+
}
|
|
152
|
+
for (const p of filterRes.items) {
|
|
153
|
+
lines.push(` ${p.id.padEnd(24)} ${p.description}`);
|
|
154
|
+
}
|
|
155
|
+
return lines.join("\n");
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// List playbooks across all linked packages
|
|
161
|
+
const linkedList = listLinkedPackages();
|
|
162
|
+
if (linkedList.length === 0) {
|
|
163
|
+
renderResult([], {
|
|
164
|
+
json: options.json,
|
|
165
|
+
envelope: options.envelope,
|
|
166
|
+
humanFormatter: () =>
|
|
167
|
+
"No ActionDock project in current directory, and no packages linked.\nRun 'ad link' inside an Action package to register it.",
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
120
171
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
172
|
+
const aggregated: Array<{
|
|
173
|
+
packageId: string;
|
|
174
|
+
packageName: string;
|
|
175
|
+
path: string;
|
|
176
|
+
playbooks: Array<{
|
|
177
|
+
id: string;
|
|
178
|
+
description: string;
|
|
179
|
+
actions: string[];
|
|
180
|
+
file: string;
|
|
181
|
+
}>;
|
|
182
|
+
}> = [];
|
|
183
|
+
|
|
184
|
+
for (const pkg of linkedList) {
|
|
185
|
+
if (!existsSync(pkg.path)) continue;
|
|
186
|
+
try {
|
|
187
|
+
const config = loadProjectConfig(pkg.path);
|
|
188
|
+
const playbooks = loadPlaybooks(pkg.path, config.playbooksDir);
|
|
189
|
+
const pkgPlaybooks = Array.from(playbooks.values()).map((p) => ({
|
|
190
|
+
id: p.id,
|
|
191
|
+
description: p.description || "",
|
|
192
|
+
actions: p.actions || [],
|
|
193
|
+
file: p.filePath,
|
|
194
|
+
}));
|
|
195
|
+
|
|
196
|
+
aggregated.push({
|
|
197
|
+
packageId: pkg.id,
|
|
198
|
+
packageName: pkg.name,
|
|
199
|
+
path: pkg.path,
|
|
200
|
+
playbooks: pkgPlaybooks,
|
|
201
|
+
});
|
|
202
|
+
} catch {
|
|
203
|
+
// Ignore broken linked package
|
|
142
204
|
}
|
|
205
|
+
}
|
|
143
206
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
207
|
+
let filteredPackages: typeof aggregated = [];
|
|
208
|
+
let isFallback = false;
|
|
209
|
+
if (!effectiveIntent) {
|
|
210
|
+
filteredPackages = aggregated;
|
|
211
|
+
} else {
|
|
212
|
+
for (const pkg of aggregated) {
|
|
213
|
+
const pkgMatches = filterWithFallbackInfo(
|
|
214
|
+
[pkg],
|
|
215
|
+
effectiveIntent,
|
|
216
|
+
[(p) => p.packageId, (p) => p.packageName, (p) => p.path],
|
|
217
|
+
false
|
|
218
|
+
).matchedCount > 0;
|
|
219
|
+
|
|
220
|
+
if (pkgMatches) {
|
|
221
|
+
filteredPackages.push(pkg);
|
|
222
|
+
} else {
|
|
223
|
+
const matchedPlaybooks = filterWithFallbackInfo(
|
|
224
|
+
pkg.playbooks,
|
|
151
225
|
effectiveIntent,
|
|
152
|
-
[(p) => p.
|
|
226
|
+
[(p) => p.id, (p) => p.description, (p) => p.actions, (p) => p.file],
|
|
153
227
|
false
|
|
154
|
-
).
|
|
228
|
+
).items;
|
|
155
229
|
|
|
156
|
-
if (
|
|
157
|
-
filteredPackages.push(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
effectiveIntent,
|
|
162
|
-
[(p) => p.id, (p) => p.description, (p) => p.actions, (p) => p.file],
|
|
163
|
-
false
|
|
164
|
-
).items;
|
|
165
|
-
|
|
166
|
-
if (matchedPlaybooks.length > 0) {
|
|
167
|
-
filteredPackages.push({
|
|
168
|
-
...pkg,
|
|
169
|
-
playbooks: matchedPlaybooks,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
230
|
+
if (matchedPlaybooks.length > 0) {
|
|
231
|
+
filteredPackages.push({
|
|
232
|
+
...pkg,
|
|
233
|
+
playbooks: matchedPlaybooks,
|
|
234
|
+
});
|
|
172
235
|
}
|
|
173
236
|
}
|
|
237
|
+
}
|
|
174
238
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
239
|
+
if (filteredPackages.length === 0 && shouldFallback) {
|
|
240
|
+
filteredPackages = aggregated;
|
|
241
|
+
isFallback = true;
|
|
178
242
|
}
|
|
243
|
+
}
|
|
179
244
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
245
|
+
if (isFallback && isMachine) {
|
|
246
|
+
renderResult(
|
|
247
|
+
{ packages: filteredPackages, isFallback: true, matchedCount: 0 },
|
|
248
|
+
{ json: options.json, envelope: options.envelope }
|
|
249
|
+
);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
renderResult(filteredPackages, {
|
|
254
|
+
json: options.json,
|
|
255
|
+
envelope: options.envelope,
|
|
256
|
+
humanFormatter: () => {
|
|
257
|
+
const lines = ["Playbooks in Linked Packages:\n"];
|
|
184
258
|
for (const pkg of filteredPackages) {
|
|
185
|
-
|
|
259
|
+
lines.push(`* Package: ${pkg.packageId} (${pkg.path})`);
|
|
186
260
|
if (pkg.playbooks.length === 0) {
|
|
187
|
-
|
|
261
|
+
lines.push(" (No playbooks)");
|
|
188
262
|
} else {
|
|
189
263
|
for (const p of pkg.playbooks) {
|
|
190
|
-
|
|
264
|
+
lines.push(` - ${p.id.padEnd(26)} ${p.description}`);
|
|
191
265
|
}
|
|
192
266
|
}
|
|
193
267
|
}
|
|
194
|
-
|
|
195
|
-
|
|
268
|
+
return lines.join("\n");
|
|
269
|
+
},
|
|
270
|
+
});
|
|
196
271
|
} catch (err: any) {
|
|
197
|
-
|
|
198
|
-
|
|
272
|
+
if (err instanceof ArgumentError || err instanceof CliError) throw err;
|
|
273
|
+
throw new ExecutionError(err.message);
|
|
199
274
|
}
|
|
200
275
|
});
|
|
201
276
|
|
|
@@ -210,8 +285,7 @@ export function registerPlaybookCommands(program: Command): void {
|
|
|
210
285
|
.action((id, options) => {
|
|
211
286
|
const root = findProjectRoot();
|
|
212
287
|
if (!root) {
|
|
213
|
-
|
|
214
|
-
process.exit(1);
|
|
288
|
+
throw new ExecutionError("Not in an ActionDock project (actiondock.json not found)");
|
|
215
289
|
}
|
|
216
290
|
try {
|
|
217
291
|
const config = loadProjectConfig(root);
|
|
@@ -225,8 +299,7 @@ export function registerPlaybookCommands(program: Command): void {
|
|
|
225
299
|
const targetFullFile = resolve(pbDir, targetRelFile);
|
|
226
300
|
|
|
227
301
|
if (existsSync(targetFullFile)) {
|
|
228
|
-
|
|
229
|
-
process.exit(1);
|
|
302
|
+
throw new ExecutionError(`File '${targetFullFile}' already exists`);
|
|
230
303
|
}
|
|
231
304
|
|
|
232
305
|
mkdirSync(dirname(targetFullFile), { recursive: true });
|
|
@@ -256,8 +329,10 @@ This playbook provides task execution guidance for AI Agents.
|
|
|
256
329
|
writeFileSync(targetFullFile, template, "utf-8");
|
|
257
330
|
console.log(`[OK] Created Playbook '${id}' at ${targetFullFile}`);
|
|
258
331
|
} catch (err: any) {
|
|
259
|
-
|
|
260
|
-
|
|
332
|
+
if (err instanceof CliError) {
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
335
|
+
throw new ExecutionError(err.message);
|
|
261
336
|
}
|
|
262
337
|
});
|
|
263
338
|
|
|
@@ -265,12 +340,19 @@ This playbook provides task execution guidance for AI Agents.
|
|
|
265
340
|
pbCmd
|
|
266
341
|
.command("show <id>")
|
|
267
342
|
.description("Show playbook content and metadata (from current project or linked packages)")
|
|
343
|
+
.option("-P, --package <id>", "Target package ID or path")
|
|
268
344
|
.option("-p, --profile <name>", "Query against a specific profile")
|
|
269
345
|
.option("-s, --server <url>", "Remote server URL")
|
|
270
346
|
.option("-t, --token <token>", "Auth token for remote server")
|
|
271
347
|
.option("--json", "Output as JSON")
|
|
272
|
-
.
|
|
348
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
349
|
+
.action(async (id, rawOptions, cmd) => {
|
|
273
350
|
try {
|
|
351
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
352
|
+
if (!id) {
|
|
353
|
+
throw new ArgumentError("Playbook ID is required for show");
|
|
354
|
+
}
|
|
355
|
+
|
|
274
356
|
const target = resolveTarget({
|
|
275
357
|
profile: options.profile,
|
|
276
358
|
server: options.server,
|
|
@@ -279,39 +361,64 @@ This playbook provides task execution guidance for AI Agents.
|
|
|
279
361
|
|
|
280
362
|
if (target.type === "remote") {
|
|
281
363
|
const pb = await fetchRemotePlaybookShow(target.serverUrl!, id, target.token);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
364
|
+
renderResult(pb, {
|
|
365
|
+
json: options.json,
|
|
366
|
+
envelope: options.envelope,
|
|
367
|
+
humanFormatter: () => {
|
|
368
|
+
const lines = [`Playbook: ${pb.id} (Package: ${pb.packageId})`];
|
|
369
|
+
if (pb.description) lines.push(`Description: ${pb.description}`);
|
|
370
|
+
if (pb.actions && pb.actions.length > 0) {
|
|
371
|
+
lines.push(`Actions: ${pb.actions.join(", ")}`);
|
|
372
|
+
}
|
|
373
|
+
if (pb.filePath) lines.push(`File: ${pb.filePath}\n`);
|
|
374
|
+
lines.push("--- Content ---", pb.content);
|
|
375
|
+
return lines.join("\n");
|
|
376
|
+
},
|
|
377
|
+
});
|
|
294
378
|
return;
|
|
295
379
|
}
|
|
296
380
|
|
|
297
|
-
|
|
298
|
-
|
|
381
|
+
let showTarget = id;
|
|
382
|
+
if (options.package && !id.includes("/") && !id.includes(":")) {
|
|
383
|
+
const pkgRoot = resolvePackageRoot(options.package);
|
|
384
|
+
if (!pkgRoot) {
|
|
385
|
+
throw new ArgumentError(
|
|
386
|
+
`Package '${options.package}' not found in linked packages or path`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
showTarget = `${options.package}/${id}`;
|
|
390
|
+
}
|
|
299
391
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
console.log(`Actions: ${pb.actions.join(", ")}`);
|
|
392
|
+
let resolved;
|
|
393
|
+
try {
|
|
394
|
+
resolved = resolvePlaybookProject(showTarget);
|
|
395
|
+
} catch (err: any) {
|
|
396
|
+
if (err.message?.includes("not found") || err.message?.includes("no longer exists")) {
|
|
397
|
+
throw new ArgumentError(err.message);
|
|
307
398
|
}
|
|
308
|
-
|
|
309
|
-
console.log("--- Content ---");
|
|
310
|
-
console.log(pb.content);
|
|
399
|
+
throw new ExecutionError(err.message);
|
|
311
400
|
}
|
|
401
|
+
|
|
402
|
+
const pb = resolved.playbook;
|
|
403
|
+
const payload = { ...pb, packageId: resolved.packageId };
|
|
404
|
+
|
|
405
|
+
renderResult(payload, {
|
|
406
|
+
json: options.json,
|
|
407
|
+
envelope: options.envelope,
|
|
408
|
+
humanFormatter: () => {
|
|
409
|
+
const lines = [`Playbook: ${pb.id} (Package: ${resolved.packageId})`];
|
|
410
|
+
if (pb.description) lines.push(`Description: ${pb.description}`);
|
|
411
|
+
if (pb.actions && pb.actions.length > 0) {
|
|
412
|
+
lines.push(`Actions: ${pb.actions.join(", ")}`);
|
|
413
|
+
}
|
|
414
|
+
if (pb.filePath) lines.push(`File: ${pb.filePath}\n`);
|
|
415
|
+
lines.push("--- Content ---", pb.content);
|
|
416
|
+
return lines.join("\n");
|
|
417
|
+
},
|
|
418
|
+
});
|
|
312
419
|
} catch (err: any) {
|
|
313
|
-
|
|
314
|
-
|
|
420
|
+
if (err instanceof ArgumentError || err instanceof CliError) throw err;
|
|
421
|
+
throw new ExecutionError(err.message);
|
|
315
422
|
}
|
|
316
423
|
});
|
|
317
424
|
|
|
@@ -319,10 +426,24 @@ This playbook provides task execution guidance for AI Agents.
|
|
|
319
426
|
pbCmd
|
|
320
427
|
.command("validate [id]")
|
|
321
428
|
.description("Validate playbook format and action references (in current project or linked packages)")
|
|
429
|
+
.option("-P, --package <id>", "Target package ID or path")
|
|
322
430
|
.option("--json", "Output as JSON")
|
|
323
|
-
.
|
|
431
|
+
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
432
|
+
.action(async (id, rawOptions, cmd) => {
|
|
324
433
|
try {
|
|
325
|
-
const
|
|
434
|
+
const options = getEffectiveOptions(rawOptions, cmd);
|
|
435
|
+
let root: string | null = null;
|
|
436
|
+
if (options.package) {
|
|
437
|
+
root = resolvePackageRoot(options.package);
|
|
438
|
+
if (!root) {
|
|
439
|
+
throw new ArgumentError(
|
|
440
|
+
`Package '${options.package}' not found in linked packages or path`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
} else {
|
|
444
|
+
root = findProjectRoot();
|
|
445
|
+
}
|
|
446
|
+
|
|
326
447
|
const targets: Array<{ root: string; packageId: string; playbooks: PlaybookDefinition[] }> = [];
|
|
327
448
|
|
|
328
449
|
if (root) {
|
|
@@ -331,8 +452,7 @@ This playbook provides task execution guidance for AI Agents.
|
|
|
331
452
|
if (id) {
|
|
332
453
|
const pb = playbooks.get(id);
|
|
333
454
|
if (!pb) {
|
|
334
|
-
|
|
335
|
-
process.exit(1);
|
|
455
|
+
throw new ArgumentError(`Playbook '${id}' not found in package '${config.id}'`);
|
|
336
456
|
}
|
|
337
457
|
targets.push({ root, packageId: config.id, playbooks: [pb] });
|
|
338
458
|
} else {
|
|
@@ -349,8 +469,7 @@ This playbook provides task execution guidance for AI Agents.
|
|
|
349
469
|
// Outside project: validate all linked packages
|
|
350
470
|
const linkedList = listLinkedPackages();
|
|
351
471
|
if (linkedList.length === 0) {
|
|
352
|
-
|
|
353
|
-
process.exit(1);
|
|
472
|
+
throw new ArgumentError("Not in an ActionDock project, and no packages linked.");
|
|
354
473
|
}
|
|
355
474
|
for (const pkg of linkedList) {
|
|
356
475
|
if (!existsSync(pkg.path)) continue;
|
|
@@ -415,23 +534,32 @@ This playbook provides task execution guidance for AI Agents.
|
|
|
415
534
|
}
|
|
416
535
|
|
|
417
536
|
const allValid = results.every((r) => r.valid);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
537
|
+
renderResult(
|
|
538
|
+
{ valid: allValid, results },
|
|
539
|
+
{
|
|
540
|
+
json: options.json,
|
|
541
|
+
envelope: options.envelope,
|
|
542
|
+
humanFormatter: () => {
|
|
543
|
+
const lines: string[] = [];
|
|
544
|
+
for (const r of results) {
|
|
545
|
+
const prefix = targets.length > 1 || !root ? `[${r.packageId}] ` : "";
|
|
546
|
+
if (r.valid) {
|
|
547
|
+
const warn = r.warnings.length > 0 ? ` (Warnings: ${r.warnings.join("; ")})` : "";
|
|
548
|
+
lines.push(`[OK] ${prefix}${r.id}: Valid${warn}`);
|
|
549
|
+
} else {
|
|
550
|
+
lines.push(`[FAIL] ${prefix}${r.id}: ${r.errors.join("; ")}`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return lines.join("\n");
|
|
554
|
+
},
|
|
429
555
|
}
|
|
430
|
-
|
|
431
|
-
if (!allValid) process.
|
|
556
|
+
);
|
|
557
|
+
if (!allValid) process.exitCode = 1;
|
|
432
558
|
} catch (err: any) {
|
|
433
|
-
|
|
434
|
-
|
|
559
|
+
if (err instanceof ArgumentError || err instanceof CliError) {
|
|
560
|
+
throw err;
|
|
561
|
+
}
|
|
562
|
+
throw new ExecutionError(err.message);
|
|
435
563
|
}
|
|
436
564
|
});
|
|
437
565
|
}
|