akm-cli 0.9.0-rc.13 → 0.9.0-rc.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +162 -23
  2. package/dist/akm-migrate +3 -1
  3. package/dist/assets/hints/cli-hints-full.md +3 -4
  4. package/dist/assets/hints/cli-hints-short.md +5 -5
  5. package/dist/assets/workflows/workflow-template.md +4 -3
  6. package/dist/cli/invocation.js +3 -2
  7. package/dist/cli/retired-commands.js +5 -0
  8. package/dist/cli/unknown-flags.js +226 -0
  9. package/dist/cli.js +19 -35
  10. package/dist/commands/agent/agent-dispatch.js +7 -3
  11. package/dist/commands/agent/contribute-cli.js +15 -3
  12. package/dist/commands/feedback-cli.js +6 -3
  13. package/dist/commands/health/checks.js +6 -3
  14. package/dist/commands/improve/collapse-detector.js +2 -3
  15. package/dist/commands/improve/reflect.js +5 -3
  16. package/dist/commands/lint/base-linter.js +4 -16
  17. package/dist/commands/lint/index.js +13 -13
  18. package/dist/commands/log.js +6 -1
  19. package/dist/commands/migration-tool.js +4 -5
  20. package/dist/commands/observability-cli.js +1 -1
  21. package/dist/commands/proposal/propose.js +5 -3
  22. package/dist/commands/proposal/repository.js +5 -5
  23. package/dist/commands/read/knowledge.js +2 -0
  24. package/dist/commands/registry-cli.js +5 -3
  25. package/dist/commands/sources/add-cli.js +6 -6
  26. package/dist/commands/sources/init.js +1 -1
  27. package/dist/commands/sources/self-update.js +30 -7
  28. package/dist/commands/sources/source-add.js +17 -2
  29. package/dist/commands/tasks/tasks.js +10 -10
  30. package/dist/commands/workflow-cli.js +133 -259
  31. package/dist/core/adapter/adapters/akm-lint.js +17 -13
  32. package/dist/core/adapter/adapters/akm-task-adapter.js +14 -11
  33. package/dist/core/asset/akm-markdown.js +41 -8
  34. package/dist/core/asset/frontmatter.js +22 -0
  35. package/dist/core/asset/resolve-ref.js +23 -3
  36. package/dist/core/common.js +45 -2
  37. package/dist/core/config/config-schema.js +8 -0
  38. package/dist/core/config/schema/experimental.js +0 -17
  39. package/dist/core/config/schema/sources-bundles.js +11 -0
  40. package/dist/core/config/schema/workflow.js +3 -3
  41. package/dist/core/errors.js +5 -1
  42. package/dist/core/logs-db.js +2 -1
  43. package/dist/core/parse.js +4 -1
  44. package/dist/core/state/migrations.js +11 -14
  45. package/dist/core/state-db.js +4 -6
  46. package/dist/core/subprocess.js +6 -4
  47. package/dist/core/type-presentation.js +1 -1
  48. package/dist/indexer/indexer.js +16 -1
  49. package/dist/indexer/search/search-source.js +1 -1
  50. package/dist/indexer/walk/matchers.js +3 -1
  51. package/dist/integrations/agent/config.js +2 -2
  52. package/dist/integrations/agent/detect.js +49 -19
  53. package/dist/integrations/agent/engine-fallback.js +76 -0
  54. package/dist/integrations/agent/profiles.js +14 -0
  55. package/dist/integrations/agent/spawn.js +1 -2
  56. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  57. package/dist/integrations/lockfile.js +11 -56
  58. package/dist/output/shapes/passthrough.js +0 -6
  59. package/dist/output/text/command-format.js +3 -3
  60. package/dist/output/text/helpers.js +1 -1
  61. package/dist/output/text/show-directives.js +4 -6
  62. package/dist/output/text/workflow-format.js +15 -181
  63. package/dist/output/text/workflow.js +1 -6
  64. package/dist/scripts/akm-migrate-node.js +58666 -0
  65. package/dist/scripts/akm-migrate.js +33976 -11498
  66. package/dist/setup/detect.js +42 -15
  67. package/dist/setup/setup.js +1 -1
  68. package/dist/sources/providers/git-stash.js +4 -2
  69. package/dist/sources/providers/website.js +5 -0
  70. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  71. package/dist/sources/snapshot-fetchers/content-extract.js +370 -0
  72. package/dist/sources/snapshot-fetchers/fetcher-util.js +40 -0
  73. package/dist/sources/snapshot-fetchers/host-guard.js +199 -0
  74. package/dist/sources/snapshot-fetchers/registry.js +10 -1
  75. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  76. package/dist/sources/snapshot-fetchers/rss.js +279 -0
  77. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  78. package/dist/sources/snapshot-fetchers/website-ingest.js +488 -257
  79. package/dist/sources/snapshot-fetchers/x.js +193 -0
  80. package/dist/storage/engines/sqlite-migrations.js +22 -107
  81. package/dist/storage/repositories/workflow-runs-repository.js +9 -23
  82. package/dist/tasks/runner.js +43 -19
  83. package/dist/tasks/schema.js +24 -1
  84. package/dist/tasks/validator.js +6 -2
  85. package/dist/workflows/exec/frozen-judge.js +28 -2
  86. package/dist/workflows/exec/native-executor.js +44 -28
  87. package/dist/workflows/exec/param-secrets.js +9 -9
  88. package/dist/workflows/exec/run-workflow.js +122 -60
  89. package/dist/workflows/exec/step-work.js +64 -175
  90. package/dist/workflows/exec/unit-dispatch.js +4 -0
  91. package/dist/workflows/ir/compile.js +2 -2
  92. package/dist/workflows/ir/freeze.js +29 -9
  93. package/dist/workflows/ir/params.js +135 -11
  94. package/dist/workflows/ir/plan-hash.js +1 -1
  95. package/dist/workflows/ir/schema.js +6 -2
  96. package/dist/workflows/renderer.js +2 -2
  97. package/dist/workflows/runtime/checkin.js +3 -3
  98. package/dist/workflows/runtime/runs.js +72 -92
  99. package/dist/workflows/runtime/unit-checkin.js +1 -1
  100. package/dist/workflows/runtime/unit-phases.js +2 -2
  101. package/dist/workflows/validate-summary.js +30 -14
  102. package/docs/migration/release-notes/0.9.0.md +31 -4
  103. package/docs/migration/v0.8-to-v0.9.md +70 -100
  104. package/docs/reference/data-and-telemetry.md +5 -5
  105. package/package.json +7 -3
  106. package/schemas/akm-config.json +24 -6
  107. package/schemas/akm-workflow.json +1 -1
  108. package/dist/workflows/cli.js +0 -33
  109. package/dist/workflows/exec/brief.js +0 -450
  110. package/dist/workflows/exec/report.js +0 -1460
  111. package/dist/workflows/exec/workflow-engine-gate.js +0 -67
@@ -188,7 +188,9 @@ function classifyBySmartMd(ctx) {
188
188
  return { type: "workflow", specificity: 19 };
189
189
  }
190
190
  if (fm) {
191
- if ("toolPolicy" in fm || "tools" in fm) {
191
+ // `tools` is the one authoring key for an agent's tool grant. Recognition
192
+ // covers only the key the renderer honors.
193
+ if ("tools" in fm) {
192
194
  return { type: "agent", specificity: 20 };
193
195
  }
194
196
  if ("agent" in fm) {
@@ -1,7 +1,7 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /** Default hard timeout for an agent CLI when neither engine nor call overrides it. */
5
- export const DEFAULT_AGENT_TIMEOUT_MS = 60_000;
4
+ /** Default agent CLI timeout; null means agents run until they finish. */
5
+ export const DEFAULT_AGENT_TIMEOUT_MS = null;
6
6
  /** Default hard timeout for direct LLM calls when no engine/use override exists. */
7
7
  export const DEFAULT_LLM_TIMEOUT_MS = 600_000;
@@ -15,26 +15,34 @@
15
15
  */
16
16
  import fs from "node:fs";
17
17
  import path from "node:path";
18
- import { getBuiltinAgentProfile, listBuiltinAgentProfiles } from "./profiles.js";
18
+ import { getBuiltinAgentProfile, listBuiltinAgentProfiles, OPENCODE_SDK_SERVER_BIN, } from "./profiles.js";
19
19
  /**
20
20
  * Default PATH lookup. Walks `process.env.PATH` and returns the first
21
21
  * existing executable file. Returns `undefined` when the bin is not on
22
22
  * PATH or the env is empty.
23
23
  *
24
- * `process.env.PATH` is split on the platform-correct delimiter; on
25
- * Windows the binary may have an executable extension, but for v1 we
26
- * keep this Unix-flavoured (Bun's primary target) and look for an exact
27
- * match.
24
+ * `process.env.PATH` is split on the platform-correct delimiter. On Windows
25
+ * agent CLIs install as extension-bearing shims (`claude.cmd`, `q.exe`), so
26
+ * each PATH entry is probed for the bare name AND for `<bin><ext>` over
27
+ * PATHEXT — an exact-match-only probe reports every agent CLI as missing
28
+ * there, which silently hides the "installed CLI agent" option during setup.
28
29
  */
29
30
  export function defaultWhich(bin, envSource = process.env) {
31
+ // Computed once per call — not per PATH entry — since it never varies.
32
+ const suffixes = executableSuffixes(envSource);
30
33
  if (!bin || bin.includes("/") || bin.includes("\\")) {
31
34
  // Absolute / relative paths: caller already specified location.
32
- try {
33
- return fs.statSync(bin).isFile() ? bin : undefined;
34
- }
35
- catch {
36
- return undefined;
35
+ for (const suffix of suffixes) {
36
+ const candidate = bin + suffix;
37
+ try {
38
+ if (fs.statSync(candidate).isFile())
39
+ return candidate;
40
+ }
41
+ catch {
42
+ /* try the next extension */
43
+ }
37
44
  }
45
+ return undefined;
38
46
  }
39
47
  const pathVar = envSource.PATH ?? envSource.Path ?? envSource.path ?? "";
40
48
  if (!pathVar)
@@ -43,18 +51,40 @@ export function defaultWhich(bin, envSource = process.env) {
43
51
  for (const dir of pathVar.split(sep)) {
44
52
  if (!dir)
45
53
  continue;
46
- const candidate = path.join(dir, bin);
47
- try {
48
- const st = fs.statSync(candidate);
49
- if (st.isFile())
50
- return candidate;
51
- }
52
- catch {
53
- /* keep walking */
54
+ const base = path.join(dir, bin);
55
+ for (const suffix of suffixes) {
56
+ const candidate = base + suffix;
57
+ try {
58
+ const st = fs.statSync(candidate);
59
+ if (st.isFile())
60
+ return candidate;
61
+ }
62
+ catch {
63
+ /* keep walking */
64
+ }
54
65
  }
55
66
  }
56
67
  return undefined;
57
68
  }
69
+ /** Windows PATHEXT default, used when the env carries no explicit list. */
70
+ const DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
71
+ /**
72
+ * Executable suffixes to try for each candidate path, bare name (`""`) first
73
+ * so POSIX resolution is byte-for-byte unchanged. Extensions are appended only
74
+ * on win32 or when the env supplies PATHEXT (which is also the seam tests use
75
+ * to cover Windows resolution from a POSIX runner).
76
+ */
77
+ function executableSuffixes(envSource) {
78
+ const pathext = envSource.PATHEXT ?? envSource.Pathext ?? envSource.pathext;
79
+ if (process.platform !== "win32" && !pathext)
80
+ return [""];
81
+ const exts = (pathext ?? DEFAULT_PATHEXT)
82
+ .split(";")
83
+ .map((ext) => ext.trim())
84
+ .filter(Boolean)
85
+ .map((ext) => (ext.startsWith(".") ? ext : `.${ext}`));
86
+ return ["", ...exts];
87
+ }
58
88
  let detectOverrides;
59
89
  /** TEST-ONLY. Swap the detection implementations; pass undefined to restore. */
60
90
  export function _setAgentDetectForTests(fakes) {
@@ -79,7 +109,7 @@ export function detectAgentCliProfiles(agent, whichFn = defaultWhich) {
79
109
  profilesByName.set(name, {
80
110
  name,
81
111
  platform: engine.platform,
82
- bin: engine.bin ?? builtin?.bin ?? (engine.platform === "opencode-sdk" ? "opencode" : engine.platform),
112
+ bin: engine.bin ?? builtin?.bin ?? (engine.platform === "opencode-sdk" ? OPENCODE_SDK_SERVER_BIN : engine.platform),
83
113
  args: engine.args ?? builtin?.args ?? [],
84
114
  stdio: "captured",
85
115
  envPassthrough: builtin?.envPassthrough ?? [],
@@ -0,0 +1,76 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { defaultWhich } from "./detect.js";
5
+ import { OPENCODE_SDK_SERVER_BIN } from "./profiles.js";
6
+ /**
7
+ * Engine name used for the synthesized entry. Matches the platform id so it
8
+ * reads correctly in `akm workflow status`, frozen plans, and error messages.
9
+ * A user-configured engine of the same name is preferred over synthesizing.
10
+ */
11
+ export const FALLBACK_ENGINE_NAME = "opencode-sdk";
12
+ /** Announcement text, surfaced once per run/dispatch. */
13
+ export const FALLBACK_ANNOUNCEMENT = `No engine is configured; falling back to \`${FALLBACK_ENGINE_NAME}\` via the ` +
14
+ `\`${OPENCODE_SDK_SERVER_BIN}\` binary on PATH — provider, model, and auth come ` +
15
+ "from opencode's own configuration. " +
16
+ "Run `akm setup`, or set `defaults.engine`, to choose explicitly.";
17
+ /**
18
+ * Failure suffix for every surface that needs an engine and found none.
19
+ * Owned HERE, beside the probe whose outcome it describes: the probe follows a
20
+ * configured `bin` when one is pinned, so a consumer restating "not on PATH"
21
+ * would misdescribe a missing absolute path.
22
+ */
23
+ export const NO_ENGINE_MESSAGE_SUFFIX = `has no selected engine, and no usable \`${OPENCODE_SDK_SERVER_BIN}\` binary was found to fall back to.`;
24
+ /**
25
+ * Guidance used when the fallback itself is unavailable.
26
+ *
27
+ * The opencode route names a BINARY install, not `npm i @opencode-ai/sdk`:
28
+ * that package is an HTTP client with no dependencies and cannot serve
29
+ * anything on its own (see the module doc).
30
+ */
31
+ export const NO_ENGINE_REMEDY = "Run `akm setup` to detect an installed agent, or set one explicitly: " +
32
+ '`akm config set engines.claude \'{"kind":"agent","platform":"claude"}\'` ' +
33
+ "then `akm config set defaults.engine claude`. " +
34
+ `Installing the \`${OPENCODE_SDK_SERVER_BIN}\` binary also works ` +
35
+ "(`npm i -g opencode-ai`) — akm falls back to it automatically.";
36
+ /**
37
+ * Return a config whose `defaults.engine` resolves, applying the implicit
38
+ * `opencode-sdk` fallback when it does not and an opencode binary is present.
39
+ *
40
+ * Pure with respect to the input: never mutates `config`. Returns the input
41
+ * object identity unchanged when no fallback is needed, so callers can cheaply
42
+ * detect the common case.
43
+ */
44
+ export function withEngineFallback(config, whichFn = defaultWhich) {
45
+ if (config.defaults?.engine)
46
+ return { config };
47
+ // An operator-configured engine of this name wins over a synthesized one —
48
+ // theirs may carry a model, an llmEngine fallback, or a pinned bin. Probe
49
+ // THAT bin rather than the bare command: a configured absolute path outside
50
+ // PATH is still a usable engine, and reporting it as missing would contradict
51
+ // the operator-configured-wins rule.
52
+ const existing = config.engines?.[FALLBACK_ENGINE_NAME];
53
+ if (!whichFn(existing?.bin ?? OPENCODE_SDK_SERVER_BIN))
54
+ return { config };
55
+ const engines = existing
56
+ ? config.engines
57
+ : { ...(config.engines ?? {}), [FALLBACK_ENGINE_NAME]: { kind: "agent", platform: "opencode-sdk" } };
58
+ return {
59
+ config: {
60
+ ...config,
61
+ engines,
62
+ defaults: { ...(config.defaults ?? {}), engine: FALLBACK_ENGINE_NAME },
63
+ },
64
+ fallbackEngineName: FALLBACK_ENGINE_NAME,
65
+ };
66
+ }
67
+ /**
68
+ * The announcement, but only when the fallback candidate is the engine that
69
+ * actually won selection. Returns `undefined` otherwise, so an explicitly
70
+ * selected engine never triggers a claim that opencode supplied the model.
71
+ */
72
+ export function fallbackAnnouncement(fallbackEngineName, selectedEngineName) {
73
+ if (!fallbackEngineName || selectedEngineName !== fallbackEngineName)
74
+ return undefined;
75
+ return FALLBACK_ANNOUNCEMENT;
76
+ }
@@ -91,6 +91,20 @@ const BUILTINS = {
91
91
  parseOutput: "text",
92
92
  },
93
93
  };
94
+ /**
95
+ * Binary the `opencode-sdk` harness needs on PATH.
96
+ *
97
+ * The embedded SDK is not self-contained, and that is the SDK's own design
98
+ * rather than a consequence of how akm drives it: `@opencode-ai/sdk` ships
99
+ * with `"dependencies": {}` and its `createOpencodeServer` is itself a
100
+ * `spawn("opencode", ["serve", ...])`. akm's runner spawns the same binary
101
+ * directly and talks HTTP to it (see `harnesses/opencode-sdk/sdk-runner.ts`),
102
+ * so the `opencode` binary gates the SDK path exactly as it gates the CLI
103
+ * path — a host with the npm package but no binary can dispatch neither.
104
+ * `opencode-sdk` deliberately has no {@link BUILTINS} entry — it dispatches
105
+ * without argv construction — so this is the one place that pairing lives.
106
+ */
107
+ export const OPENCODE_SDK_SERVER_BIN = "opencode";
94
108
  /** Names of the canonical built-in harness descriptors. Stable, sorted. */
95
109
  export const BUILTIN_AGENT_PROFILE_NAMES = Object.freeze(Object.keys(BUILTINS).sort());
96
110
  /** Returns the built-in descriptor for a canonical harness id. */
@@ -21,7 +21,6 @@ import { parseEmbeddedJsonResponse } from "../../core/parse.js";
21
21
  import { runManagedSubprocess, } from "../../core/subprocess.js";
22
22
  import { getCommandBuilder } from "./builders.js";
23
23
  import { DEFAULT_AGENT_TIMEOUT_MS } from "./config.js";
24
- const DEFAULT_TIMEOUT_MS = DEFAULT_AGENT_TIMEOUT_MS;
25
24
  /**
26
25
  * Supplement `existingPath` with well-known user binary directories when
27
26
  * running in a scheduler context (cron/launchd) where PATH is stripped.
@@ -144,7 +143,7 @@ function streamFailureMessage(profileName, stdout, stderr) {
144
143
  export async function runAgent(profile, prompt, options = {}) {
145
144
  const stdioMode = options.stdio ?? profile.stdio;
146
145
  // null = explicitly disabled (no kill timer). undefined = runtime default.
147
- const timeoutMs = options.timeoutMs !== undefined ? options.timeoutMs : DEFAULT_TIMEOUT_MS;
146
+ const timeoutMs = options.timeoutMs !== undefined ? options.timeoutMs : DEFAULT_AGENT_TIMEOUT_MS;
148
147
  const parseOutput = options.parseOutput ?? profile.parseOutput;
149
148
  const setTimeoutImpl = options.setTimeoutFn ?? setTimeout;
150
149
  const clearTimeoutImpl = options.clearTimeoutFn ?? clearTimeout;
@@ -4,9 +4,13 @@
4
4
  /**
5
5
  * OpenCode SDK agent runner (migrated from `agent/sdk-runner.ts`, #564).
6
6
  *
7
- * Uses the embedded `@opencode-ai/sdk` instead of `Bun.spawn`. Requires no
8
- * agent CLI binary to be installed. The user provides an OpenAI-compatible
9
- * endpoint (or inherits from the selected fallback LLM engine) for the SDK.
7
+ * Uses the embedded `@opencode-ai/sdk` instead of `Bun.spawn` for dispatch, so
8
+ * no agent CLI argv is ever constructed — but the SDK is not self-contained:
9
+ * {@link createManagedOpencode} spawns `opencode serve` and talks HTTP to it,
10
+ * so the `opencode` binary must be on PATH (which is why setup's
11
+ * `detectHarness` gates this harness on that probe). The user provides an
12
+ * OpenAI-compatible endpoint (or inherits from the selected fallback LLM
13
+ * engine) for the SDK.
10
14
  *
11
15
  * This is the runtime surface of the {@link OpencodeSdkHarness} (`id =
12
16
  * 'opencode-sdk'`). It is the dispatch path for SDK runner specs; it exposes
@@ -70,7 +74,12 @@
70
74
  * until the child ACTUALLY exits — and a real `opencode serve` (a live HTTP
71
75
  * server with provider children) can outlive SIGTERM long enough to hang the
72
76
  * caller indefinitely. {@link createManagedOpencode} therefore owns the spawn
73
- * (the SDK package is used only for `createOpencodeClient`):
77
+ * (the SDK package is used only for `createOpencodeClient`).
78
+ *
79
+ * Owning the spawn changes the LIFECYCLE, not the REQUIREMENT: the SDK's
80
+ * `createOpencodeServer` is itself a `spawn("opencode", ["serve", ...])`, and
81
+ * the package declares `"dependencies": {}`, so `opencode` must be on PATH
82
+ * either way. akm's version differs only in how it supervises the child:
74
83
  *
75
84
  * - after the URL handshake, the child and its stdio are `unref()`ed /
76
85
  * destroyed, so the handle can never hold akm open;
@@ -169,9 +169,7 @@ export async function upsertLockEntry(entry) {
169
169
  * migrate-apply's config lock + maintenance barrier) whose synchronous body
170
170
  * cannot await the sentinel's retry loop. No-op for an empty list.
171
171
  */
172
- export function mergeLockEntriesSync(entries) {
173
- if (entries.length === 0)
174
- return;
172
+ function readLockEntriesForMigration() {
175
173
  let existing = [];
176
174
  const lockfilePath = getLockfilePath();
177
175
  if (fs.existsSync(lockfilePath)) {
@@ -187,6 +185,16 @@ export function mergeLockEntriesSync(entries) {
187
185
  }
188
186
  existing = raw;
189
187
  }
188
+ return existing;
189
+ }
190
+ /** Validate the current lockfile before migrate-apply creates its backup or sentinel. */
191
+ export function assertMigrationLockfileReadable() {
192
+ readLockEntriesForMigration();
193
+ }
194
+ export function mergeLockEntriesSync(entries) {
195
+ const existing = readLockEntriesForMigration();
196
+ if (entries.length === 0)
197
+ return;
190
198
  // MERGE per id, never replace: the migrator's entries are sparse (id/source/
191
199
  // ref/localRoot), while an existing row may carry `resolvedVersion`,
192
200
  // `resolvedRevision`, `integrity`, `installedAt`, … from a real install.
@@ -202,59 +210,6 @@ export function mergeLockEntriesSync(entries) {
202
210
  const incomingIds = new Set(entries.map((e) => e.id));
203
211
  writeLockfileUnlocked([...existing.filter((e) => !incomingIds.has(e.id)), ...merged]);
204
212
  }
205
- /** Freeze exact old/desired lockfile bytes while the migration lifecycle lock is held. */
206
- export function planLockMergeSync(entries) {
207
- const lockfilePath = getLockfilePath();
208
- let oldBytes = null;
209
- let existing = [];
210
- try {
211
- oldBytes = fs.readFileSync(lockfilePath, "utf8");
212
- }
213
- catch (error) {
214
- if (error.code !== "ENOENT")
215
- throw error;
216
- }
217
- if (oldBytes !== null) {
218
- let parsed;
219
- try {
220
- parsed = JSON.parse(oldBytes);
221
- }
222
- catch (error) {
223
- throw new ConfigError(`Cannot plan migration lock entries from unreadable lockfile ${lockfilePath}: ${error instanceof Error ? error.message : String(error)}.`, "INVALID_CONFIG_FILE");
224
- }
225
- if (!Array.isArray(parsed) || !parsed.every(isValidLockfileEntry)) {
226
- throw new ConfigError(`Cannot plan migration lock entries from malformed lockfile ${lockfilePath}.`, "INVALID_CONFIG_FILE");
227
- }
228
- existing = parsed;
229
- }
230
- if (entries.length === 0)
231
- return { oldBytes, desiredBytes: oldBytes };
232
- const byId = new Map(existing.map((entry) => [entry.id, entry]));
233
- for (const entry of entries)
234
- byId.set(entry.id, { ...byId.get(entry.id), ...entry });
235
- return { oldBytes, desiredBytes: `${JSON.stringify([...byId.values()], null, 2)}\n` };
236
- }
237
- /** Publish a frozen migration lock generation, accepting only exact old or exact desired bytes. */
238
- export function publishPlannedLockMergeSync(plan) {
239
- const lockfilePath = getLockfilePath();
240
- let current = null;
241
- try {
242
- current = fs.readFileSync(lockfilePath, "utf8");
243
- }
244
- catch (error) {
245
- if (error.code !== "ENOENT")
246
- throw error;
247
- }
248
- if (current === plan.desiredBytes)
249
- return;
250
- if (current !== plan.oldBytes) {
251
- throw new ConfigError(`Refusing migration lockfile publication because ${lockfilePath} is a third generation, neither the journaled old nor desired bytes.`, "INVALID_CONFIG_FILE");
252
- }
253
- if (plan.desiredBytes === null)
254
- return;
255
- fs.mkdirSync(path.dirname(lockfilePath), { recursive: true });
256
- writeFileAtomic(lockfilePath, plan.desiredBytes, 0o600);
257
- }
258
213
  export async function removeLockEntry(id) {
259
214
  if (!fs.existsSync(getDataDir()))
260
215
  return;
@@ -57,16 +57,10 @@ const PASSTHROUGH_COMMANDS = [
57
57
  "update",
58
58
  "upgrade",
59
59
  "workflow-abandon",
60
- "workflow-brief",
61
- "workflow-complete",
62
- "workflow-complete-rejected",
63
60
  "workflow-create",
64
61
  "workflow-list",
65
- "workflow-next",
66
- "workflow-report",
67
62
  "workflow-resume",
68
63
  "workflow-run",
69
- "workflow-start",
70
64
  "workflow-status",
71
65
  ];
72
66
  export const passthroughShapes = PASSTHROUGH_COMMANDS.map((command) => ({
@@ -289,12 +289,12 @@ export function formatSearchPlain(r, detail) {
289
289
  if (hasWorkflowHit) {
290
290
  const workflowRef = hits.find((h) => h.type === "workflow");
291
291
  const wfRef = workflowRef && typeof workflowRef.ref === "string" ? workflowRef.ref : topRef;
292
- lines.push(`Next: akm show '${topRef}' | To start a workflow: akm workflow next '${wfRef}'`);
293
- lines.push("After running workflow next: follow each step and run `akm workflow complete <run-id> --step <step-id>` when done.");
292
+ lines.push(`Next: akm show '${topRef}' | To execute a workflow: akm workflow run '${wfRef}'`);
293
+ lines.push("Inspect the workflow before running it; `workflow run` executes and verifies its steps automatically.");
294
294
  }
295
295
  else {
296
296
  lines.push(`Next: akm show '${topRef}'`);
297
- lines.push("After reading the asset: check whether a workflow applies before editing — if so, use `akm workflow next` instead.");
297
+ lines.push("After reading the asset: check whether a workflow applies before editing — if so, inspect it and use `akm workflow run`.");
298
298
  }
299
299
  }
300
300
  }
@@ -18,4 +18,4 @@
18
18
  export { formatAddPlain, formatBundleShowPlain, formatClonePlain, formatConfigPlain, formatCuratePlain, formatEnvCreatePlain, formatEnvExportPlain, formatEnvListPlain, formatEnvRemovePlain, formatEventLine, formatEventsPlain, formatFeedbackPlain, formatImportPlain, formatIndexPlain, formatInfoPlain, formatInitPlain, formatListPlain, formatRegistryAddPlain, formatRegistryListPlain, formatRegistryRemovePlain, formatRegistrySearchPlain, formatRememberPlain, formatRemovePlain, formatSearchPlain, formatSyncPlain, formatUpdatePlain, formatUpgradePlain, } from "./command-format.js";
19
19
  export { formatGateDecisionSummary, formatProposalAcceptPlain, formatProposalDiffPlain, formatProposalDrainPlain, formatProposalListPlain, formatProposalProducerPlain, formatProposalRejectPlain, formatProposalShowPlain, } from "./proposal-format.js";
20
20
  export { formatShowPlain } from "./show-format.js";
21
- export { formatWorkflowBriefPlain, formatWorkflowCompleteRejectedPlain, formatWorkflowCreatePlain, formatWorkflowListPlain, formatWorkflowNextPlain, formatWorkflowResumePlain, formatWorkflowRunPlain, formatWorkflowStatusPlain, } from "./workflow-format.js";
21
+ export { formatWorkflowCreatePlain, formatWorkflowListPlain, formatWorkflowResumePlain, formatWorkflowRunPlain, formatWorkflowStatusPlain, } from "./workflow-format.js";
@@ -45,9 +45,7 @@ export function appendShowDirectives(lines, r) {
45
45
  if (assetType === "skill" || assetType === "knowledge") {
46
46
  const activeRun = r.activeRun;
47
47
  if (activeRun) {
48
- // Active workflow: redirect agent to workflow commands instead of direct apply
49
- lines.unshift(` akm workflow complete '${activeRun.runId}'${activeRun.stepId ? ` --step '${activeRun.stepId}'` : ""}`);
50
- lines.unshift("Read this schema, then follow your workflow step's instructions to edit the workspace file. When done, mark the step complete:");
48
+ lines.unshift("Read this reference, follow the active unit's instructions, and return the result to the workflow runner. Step completion is automatic.");
51
49
  lines.unshift(`WORKFLOW ACTIVE — schema shown as reference (run: ${activeRun.runId})`);
52
50
  lines.unshift("---");
53
51
  lines.unshift("");
@@ -88,14 +86,14 @@ export function appendShowDirectives(lines, r) {
88
86
  const insertIdx = separatorIdx >= 0 ? separatorIdx + 1 : r.type || r.name ? 1 : 0;
89
87
  const actionDirective = [
90
88
  `ACTION REQUIRED: Do not execute steps manually from this output.`,
91
- `Run \`akm workflow next '${workflowRef}'\` to get your current step with exact instructions.`,
89
+ `Run \`akm workflow run '${workflowRef}'\` to execute the workflow through its declared gates.`,
92
90
  "---",
93
91
  ];
94
92
  lines.splice(insertIdx, 0, "", ...actionDirective);
95
93
  lines.push("");
96
94
  lines.push("---");
97
- lines.push(`NEXT STEP: Run \`akm workflow next '${workflowRef}'\` to see the current workflow step.`);
98
- lines.push("Do not edit workspace files before completing each step with `akm workflow complete`.");
95
+ lines.push(`NEXT STEP: Run \`akm workflow run '${workflowRef}'\` to execute the workflow.`);
96
+ lines.push("Inspect the workflow first; the runner dispatches and completes each step automatically.");
99
97
  }
100
98
  }
101
99
  /**