akm-cli 0.7.5 → 0.8.0-rc.3
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/.github/CHANGELOG.md +1 -1
- package/dist/cli/parse-args.js +86 -0
- package/dist/cli.js +1023 -521
- package/dist/commands/agent-dispatch.js +107 -0
- package/dist/commands/agent-support.js +62 -0
- package/dist/commands/config-cli.js +68 -84
- package/dist/commands/consolidate.js +812 -0
- package/dist/commands/distill-promotion-policy.js +658 -0
- package/dist/commands/distill.js +218 -43
- package/dist/commands/eval-cases.js +40 -0
- package/dist/commands/events.js +2 -23
- package/dist/commands/graph.js +222 -0
- package/dist/commands/health.js +376 -0
- package/dist/commands/help/help-accept.md +9 -0
- package/dist/commands/help/help-improve.md +53 -0
- package/dist/commands/help/help-proposals.md +15 -0
- package/dist/commands/help/help-propose.md +17 -0
- package/dist/commands/help/help-reject.md +8 -0
- package/dist/commands/history.js +3 -30
- package/dist/commands/improve.js +1161 -0
- package/dist/commands/info.js +2 -2
- package/dist/commands/init.js +2 -2
- package/dist/commands/install-audit.js +5 -1
- package/dist/commands/installed-stashes.js +118 -138
- package/dist/commands/knowledge.js +133 -0
- package/dist/commands/lint/agent-linter.js +46 -0
- package/dist/commands/lint/base-linter.js +291 -0
- package/dist/commands/lint/command-linter.js +46 -0
- package/dist/commands/lint/default-linter.js +13 -0
- package/dist/commands/lint/index.js +145 -0
- package/dist/commands/lint/knowledge-linter.js +13 -0
- package/dist/commands/lint/memory-linter.js +58 -0
- package/dist/commands/lint/registry.js +33 -0
- package/dist/commands/lint/skill-linter.js +42 -0
- package/dist/commands/lint/task-linter.js +47 -0
- package/dist/commands/lint/types.js +1 -0
- package/dist/commands/lint/vault-key-rules.js +67 -0
- package/dist/commands/lint/workflow-linter.js +53 -0
- package/dist/commands/lint.js +1 -0
- package/dist/commands/proposal.js +8 -7
- package/dist/commands/propose.js +71 -28
- package/dist/commands/reflect.js +135 -35
- package/dist/commands/registry-search.js +2 -2
- package/dist/commands/remember.js +54 -0
- package/dist/commands/schema-repair.js +130 -0
- package/dist/commands/search.js +21 -5
- package/dist/commands/show.js +125 -20
- package/dist/commands/source-add.js +10 -10
- package/dist/commands/source-manage.js +11 -19
- package/dist/commands/tasks.js +385 -0
- package/dist/commands/url-checker.js +39 -0
- package/dist/commands/vault.js +168 -77
- package/dist/core/action-contributors.js +25 -0
- package/dist/core/asset-ref.js +4 -0
- package/dist/core/asset-registry.js +4 -16
- package/dist/core/asset-spec.js +10 -0
- package/dist/core/common.js +100 -0
- package/dist/core/concurrent.js +22 -0
- package/dist/core/config.js +233 -133
- package/dist/core/events.js +73 -126
- package/dist/core/frontmatter.js +0 -6
- package/dist/core/markdown.js +17 -0
- package/dist/core/memory-improve.js +678 -0
- package/dist/core/parse.js +155 -0
- package/dist/core/paths.js +101 -3
- package/dist/core/proposal-validators.js +61 -0
- package/dist/core/proposals.js +49 -38
- package/dist/core/state-db.js +731 -0
- package/dist/core/time.js +51 -0
- package/dist/core/warn.js +59 -1
- package/dist/indexer/db-search.js +52 -238
- package/dist/indexer/db.js +403 -54
- package/dist/indexer/ensure-index.js +61 -0
- package/dist/indexer/graph-boost.js +247 -94
- package/dist/indexer/graph-db.js +201 -0
- package/dist/indexer/graph-dedup.js +99 -0
- package/dist/indexer/graph-extraction.js +409 -76
- package/dist/indexer/index-context.js +10 -0
- package/dist/indexer/indexer.js +456 -290
- package/dist/indexer/llm-cache.js +47 -0
- package/dist/indexer/matchers.js +124 -160
- package/dist/indexer/memory-inference.js +63 -29
- package/dist/indexer/metadata-contributors.js +26 -0
- package/dist/indexer/metadata.js +196 -197
- package/dist/indexer/path-resolver.js +89 -0
- package/dist/indexer/ranking-contributors.js +204 -0
- package/dist/indexer/ranking.js +74 -0
- package/dist/indexer/search-hit-enrichers.js +22 -0
- package/dist/indexer/search-source.js +24 -9
- package/dist/indexer/semantic-status.js +2 -16
- package/dist/indexer/walker.js +25 -0
- package/dist/integrations/agent/builders.js +109 -0
- package/dist/integrations/agent/config.js +203 -3
- package/dist/integrations/agent/index.js +5 -2
- package/dist/integrations/agent/model-aliases.js +63 -0
- package/dist/integrations/agent/profiles.js +67 -5
- package/dist/integrations/agent/prompts.js +77 -72
- package/dist/integrations/agent/sdk-runner.js +120 -0
- package/dist/integrations/agent/spawn.js +93 -22
- package/dist/integrations/lockfile.js +10 -18
- package/dist/integrations/session-logs/index.js +65 -0
- package/dist/integrations/session-logs/providers/claude-code.js +56 -0
- package/dist/integrations/session-logs/providers/opencode.js +52 -0
- package/dist/integrations/session-logs/types.js +1 -0
- package/dist/llm/call-ai.js +74 -0
- package/dist/llm/client.js +61 -122
- package/dist/llm/feature-gate.js +27 -16
- package/dist/llm/graph-extract.js +297 -62
- package/dist/llm/memory-infer.js +49 -71
- package/dist/llm/metadata-enhance.js +39 -22
- package/dist/llm/prompts/graph-extract-user-prompt.md +12 -0
- package/dist/output/cli-hints-full.md +277 -0
- package/dist/output/cli-hints-short.md +65 -0
- package/dist/output/cli-hints.js +2 -318
- package/dist/output/renderers.js +220 -256
- package/dist/output/shapes.js +101 -93
- package/dist/output/text.js +256 -17
- package/dist/registry/providers/skills-sh.js +61 -49
- package/dist/registry/providers/static-index.js +44 -48
- package/dist/registry/resolve.js +8 -16
- package/dist/setup/setup.js +510 -11
- package/dist/sources/provider-factory.js +2 -1
- package/dist/sources/providers/filesystem.js +16 -23
- package/dist/sources/providers/git.js +4 -5
- package/dist/sources/providers/website.js +15 -22
- package/dist/sources/website-ingest.js +4 -0
- package/dist/tasks/backends/cron.js +200 -0
- package/dist/tasks/backends/exec-utils.js +25 -0
- package/dist/tasks/backends/index.js +32 -0
- package/dist/tasks/backends/launchd-template.xml +19 -0
- package/dist/tasks/backends/launchd.js +184 -0
- package/dist/tasks/backends/schtasks-template.xml +29 -0
- package/dist/tasks/backends/schtasks.js +212 -0
- package/dist/tasks/parser.js +198 -0
- package/dist/tasks/resolveAkmBin.js +84 -0
- package/dist/tasks/runner.js +432 -0
- package/dist/tasks/schedule.js +208 -0
- package/dist/tasks/schema.js +13 -0
- package/dist/tasks/validator.js +59 -0
- package/dist/wiki/index-template.md +12 -0
- package/dist/wiki/ingest-workflow-template.md +54 -0
- package/dist/wiki/log-template.md +8 -0
- package/dist/wiki/schema-template.md +61 -0
- package/dist/wiki/wiki-templates.js +12 -0
- package/dist/wiki/wiki.js +10 -61
- package/dist/workflows/authoring.js +5 -25
- package/dist/workflows/renderer.js +8 -3
- package/dist/workflows/runs.js +59 -91
- package/dist/workflows/validator.js +1 -1
- package/dist/workflows/workflow-template.md +24 -0
- package/docs/README.md +5 -2
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.8.0.md +43 -0
- package/package.json +3 -2
- package/dist/templates/wiki-templates.js +0 -100
package/dist/output/shapes.js
CHANGED
|
@@ -6,6 +6,70 @@
|
|
|
6
6
|
* `Record<string, unknown>` shapes, which makes them trivial to unit test.
|
|
7
7
|
*/
|
|
8
8
|
const NORMAL_DESCRIPTION_LIMIT = 250;
|
|
9
|
+
const PASSTHROUGH_COMMANDS = new Set([
|
|
10
|
+
"add",
|
|
11
|
+
"agent-result",
|
|
12
|
+
"clone",
|
|
13
|
+
"config",
|
|
14
|
+
"consolidate",
|
|
15
|
+
"curate",
|
|
16
|
+
"disable",
|
|
17
|
+
"enable",
|
|
18
|
+
"feedback",
|
|
19
|
+
"graph-entities",
|
|
20
|
+
"graph-export",
|
|
21
|
+
"graph-related",
|
|
22
|
+
"graph-relations",
|
|
23
|
+
"graph-summary",
|
|
24
|
+
"health",
|
|
25
|
+
"import",
|
|
26
|
+
"improve",
|
|
27
|
+
"index",
|
|
28
|
+
"info",
|
|
29
|
+
"init",
|
|
30
|
+
"lint",
|
|
31
|
+
"list",
|
|
32
|
+
"registry-add",
|
|
33
|
+
"registry-build-index",
|
|
34
|
+
"registry-list",
|
|
35
|
+
"registry-remove",
|
|
36
|
+
"remember",
|
|
37
|
+
"remove",
|
|
38
|
+
"save",
|
|
39
|
+
"setup",
|
|
40
|
+
"tasks-add",
|
|
41
|
+
"tasks-disable",
|
|
42
|
+
"tasks-doctor",
|
|
43
|
+
"tasks-enable",
|
|
44
|
+
"tasks-history",
|
|
45
|
+
"tasks-list",
|
|
46
|
+
"tasks-remove",
|
|
47
|
+
"tasks-run",
|
|
48
|
+
"tasks-show",
|
|
49
|
+
"tasks-sync",
|
|
50
|
+
"update",
|
|
51
|
+
"upgrade",
|
|
52
|
+
"vault-create",
|
|
53
|
+
"vault-set",
|
|
54
|
+
"vault-unset",
|
|
55
|
+
"wiki-create",
|
|
56
|
+
"wiki-ingest",
|
|
57
|
+
"wiki-lint",
|
|
58
|
+
"wiki-list",
|
|
59
|
+
"wiki-pages",
|
|
60
|
+
"wiki-register",
|
|
61
|
+
"wiki-remove",
|
|
62
|
+
"wiki-show",
|
|
63
|
+
"wiki-stash",
|
|
64
|
+
"workflow-complete",
|
|
65
|
+
"workflow-create",
|
|
66
|
+
"workflow-list",
|
|
67
|
+
"workflow-next",
|
|
68
|
+
"workflow-resume",
|
|
69
|
+
"workflow-start",
|
|
70
|
+
"workflow-status",
|
|
71
|
+
"workflow-validate",
|
|
72
|
+
]);
|
|
9
73
|
export function shapeForCommand(command, result, detail, forAgent = false) {
|
|
10
74
|
switch (command) {
|
|
11
75
|
case "search":
|
|
@@ -45,66 +109,31 @@ export function shapeForCommand(command, result, detail, forAgent = false) {
|
|
|
45
109
|
case "reflect":
|
|
46
110
|
case "propose":
|
|
47
111
|
return shapeProposalProducerOutput(result, detail);
|
|
48
|
-
// Output shape registration for `akm distill <ref>` (#228). The shape is
|
|
49
|
-
// simple — outcome + ids + optional payload — so `brief` strips the full
|
|
50
|
-
// proposal blob, `normal` keeps the headline fields, and `full` projects
|
|
51
|
-
// everything for downstream automation.
|
|
52
112
|
case "distill":
|
|
53
113
|
return shapeDistillOutput(result, detail);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
case "import":
|
|
66
|
-
case "index":
|
|
67
|
-
case "info":
|
|
68
|
-
case "init":
|
|
69
|
-
case "list":
|
|
70
|
-
case "registry-add":
|
|
71
|
-
case "registry-build-index":
|
|
72
|
-
case "registry-list":
|
|
73
|
-
case "registry-remove":
|
|
74
|
-
case "remember":
|
|
75
|
-
case "remove":
|
|
76
|
-
case "save":
|
|
77
|
-
case "update":
|
|
78
|
-
case "upgrade":
|
|
79
|
-
case "vault-create":
|
|
80
|
-
case "vault-list":
|
|
81
|
-
case "vault-set":
|
|
82
|
-
case "vault-unset":
|
|
83
|
-
case "wiki-create":
|
|
84
|
-
case "wiki-ingest":
|
|
85
|
-
case "wiki-lint":
|
|
86
|
-
case "wiki-list":
|
|
87
|
-
case "wiki-pages":
|
|
88
|
-
case "wiki-register":
|
|
89
|
-
case "wiki-remove":
|
|
90
|
-
case "wiki-show":
|
|
91
|
-
case "wiki-stash":
|
|
92
|
-
case "workflow-complete":
|
|
93
|
-
case "workflow-create":
|
|
94
|
-
case "workflow-list":
|
|
95
|
-
case "workflow-next":
|
|
96
|
-
case "workflow-resume":
|
|
97
|
-
case "workflow-start":
|
|
98
|
-
case "workflow-status":
|
|
99
|
-
case "workflow-validate":
|
|
100
|
-
return result;
|
|
114
|
+
case "vault-list": {
|
|
115
|
+
const r = result;
|
|
116
|
+
const vaults = Array.isArray(r.vaults) ? r.vaults : [];
|
|
117
|
+
return {
|
|
118
|
+
...r,
|
|
119
|
+
vaults: vaults.map((v) => {
|
|
120
|
+
const { path: _path, ...rest } = v;
|
|
121
|
+
return rest;
|
|
122
|
+
}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
101
125
|
default:
|
|
102
|
-
// v1 spec §9 (output-shape registry exhaustive):
|
|
103
|
-
//
|
|
104
|
-
//
|
|
126
|
+
// v1 spec §9 (output-shape registry exhaustive): identity-passthrough
|
|
127
|
+
// commands are listed in PASSTHROUGH_COMMANDS; anything not in that set
|
|
128
|
+
// is a registration bug — fail loudly.
|
|
129
|
+
if (PASSTHROUGH_COMMANDS.has(command))
|
|
130
|
+
return result;
|
|
105
131
|
throw new Error(`output shape not registered for command: ${command}`);
|
|
106
132
|
}
|
|
107
133
|
}
|
|
134
|
+
function maybeAddSchema(base, detail, version) {
|
|
135
|
+
return detail === "full" ? { schemaVersion: version ?? 1, ...base } : base;
|
|
136
|
+
}
|
|
108
137
|
/**
|
|
109
138
|
* Shape the result of `akm reflect` / `akm propose`. On success we surface
|
|
110
139
|
* the queued proposal entry (using the standard proposal-entry shaper so
|
|
@@ -141,10 +170,7 @@ export function shapeProposalProducerOutput(result, detail) {
|
|
|
141
170
|
...(typeof result.durationMs === "number" ? { durationMs: result.durationMs } : {}),
|
|
142
171
|
proposal: shapeProposalEntry(proposal, detail === "brief" ? "normal" : detail),
|
|
143
172
|
};
|
|
144
|
-
|
|
145
|
-
return { schemaVersion: result.schemaVersion ?? 1, ...base };
|
|
146
|
-
}
|
|
147
|
-
return base;
|
|
173
|
+
return maybeAddSchema(base, detail, result.schemaVersion);
|
|
148
174
|
}
|
|
149
175
|
export function shapeProposalEntry(entry, detail) {
|
|
150
176
|
if (detail === "brief") {
|
|
@@ -173,10 +199,7 @@ export function shapeProposalListOutput(result, detail) {
|
|
|
173
199
|
totalCount: result.totalCount ?? shaped.length,
|
|
174
200
|
proposals: shaped,
|
|
175
201
|
};
|
|
176
|
-
|
|
177
|
-
return { schemaVersion: result.schemaVersion ?? 1, ...base };
|
|
178
|
-
}
|
|
179
|
-
return base;
|
|
202
|
+
return maybeAddSchema(base, detail, result.schemaVersion);
|
|
180
203
|
}
|
|
181
204
|
export function shapeProposalShowOutput(result, detail) {
|
|
182
205
|
const proposal = result.proposal ?? {};
|
|
@@ -185,10 +208,7 @@ export function shapeProposalShowOutput(result, detail) {
|
|
|
185
208
|
proposal: shapeProposalEntry(proposal, detail === "brief" ? "normal" : detail),
|
|
186
209
|
...(validation ? { validation } : {}),
|
|
187
210
|
};
|
|
188
|
-
|
|
189
|
-
return { schemaVersion: result.schemaVersion ?? 1, ...base };
|
|
190
|
-
}
|
|
191
|
-
return base;
|
|
211
|
+
return maybeAddSchema(base, detail, result.schemaVersion);
|
|
192
212
|
}
|
|
193
213
|
export function shapeProposalAcceptOutput(result, detail) {
|
|
194
214
|
const proposal = result.proposal ?? {};
|
|
@@ -199,10 +219,7 @@ export function shapeProposalAcceptOutput(result, detail) {
|
|
|
199
219
|
assetPath: result.assetPath,
|
|
200
220
|
proposal: shapeProposalEntry(proposal, detail === "brief" ? "normal" : detail),
|
|
201
221
|
};
|
|
202
|
-
|
|
203
|
-
return { schemaVersion: result.schemaVersion ?? 1, ...base };
|
|
204
|
-
}
|
|
205
|
-
return base;
|
|
222
|
+
return maybeAddSchema(base, detail, result.schemaVersion);
|
|
206
223
|
}
|
|
207
224
|
export function shapeProposalRejectOutput(result, detail) {
|
|
208
225
|
const proposal = result.proposal ?? {};
|
|
@@ -213,10 +230,7 @@ export function shapeProposalRejectOutput(result, detail) {
|
|
|
213
230
|
...(result.reason !== undefined ? { reason: result.reason } : {}),
|
|
214
231
|
proposal: shapeProposalEntry(proposal, detail === "brief" ? "normal" : detail),
|
|
215
232
|
};
|
|
216
|
-
|
|
217
|
-
return { schemaVersion: result.schemaVersion ?? 1, ...base };
|
|
218
|
-
}
|
|
219
|
-
return base;
|
|
233
|
+
return maybeAddSchema(base, detail, result.schemaVersion);
|
|
220
234
|
}
|
|
221
235
|
export function shapeDistillOutput(result, detail) {
|
|
222
236
|
const proposal = result.proposal;
|
|
@@ -233,10 +247,7 @@ export function shapeDistillOutput(result, detail) {
|
|
|
233
247
|
...(Array.isArray(result.findings) && result.findings.length > 0 ? { findings: result.findings } : {}),
|
|
234
248
|
...(proposal ? { proposal: shapeProposalEntry(proposal, detail === "summary" ? "normal" : detail) } : {}),
|
|
235
249
|
};
|
|
236
|
-
|
|
237
|
-
return { schemaVersion: result.schemaVersion ?? 1, ...base };
|
|
238
|
-
}
|
|
239
|
-
return base;
|
|
250
|
+
return maybeAddSchema(base, detail, result.schemaVersion);
|
|
240
251
|
}
|
|
241
252
|
export function shapeProposalDiffOutput(result, detail) {
|
|
242
253
|
const base = {
|
|
@@ -246,10 +257,7 @@ export function shapeProposalDiffOutput(result, detail) {
|
|
|
246
257
|
unified: result.unified,
|
|
247
258
|
...(result.targetPath !== undefined ? { targetPath: result.targetPath } : {}),
|
|
248
259
|
};
|
|
249
|
-
|
|
250
|
-
return { schemaVersion: result.schemaVersion ?? 1, ...base };
|
|
251
|
-
}
|
|
252
|
-
return base;
|
|
260
|
+
return maybeAddSchema(base, detail, result.schemaVersion);
|
|
253
261
|
}
|
|
254
262
|
export function shapeEventsOutput(result, detail) {
|
|
255
263
|
const events = Array.isArray(result.events) ? result.events : [];
|
|
@@ -268,16 +276,10 @@ export function shapeEventsOutput(result, detail) {
|
|
|
268
276
|
if (typeof result.reason === "string") {
|
|
269
277
|
base.reason = result.reason;
|
|
270
278
|
}
|
|
271
|
-
|
|
272
|
-
return { schemaVersion: result.schemaVersion ?? 1, ...base };
|
|
273
|
-
}
|
|
274
|
-
return base;
|
|
279
|
+
return maybeAddSchema(base, detail, result.schemaVersion);
|
|
275
280
|
}
|
|
276
281
|
export function shapeEventEntry(entry, detail) {
|
|
277
|
-
if (detail === "brief") {
|
|
278
|
-
return pickFields(entry, ["eventType", "ref", "ts"]);
|
|
279
|
-
}
|
|
280
|
-
if (detail === "normal" || detail === "summary") {
|
|
282
|
+
if (detail === "brief" || detail === "normal" || detail === "summary") {
|
|
281
283
|
return pickFields(entry, ["eventType", "ref", "ts"]);
|
|
282
284
|
}
|
|
283
285
|
// full / agent: project everything the reader emits.
|
|
@@ -462,6 +464,7 @@ export function shapeShowOutput(result, detail, forAgent = false) {
|
|
|
462
464
|
"steps",
|
|
463
465
|
"keys",
|
|
464
466
|
"comments",
|
|
467
|
+
"related",
|
|
465
468
|
]);
|
|
466
469
|
}
|
|
467
470
|
if (detail === "summary") {
|
|
@@ -477,9 +480,10 @@ export function shapeShowOutput(result, detail, forAgent = false) {
|
|
|
477
480
|
"origin",
|
|
478
481
|
"keys",
|
|
479
482
|
"comments",
|
|
483
|
+
"related",
|
|
480
484
|
]);
|
|
481
485
|
}
|
|
482
|
-
const
|
|
486
|
+
const baseFields = [
|
|
483
487
|
"type",
|
|
484
488
|
"name",
|
|
485
489
|
"origin",
|
|
@@ -502,11 +506,15 @@ export function shapeShowOutput(result, detail, forAgent = false) {
|
|
|
502
506
|
"activeRun",
|
|
503
507
|
"keys",
|
|
504
508
|
"comments",
|
|
509
|
+
"related",
|
|
505
510
|
// path and editable are always projected so JSON consumers can locate and
|
|
506
511
|
// edit the asset without needing --detail full (QA #7).
|
|
507
|
-
|
|
512
|
+
// Exception: vault assets omit path to avoid leaking absolute disk paths
|
|
513
|
+
// into structured JSON output (security fix M3).
|
|
514
|
+
...(result.type === "vault" ? [] : ["path"]),
|
|
508
515
|
"editable",
|
|
509
|
-
]
|
|
516
|
+
];
|
|
517
|
+
const base = pickFields(result, baseFields);
|
|
510
518
|
if (detail !== "full") {
|
|
511
519
|
return base;
|
|
512
520
|
}
|
package/dist/output/text.js
CHANGED
|
@@ -38,6 +38,13 @@ export function formatPlain(command, result, detail) {
|
|
|
38
38
|
case "index": {
|
|
39
39
|
const indexResult = result;
|
|
40
40
|
let out = `Indexed ${indexResult.totalEntries ?? 0} entries from ${indexResult.directoriesScanned ?? 0} directories (mode: ${indexResult.mode ?? "unknown"})`;
|
|
41
|
+
const graphQuality = indexResult.graphQuality;
|
|
42
|
+
if (graphQuality) {
|
|
43
|
+
const coverage = typeof graphQuality.extractionCoverage === "number"
|
|
44
|
+
? `${Math.round(graphQuality.extractionCoverage * 100)}%`
|
|
45
|
+
: "n/a";
|
|
46
|
+
out += `\nGraph quality: entities ${graphQuality.entityCount ?? 0}, relations ${graphQuality.relationCount ?? 0}, coverage ${coverage}, density ${graphQuality.density ?? 0}`;
|
|
47
|
+
}
|
|
41
48
|
const warnings = indexResult.warnings;
|
|
42
49
|
if (Array.isArray(warnings) && warnings.length > 0) {
|
|
43
50
|
out += `\nWarnings (${warnings.length}):`;
|
|
@@ -221,6 +228,31 @@ export function formatPlain(command, result, detail) {
|
|
|
221
228
|
case "distill": {
|
|
222
229
|
return formatDistillPlain(r);
|
|
223
230
|
}
|
|
231
|
+
case "graph-summary":
|
|
232
|
+
return formatGraphSummaryPlain(r);
|
|
233
|
+
case "graph-entities":
|
|
234
|
+
return formatGraphEntitiesPlain(r);
|
|
235
|
+
case "graph-relations":
|
|
236
|
+
return formatGraphRelationsPlain(r);
|
|
237
|
+
case "graph-related":
|
|
238
|
+
return formatGraphRelatedPlain(r);
|
|
239
|
+
case "graph-export":
|
|
240
|
+
return formatGraphExportPlain(r);
|
|
241
|
+
case "improve": {
|
|
242
|
+
return formatImprovePlain(r);
|
|
243
|
+
}
|
|
244
|
+
case "consolidate": {
|
|
245
|
+
return formatConsolidatePlain(r);
|
|
246
|
+
}
|
|
247
|
+
// Output shape registration for `akm agent <profile>` (#agent-dispatch).
|
|
248
|
+
// In interactive mode stdout/stderr are empty (they went to the TTY), so
|
|
249
|
+
// we print only the profile name and exit status. In captured mode we
|
|
250
|
+
// emit stdout first, then stderr, then the exit code summary.
|
|
251
|
+
case "agent-result": {
|
|
252
|
+
return formatAgentResultPlain(r);
|
|
253
|
+
}
|
|
254
|
+
case "health":
|
|
255
|
+
return formatHealthPlain(r);
|
|
224
256
|
case "info":
|
|
225
257
|
return formatInfoPlain(r);
|
|
226
258
|
case "config":
|
|
@@ -249,7 +281,7 @@ export function formatPlain(command, result, detail) {
|
|
|
249
281
|
case "vault-list":
|
|
250
282
|
return formatVaultListPlain(r);
|
|
251
283
|
case "vault-create":
|
|
252
|
-
return `Created vault ${String(r.ref ?? "?")}
|
|
284
|
+
return `Created vault ${String(r.ref ?? "?")}`;
|
|
253
285
|
case "vault-set":
|
|
254
286
|
return `Set ${String(r.key ?? "?")} in ${String(r.ref ?? "?")} (value not displayed)`;
|
|
255
287
|
case "vault-unset": {
|
|
@@ -299,6 +331,50 @@ export function formatInfoPlain(r) {
|
|
|
299
331
|
return JSON.stringify(r, null, 2);
|
|
300
332
|
return lines.join("\n");
|
|
301
333
|
}
|
|
334
|
+
export function formatHealthPlain(r) {
|
|
335
|
+
const lines = [];
|
|
336
|
+
lines.push(`health: ${String(r.status ?? "unknown")}`);
|
|
337
|
+
if (typeof r.since === "string")
|
|
338
|
+
lines.push(`since: ${r.since}`);
|
|
339
|
+
const metrics = typeof r.metrics === "object" && r.metrics !== null ? r.metrics : undefined;
|
|
340
|
+
if (metrics) {
|
|
341
|
+
lines.push("metrics:");
|
|
342
|
+
lines.push(` taskFailRate: ${String(metrics.taskFailRate ?? 0)}`);
|
|
343
|
+
lines.push(` agentFailureRate: ${String(metrics.agentFailureRate ?? 0)}`);
|
|
344
|
+
lines.push(` stuckActiveRuns: ${String(metrics.stuckActiveRuns ?? 0)}`);
|
|
345
|
+
lines.push(` logBackingRate: ${String(metrics.logBackingRate ?? 0)}`);
|
|
346
|
+
lines.push(` probeRoundTripMs: ${String(metrics.probeRoundTripMs ?? "null")}`);
|
|
347
|
+
}
|
|
348
|
+
const improve = typeof r.improve === "object" && r.improve !== null ? r.improve : undefined;
|
|
349
|
+
if (improve) {
|
|
350
|
+
const actions = typeof improve.actions === "object" && improve.actions !== null
|
|
351
|
+
? improve.actions
|
|
352
|
+
: {};
|
|
353
|
+
lines.push("improve:");
|
|
354
|
+
lines.push(` invoked: ${String(improve.invoked ?? 0)}`);
|
|
355
|
+
lines.push(` completed: ${String(improve.completed ?? 0)}`);
|
|
356
|
+
lines.push(` skipped: ${String(improve.skipped ?? 0)}`);
|
|
357
|
+
lines.push(` plannedRefs: ${String(improve.plannedRefs ?? 0)}`);
|
|
358
|
+
lines.push(` actions: reflect=${String(actions.reflect ?? 0)} distill=${String(actions.distill ?? 0)} distillSkipped=${String(actions.distillSkipped ?? 0)} memoryPrune=${String(actions.memoryPrune ?? 0)} memoryInference=${String(actions.memoryInference ?? 0)} graphExtraction=${String(actions.graphExtraction ?? 0)} error=${String(actions.error ?? 0)}`);
|
|
359
|
+
lines.push(` coverageGapCount: ${String(improve.coverageGapCount ?? 0)}`);
|
|
360
|
+
lines.push(` executionLogCandidateCount: ${String(improve.executionLogCandidateCount ?? 0)}`);
|
|
361
|
+
lines.push(` deadUrlCount: ${String(improve.deadUrlCount ?? 0)}`);
|
|
362
|
+
}
|
|
363
|
+
const sections = [
|
|
364
|
+
["hardChecks", r.hardChecks],
|
|
365
|
+
["advisories", r.advisories],
|
|
366
|
+
];
|
|
367
|
+
for (const [label, value] of sections) {
|
|
368
|
+
const checks = Array.isArray(value) ? value : [];
|
|
369
|
+
if (checks.length === 0)
|
|
370
|
+
continue;
|
|
371
|
+
lines.push(`${label}:`);
|
|
372
|
+
for (const check of checks) {
|
|
373
|
+
lines.push(` - [${String(check.status ?? "unknown")}] ${String(check.name ?? "check")}: ${String(check.message ?? "")}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return lines.join("\n");
|
|
377
|
+
}
|
|
302
378
|
export function formatConfigPlain(r) {
|
|
303
379
|
// Recursive flattener: prints `key=value` lines, and nested objects as
|
|
304
380
|
// `parent.child=value`. Arrays render as JSON for compactness.
|
|
@@ -440,6 +516,57 @@ export function formatWorkflowValidatePlain(r) {
|
|
|
440
516
|
const stepCount = typeof r.stepCount === "number" ? r.stepCount : 0;
|
|
441
517
|
return `workflow validate: ok — ${title || pathValue} (${stepCount} step(s))`;
|
|
442
518
|
}
|
|
519
|
+
export function formatGraphSummaryPlain(r) {
|
|
520
|
+
const lines = [
|
|
521
|
+
`Graph: ${String(r.graphPath ?? "?")}`,
|
|
522
|
+
`Generated: ${String(r.generatedAt ?? "?")}`,
|
|
523
|
+
`Files: ${String(r.fileCount ?? 0)} Entities: ${String(r.entityCount ?? 0)} Relations: ${String(r.relationCount ?? 0)}`,
|
|
524
|
+
];
|
|
525
|
+
const quality = r.quality;
|
|
526
|
+
if (quality) {
|
|
527
|
+
const coverage = typeof quality.extractionCoverage === "number" ? `${Math.round(quality.extractionCoverage * 100)}%` : "n/a";
|
|
528
|
+
lines.push(`Coverage: ${coverage} Density: ${String(quality.density ?? 0)}`);
|
|
529
|
+
}
|
|
530
|
+
return lines.join("\n");
|
|
531
|
+
}
|
|
532
|
+
export function formatGraphEntitiesPlain(r) {
|
|
533
|
+
const entities = Array.isArray(r.entities) ? r.entities : [];
|
|
534
|
+
if (entities.length === 0)
|
|
535
|
+
return "No entities found in graph.";
|
|
536
|
+
const lines = [`Entities (${String(r.total ?? entities.length)} total):`];
|
|
537
|
+
for (const entity of entities) {
|
|
538
|
+
lines.push(`- ${String(entity.name ?? "?")} (${String(entity.fileCount ?? 0)} files)`);
|
|
539
|
+
}
|
|
540
|
+
return lines.join("\n");
|
|
541
|
+
}
|
|
542
|
+
export function formatGraphRelationsPlain(r) {
|
|
543
|
+
const relations = Array.isArray(r.relations) ? r.relations : [];
|
|
544
|
+
if (relations.length === 0)
|
|
545
|
+
return "No relations found in graph.";
|
|
546
|
+
const lines = [`Relations (${String(r.total ?? relations.length)} total):`];
|
|
547
|
+
for (const relation of relations) {
|
|
548
|
+
const type = relation.type ? ` [${String(relation.type)}]` : "";
|
|
549
|
+
lines.push(`- ${String(relation.from ?? "?")} -> ${String(relation.to ?? "?")}${type} x${String(relation.count ?? 0)}`);
|
|
550
|
+
}
|
|
551
|
+
return lines.join("\n");
|
|
552
|
+
}
|
|
553
|
+
export function formatGraphRelatedPlain(r) {
|
|
554
|
+
const related = Array.isArray(r.related) ? r.related : [];
|
|
555
|
+
if (related.length === 0)
|
|
556
|
+
return String(r.tip ?? "No related graph neighbors were found.");
|
|
557
|
+
const lines = [`Related (${String(r.total ?? related.length)} total) for ${String(r.ref ?? "?")}:`];
|
|
558
|
+
for (const hit of related) {
|
|
559
|
+
const shared = Array.isArray(hit.sharedEntities) ? hit.sharedEntities.map(String).join(", ") : "";
|
|
560
|
+
lines.push(`- ${String(hit.type ?? "?")}: ${formatRelatedLabel(hit)}`);
|
|
561
|
+
if (shared)
|
|
562
|
+
lines.push(` shared: ${shared}`);
|
|
563
|
+
lines.push(` relationCount: ${String(hit.relationCount ?? 0)}`);
|
|
564
|
+
}
|
|
565
|
+
return lines.join("\n");
|
|
566
|
+
}
|
|
567
|
+
export function formatGraphExportPlain(r) {
|
|
568
|
+
return `Exported graph (${String(r.format ?? "json")}, ${String(r.bytes ?? 0)} bytes) to ${String(r.outPath ?? "?")}`;
|
|
569
|
+
}
|
|
443
570
|
export function formatProposalProducerPlain(command, r) {
|
|
444
571
|
if (r.ok === false) {
|
|
445
572
|
const reason = String(r.reason ?? "unknown");
|
|
@@ -464,7 +591,7 @@ export function formatProposalListPlain(r) {
|
|
|
464
591
|
const proposals = Array.isArray(r.proposals) ? r.proposals : [];
|
|
465
592
|
const total = typeof r.totalCount === "number" ? r.totalCount : proposals.length;
|
|
466
593
|
if (proposals.length === 0) {
|
|
467
|
-
return `${total} proposal(s).\nNo proposals.\nGenerate one with \`akm
|
|
594
|
+
return `${total} proposal(s).\nNo proposals.\nGenerate one with \`akm improve\`, \`akm propose <type> <name> --task ...\`, or \`akm improve <ref>\`.`;
|
|
468
595
|
}
|
|
469
596
|
const lines = [`${total} proposal(s)`, ""];
|
|
470
597
|
for (const p of proposals) {
|
|
@@ -529,7 +656,7 @@ export function formatDistillPlain(r) {
|
|
|
529
656
|
const lessonRef = String(r.lessonRef ?? "?");
|
|
530
657
|
if (outcome === "queued") {
|
|
531
658
|
const id = String(r.proposalId ?? "?");
|
|
532
|
-
return `Distilled ${inputRef} → proposal ${id} (${lessonRef}). Run \`akm proposal
|
|
659
|
+
return `Distilled ${inputRef} → proposal ${id} (${lessonRef}). Run \`akm show proposal ${id}\` to review.`;
|
|
533
660
|
}
|
|
534
661
|
if (outcome === "validation_failed") {
|
|
535
662
|
const findings = Array.isArray(r.findings) ? r.findings : [];
|
|
@@ -543,6 +670,50 @@ export function formatDistillPlain(r) {
|
|
|
543
670
|
const message = typeof r.message === "string" ? r.message : "feature disabled or LLM unavailable";
|
|
544
671
|
return `Distill skipped for ${inputRef}: ${message}`;
|
|
545
672
|
}
|
|
673
|
+
export function formatConsolidatePlain(r) {
|
|
674
|
+
const processed = typeof r.processed === "number" ? r.processed : 0;
|
|
675
|
+
const merged = typeof r.merged === "number" ? r.merged : 0;
|
|
676
|
+
const deleted = typeof r.deleted === "number" ? r.deleted : 0;
|
|
677
|
+
const promoted = Array.isArray(r.promoted) ? r.promoted.length : 0;
|
|
678
|
+
const warnings = Array.isArray(r.warnings) ? r.warnings : [];
|
|
679
|
+
const lines = [];
|
|
680
|
+
if (r.dryRun === true) {
|
|
681
|
+
lines.push(`[consolidate] dry-run: ${processed} memories found, no AI call`);
|
|
682
|
+
}
|
|
683
|
+
else if (r.previewOnly === true) {
|
|
684
|
+
lines.push(`[consolidate] preview: processed=${processed}`);
|
|
685
|
+
const planned = Array.isArray(r.planned) ? r.planned : [];
|
|
686
|
+
for (const op of planned) {
|
|
687
|
+
if (op.op === "merge") {
|
|
688
|
+
lines.push(` merge: ${String(op.primary)} ← ${String(Array.isArray(op.secondaries) ? op.secondaries.join(", ") : "")}`);
|
|
689
|
+
}
|
|
690
|
+
else if (op.op === "delete") {
|
|
691
|
+
lines.push(` delete: ${String(op.ref)} (${String(op.reason ?? "")})`);
|
|
692
|
+
}
|
|
693
|
+
else if (op.op === "promote") {
|
|
694
|
+
lines.push(` promote: ${String(op.ref)} → ${String(op.knowledgeRef)}`);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
lines.push(`[consolidate] processed=${processed} merged=${merged} deleted=${deleted} promoted=${promoted}`);
|
|
700
|
+
}
|
|
701
|
+
for (const w of warnings) {
|
|
702
|
+
lines.push(` warning: ${w}`);
|
|
703
|
+
}
|
|
704
|
+
return lines.join("\n");
|
|
705
|
+
}
|
|
706
|
+
export function formatImprovePlain(r) {
|
|
707
|
+
const scope = r.scope ?? {};
|
|
708
|
+
const mode = String(scope.mode ?? "all");
|
|
709
|
+
const value = typeof scope.value === "string" ? ` ${scope.value}` : "";
|
|
710
|
+
const plannedRefs = Array.isArray(r.plannedRefs) ? r.plannedRefs.length : 0;
|
|
711
|
+
if (r.dryRun === true) {
|
|
712
|
+
return `Improve dry-run:${mode === "all" ? " all assets" : value} (${plannedRefs} planned ref(s))`;
|
|
713
|
+
}
|
|
714
|
+
const actions = Array.isArray(r.actions) ? r.actions.length : 0;
|
|
715
|
+
return `Improve:${mode === "all" ? " all assets" : value} queued ${actions} action(s) across ${plannedRefs} ref(s)`;
|
|
716
|
+
}
|
|
546
717
|
export function formatProposalDiffPlain(r) {
|
|
547
718
|
const header = r.isNew
|
|
548
719
|
? `# proposal ${String(r.id ?? "?")} (new asset: ${String(r.ref ?? "?")})`
|
|
@@ -552,18 +723,25 @@ export function formatProposalDiffPlain(r) {
|
|
|
552
723
|
return `${header}\n(no changes)`;
|
|
553
724
|
return `${header}\n${unified}`;
|
|
554
725
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
726
|
+
/**
|
|
727
|
+
* Build the summary header line shared by formatEventsPlain and formatHistoryPlain.
|
|
728
|
+
* Accumulates ref/type/since label parts then appends the count label.
|
|
729
|
+
*/
|
|
730
|
+
function buildEventHeader(r, countLabel, totalCount) {
|
|
731
|
+
const parts = [];
|
|
558
732
|
if (typeof r.ref === "string" && r.ref)
|
|
559
|
-
|
|
733
|
+
parts.push(`ref: ${r.ref}`);
|
|
560
734
|
if (typeof r.type === "string" && r.type)
|
|
561
|
-
|
|
735
|
+
parts.push(`type: ${r.type}`);
|
|
562
736
|
if (typeof r.since === "string" && r.since)
|
|
563
|
-
|
|
737
|
+
parts.push(`since: ${r.since}`);
|
|
738
|
+
parts.push(`${totalCount} ${countLabel}`);
|
|
739
|
+
return parts.join(" ");
|
|
740
|
+
}
|
|
741
|
+
export function formatEventsPlain(r) {
|
|
742
|
+
const events = Array.isArray(r.events) ? r.events : [];
|
|
564
743
|
const totalCount = typeof r.totalCount === "number" ? r.totalCount : events.length;
|
|
565
|
-
|
|
566
|
-
const header = headerParts.join(" ");
|
|
744
|
+
const header = buildEventHeader(r, "event(s)", totalCount);
|
|
567
745
|
if (events.length === 0) {
|
|
568
746
|
return `${header}\nNo events.`;
|
|
569
747
|
}
|
|
@@ -586,13 +764,8 @@ export function formatEventLine(event) {
|
|
|
586
764
|
}
|
|
587
765
|
export function formatHistoryPlain(r) {
|
|
588
766
|
const entries = Array.isArray(r.entries) ? r.entries : [];
|
|
589
|
-
const headerParts = [];
|
|
590
|
-
if (typeof r.ref === "string" && r.ref)
|
|
591
|
-
headerParts.push(`ref: ${r.ref}`);
|
|
592
|
-
if (typeof r.since === "string" && r.since)
|
|
593
|
-
headerParts.push(`since: ${r.since}`);
|
|
594
767
|
const totalCount = typeof r.totalCount === "number" ? r.totalCount : entries.length;
|
|
595
|
-
headerParts
|
|
768
|
+
const headerParts = [buildEventHeader(r, "event(s)", totalCount)];
|
|
596
769
|
// Show active event sources so operators know which streams were consulted.
|
|
597
770
|
if (Array.isArray(r.sources) && r.sources.length > 0) {
|
|
598
771
|
headerParts.push(`sources: ${r.sources.join(", ")}`);
|
|
@@ -670,6 +843,19 @@ function formatShowPlain(r, detail) {
|
|
|
670
843
|
if (r.schemaVersion !== undefined)
|
|
671
844
|
lines.push(`schemaVersion: ${String(r.schemaVersion)}`);
|
|
672
845
|
}
|
|
846
|
+
const related = typeof r.related === "object" && r.related !== null ? r.related : undefined;
|
|
847
|
+
const relatedHits = related && Array.isArray(related.hits) ? related.hits : [];
|
|
848
|
+
if (related) {
|
|
849
|
+
lines.push("");
|
|
850
|
+
lines.push(`related: ${String(related.total ?? relatedHits.length)}`);
|
|
851
|
+
for (const hit of relatedHits) {
|
|
852
|
+
lines.push(` - ${String(hit.type ?? "?")}: ${formatRelatedLabel(hit)}`);
|
|
853
|
+
const shared = Array.isArray(hit.sharedEntities) ? hit.sharedEntities.map(String) : [];
|
|
854
|
+
if (shared.length > 0)
|
|
855
|
+
lines.push(` shared: ${shared.join(", ")}`);
|
|
856
|
+
lines.push(` relationCount: ${String(hit.relationCount ?? 0)}`);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
673
859
|
const payloads = [r.content, r.template, r.prompt].filter((value) => value != null).map(String);
|
|
674
860
|
if (Array.isArray(r.steps) && r.steps.length > 0) {
|
|
675
861
|
if (lines.length > 0)
|
|
@@ -786,6 +972,13 @@ function isCommandOutputSkill(lines) {
|
|
|
786
972
|
const yamlCount = codeLines.filter((l) => yamlPattern.test(l)).length;
|
|
787
973
|
return cliCount > yamlCount && cliCount > 0;
|
|
788
974
|
}
|
|
975
|
+
function formatRelatedLabel(hit) {
|
|
976
|
+
const ref = typeof hit.ref === "string" ? hit.ref : undefined;
|
|
977
|
+
if (ref)
|
|
978
|
+
return ref;
|
|
979
|
+
const pathValue = typeof hit.path === "string" ? hit.path : "?";
|
|
980
|
+
return pathValue.split("/").pop() ?? pathValue;
|
|
981
|
+
}
|
|
789
982
|
export function formatWorkflowListPlain(result) {
|
|
790
983
|
const runs = Array.isArray(result.runs) ? result.runs : [];
|
|
791
984
|
if (runs.length === 0) {
|
|
@@ -918,6 +1111,24 @@ export function formatSearchPlain(r, detail) {
|
|
|
918
1111
|
if (Array.isArray(hit.warnings) && hit.warnings.length > 0) {
|
|
919
1112
|
lines.push(` warnings: ${hit.warnings.join("; ")}`);
|
|
920
1113
|
}
|
|
1114
|
+
const graph = typeof hit.graph === "object" && hit.graph !== null ? hit.graph : undefined;
|
|
1115
|
+
if (graph) {
|
|
1116
|
+
const entities = Array.isArray(graph.entities) ? graph.entities : [];
|
|
1117
|
+
if (entities.length > 0) {
|
|
1118
|
+
const matched = entities
|
|
1119
|
+
.filter((entity) => String(entity.kind ?? "") === "matched")
|
|
1120
|
+
.map((entity) => String(entity.name ?? "?"));
|
|
1121
|
+
const neighbors = entities
|
|
1122
|
+
.filter((entity) => String(entity.kind ?? "") !== "matched")
|
|
1123
|
+
.map((entity) => String(entity.name ?? "?"));
|
|
1124
|
+
lines.push(` graph: ${[
|
|
1125
|
+
matched.length > 0 ? `query match=${matched.join(", ")}` : undefined,
|
|
1126
|
+
neighbors.length > 0 ? `neighbors=${neighbors.join(", ")}` : undefined,
|
|
1127
|
+
]
|
|
1128
|
+
.filter(Boolean)
|
|
1129
|
+
.join("; ")}`);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
921
1132
|
if (detail === "full") {
|
|
922
1133
|
if (hit.path)
|
|
923
1134
|
lines.push(` path: ${String(hit.path)}`);
|
|
@@ -1113,3 +1324,31 @@ export function formatCuratePlain(r, detail) {
|
|
|
1113
1324
|
lines.push("To search further: akm search '<query>'");
|
|
1114
1325
|
return lines.join("\n");
|
|
1115
1326
|
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Render the result of `akm agent <profile>`.
|
|
1329
|
+
*
|
|
1330
|
+
* Interactive mode: stdout and stderr are empty (output went to the TTY).
|
|
1331
|
+
* Print only the profile name and exit code so the caller knows the agent
|
|
1332
|
+
* finished.
|
|
1333
|
+
*
|
|
1334
|
+
* Captured mode: emit stdout (if non-empty), then stderr (if non-empty),
|
|
1335
|
+
* then the profile/exit-code summary line.
|
|
1336
|
+
*/
|
|
1337
|
+
export function formatAgentResultPlain(r) {
|
|
1338
|
+
const profile = String(r.profileName ?? "agent");
|
|
1339
|
+
const exitCode = r.exitCode !== undefined && r.exitCode !== null ? Number(r.exitCode) : 0;
|
|
1340
|
+
const stdout = typeof r.stdout === "string" ? r.stdout : "";
|
|
1341
|
+
const stderr = typeof r.stderr === "string" ? r.stderr : "";
|
|
1342
|
+
// Interactive mode: both streams are empty.
|
|
1343
|
+
if (!stdout && !stderr) {
|
|
1344
|
+
return `[${profile}] agent exited with code ${exitCode}`;
|
|
1345
|
+
}
|
|
1346
|
+
// Captured mode: stream content + summary.
|
|
1347
|
+
const parts = [];
|
|
1348
|
+
if (stdout)
|
|
1349
|
+
parts.push(stdout.trimEnd());
|
|
1350
|
+
if (stderr)
|
|
1351
|
+
parts.push(stderr.trimEnd());
|
|
1352
|
+
parts.push(`[${profile}] agent exited with code ${exitCode}`);
|
|
1353
|
+
return parts.join("\n");
|
|
1354
|
+
}
|