akm-cli 0.9.31 → 0.10.26101002-alpha
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/CHANGELOG.md +265 -0
- package/STABILITY.md +79 -53
- package/dist/api-node.mjs +20 -0
- package/dist/api.d.ts +23 -0
- package/dist/api.js +45 -0
- package/dist/assets/hints/cli-hints-full.md +13 -27
- package/dist/assets/hints/cli-hints-short.md +1 -1
- package/dist/assets/improve-strategies/default.json +3 -3
- package/dist/assets/prompts/consolidate-pair.md +9 -7
- package/dist/cli/shared.js +31 -48
- package/dist/cli/unknown-flags.js +6 -43
- package/dist/cli.js +8 -34
- package/dist/commands/agent/contribute-cli.js +1 -1
- package/dist/commands/completions.js +1 -2
- package/dist/commands/config-cli.js +5 -28
- package/dist/commands/env/env-cli.js +5 -5
- package/dist/commands/env/secret-cli.js +2 -2
- package/dist/commands/health/checks.js +41 -16
- package/dist/commands/health/plugin-staleness.js +52 -22
- package/dist/commands/improve/autonomy-gate.js +5 -3
- package/dist/commands/improve/consolidate/pair-pass.js +7 -1
- package/dist/commands/improve/consolidate.js +6 -35
- package/dist/commands/improve/distill/lesson-update.js +91 -0
- package/dist/commands/improve/distill-guards.js +1 -43
- package/dist/commands/improve/distill.js +140 -20
- package/dist/commands/improve/eligibility.js +14 -1
- package/dist/commands/improve/extract-cli.js +1 -1
- package/dist/commands/improve/improve-cli.js +87 -102
- package/dist/commands/improve/improve-strategies.js +8 -12
- package/dist/commands/improve/improve.js +2 -2
- package/dist/commands/improve/loop-stages.js +0 -1
- package/dist/commands/improve/planner.js +0 -1
- package/dist/commands/improve/preparation.js +40 -99
- package/dist/commands/improve/proactive-maintenance.js +2 -2
- package/dist/commands/improve/reflect-noise.js +3 -55
- package/dist/commands/improve/reflect.js +3 -8
- package/dist/commands/improve/retrieval-scope.js +2 -2
- package/dist/commands/improve/salience.js +2 -9
- package/dist/commands/improve/stage.js +14 -9
- package/dist/commands/lint/index.js +5 -20
- package/dist/commands/observability-cli.js +3 -9
- package/dist/commands/proposal/drain.js +39 -6
- package/dist/commands/proposal/proposal-cli.js +11 -31
- package/dist/commands/proposal/repository.js +3 -3
- package/dist/commands/read/knowledge.js +3 -3
- package/dist/commands/read/remember-cli.js +0 -13
- package/dist/commands/read/search-cli.js +29 -97
- package/dist/commands/read/search.js +0 -11
- package/dist/commands/read/show.js +0 -19
- package/dist/commands/sources/plugin-upgrade.js +164 -101
- package/dist/commands/sources/sources-cli.js +1 -15
- package/dist/commands/sources/stash-cli.js +4 -12
- package/dist/commands/tasks/tasks-cli.js +4 -115
- package/dist/commands/workflow/plan.js +11 -33
- package/dist/commands/workflow-cli.js +2 -7
- package/dist/core/adapter/adapter-ids.js +1 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
- package/dist/core/adapter/adapters/akm-adapter.js +9 -48
- package/dist/core/adapter/adapters/akm-lint.js +10 -26
- package/dist/core/adapter/adapters/akm-metadata.js +2 -72
- package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
- package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
- package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
- package/dist/core/adapter/recognize-match.js +3 -4
- package/dist/core/common.js +18 -2
- package/dist/core/config/config-schema.js +12 -15
- package/dist/core/config/config-types.js +6 -15
- package/dist/core/config/config-walker.js +6 -23
- package/dist/core/config/config.js +17 -80
- package/dist/core/config/engine-semantics.js +7 -3
- package/dist/core/config/schema/engines.js +7 -7
- package/dist/core/config/schema/improve-processes.js +8 -61
- package/dist/core/config/schema/improve.js +2 -21
- package/dist/core/config/schema/index-config.js +6 -39
- package/dist/core/env-secret-ref.js +2 -45
- package/dist/core/errors.js +1 -2
- package/dist/core/extra-params.js +2 -86
- package/dist/core/improve-result.js +11 -1
- package/dist/core/mutation-target.js +2 -2
- package/dist/core/recognition-util.js +4 -5
- package/dist/core/warn.js +21 -4
- package/dist/core/write-source.js +3 -3
- package/dist/execution/target-ref.js +1 -2
- package/dist/indexer/search/db-search.js +4 -19
- package/dist/indexer/search/fts-query.js +0 -19
- package/dist/integrations/agent/engine-resolution.js +5 -2
- package/dist/integrations/agent/index.js +1 -1
- package/dist/integrations/agent/profiles.js +41 -98
- package/dist/integrations/agent/request-lowering.js +3 -3
- package/dist/integrations/harnesses/aider/index.js +5 -0
- package/dist/integrations/harnesses/amazonq/index.js +1 -0
- package/dist/integrations/harnesses/claude/config-import.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +5 -0
- package/dist/integrations/harnesses/codex/index.js +1 -0
- package/dist/integrations/harnesses/copilot/index.js +1 -0
- package/dist/integrations/harnesses/gemini/index.js +1 -0
- package/dist/integrations/harnesses/index.js +18 -39
- package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
- package/dist/integrations/harnesses/opencode/config-import.js +121 -52
- package/dist/integrations/harnesses/opencode/index.js +22 -0
- package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
- package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
- package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
- package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
- package/dist/integrations/harnesses/opencode/session-log.js +104 -115
- package/dist/integrations/harnesses/opencode/version.js +98 -0
- package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
- package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
- package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +228 -0
- package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
- package/dist/integrations/harnesses/openhands/index.js +1 -0
- package/dist/integrations/harnesses/pi/index.js +1 -0
- package/dist/integrations/harnesses/shared.js +2 -0
- package/dist/integrations/harnesses/types.js +2 -0
- package/dist/llm/client.js +41 -11
- package/dist/output/cli-hints.js +1 -1
- package/dist/output/context.js +10 -19
- package/dist/output/format-exempt.js +5 -8
- package/dist/output/shapes/helpers.js +1 -22
- package/dist/output/shapes/passthrough.js +0 -1
- package/dist/output/shapes.js +3 -31
- package/dist/output/stdout.js +5 -1
- package/dist/output/text/command-format.js +6 -1
- package/dist/output/text/workflow-format.js +4 -22
- package/dist/scripts/akm-migrate-node.js +13049 -14013
- package/dist/scripts/akm-migrate.js +16316 -17276
- package/dist/setup/detect.js +10 -4
- package/dist/setup/harness-config-import.js +5 -14
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +12 -13
- package/dist/storage/repositories/task-history-repository.js +0 -5
- package/dist/storage/repositories/workflow-runs-repository.js +5 -15
- package/dist/tasks/embedded.js +2 -1
- package/dist/tasks/run/task-history.js +0 -2
- package/dist/tasks/schedule-gates.js +39 -0
- package/dist/tasks/scheduler-binding.js +0 -16
- package/dist/tasks/scheduler-sync.js +3 -97
- package/dist/tasks/source/bounded-document.js +1 -1
- package/dist/tasks/source/task-source-v4.js +2 -12
- package/dist/tasks/task-run-reserved-flags.js +2 -31
- package/dist/workflows/authoring/authoring.js +6 -6
- package/dist/workflows/compile.js +8 -65
- package/dist/workflows/exec/child-workflow.js +22 -3
- package/dist/workflows/exec/native-executor.js +12 -147
- package/dist/workflows/exec/run-workflow.js +31 -103
- package/dist/workflows/exec/step-work.js +9 -178
- package/dist/workflows/exec/unit-dispatch.js +0 -10
- package/dist/workflows/freeze/environment.js +3 -44
- package/dist/workflows/freeze/freeze.js +8 -41
- package/dist/workflows/freeze/targets/child-workflow.js +27 -63
- package/dist/workflows/freeze/targets/command.js +2 -16
- package/dist/workflows/freeze/targets/shell.js +0 -2
- package/dist/workflows/freeze/task-bindings.js +2 -28
- package/dist/workflows/ir/params.js +12 -2
- package/dist/workflows/parser.js +82 -232
- package/dist/workflows/plan.js +1 -1
- package/dist/workflows/renderer.js +10 -19
- package/dist/workflows/runtime/run-plan.js +11 -53
- package/dist/workflows/runtime/runs.js +4 -27
- package/dist/workflows/source-files.js +12 -16
- package/dist/workflows/source-semantics.js +1 -93
- package/docs/README.md +1 -1
- package/docs/agents/README.md +3 -3
- package/docs/agents/agent-install.md +1 -1
- package/docs/architecture/adapters.md +17 -31
- package/docs/architecture/architecture.md +4 -5
- package/docs/architecture/improvement.md +52 -21
- package/docs/architecture/internals/improve-workflow.md +15 -8
- package/docs/architecture/internals/indexing.md +1 -1
- package/docs/architecture/internals/search.md +1 -2
- package/docs/architecture/internals/storage-locations.md +5 -5
- package/docs/architecture/persisted-data-compat.md +13 -4
- package/docs/architecture/specs/0.9.0-decisions.md +6 -2
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
- package/docs/architecture/specs/workflow-format-unification.md +1 -6
- package/docs/architecture/testing/manual-testing-checklist.md +20 -29
- package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
- package/docs/architecture/testing/testing-workflow.md +1 -1
- package/docs/architecture/workflow-engine.md +5 -56
- package/docs/guides/author-workflows.md +39 -69
- package/docs/guides/capture-knowledge.md +1 -1
- package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
- package/docs/guides/discover-and-load.md +4 -4
- package/docs/guides/getting-started.md +1 -1
- package/docs/guides/improve-the-library.md +7 -7
- package/docs/guides/recipes/headless-install.md +2 -2
- package/docs/guides/run-workflows.md +1 -1
- package/docs/guides/use-with-any-agent.md +49 -4
- package/docs/integration/bundling-akm.md +7 -6
- package/docs/maintainers/curate-workmap.md +1 -2
- package/docs/maintainers/release-checklist.md +30 -1
- package/docs/posts/akm-command-tour.md +3 -3
- package/docs/product-surface.md +1 -1
- package/docs/reference/README.md +1 -0
- package/docs/reference/api.md +65 -0
- package/docs/reference/asset-types.md +3 -5
- package/docs/reference/bundle-types.md +2 -4
- package/docs/reference/cli.md +129 -171
- package/docs/reference/configuration.md +84 -19
- package/docs/reference/data-and-telemetry.md +1 -1
- package/docs/reference/env-and-secrets.md +2 -2
- package/docs/reference/supported-formats.md +3 -4
- package/docs/reference/tasks.md +12 -19
- package/docs/reference/website-sources.md +1 -1
- package/docs/reference/workflow-schema.md +89 -317
- package/docs/reference/workflows.md +20 -48
- package/package.json +12 -2
- package/schemas/akm-config.json +2 -243
- package/schemas/akm-task.json +2 -3
- package/schemas/akm-workflow.json +47 -84
- package/dist/assets/improve-strategies/catchup.json +0 -41
- package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
- package/dist/assets/improve-strategies/quick.json +0 -17
- package/dist/assets/improve-strategies/reflect-distill.json +0 -29
- package/dist/assets/improve-strategies/thorough.json +0 -49
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
- package/dist/cli/retired-commands.js +0 -118
- package/dist/commands/tasks/validate.js +0 -176
- package/dist/core/config/legacy-source-shape-shim.js +0 -117
- package/dist/integrations/harnesses/ids.js +0 -28
- package/dist/tasks/source/task-source-v3-frozen.js +0 -424
- package/dist/tasks/source/task-to-v4.js +0 -863
- package/dist/workflows/exec/worktree.js +0 -372
- package/dist/workflows/freeze/child-output-references.js +0 -83
- package/dist/workflows/freeze/identity.js +0 -22
- package/dist/workflows/freeze/targets/script.js +0 -50
- package/dist/workflows/freeze/targets/task.js +0 -124
- package/dist/workflows/github-yaml.js +0 -506
- package/dist/workflows/runtime/run-outputs.js +0 -80
package/dist/setup/detect.js
CHANGED
|
@@ -14,6 +14,7 @@ import { runManagedSubprocess } from "../core/subprocess.js";
|
|
|
14
14
|
import { defaultWhich } from "../integrations/agent/detect.js";
|
|
15
15
|
import { getBuiltinAgentProfile, OPENCODE_SDK_SERVER_BIN } from "../integrations/agent/profiles.js";
|
|
16
16
|
import { AGENT_DISPATCH_HARNESSES, SESSION_LOG_HARNESSES } from "../integrations/harnesses/index.js";
|
|
17
|
+
import { detectOpencodeMajor, resolveOpencodeBin } from "../integrations/harnesses/opencode/version.js";
|
|
17
18
|
import { detectHarnessConfigs } from "./harness-config-import.js";
|
|
18
19
|
let detectOverrides;
|
|
19
20
|
/** TEST-ONLY. Swap the network/host probes; pass undefined to restore. */
|
|
@@ -342,7 +343,8 @@ function findGitRepoRoot(start) {
|
|
|
342
343
|
* 3. none.
|
|
343
344
|
*
|
|
344
345
|
* The SDK import resolving is NOT evidence the SDK path can run: `@opencode-ai/sdk`
|
|
345
|
-
*
|
|
346
|
+
* (OpenCode 1) and `@opencode/client` (OpenCode 2; the binary's detected major
|
|
347
|
+
* selects) are hard dependencies of akm-cli itself, so `import()` always succeeds and an
|
|
346
348
|
* import-only check would report `opencode-sdk` on every machine — including
|
|
347
349
|
* ones where the first agentic command then dies with spawn ENOENT. The SDK
|
|
348
350
|
* runner spawns `opencode serve` (see `harnesses/opencode-sdk/sdk-runner.ts`),
|
|
@@ -355,10 +357,14 @@ function findGitRepoRoot(start) {
|
|
|
355
357
|
export async function detectHarness(whichFn = defaultWhich) {
|
|
356
358
|
// Probed once; reused below so the `opencode` CLI fallback doesn't repeat
|
|
357
359
|
// the identical full-PATH walk this just performed.
|
|
358
|
-
const opencodePath = whichFn(
|
|
360
|
+
const opencodePath = whichFn(resolveOpencodeBin(undefined, whichFn));
|
|
359
361
|
if (opencodePath) {
|
|
360
362
|
try {
|
|
361
|
-
|
|
363
|
+
// The client package of the major the binary reports.
|
|
364
|
+
if (detectOpencodeMajor(opencodePath).major === 1)
|
|
365
|
+
await import("@opencode-ai/sdk");
|
|
366
|
+
else
|
|
367
|
+
await import("@opencode/client");
|
|
362
368
|
return "opencode-sdk";
|
|
363
369
|
}
|
|
364
370
|
catch {
|
|
@@ -368,7 +374,7 @@ export async function detectHarness(whichFn = defaultWhich) {
|
|
|
368
374
|
}
|
|
369
375
|
for (const harness of AGENT_DISPATCH_HARNESSES) {
|
|
370
376
|
// The SDK harness dispatches without a CLI profile and was decided above;
|
|
371
|
-
// an explicit skip so a future
|
|
377
|
+
// an explicit skip so a future `profile` for it can't silently
|
|
372
378
|
// reintroduce it here after the import guard already rejected it.
|
|
373
379
|
if (harness.id === "opencode-sdk")
|
|
374
380
|
continue;
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* no network) and, if so, reads its config to extract LLM connection details.
|
|
9
9
|
* API key VALUES are never stored — only the env var names that hold them.
|
|
10
10
|
*
|
|
11
|
-
* To add a new harness:
|
|
12
|
-
*
|
|
11
|
+
* To add a new harness importer: declare `configImporter` on the harness
|
|
12
|
+
* descriptor (`integrations/harnesses/<id>/index.ts`).
|
|
13
13
|
*
|
|
14
14
|
* NOTE: The `detect()` method in each importer overlaps intentionally with
|
|
15
15
|
* `detectAgentPlatforms()` in `detect.ts`. That function scans for harness
|
|
@@ -17,18 +17,9 @@
|
|
|
17
17
|
* further by reading and parsing the harness config. They serve different
|
|
18
18
|
* purposes and should not be deduplicated.
|
|
19
19
|
*/
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// (`harnesses/claude/config-import.ts`) and the OpenCode importer in #564
|
|
24
|
-
// (`harnesses/opencode/config-import.ts`). Both are imported back into
|
|
25
|
-
// HARNESS_CONFIG_IMPORTERS below so detection order is unchanged.
|
|
26
|
-
// ── Registry ─────────────────────────────────────────────────────────────────
|
|
27
|
-
/**
|
|
28
|
-
* Registry of all supported harness config importers.
|
|
29
|
-
* To add a new harness: implement {@link HarnessConfigImporter} and append here.
|
|
30
|
-
*/
|
|
31
|
-
export const HARNESS_CONFIG_IMPORTERS = [claudeCodeImporter, openCodeImporter];
|
|
20
|
+
import { HARNESS_REGISTRY } from "../integrations/harnesses/index.js";
|
|
21
|
+
/** Importers declared by the registered harnesses, in registry order. */
|
|
22
|
+
export const HARNESS_CONFIG_IMPORTERS = HARNESS_REGISTRY.flatMap((h) => h.configImporter ? [h.configImporter] : []);
|
|
32
23
|
/**
|
|
33
24
|
* Run all importers whose `detect()` returns `true` and collect their configs.
|
|
34
25
|
*
|
package/dist/setup/setup.js
CHANGED
|
@@ -625,7 +625,7 @@ export async function runSetupWizard(opts) {
|
|
|
625
625
|
}
|
|
626
626
|
}
|
|
627
627
|
p.outro(`Configuration saved to ${configPath}\n` +
|
|
628
|
-
'Next: `akm bundle add <source>`, `akm index`, `akm search "<query>"`, `akm
|
|
628
|
+
'Next: `akm bundle add <source>`, `akm index`, `akm search "<query>"`, `akm hints`');
|
|
629
629
|
}
|
|
630
630
|
// ── Non-interactive / scripting entry points ─────────────────────────────────
|
|
631
631
|
/**
|
|
@@ -19,13 +19,12 @@ import { parseSchedule } from "../../tasks/schedule.js";
|
|
|
19
19
|
import { parseTaskSource } from "../../tasks/source/parse-task-source.js";
|
|
20
20
|
import { prompt } from "../prompt.js";
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* "battery heuristic" {@link detectServerDefault} preserved from that path.
|
|
22
|
+
* The nightly improve template (`core/improve`). This id is preselected in the
|
|
23
|
+
* review multiselect on a detected server install — the "battery heuristic"
|
|
24
|
+
* {@link detectServerDefault} preserved from the retired
|
|
25
|
+
* `registerDefaultTasks`/`akm tasks init` path (0.9, S6).
|
|
27
26
|
*/
|
|
28
|
-
const SERVER_SUGGESTED_TASK_ID = "
|
|
27
|
+
const SERVER_SUGGESTED_TASK_ID = "improve";
|
|
29
28
|
let scheduledTasksEnvOverrides;
|
|
30
29
|
/** TEST-ONLY. Swap the CI/server-detection probes; pass undefined to restore. */
|
|
31
30
|
export function _setScheduledTasksEnvForTests(fakes) {
|
|
@@ -66,7 +65,7 @@ export function detectServerDefault() {
|
|
|
66
65
|
}
|
|
67
66
|
catch {
|
|
68
67
|
// If we cannot read power-supply info, prefer the safe server default on
|
|
69
|
-
// Linux (the nightly
|
|
68
|
+
// Linux (the nightly improve run is low-impact and re-runnable).
|
|
70
69
|
return true;
|
|
71
70
|
}
|
|
72
71
|
}
|
|
@@ -200,11 +199,11 @@ export async function stepScheduledTasks(deps = DEFAULT_SCHEDULED_TASKS_DEPS, op
|
|
|
200
199
|
p.log.info("Non-interactive setup leaves task files and scheduler state unchanged. Run `akm setup` interactively to review tasks.");
|
|
201
200
|
return;
|
|
202
201
|
}
|
|
203
|
-
// ALL templates are offered, including ships-disabled ones
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
202
|
+
// ALL templates are offered, including ships-disabled ones: an unselected
|
|
203
|
+
// template is still PREPARED, so its YAML exists for `akm task run <id>`
|
|
204
|
+
// while its ref remains absent from local scheduler activation. Filtering on
|
|
205
|
+
// `task.enabled` here would make ships-disabled templates invisible and
|
|
206
|
+
// unpreparable.
|
|
208
207
|
const embedded = listEmbeddedTasks();
|
|
209
208
|
if (embedded.length === 0)
|
|
210
209
|
return;
|
|
@@ -228,7 +227,7 @@ export async function stepScheduledTasks(deps = DEFAULT_SCHEDULED_TASKS_DEPS, op
|
|
|
228
227
|
byId.set(normaliseTaskIdForMatch(task.id), task);
|
|
229
228
|
const preChecked = embedded.filter((task) => byId.get(task.id)?.enabled === true).map((task) => task.id);
|
|
230
229
|
// Battery heuristic preserved from the retired `registerDefaultTasks` path
|
|
231
|
-
// (S6): suggest the nightly
|
|
230
|
+
// (S6): suggest the nightly improve task on a detected server install, same
|
|
232
231
|
// as every other embedded template, still gated behind the confirmation
|
|
233
232
|
// below.
|
|
234
233
|
if (!byId.has(SERVER_SUGGESTED_TASK_ID) &&
|
|
@@ -67,10 +67,6 @@ export function decodeTaskHistoryMetadata(input) {
|
|
|
67
67
|
// A malformed `engine` (wrong type) is dropped like any other unrecognized
|
|
68
68
|
// field rather than rejecting the whole row over a non-essential value.
|
|
69
69
|
const engine = parsed.engine === null || typeof parsed.engine === "string" ? parsed.engine : undefined;
|
|
70
|
-
if (parsed.targetVocab !== undefined && parsed.targetVocab !== 2) {
|
|
71
|
-
warnOnce(`task-history-target-vocab:${String(parsed.targetVocab)}`, `task_history row has targetVocab ${String(parsed.targetVocab)}, newer than this akm's 2 — falling back to ` +
|
|
72
|
-
"the legacy target_kind mapping rather than rejecting the row.");
|
|
73
|
-
}
|
|
74
70
|
validateDetail(detail);
|
|
75
71
|
const cleanDetail = detail
|
|
76
72
|
? {
|
|
@@ -85,7 +81,6 @@ export function decodeTaskHistoryMetadata(input) {
|
|
|
85
81
|
durationMs: parsed.durationMs,
|
|
86
82
|
detail: cleanDetail,
|
|
87
83
|
...(engine !== undefined ? { engine } : {}),
|
|
88
|
-
...(parsed.targetVocab === 2 ? { targetVocab: 2 } : {}),
|
|
89
84
|
};
|
|
90
85
|
}
|
|
91
86
|
/**
|
|
@@ -353,15 +353,6 @@ export class WorkflowRunsRepository {
|
|
|
353
353
|
return db.prepare("SELECT * FROM workflow_runs WHERE id = ?").get(input.run.id);
|
|
354
354
|
});
|
|
355
355
|
}
|
|
356
|
-
/**
|
|
357
|
-
* Persist a run's resolved declared `outputs:` (migration 024, P3b §4.3).
|
|
358
|
-
* Called from INSIDE `completeWorkflowStep`'s own write transaction — this
|
|
359
|
-
* method opens none of its own, matching `updateStepCompletion` /
|
|
360
|
-
* `updateRunState` immediately above.
|
|
361
|
-
*/
|
|
362
|
-
setRunOutputs(runId, outputsJson) {
|
|
363
|
-
this.db.prepare("UPDATE workflow_runs SET outputs_json = ? WHERE id = ?").run(outputsJson, runId);
|
|
364
|
-
}
|
|
365
356
|
/** Every child run published under `parentRunId`, oldest first. `[]` when none. */
|
|
366
357
|
childRunsOf(parentRunId) {
|
|
367
358
|
return this.db
|
|
@@ -432,13 +423,13 @@ export class WorkflowRunsRepository {
|
|
|
432
423
|
const dispatchId = randomUUID();
|
|
433
424
|
db.prepare(`INSERT INTO workflow_run_unit_attempts (
|
|
434
425
|
run_id, unit_id, attempt, dispatch_id, step_id, node_id, phase,
|
|
435
|
-
runner, engine, model, input_hash, status,
|
|
426
|
+
runner, engine, model, input_hash, status, started_at,
|
|
436
427
|
claim_holder, claim_expires_at
|
|
437
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?,
|
|
428
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?)`).run(input.runId, input.unitId, attemptNumber, dispatchId, input.stepId, input.nodeId, input.phase, input.runner, input.engine, input.model, input.inputHash, input.now, holder, input.now);
|
|
438
429
|
db.prepare(`INSERT INTO workflow_run_units (
|
|
439
430
|
run_id, unit_id, step_id, node_id, parent_unit_id, phase, runner, engine, model,
|
|
440
|
-
status, input_hash,
|
|
441
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?, ?,
|
|
431
|
+
status, input_hash, started_at, claim_holder, claim_expires_at, attempts
|
|
432
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?, ?, ?)
|
|
442
433
|
ON CONFLICT(run_id, unit_id) DO UPDATE SET
|
|
443
434
|
step_id = excluded.step_id,
|
|
444
435
|
node_id = excluded.node_id,
|
|
@@ -449,7 +440,6 @@ export class WorkflowRunsRepository {
|
|
|
449
440
|
model = excluded.model,
|
|
450
441
|
status = 'running',
|
|
451
442
|
input_hash = excluded.input_hash,
|
|
452
|
-
worktree_path = excluded.worktree_path,
|
|
453
443
|
started_at = excluded.started_at,
|
|
454
444
|
claim_holder = excluded.claim_holder,
|
|
455
445
|
claim_expires_at = excluded.claim_expires_at,
|
|
@@ -458,7 +448,7 @@ export class WorkflowRunsRepository {
|
|
|
458
448
|
failure_reason = NULL,
|
|
459
449
|
session_id = NULL,
|
|
460
450
|
finished_at = NULL,
|
|
461
|
-
attempts = excluded.attempts`).run(input.runId, input.unitId, input.stepId, input.nodeId, input.parentUnitId ?? null, input.phase, input.runner, input.engine, input.model, input.inputHash, input.
|
|
451
|
+
attempts = excluded.attempts`).run(input.runId, input.unitId, input.stepId, input.nodeId, input.parentUnitId ?? null, input.phase, input.runner, input.engine, input.model, input.inputHash, input.now, holder, input.now, attemptNumber);
|
|
462
452
|
insertEventStrict(db, {
|
|
463
453
|
eventType: "workflow_unit_started",
|
|
464
454
|
ts: input.now,
|
package/dist/tasks/embedded.js
CHANGED
|
@@ -25,7 +25,8 @@ import { getDirname } from "../runtime.js";
|
|
|
25
25
|
import { parseTaskSource } from "./source/parse-task-source.js";
|
|
26
26
|
/** Directory holding the bundled task template categories. */
|
|
27
27
|
const TASKS_ASSETS_DIR = path.join(getDirname(import.meta.url), "../assets/tasks");
|
|
28
|
-
|
|
28
|
+
/** Templates setup offers but ships disabled (`category/id`). None ship disabled since catchup was removed (#1130). */
|
|
29
|
+
const DEFAULT_DISABLED_TASKS = new Set();
|
|
29
30
|
/**
|
|
30
31
|
* Enumerate the embedded task templates from every category subdirectory of
|
|
31
32
|
* the bundled assets directory. Sorted by category then id for deterministic
|
|
@@ -49,8 +49,6 @@ export function appendHistory(result, historyReserved = false) {
|
|
|
49
49
|
metadataVersion: 2,
|
|
50
50
|
durationMs: result.durationMs,
|
|
51
51
|
detail: result.detail ?? null,
|
|
52
|
-
// D8 (spec §5.3): every NEW row carries the vocabulary marker.
|
|
53
|
-
targetVocab: 2,
|
|
54
52
|
...(result.target.kind === "command" ? { engine: result.target.engine } : {}),
|
|
55
53
|
}),
|
|
56
54
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* The two per-source schedule gates `akm task sync` runs before it installs a
|
|
6
|
+
* schedule, kept in a leaf module so `akm lint` can run them without importing
|
|
7
|
+
* the scheduler's task-preparation graph (which reaches the adapter registry
|
|
8
|
+
* that lint itself is part of).
|
|
9
|
+
*/
|
|
10
|
+
import { UsageError } from "../core/errors.js";
|
|
11
|
+
import { applyInputDefaults, validateInputs } from "../execution/input-contract.js";
|
|
12
|
+
import { parseSchedule } from "./schedule.js";
|
|
13
|
+
/**
|
|
14
|
+
* Validate every v4 `schedule:` entry's `inputs` against the task's own
|
|
15
|
+
* declared contract with defaults applied — the exact set of values a
|
|
16
|
+
* compiled invocation delivers — so a violation is reported at sync rather
|
|
17
|
+
* than when the scheduler fires. Shared with `akm lint`.
|
|
18
|
+
*/
|
|
19
|
+
export function assertTaskScheduleInputsSatisfyContract(v4, refLabel) {
|
|
20
|
+
const contract = v4.inputs ?? {};
|
|
21
|
+
for (const scheduleEntry of v4.schedule) {
|
|
22
|
+
const defaultedInputs = applyInputDefaults(contract, { ...scheduleEntry.inputs });
|
|
23
|
+
const errors = validateInputs(contract, defaultedInputs);
|
|
24
|
+
if (errors.length > 0) {
|
|
25
|
+
throw new UsageError(`Task ${JSON.stringify(refLabel)} schedule[${scheduleEntry.ordinal}].inputs does not satisfy ` +
|
|
26
|
+
`its declared inputs once defaults are applied: ${errors.join("; ")}`, "TASK_SOURCE_INVALID");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate every v4 `schedule:` entry's `cron` against the active backend's
|
|
32
|
+
* dialect: cron is the most permissive of the three, so a task authored on
|
|
33
|
+
* Linux can carry an expression launchd/schtasks cannot translate. Shared
|
|
34
|
+
* with `akm lint`.
|
|
35
|
+
*/
|
|
36
|
+
export function assertTaskScheduleCronValid(v4, backend) {
|
|
37
|
+
for (const scheduleEntry of v4.schedule)
|
|
38
|
+
parseSchedule(scheduleEntry.cron, backend);
|
|
39
|
+
}
|
|
@@ -62,22 +62,6 @@ function schedulerInputFlagValueText(value) {
|
|
|
62
62
|
return String(value);
|
|
63
63
|
return canonicalInputJson(value);
|
|
64
64
|
}
|
|
65
|
-
export function compileWorkflowSchedulerBindings(input) {
|
|
66
|
-
const ref = assertQualifiedRef(input.qualifiedRef, "workflow");
|
|
67
|
-
return Object.freeze(input.schedules.map((schedule) => {
|
|
68
|
-
const bindingId = digestBindingId("workflow", ref, schedule.ordinal);
|
|
69
|
-
return freezeBinding({
|
|
70
|
-
id: bindingId,
|
|
71
|
-
nativeId: bindingId,
|
|
72
|
-
logicalSource: { kind: "workflow", ref },
|
|
73
|
-
cron: schedule.cron,
|
|
74
|
-
source: schedule.source,
|
|
75
|
-
ordinal: schedule.ordinal,
|
|
76
|
-
enabled: true,
|
|
77
|
-
invocation: ["workflow", "run", ref],
|
|
78
|
-
});
|
|
79
|
-
}));
|
|
80
|
-
}
|
|
81
65
|
/**
|
|
82
66
|
* Map a logical binding id to the flat portable token persisted by native
|
|
83
67
|
* schedulers. Flat ids are byte-for-byte stable; only nested standalone
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
/**
|
|
5
5
|
* Scheduler reconciliation: compile the desired bindings from a bundle's
|
|
6
|
-
* task
|
|
6
|
+
* task sources, then diff them against the rows the native
|
|
7
7
|
* scheduler holds.
|
|
8
8
|
*
|
|
9
9
|
* `compileSchedulerSources` reads each source once. One source that fails
|
|
@@ -17,14 +17,9 @@ import fs from "node:fs";
|
|
|
17
17
|
import path from "node:path";
|
|
18
18
|
import { makeBundleRef, parseBundleRef } from "../core/asset/asset-ref.js";
|
|
19
19
|
import { compareCodePoints, toPosix } from "../core/common.js";
|
|
20
|
-
import { UsageError } from "../core/errors.js";
|
|
21
|
-
import { canonicalizeWorkflowName, WORKFLOW_EXTENSIONS } from "../core/recognition-util.js";
|
|
22
|
-
import { applyInputDefaults, validateInputs } from "../execution/input-contract.js";
|
|
23
|
-
import { checkWorkflowPlan, compileWorkflowSource } from "../workflows/compile.js";
|
|
24
|
-
import { WorkflowSourceCollisionError, WorkflowSourceNameError, workflowNameForSourcePath, } from "../workflows/source-files.js";
|
|
25
20
|
import { prepareTaskV3Execution } from "./prepare/prepare.js";
|
|
26
|
-
import {
|
|
27
|
-
import { compileTaskSchedulerBindings,
|
|
21
|
+
import { assertTaskScheduleCronValid, assertTaskScheduleInputsSatisfyContract } from "./schedule-gates.js";
|
|
22
|
+
import { compileTaskSchedulerBindings, schedulerBindingNativeId, schedulerNativeBindingId, } from "./scheduler-binding.js";
|
|
28
23
|
import { parseTaskSource } from "./source/parse-task-source.js";
|
|
29
24
|
import { projectTaskSourceV4 } from "./source/project-v4.js";
|
|
30
25
|
import { taskSourceErrorDetail } from "./source-v3.js";
|
|
@@ -37,7 +32,6 @@ export async function compileSchedulerSources(input) {
|
|
|
37
32
|
const desired = [];
|
|
38
33
|
const failures = [];
|
|
39
34
|
await compileTaskSources({ ...input, sourceRoot }, desired, failures);
|
|
40
|
-
compileWorkflowSources({ ...input, sourceRoot }, desired, failures);
|
|
41
35
|
return Object.freeze({
|
|
42
36
|
desired: Object.freeze(desired),
|
|
43
37
|
failures: Object.freeze(failures.sort((left, right) => compareCodePoints(left.path, right.path))),
|
|
@@ -107,67 +101,6 @@ function taskSourceFiles(sourceRoot, adapterId) {
|
|
|
107
101
|
return walkFiles(sourceRoot).filter((file) => file.endsWith(".yml"));
|
|
108
102
|
return [];
|
|
109
103
|
}
|
|
110
|
-
function compileWorkflowSources(input, desired, failures) {
|
|
111
|
-
if (input.adapterId !== "akm" && input.adapterId !== "akm-workflow")
|
|
112
|
-
return;
|
|
113
|
-
const root = input.adapterId === "akm" ? path.join(input.sourceRoot, "workflows") : input.sourceRoot;
|
|
114
|
-
const byName = new Map();
|
|
115
|
-
for (const file of walkFiles(root)) {
|
|
116
|
-
if (path.basename(file).toLowerCase() === "readme.md")
|
|
117
|
-
continue;
|
|
118
|
-
const authoredName = workflowNameForSourcePath(input.sourceRoot, input.adapterId, file);
|
|
119
|
-
if (authoredName === undefined)
|
|
120
|
-
continue;
|
|
121
|
-
const extension = path.posix.extname(authoredName).toLowerCase();
|
|
122
|
-
const stem = authoredName.slice(0, -extension.length).toLowerCase();
|
|
123
|
-
const nestedSuffix = WORKFLOW_EXTENSIONS.find((suffix) => stem.endsWith(suffix));
|
|
124
|
-
if (nestedSuffix) {
|
|
125
|
-
const relative = toPosix(path.relative(input.sourceRoot, file));
|
|
126
|
-
failures.push({ path: file, reason: new WorkflowSourceNameError(relative, nestedSuffix).message });
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
const canonicalName = canonicalizeWorkflowName(authoredName);
|
|
130
|
-
byName.set(canonicalName, [...(byName.get(canonicalName) ?? []), file]);
|
|
131
|
-
}
|
|
132
|
-
for (const [canonicalName, sources] of [...byName].sort(([left], [right]) => compareCodePoints(left, right))) {
|
|
133
|
-
const conceptId = input.adapterId === "akm" ? `workflows/${canonicalName}` : canonicalName;
|
|
134
|
-
const ref = sourceRef(input.bundleName, conceptId);
|
|
135
|
-
if (ref === undefined || (input.enabledRefs && !input.enabledRefs.has(ref)))
|
|
136
|
-
continue;
|
|
137
|
-
const file = sources[0];
|
|
138
|
-
try {
|
|
139
|
-
if (sources.length > 1) {
|
|
140
|
-
throw new WorkflowSourceCollisionError(conceptId, sources.map((source) => toPosix(path.relative(input.sourceRoot, source))));
|
|
141
|
-
}
|
|
142
|
-
const relative = toPosix(path.relative(input.sourceRoot, file));
|
|
143
|
-
const compiled = compileWorkflowSource(fs.readFileSync(file, "utf8"), {
|
|
144
|
-
path: relative,
|
|
145
|
-
workspaceRoot: input.sourceRoot,
|
|
146
|
-
});
|
|
147
|
-
if (!compiled.ok) {
|
|
148
|
-
throw new UsageError(compiled.errors
|
|
149
|
-
.map((error) => `${error.path}:${error.line ?? 1} [${error.code}] ${error.message}`)
|
|
150
|
-
.join("; "), "WORKFLOW_SOURCE_INVALID");
|
|
151
|
-
}
|
|
152
|
-
const planDraft = checkWorkflowPlan(compiled.plan);
|
|
153
|
-
if (!planDraft.ok) {
|
|
154
|
-
throw new UsageError(planDraft.errors.map((error) => `${relative}:${error.line} ${error.message}`).join("; "), "WORKFLOW_SOURCE_INVALID");
|
|
155
|
-
}
|
|
156
|
-
const schedules = (compiled.plan.schedules ?? []).map((schedule) => ({
|
|
157
|
-
cron: schedule.cron,
|
|
158
|
-
source: `${relative}:${schedule.line}`,
|
|
159
|
-
ordinal: schedule.ordinal,
|
|
160
|
-
}));
|
|
161
|
-
for (const binding of compileWorkflowSchedulerBindings({ qualifiedRef: ref, schedules })) {
|
|
162
|
-
parseSchedule(binding.cron, input.backend);
|
|
163
|
-
desired.push(binding);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
catch (cause) {
|
|
167
|
-
failures.push({ path: file, ref, reason: errorMessage(cause) });
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
104
|
/** A file whose name cannot form a ref (a `#` in it) cannot be enabled, so it is not this sync's. */
|
|
172
105
|
function sourceRef(bundleName, conceptId) {
|
|
173
106
|
try {
|
|
@@ -213,33 +146,6 @@ function statOrUndefined(file) {
|
|
|
213
146
|
return undefined;
|
|
214
147
|
}
|
|
215
148
|
}
|
|
216
|
-
/**
|
|
217
|
-
* Validate every v4 `schedule:` entry's `inputs` against the task's own
|
|
218
|
-
* declared contract with defaults applied — the exact set of values a
|
|
219
|
-
* compiled invocation delivers — so a violation is reported at sync rather
|
|
220
|
-
* than when the scheduler fires. Shared with `akm task validate`.
|
|
221
|
-
*/
|
|
222
|
-
export function assertTaskScheduleInputsSatisfyContract(v4, refLabel) {
|
|
223
|
-
const contract = v4.inputs ?? {};
|
|
224
|
-
for (const scheduleEntry of v4.schedule) {
|
|
225
|
-
const defaultedInputs = applyInputDefaults(contract, { ...scheduleEntry.inputs });
|
|
226
|
-
const errors = validateInputs(contract, defaultedInputs);
|
|
227
|
-
if (errors.length > 0) {
|
|
228
|
-
throw new UsageError(`Task ${JSON.stringify(refLabel)} schedule[${scheduleEntry.ordinal}].inputs does not satisfy ` +
|
|
229
|
-
`its declared inputs once defaults are applied: ${errors.join("; ")}`, "TASK_SOURCE_INVALID");
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Validate every v4 `schedule:` entry's `cron` against the active backend's
|
|
235
|
-
* dialect: cron is the most permissive of the three, so a task authored on
|
|
236
|
-
* Linux can carry an expression launchd/schtasks cannot translate. Shared
|
|
237
|
-
* with `akm task validate`.
|
|
238
|
-
*/
|
|
239
|
-
export function assertTaskScheduleCronValid(v4, backend) {
|
|
240
|
-
for (const scheduleEntry of v4.schedule)
|
|
241
|
-
parseSchedule(scheduleEntry.cron, backend);
|
|
242
|
-
}
|
|
243
149
|
export function installedRowNativeId(row) {
|
|
244
150
|
return row.nativeId ?? schedulerNativeBindingId(row.id);
|
|
245
151
|
}
|
|
@@ -380,7 +380,7 @@ export function assertBoundedTaskYamlDocument(document, options) {
|
|
|
380
380
|
* P4 (docs/plans/specs/p4-deletions-closeout.md §3.2) deleted task v3
|
|
381
381
|
* acceptance from `src` entirely — `parseTaskV3Yaml` and its `"task v3
|
|
382
382
|
* source"` label no longer exist here (the grammar survives only in the
|
|
383
|
-
* vendored, frozen `
|
|
383
|
+
* vendored, frozen `scripts/akm-migrate/migrate/task-source-v3-frozen.ts`
|
|
384
384
|
* copy, which does not call this function). The two live `src` callers
|
|
385
385
|
* today: `parseTaskSourceV4`'s standalone YAML-string entry
|
|
386
386
|
* (`task-source-v4.ts:790`) passes `sourceLabel: "task source v4"`; the
|
|
@@ -44,7 +44,7 @@ import { classifyTargetRef } from "../../execution/target-ref.js";
|
|
|
44
44
|
import { detectSecretShapedParams } from "../../workflows/exec/param-secrets.js";
|
|
45
45
|
import { WORKFLOW_ENV_VAR_NAME_PATTERN, WORKFLOW_MAX_SCHEMA_BYTES } from "../../workflows/resource-limits.js";
|
|
46
46
|
import { TASK_V3_HOST_SHELLS, TASK_V3_MAX_SCHEDULES } from "../source-v3.js";
|
|
47
|
-
import { TASK_RUN_RESERVED_FLAG_NAMES
|
|
47
|
+
import { TASK_RUN_RESERVED_FLAG_NAMES } from "../task-run-reserved-flags.js";
|
|
48
48
|
import { asRecord, checkKeys, cloneBoundedJson, noGithubExpression, own, parseEnvironment, parseStringArray, presentJsonValue, readBoundedTaskSourceYaml, sourceError, stringField, TASK_V3_MAX_REDACT_NAMES, utf8Bytes, validateWorkingDirectory, } from "./bounded-document.js";
|
|
49
49
|
// ── Closed constants (D1, D2-N3, D2-N7) ─────────────────────────────────────
|
|
50
50
|
export const TASK_SOURCE_V4_VERSION = 4;
|
|
@@ -391,18 +391,8 @@ function parseInputDeclarations(value, ctx) {
|
|
|
391
391
|
// checked against this same contract below, so a banned name can never
|
|
392
392
|
// reach compileTaskSchedulerBindings's invocation tail either) — see
|
|
393
393
|
// ../task-run-reserved-flags.ts's own header.
|
|
394
|
-
//
|
|
395
|
-
// 0.9.2 review round 2: `target` joins that set from the CLI's DIAGNOSTIC
|
|
396
|
-
// side rather than its declared-arg side. `rejectRetiredTaskTargetFlag`
|
|
397
|
-
// (../../commands/tasks/tasks-cli.ts) throws the 0.9 `--target` ->
|
|
398
|
-
// `--bundle` rename hint for every spelling of the name, before
|
|
399
|
-
// parseTaskInputFlags scans argv at all, so an input declared under that
|
|
400
|
-
// name could never be supplied either — it is the same unusable
|
|
401
|
-
// declaration, reached by a different route (TASK_RUN_SELF_DIAGNOSED_FLAGS).
|
|
402
394
|
if (TASK_RUN_RESERVED_FLAG_NAMES.has(name)) {
|
|
403
|
-
sourceError(ctx, ["inputs", name],
|
|
404
|
-
? `collides with the retired \`akm task --${name}\` spelling, which every task subcommand still rejects with a rename hint, so no --${name} flag can ever reach this input; declare the input under a different name.`
|
|
405
|
-
: `collides with akm task run's own --${name} flag; declare the input under a different name.`);
|
|
395
|
+
sourceError(ctx, ["inputs", name], `collides with akm task run's own --${name} flag; declare the input under a different name.`);
|
|
406
396
|
}
|
|
407
397
|
result[name] = parseInputDeclaration(name, input[name], ctx);
|
|
408
398
|
}
|
|
@@ -26,9 +26,7 @@
|
|
|
26
26
|
* `src/commands/tasks/tasks-cli.ts` re-exports `TASK_RUN_VALUE_FLAGS` /
|
|
27
27
|
* `TASK_RUN_BOOLEAN_FLAGS` from here unchanged, so this module is the single
|
|
28
28
|
* source of truth both the CLI's own argv scanner and the source parser read
|
|
29
|
-
* — the two can never drift apart.
|
|
30
|
-
* covers the third category the scanner sets cannot express: a name the CLI
|
|
31
|
-
* claims by REJECTING it rather than by declaring it. This file imports
|
|
29
|
+
* — the two can never drift apart. This file imports
|
|
32
30
|
* nothing, and nothing it
|
|
33
31
|
* exports depends on IO, config, or any other `src/tasks/**` module, so it
|
|
34
32
|
* can be imported from either side of that boundary without participating in
|
|
@@ -45,35 +43,8 @@ export const TASK_RUN_BOOLEAN_FLAGS = [
|
|
|
45
43
|
"no-quiet",
|
|
46
44
|
"no-verbose",
|
|
47
45
|
];
|
|
48
|
-
/**
|
|
49
|
-
* Flag names `akm task` claims WITHOUT declaring them as citty args, so they
|
|
50
|
-
* appear in neither list above and must be reserved separately.
|
|
51
|
-
*
|
|
52
|
-
* `target` is the only member: the 0.9 rename of `akm task --target` to
|
|
53
|
-
* `--bundle` (S8.4) is self-diagnosed rather than merely dropped —
|
|
54
|
-
* `rejectRetiredTaskTargetFlag` (`../commands/tasks/tasks-cli.ts`) throws the
|
|
55
|
-
* rename hint for a `--target` in ANY spelling, bare or `--target=<value>`,
|
|
56
|
-
* and the generic pre-dispatch gate exempts the name on every `task`
|
|
57
|
-
* subcommand precisely so that handler can answer (`../cli/unknown-flags.ts`'s
|
|
58
|
-
* `SELF_DIAGNOSED_FLAGS`). That rejection runs before `parseTaskInputFlags`
|
|
59
|
-
* ever scans argv, so a declared input named `target` is unreachable through
|
|
60
|
-
* every spelling of its own flag — the exact value-misrouting hole the union
|
|
61
|
-
* below exists to close, just reached from the CLI's diagnostic side instead
|
|
62
|
-
* of from its declared-arg side (0.9.2 review round 2). Rejecting the
|
|
63
|
-
* DECLARATION keeps the rename hint intact for everyone typing the retired
|
|
64
|
-
* spelling while making the unusable declaration impossible to author, rather
|
|
65
|
-
* than trading one silent failure for another.
|
|
66
|
-
*
|
|
67
|
-
* It deliberately stays OUT of `TASK_RUN_VALUE_FLAGS` /
|
|
68
|
-
* `TASK_RUN_BOOLEAN_FLAGS`: those two are `parseTaskInputFlags`' own scanner
|
|
69
|
-
* sets (a value flag makes the scanner swallow the following token), and
|
|
70
|
-
* `--target` must keep reaching the rename diagnostic rather than being
|
|
71
|
-
* silently consumed as one of `akm task run`'s own flags.
|
|
72
|
-
*/
|
|
73
|
-
export const TASK_RUN_SELF_DIAGNOSED_FLAGS = ["target"];
|
|
74
|
-
/** The union of all three lists above, for a single membership check against a candidate input name. */
|
|
46
|
+
/** The union of the two lists above, for a single membership check against a candidate input name. */
|
|
75
47
|
export const TASK_RUN_RESERVED_FLAG_NAMES = new Set([
|
|
76
48
|
...TASK_RUN_VALUE_FLAGS,
|
|
77
49
|
...TASK_RUN_BOOLEAN_FLAGS,
|
|
78
|
-
...TASK_RUN_SELF_DIAGNOSED_FLAGS,
|
|
79
50
|
]);
|
|
@@ -41,13 +41,13 @@ function validateWorkflowContent(content, sourcePath) {
|
|
|
41
41
|
throw new UsageError(formatWorkflowErrors(sourcePath, compiled.errors));
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
44
|
+
/** Workflows are Markdown; `akm workflow create` rejects a YAML suffix. */
|
|
45
45
|
const YAML_SUFFIX_RE = /\.ya?ml$/i;
|
|
46
46
|
export function assertWorkflowMarkdownName(name) {
|
|
47
47
|
if (!YAML_SUFFIX_RE.test(name.trim()))
|
|
48
48
|
return;
|
|
49
49
|
throw new UsageError(`akm workflow create is markdown-only: it emits Markdown and cannot create "${name}". ` +
|
|
50
|
-
`Use a plain name (no ".yaml"/".yml" suffix)
|
|
50
|
+
`Use a plain name (no ".yaml"/".yml" suffix).`);
|
|
51
51
|
}
|
|
52
52
|
// 0.9.2 review round 2 (P4 sweep criterion-21 caveat, spec
|
|
53
53
|
// docs/plans/specs/p4-deletions-closeout.md §4.1 row B-49): this return's
|
|
@@ -75,7 +75,7 @@ export function createWorkflowAsset(input) {
|
|
|
75
75
|
path.isAbsolute(relativeAssetPath)) {
|
|
76
76
|
throw new UsageError(`Resolved workflow path escapes the bundle: "${normalizedName}"`, "PATH_ESCAPE_VIOLATION");
|
|
77
77
|
}
|
|
78
|
-
// A
|
|
78
|
+
// A `.md` file whose canonical name matches already owns this
|
|
79
79
|
// ref. Case variants (`upper.MD` vs `upper.md`) may also name one physical
|
|
80
80
|
// file on a case-insensitive filesystem, so writing the Markdown create
|
|
81
81
|
// target would silently clobber or shadow the existing asset.
|
|
@@ -160,9 +160,9 @@ export function formatWorkflowErrors(path, errors) {
|
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
162
|
* Every file under `typeRoot` whose canonical workflow name equals
|
|
163
|
-
* `normalizedName`.
|
|
164
|
-
*
|
|
165
|
-
*
|
|
163
|
+
* `normalizedName`. `.md` extension matching is case-INSENSITIVE, so case
|
|
164
|
+
* variants are returned for the create path to refuse rather than silently
|
|
165
|
+
* clobber or shadow them.
|
|
166
166
|
*/
|
|
167
167
|
function findExistingWorkflowPaths(typeRoot, normalizedName) {
|
|
168
168
|
const parent = path.join(typeRoot, path.dirname(normalizedName));
|