akm-cli 0.7.5 → 0.8.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{.github/CHANGELOG.md → CHANGELOG.md} +192 -2
- package/README.md +22 -6
- package/SECURITY.md +93 -0
- package/dist/cli/config-migrate.js +144 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/confirm.js +73 -0
- package/dist/cli/parse-args.js +133 -0
- package/dist/cli/shared.js +129 -0
- package/dist/cli.js +2569 -1449
- package/dist/commands/add-cli.js +279 -0
- package/dist/commands/agent-dispatch.js +110 -0
- package/dist/commands/agent-support.js +68 -0
- package/dist/commands/completions.js +3 -0
- package/dist/commands/config-cli.js +130 -534
- package/dist/commands/consolidate.js +2122 -0
- package/dist/commands/curate.js +44 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +660 -0
- package/dist/commands/distill.js +1075 -77
- package/dist/commands/env.js +213 -0
- package/dist/commands/eval-cases.js +43 -0
- package/dist/commands/events.js +5 -23
- package/dist/commands/extract-cli.js +127 -0
- package/dist/commands/extract-prompt.js +204 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/feedback-cli.js +331 -0
- package/dist/commands/graph.js +477 -0
- package/dist/commands/health.js +1302 -0
- package/dist/commands/help/help-accept.md +12 -0
- package/dist/commands/help/help-improve.md +69 -0
- package/dist/commands/help/help-proposals.md +18 -0
- package/dist/commands/help/help-propose.md +17 -0
- package/dist/commands/help/help-reject.md +11 -0
- package/dist/commands/history.js +54 -46
- package/dist/commands/improve-auto-accept.js +97 -0
- package/dist/commands/improve-cli.js +217 -0
- package/dist/commands/improve-profiles.js +166 -0
- package/dist/commands/improve-result-file.js +167 -0
- package/dist/commands/improve.js +2373 -0
- package/dist/commands/info.js +5 -2
- package/dist/commands/init.js +50 -2
- package/dist/commands/installed-stashes.js +102 -139
- package/dist/commands/knowledge.js +136 -0
- package/dist/commands/lint/agent-linter.js +49 -0
- package/dist/commands/lint/base-linter.js +479 -0
- package/dist/commands/lint/command-linter.js +49 -0
- package/dist/commands/lint/default-linter.js +16 -0
- package/dist/commands/lint/env-key-rules.js +154 -0
- package/dist/commands/lint/index.js +196 -0
- package/dist/commands/lint/knowledge-linter.js +16 -0
- package/dist/commands/lint/markdown-insertion.js +343 -0
- package/dist/commands/lint/memory-linter.js +61 -0
- package/dist/commands/lint/registry.js +36 -0
- package/dist/commands/lint/skill-linter.js +45 -0
- package/dist/commands/lint/task-linter.js +50 -0
- package/dist/commands/lint/types.js +4 -0
- package/dist/commands/lint/workflow-linter.js +56 -0
- package/dist/commands/lint.js +4 -0
- package/dist/commands/migration-help.js +5 -2
- package/dist/commands/proposal.js +67 -12
- package/dist/commands/propose.js +86 -31
- package/dist/commands/reflect.js +1091 -73
- package/dist/commands/registry-cli.js +150 -0
- package/dist/commands/registry-search.js +5 -2
- package/dist/commands/remember-cli.js +257 -0
- package/dist/commands/remember.js +69 -6
- package/dist/commands/schema-repair.js +203 -0
- package/dist/commands/search.js +115 -14
- package/dist/commands/secret.js +173 -0
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +148 -25
- package/dist/commands/source-add.js +17 -45
- package/dist/commands/source-clone.js +3 -0
- package/dist/commands/source-manage.js +14 -19
- package/dist/commands/tasks.js +437 -0
- package/dist/commands/url-checker.js +42 -0
- package/dist/core/action-contributors.js +28 -0
- package/dist/core/asset-ref.js +17 -2
- package/dist/core/asset-registry.js +12 -17
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +67 -1
- package/dist/core/common.js +182 -0
- package/dist/core/concurrent.js +25 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +622 -0
- package/dist/core/config-schema.js +534 -0
- package/dist/core/config-sources.js +108 -0
- package/dist/core/config-types.js +4 -0
- package/dist/core/config-walker.js +337 -0
- package/dist/core/config.js +364 -981
- package/dist/core/errors.js +42 -20
- package/dist/core/events.js +91 -138
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +75 -8
- package/dist/core/lesson-lint.js +3 -0
- package/dist/core/markdown.js +20 -0
- package/dist/core/memory-belief.js +62 -0
- package/dist/core/memory-contradiction-detect.js +274 -0
- package/dist/core/memory-improve.js +806 -0
- package/dist/core/parse.js +158 -0
- package/dist/core/paths.js +280 -14
- package/dist/core/proposal-quality-validators.js +380 -0
- package/dist/core/proposal-validators.js +69 -0
- package/dist/core/proposals.js +512 -42
- package/dist/core/state-db.js +1068 -0
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +54 -0
- package/dist/core/tty.js +59 -0
- package/dist/core/warn.js +64 -1
- package/dist/core/write-source.js +3 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +163 -254
- package/dist/indexer/db.js +975 -103
- package/dist/indexer/ensure-index.js +64 -0
- package/dist/indexer/file-context.js +3 -0
- package/dist/indexer/graph-boost.js +376 -101
- package/dist/indexer/graph-db.js +391 -0
- package/dist/indexer/graph-dedup.js +95 -0
- package/dist/indexer/graph-extraction.js +550 -124
- package/dist/indexer/index-context.js +4 -0
- package/dist/indexer/indexer.js +523 -301
- package/dist/indexer/llm-cache.js +52 -0
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +167 -160
- package/dist/indexer/memory-inference.js +152 -74
- package/dist/indexer/metadata-contributors.js +29 -0
- package/dist/indexer/metadata.js +275 -196
- package/dist/indexer/path-resolver.js +92 -0
- package/dist/indexer/project-context.js +192 -0
- package/dist/indexer/ranking-contributors.js +331 -0
- package/dist/indexer/ranking.js +81 -0
- package/dist/indexer/search-fields.js +5 -9
- package/dist/indexer/search-hit-enrichers.js +111 -0
- package/dist/indexer/search-source.js +44 -10
- package/dist/indexer/semantic-status.js +6 -17
- package/dist/indexer/staleness-detect.js +447 -0
- package/dist/indexer/usage-events.js +12 -9
- package/dist/indexer/walker.js +28 -0
- package/dist/integrations/agent/builders.js +135 -0
- package/dist/integrations/agent/config.js +122 -230
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +7 -13
- package/dist/integrations/agent/model-aliases.js +55 -0
- package/dist/integrations/agent/profiles.js +70 -5
- package/dist/integrations/agent/prompts.js +214 -80
- package/dist/integrations/agent/runner.js +151 -0
- package/dist/integrations/agent/sdk-runner.js +126 -0
- package/dist/integrations/agent/spawn.js +118 -23
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +32 -69
- package/dist/integrations/session-logs/index.js +69 -0
- package/dist/integrations/session-logs/inline-refs.js +35 -0
- package/dist/integrations/session-logs/pre-filter.js +152 -0
- package/dist/integrations/session-logs/providers/claude-code.js +282 -0
- package/dist/integrations/session-logs/providers/opencode.js +258 -0
- package/dist/integrations/session-logs/types.js +4 -0
- package/dist/llm/call-ai.js +62 -0
- package/dist/llm/client.js +77 -124
- package/dist/llm/embedder.js +20 -29
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +42 -1
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +95 -48
- package/dist/llm/graph-extract.js +676 -70
- package/dist/llm/index-passes.js +44 -29
- package/dist/llm/memory-infer.js +77 -71
- package/dist/llm/metadata-enhance.js +42 -29
- package/dist/llm/prompts/extract-session.md +80 -0
- package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
- package/dist/output/cli-hints-full.md +292 -0
- package/dist/output/cli-hints-short.md +66 -0
- package/dist/output/cli-hints.js +7 -320
- package/dist/output/context.js +60 -8
- package/dist/output/renderers.js +300 -257
- package/dist/output/shapes/curate.js +56 -0
- package/dist/output/shapes/distill.js +10 -0
- package/dist/output/shapes/env-list.js +19 -0
- package/dist/output/shapes/events.js +11 -0
- package/dist/output/shapes/helpers.js +424 -0
- package/dist/output/shapes/history.js +7 -0
- package/dist/output/shapes/passthrough.js +102 -0
- package/dist/output/shapes/proposal-accept.js +7 -0
- package/dist/output/shapes/proposal-diff.js +7 -0
- package/dist/output/shapes/proposal-list.js +7 -0
- package/dist/output/shapes/proposal-producer.js +11 -0
- package/dist/output/shapes/proposal-reject.js +7 -0
- package/dist/output/shapes/proposal-show.js +7 -0
- package/dist/output/shapes/registry-search.js +6 -0
- package/dist/output/shapes/registry.js +30 -0
- package/dist/output/shapes/search.js +6 -0
- package/dist/output/shapes/secret-list.js +19 -0
- package/dist/output/shapes/show.js +6 -0
- package/dist/output/shapes/vault-list.js +19 -0
- package/dist/output/shapes.js +51 -516
- package/dist/output/text/add.js +6 -0
- package/dist/output/text/clone.js +6 -0
- package/dist/output/text/config.js +6 -0
- package/dist/output/text/curate.js +6 -0
- package/dist/output/text/distill.js +7 -0
- package/dist/output/text/enable-disable.js +7 -0
- package/dist/output/text/events.js +10 -0
- package/dist/output/text/feedback.js +6 -0
- package/dist/output/text/helpers.js +1039 -0
- package/dist/output/text/history.js +7 -0
- package/dist/output/text/import.js +6 -0
- package/dist/output/text/index.js +6 -0
- package/dist/output/text/info.js +6 -0
- package/dist/output/text/init.js +6 -0
- package/dist/output/text/list.js +6 -0
- package/dist/output/text/proposal-producer.js +8 -0
- package/dist/output/text/proposal.js +11 -0
- package/dist/output/text/registry-commands.js +11 -0
- package/dist/output/text/registry.js +30 -0
- package/dist/output/text/remember.js +6 -0
- package/dist/output/text/remove.js +6 -0
- package/dist/output/text/save.js +6 -0
- package/dist/output/text/search.js +6 -0
- package/dist/output/text/show.js +6 -0
- package/dist/output/text/update.js +6 -0
- package/dist/output/text/upgrade.js +6 -0
- package/dist/output/text/vault.js +16 -0
- package/dist/output/text/wiki.js +15 -0
- package/dist/output/text/workflow.js +14 -0
- package/dist/output/text.js +44 -1092
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +4 -1
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +71 -50
- package/dist/registry/providers/static-index.js +53 -48
- package/dist/registry/providers/types.js +3 -24
- package/dist/registry/resolve.js +11 -16
- package/dist/registry/types.js +3 -0
- package/dist/scripts/migrate-storage.js +17750 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
- package/dist/scripts/migrations/v16-to-v17.js +141 -0
- package/dist/setup/detect.js +3 -0
- package/dist/setup/ripgrep-install.js +3 -0
- package/dist/setup/ripgrep-resolve.js +3 -0
- package/dist/setup/setup.js +775 -37
- package/dist/setup/steps.js +3 -15
- package/dist/sources/include.js +3 -0
- package/dist/sources/provider-factory.js +5 -12
- package/dist/sources/provider.js +3 -20
- package/dist/sources/providers/filesystem.js +19 -23
- package/dist/sources/providers/git.js +138 -21
- package/dist/sources/providers/index.js +3 -0
- package/dist/sources/providers/install-types.js +3 -13
- package/dist/sources/providers/npm.js +3 -4
- package/dist/sources/providers/provider-utils.js +3 -0
- package/dist/sources/providers/sync-from-ref.js +3 -11
- package/dist/sources/providers/tar-utils.js +3 -0
- package/dist/sources/providers/website.js +18 -22
- package/dist/sources/resolve.js +3 -0
- package/dist/sources/types.js +3 -0
- package/dist/sources/website-ingest.js +7 -0
- package/dist/tasks/backends/cron.js +203 -0
- package/dist/tasks/backends/exec-utils.js +28 -0
- package/dist/tasks/backends/index.js +24 -0
- package/dist/tasks/backends/launchd-template.xml +19 -0
- package/dist/tasks/backends/launchd.js +187 -0
- package/dist/tasks/backends/schtasks-template.xml +29 -0
- package/dist/tasks/backends/schtasks.js +215 -0
- package/dist/tasks/parser.js +211 -0
- package/dist/tasks/resolveAkmBin.js +87 -0
- package/dist/tasks/runner.js +458 -0
- package/dist/tasks/schedule.js +227 -0
- package/dist/tasks/schema.js +15 -0
- package/dist/tasks/validator.js +62 -0
- package/dist/version.js +3 -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 +15 -0
- package/dist/wiki/wiki.js +13 -61
- package/dist/workflows/authoring.js +8 -25
- package/dist/workflows/cli.js +3 -0
- package/dist/workflows/db.js +140 -10
- package/dist/workflows/document-cache.js +3 -10
- package/dist/workflows/parser.js +3 -0
- package/dist/workflows/renderer.js +11 -3
- package/dist/workflows/runs.js +77 -92
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +3 -0
- package/dist/workflows/validator.js +4 -8
- package/dist/workflows/workflow-template.md +24 -0
- package/docs/README.md +10 -2
- package/docs/data-and-telemetry.md +225 -0
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +2 -2
- package/docs/migration/release-notes/0.8.0.md +48 -0
- package/docs/migration/v0.7-to-v0.8.md +1307 -0
- package/package.json +30 -12
- package/.github/LICENSE +0 -374
- package/dist/commands/install-audit.js +0 -381
- package/dist/commands/vault.js +0 -328
- package/dist/templates/wiki-templates.js +0 -100
|
@@ -1,3 +1,6 @@
|
|
|
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/.
|
|
1
4
|
/**
|
|
2
5
|
* `akm proposal {list,show,accept,reject,diff}` — review surface for the
|
|
3
6
|
* proposal substrate (#225).
|
|
@@ -12,7 +15,7 @@ import { resolveStashDir } from "../core/common";
|
|
|
12
15
|
import { loadConfig } from "../core/config";
|
|
13
16
|
import { UsageError } from "../core/errors";
|
|
14
17
|
import { appendEvent } from "../core/events";
|
|
15
|
-
import { archiveProposal, createProposal, diffProposal, getProposal, listProposals, promoteProposal, validateProposal, } from "../core/proposals";
|
|
18
|
+
import { archiveProposal, createProposal, diffProposal, getProposal, isProposalSkipped, listProposals, promoteProposal, resolveProposalId, revertProposal, validateProposal, } from "../core/proposals";
|
|
16
19
|
// ── Shared helpers ──────────────────────────────────────────────────────────
|
|
17
20
|
function resolveStash(stashDir) {
|
|
18
21
|
if (stashDir)
|
|
@@ -21,13 +24,17 @@ function resolveStash(stashDir) {
|
|
|
21
24
|
}
|
|
22
25
|
export function akmProposalList(options = {}) {
|
|
23
26
|
const stash = resolveStash(options.stashDir);
|
|
24
|
-
// `--status accepted|rejected` implies archive-inclusion since the
|
|
25
|
-
// queue only ever contains pending entries.
|
|
26
|
-
const includeArchive = options.includeArchive === true ||
|
|
27
|
+
// `--status accepted|rejected|reverted` implies archive-inclusion since the
|
|
28
|
+
// live queue only ever contains pending entries.
|
|
29
|
+
const includeArchive = options.includeArchive === true ||
|
|
30
|
+
options.status === "accepted" ||
|
|
31
|
+
options.status === "rejected" ||
|
|
32
|
+
options.status === "reverted";
|
|
27
33
|
const proposals = listProposals(stash, {
|
|
28
34
|
includeArchive,
|
|
29
35
|
status: options.status,
|
|
30
36
|
ref: options.ref,
|
|
37
|
+
type: options.type,
|
|
31
38
|
});
|
|
32
39
|
return { schemaVersion: 1, totalCount: proposals.length, proposals };
|
|
33
40
|
}
|
|
@@ -43,7 +50,8 @@ export function akmProposalShow(options) {
|
|
|
43
50
|
export async function akmProposalAccept(options) {
|
|
44
51
|
const stash = resolveStash(options.stashDir);
|
|
45
52
|
const config = options.config ?? loadConfig();
|
|
46
|
-
const
|
|
53
|
+
const resolvedId = resolveProposalId(stash, options.id).id;
|
|
54
|
+
const result = await promoteProposal(stash, config, resolvedId, { target: options.target }, options.ctx);
|
|
47
55
|
// Emit `promoted` to the events stream so observers (audit, dashboards,
|
|
48
56
|
// sync) see the accept happen. Only emit on the happy path — promotion
|
|
49
57
|
// throws on validation failure, so reaching this point means the asset
|
|
@@ -69,11 +77,11 @@ export async function akmProposalAccept(options) {
|
|
|
69
77
|
}
|
|
70
78
|
export function akmProposalReject(options) {
|
|
71
79
|
const stash = resolveStash(options.stashDir);
|
|
72
|
-
const existing =
|
|
80
|
+
const existing = resolveProposalId(stash, options.id);
|
|
73
81
|
if (existing.status !== "pending") {
|
|
74
|
-
throw new UsageError(`Proposal ${
|
|
82
|
+
throw new UsageError(`Proposal ${existing.id} is not pending (current status: ${existing.status}). Only pending proposals can be rejected.`, "INVALID_FLAG_VALUE");
|
|
75
83
|
}
|
|
76
|
-
const updated = archiveProposal(stash,
|
|
84
|
+
const updated = archiveProposal(stash, existing.id, "rejected", options.reason, options.ctx);
|
|
77
85
|
appendEvent({
|
|
78
86
|
eventType: "rejected",
|
|
79
87
|
ref: updated.ref,
|
|
@@ -96,8 +104,8 @@ export function akmProposalReject(options) {
|
|
|
96
104
|
export function akmProposalDiff(options) {
|
|
97
105
|
const stash = resolveStash(options.stashDir);
|
|
98
106
|
const config = options.config ?? loadConfig();
|
|
99
|
-
const proposal =
|
|
100
|
-
const diff = diffProposal(stash, config,
|
|
107
|
+
const proposal = resolveProposalId(stash, options.id);
|
|
108
|
+
const diff = diffProposal(stash, config, proposal.id, { target: options.target });
|
|
101
109
|
return {
|
|
102
110
|
schemaVersion: 1,
|
|
103
111
|
id: proposal.id,
|
|
@@ -109,11 +117,58 @@ export function akmProposalDiff(options) {
|
|
|
109
117
|
}
|
|
110
118
|
export function akmProposalCreate(options) {
|
|
111
119
|
const stash = resolveStash(options.stashDir);
|
|
112
|
-
|
|
120
|
+
// Manual proposal creation (via `akm proposal create`) always bypasses
|
|
121
|
+
// dedup/cooldown guards — the operator is explicitly requesting a proposal.
|
|
122
|
+
const result = createProposal(stash, {
|
|
113
123
|
ref: options.ref,
|
|
114
124
|
source: options.source,
|
|
115
125
|
...(options.sourceRun !== undefined ? { sourceRun: options.sourceRun } : {}),
|
|
116
126
|
payload: options.payload,
|
|
127
|
+
force: true,
|
|
117
128
|
}, options.ctx);
|
|
118
|
-
|
|
129
|
+
if (isProposalSkipped(result)) {
|
|
130
|
+
// Should never happen with force:true — defensive only.
|
|
131
|
+
throw new Error(`Unexpected proposal skip: ${result.message}`);
|
|
132
|
+
}
|
|
133
|
+
return { schemaVersion: 1, ok: true, proposal: result };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Restore an accepted proposal's prior content from the backup captured at
|
|
137
|
+
* promotion time (Advantage D6c / Phase 6C).
|
|
138
|
+
*
|
|
139
|
+
* Failure modes (all surface as typed errors so the CLI can map exit codes):
|
|
140
|
+
* - Proposal id does not resolve → `NotFoundError("FILE_NOT_FOUND")`
|
|
141
|
+
* (raised by `resolveProposalId` / `getProposal`).
|
|
142
|
+
* - Proposal is not `status === "accepted"` → `UsageError("INVALID_FLAG_VALUE")`
|
|
143
|
+
* with message `"only accepted proposals can be reverted ..."`.
|
|
144
|
+
* - No `backup` field, or the backup file is missing on disk →
|
|
145
|
+
* `UsageError` with message `"no backup available for this proposal ..."`.
|
|
146
|
+
*
|
|
147
|
+
* On success, emits a `proposal_reverted` event for observability, mirroring
|
|
148
|
+
* how `akmProposalAccept` emits `promoted` and `akmProposalReject` emits
|
|
149
|
+
* `rejected`.
|
|
150
|
+
*/
|
|
151
|
+
export async function akmProposalRevert(options) {
|
|
152
|
+
const stash = resolveStash(options.stashDir);
|
|
153
|
+
const config = options.config ?? loadConfig();
|
|
154
|
+
const resolvedId = resolveProposalId(stash, options.id).id;
|
|
155
|
+
const result = await revertProposal(stash, config, resolvedId, { target: options.target }, options.ctx);
|
|
156
|
+
appendEvent({
|
|
157
|
+
eventType: "proposal_reverted",
|
|
158
|
+
ref: result.ref,
|
|
159
|
+
metadata: {
|
|
160
|
+
proposalId: result.proposal.id,
|
|
161
|
+
source: result.proposal.source,
|
|
162
|
+
...(result.proposal.sourceRun !== undefined ? { sourceRun: result.proposal.sourceRun } : {}),
|
|
163
|
+
assetPath: result.assetPath,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
schemaVersion: 1,
|
|
168
|
+
ok: true,
|
|
169
|
+
id: result.proposal.id,
|
|
170
|
+
ref: result.ref,
|
|
171
|
+
assetPath: result.assetPath,
|
|
172
|
+
proposal: result.proposal,
|
|
173
|
+
};
|
|
119
174
|
}
|
package/dist/commands/propose.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
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/.
|
|
1
4
|
/**
|
|
2
5
|
* `akm propose <type> <name> --task ...` — proposal-producing agent
|
|
3
6
|
* command (#226).
|
|
@@ -9,37 +12,23 @@
|
|
|
9
12
|
* Failures use the same {@link AgentFailureReason} discriminants as
|
|
10
13
|
* `akm reflect`. `propose_invoked` is emitted at command entry.
|
|
11
14
|
*/
|
|
15
|
+
import fs from "node:fs";
|
|
12
16
|
import { parseAssetRef } from "../core/asset-ref";
|
|
13
17
|
import { TYPE_DIRS } from "../core/asset-spec";
|
|
14
18
|
import { resolveStashDir } from "../core/common";
|
|
15
|
-
import { loadConfig } from "../core/config";
|
|
16
19
|
import { ConfigError, UsageError } from "../core/errors";
|
|
17
20
|
import { appendEvent } from "../core/events";
|
|
18
|
-
import { createProposal } from "../core/proposals";
|
|
19
|
-
import {
|
|
21
|
+
import { createProposal, isProposalSkipped, } from "../core/proposals";
|
|
22
|
+
import { runAgent, } from "../integrations/agent";
|
|
23
|
+
import { resolveProcessAgentProfile } from "../integrations/agent/config";
|
|
20
24
|
import { buildProposePrompt, parseAgentProposalPayload } from "../integrations/agent/prompts";
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return parseAgentConfig(config.agent);
|
|
24
|
-
}
|
|
25
|
-
function resolveProfile(options) {
|
|
26
|
-
if (options.agentProfile)
|
|
27
|
-
return options.agentProfile;
|
|
28
|
-
const agent = options.agentConfig ?? loadAgentConfigFromDisk();
|
|
29
|
-
return requireAgentProfile(agent, options.profile);
|
|
30
|
-
}
|
|
25
|
+
import { runAgentSdk } from "../integrations/agent/sdk-runner";
|
|
26
|
+
import { baseFailureFields, enoentHintMessage, isEnoentFailure, loadAgentConfigFromDisk, resolveAgentProfile, } from "./agent-support";
|
|
31
27
|
function failureEnvelope(result, type, name, fallbackReason = "non_zero_exit") {
|
|
32
|
-
const reason = result.reason ?? fallbackReason;
|
|
33
28
|
return {
|
|
34
|
-
|
|
35
|
-
ok: false,
|
|
36
|
-
reason,
|
|
37
|
-
error: result.error ?? `agent failure (${reason})`,
|
|
29
|
+
...baseFailureFields(result, fallbackReason),
|
|
38
30
|
type,
|
|
39
31
|
name,
|
|
40
|
-
exitCode: result.exitCode,
|
|
41
|
-
...(result.stdout ? { stdout: result.stdout } : {}),
|
|
42
|
-
...(result.stderr ? { stderr: result.stderr } : {}),
|
|
43
32
|
};
|
|
44
33
|
}
|
|
45
34
|
export async function akmPropose(options) {
|
|
@@ -68,9 +57,31 @@ export async function akmPropose(options) {
|
|
|
68
57
|
},
|
|
69
58
|
});
|
|
70
59
|
// 2. Resolve profile.
|
|
60
|
+
// When an explicit --profile flag is given, honour it directly (existing
|
|
61
|
+
// behaviour). Otherwise use resolveProcessAgentProfile so that per-process
|
|
62
|
+
// agent config (agent.processes["propose"]) is picked up automatically.
|
|
71
63
|
let profile;
|
|
64
|
+
let resolvedTimeoutMs = options.timeoutMs;
|
|
72
65
|
try {
|
|
73
|
-
|
|
66
|
+
if (options.agentProfile) {
|
|
67
|
+
// Test seam: injected profile bypasses all config.
|
|
68
|
+
profile = options.agentProfile;
|
|
69
|
+
}
|
|
70
|
+
else if (options.profile) {
|
|
71
|
+
// Explicit --profile flag wins over process config.
|
|
72
|
+
profile = resolveAgentProfile(options);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Use per-process config resolution (falls back to agent.default).
|
|
76
|
+
const agent = options.agentConfig ?? loadAgentConfigFromDisk();
|
|
77
|
+
const processName = options.agentProcess ?? "propose";
|
|
78
|
+
const resolved = resolveProcessAgentProfile(processName, agent);
|
|
79
|
+
profile = resolved.profile;
|
|
80
|
+
// Only apply process-resolved timeoutMs when caller didn't supply one.
|
|
81
|
+
if (resolvedTimeoutMs === undefined) {
|
|
82
|
+
resolvedTimeoutMs = resolved.timeoutMs;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
74
85
|
}
|
|
75
86
|
catch (err) {
|
|
76
87
|
if (err instanceof ConfigError || err instanceof UsageError)
|
|
@@ -91,20 +102,40 @@ export async function akmPropose(options) {
|
|
|
91
102
|
// 4. Spawn the agent.
|
|
92
103
|
// Real agent runs use interactive mode so file tools can write the draft.
|
|
93
104
|
// Injected/custom spawns still need captured stdout for JSON payload tests.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
// Use callAi for the unified AI dispatch path (agent CLI preferred, LLM HTTP fallback).
|
|
106
|
+
const useCustomSpawn = Boolean(options.runAgentOptions?.spawn);
|
|
107
|
+
let result;
|
|
108
|
+
if (useCustomSpawn) {
|
|
109
|
+
// Test seam: use raw runAgent with injected spawn so tests remain deterministic.
|
|
110
|
+
const runOptions = {
|
|
111
|
+
stdio: "captured",
|
|
112
|
+
parseOutput: "text",
|
|
113
|
+
...(resolvedTimeoutMs !== undefined ? { timeoutMs: resolvedTimeoutMs } : {}),
|
|
114
|
+
...(options.runAgentOptions ?? {}),
|
|
115
|
+
};
|
|
116
|
+
result = await runAgent(profile, prompt, runOptions);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Production path: dispatch directly to the appropriate runner.
|
|
120
|
+
const runOptions = {
|
|
121
|
+
stdio: resolvedDraftPath ? "interactive" : "captured",
|
|
122
|
+
parseOutput: "text",
|
|
123
|
+
...(resolvedTimeoutMs !== undefined ? { timeoutMs: resolvedTimeoutMs } : {}),
|
|
124
|
+
};
|
|
125
|
+
result = profile.sdkMode
|
|
126
|
+
? await runAgentSdk(profile, prompt ?? "", runOptions)
|
|
127
|
+
: await runAgent(profile, prompt, runOptions);
|
|
128
|
+
}
|
|
101
129
|
if (!result.ok) {
|
|
130
|
+
// B3: ENOENT / not-found gives an actionable hint.
|
|
131
|
+
if (isEnoentFailure(result)) {
|
|
132
|
+
return { ...failureEnvelope(result, options.type, options.name), error: enoentHintMessage(profile.bin) };
|
|
133
|
+
}
|
|
102
134
|
return failureEnvelope(result, options.type, options.name);
|
|
103
135
|
}
|
|
104
136
|
// 5. Resolve the proposal content.
|
|
105
137
|
// Path A: opencode wrote the draft file — read it directly (no stdout parse).
|
|
106
138
|
// Path B: fallback to stdout JSON parse for non-file-writing agents.
|
|
107
|
-
const fs = await import("node:fs");
|
|
108
139
|
let payload;
|
|
109
140
|
if (fs.existsSync(resolvedDraftPath)) {
|
|
110
141
|
const draftContent = fs.readFileSync(resolvedDraftPath, "utf8");
|
|
@@ -115,6 +146,21 @@ export async function akmPropose(options) {
|
|
|
115
146
|
};
|
|
116
147
|
}
|
|
117
148
|
else {
|
|
149
|
+
// B1: When interactive mode was used and stdout is empty, the agent did not
|
|
150
|
+
// write the draft file and stdout was not captured — surface an actionable error.
|
|
151
|
+
const stdioWasInteractive = !useCustomSpawn;
|
|
152
|
+
if (stdioWasInteractive && (result.stdout ?? "") === "") {
|
|
153
|
+
return {
|
|
154
|
+
schemaVersion: 1,
|
|
155
|
+
ok: false,
|
|
156
|
+
reason: "parse_error",
|
|
157
|
+
error: "Agent did not write draft file and stdout was not captured (interactive mode). Check that the agent CLI understood the file-write instruction, or configure a headless profile with stdio: 'captured'.",
|
|
158
|
+
type: options.type,
|
|
159
|
+
name: options.name,
|
|
160
|
+
exitCode: result.exitCode,
|
|
161
|
+
...(result.stderr ? { stderr: result.stderr } : {}),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
118
164
|
try {
|
|
119
165
|
payload = parseAgentProposalPayload(result.stdout ?? "");
|
|
120
166
|
}
|
|
@@ -174,12 +220,21 @@ export async function akmPropose(options) {
|
|
|
174
220
|
ref,
|
|
175
221
|
source: "propose",
|
|
176
222
|
sourceRun: `propose-${Date.now()}`,
|
|
223
|
+
// User-initiated proposals always bypass dedup/cooldown guards — the
|
|
224
|
+
// operator is explicitly asking for a new proposal.
|
|
225
|
+
force: true,
|
|
177
226
|
payload: {
|
|
178
227
|
content: payload.content,
|
|
179
228
|
...(payload.frontmatter ? { frontmatter: payload.frontmatter } : {}),
|
|
180
229
|
},
|
|
181
230
|
};
|
|
182
|
-
const
|
|
231
|
+
const proposalResult = createProposal(stash, createInput, options.ctx);
|
|
232
|
+
// With force:true, the result is always a Proposal (never skipped).
|
|
233
|
+
if (isProposalSkipped(proposalResult)) {
|
|
234
|
+
// Should never happen when force:true, but be defensive.
|
|
235
|
+
throw new Error(`Unexpected skip in propose command: ${proposalResult.message}`);
|
|
236
|
+
}
|
|
237
|
+
const proposal = proposalResult;
|
|
183
238
|
return {
|
|
184
239
|
schemaVersion: 1,
|
|
185
240
|
ok: true,
|