akm-cli 0.8.6 → 0.8.14
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 +442 -0
- package/dist/assets/help/help-proposals.md +1 -2
- package/dist/assets/hints/cli-hints-full.md +34 -19
- package/dist/assets/hints/cli-hints-short.md +1 -1
- package/dist/assets/profiles/catchup.json +13 -0
- package/dist/assets/profiles/consolidate.json +13 -0
- package/dist/assets/profiles/frequent.json +13 -0
- package/dist/assets/tasks/core/backup.yml +4 -0
- package/dist/assets/tasks/core/extract.yml +4 -0
- package/dist/assets/tasks/core/improve.yml +4 -0
- package/dist/assets/tasks/core/index-refresh.yml +4 -0
- package/dist/assets/tasks/core/sync.yml +4 -0
- package/dist/assets/tasks/core/update-stashes.yml +4 -0
- package/dist/assets/tasks/core/version-check.yml +4 -0
- package/dist/assets/templates/html/default.html +78 -0
- package/dist/assets/templates/html/health.html +560 -0
- package/dist/assets/templates/html/vendor/echarts.min.js +45 -0
- package/dist/cli/config-migrate.js +6 -6
- package/dist/cli/config-validate.js +4 -4
- package/dist/cli/confirm.js +3 -3
- package/dist/cli/parse-args.js +1 -1
- package/dist/cli/shared.js +72 -19
- package/dist/cli-node.mjs +26 -0
- package/dist/cli.js +206 -3866
- package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
- package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
- package/dist/commands/agent/contribute-cli.js +200 -0
- package/dist/commands/completions.js +1 -1
- package/dist/commands/config-cli.js +230 -3
- package/dist/commands/db-cli.js +2 -2
- package/dist/commands/env/env-cli.js +529 -0
- package/dist/commands/env/env.js +410 -0
- package/dist/commands/env/secret-cli.js +259 -0
- package/dist/commands/{secret.js → env/secret.js} +6 -47
- package/dist/commands/events.js +4 -4
- package/dist/commands/feedback-cli.js +18 -34
- package/dist/commands/graph/graph-cli.js +132 -0
- package/dist/commands/{graph.js → graph/graph.js} +22 -16
- package/dist/commands/health/checks.js +279 -0
- package/dist/commands/health/html-report.js +448 -0
- package/dist/commands/health.js +189 -266
- package/dist/commands/{consolidate.js → improve/consolidate.js} +63 -38
- package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
- package/dist/commands/{distill.js → improve/distill.js} +39 -18
- package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
- package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
- package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
- package/dist/commands/{extract.js → improve/extract.js} +221 -26
- package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +30 -4
- package/dist/commands/{improve-cli.js → improve/improve-cli.js} +44 -22
- package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
- package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +1 -1
- package/dist/commands/{improve.js → improve/improve.js} +672 -292
- package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
- package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
- package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/{reflect.js → improve/reflect.js} +58 -28
- package/dist/commands/improve/session-asset.js +248 -0
- package/dist/commands/lint/agent-linter.js +1 -1
- package/dist/commands/lint/base-linter.js +55 -37
- package/dist/commands/lint/command-linter.js +1 -1
- package/dist/commands/lint/default-linter.js +1 -1
- package/dist/commands/lint/env-key-rules.js +1 -1
- package/dist/commands/lint/index.js +19 -25
- package/dist/commands/lint/knowledge-linter.js +1 -1
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +8 -8
- package/dist/commands/lint/skill-linter.js +1 -1
- package/dist/commands/lint/task-linter.js +1 -1
- package/dist/commands/lint/workflow-linter.js +1 -1
- package/dist/commands/lint.js +1 -1
- package/dist/commands/observability-cli.js +244 -0
- package/dist/commands/proposal/drain-policies.js +3 -3
- package/dist/commands/proposal/drain.js +87 -15
- package/dist/commands/proposal/proposal-cli.js +490 -0
- package/dist/commands/{proposal.js → proposal/proposal.js} +17 -6
- package/dist/commands/{propose.js → proposal/propose.js} +11 -11
- package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
- package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
- package/dist/{core → commands/proposal/validators}/proposals.js +374 -345
- package/dist/commands/{curate.js → read/curate.js} +7 -7
- package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
- package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
- package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
- package/dist/commands/read/search-cli.js +207 -0
- package/dist/commands/{search.js → read/search.js} +22 -27
- package/dist/commands/{show.js → read/show.js} +31 -45
- package/dist/commands/registry-cli.js +8 -8
- package/dist/commands/remember.js +14 -10
- package/dist/commands/sources/add-cli.js +293 -0
- package/dist/commands/{history.js → sources/history.js} +27 -25
- package/dist/commands/{info.js → sources/info.js} +6 -6
- package/dist/commands/{init.js → sources/init.js} +6 -6
- package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
- package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
- package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
- package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
- package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
- package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
- package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
- package/dist/commands/sources/sources-cli.js +305 -0
- package/dist/commands/sources/stash-cli.js +219 -0
- package/dist/commands/{stash-skeleton.js → sources/stash-skeleton.js} +2 -1
- package/dist/commands/tasks/default-tasks.js +173 -0
- package/dist/commands/tasks/tasks-cli.js +210 -0
- package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
- package/dist/commands/wiki-cli.js +307 -0
- package/dist/commands/workflow-cli.js +329 -0
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/assert.js +40 -0
- package/dist/core/asset/asset-create.js +54 -0
- package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
- package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
- package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
- package/dist/core/{markdown.js → asset/markdown.js} +1 -1
- package/dist/core/{stash-meta.js → asset/stash-meta.js} +1 -1
- package/dist/core/best-effort.js +64 -0
- package/dist/core/common.js +32 -18
- package/dist/core/{config-io.js → config/config-io.js} +29 -19
- package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
- package/dist/core/{config-schema.js → config/config-schema.js} +50 -7
- package/dist/core/config/config-types.js +16 -0
- package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
- package/dist/core/{config.js → config/config.js} +10 -8
- package/dist/core/env-secret-ref.js +90 -0
- package/dist/core/errors.js +13 -3
- package/dist/core/events.js +27 -4
- package/dist/core/file-lock.js +1 -1
- package/dist/core/improve-types.js +48 -0
- package/dist/core/lesson-lint.js +2 -2
- package/dist/core/logs-db.js +304 -0
- package/dist/core/paths.js +2 -2
- package/dist/core/ripgrep/install.js +2 -2
- package/dist/core/ripgrep/resolve.js +2 -2
- package/dist/core/state-db.js +195 -60
- package/dist/core/text-truncation.js +148 -0
- package/dist/core/time.js +1 -1
- package/dist/core/write-source.js +98 -85
- package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
- package/dist/indexer/{db.js → db/db.js} +128 -118
- package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
- package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
- package/dist/indexer/ensure-index.js +4 -4
- package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
- package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
- package/dist/indexer/indexer.js +37 -30
- package/dist/indexer/init.js +54 -0
- package/dist/indexer/manifest.js +10 -10
- package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +141 -33
- package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
- package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
- package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
- package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
- package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
- package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
- package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
- package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
- package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
- package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
- package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
- package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
- package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
- package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
- package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
- package/dist/indexer/{walker.js → walk/walker.js} +4 -3
- package/dist/integrations/agent/builder-shared.js +39 -0
- package/dist/integrations/agent/builders.js +14 -81
- package/dist/integrations/agent/config.js +6 -4
- package/dist/integrations/agent/detect.js +1 -1
- package/dist/integrations/agent/index.js +23 -8
- package/dist/integrations/agent/prompts.js +2 -3
- package/dist/integrations/agent/runner.js +22 -3
- package/dist/integrations/agent/spawn.js +9 -10
- package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
- package/dist/integrations/harnesses/claude/config-import.js +70 -0
- package/dist/integrations/harnesses/claude/index.js +64 -0
- package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +32 -5
- package/dist/integrations/harnesses/index.js +144 -0
- package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
- package/dist/integrations/harnesses/opencode/config-import.js +82 -0
- package/dist/integrations/harnesses/opencode/index.js +59 -0
- package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
- package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
- package/dist/integrations/harnesses/types.js +43 -0
- package/dist/integrations/lockfile.js +7 -16
- package/dist/integrations/session-logs/index.js +82 -9
- package/dist/llm/call-ai.js +4 -4
- package/dist/llm/client.js +146 -6
- package/dist/llm/embedder.js +6 -6
- package/dist/llm/embedders/local.js +9 -22
- package/dist/llm/embedders/remote.js +2 -2
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +31 -12
- package/dist/llm/index-passes.js +1 -1
- package/dist/llm/memory-infer.js +12 -5
- package/dist/llm/metadata-enhance.js +2 -2
- package/dist/llm/usage-persist.js +77 -0
- package/dist/llm/usage-telemetry.js +103 -0
- package/dist/output/context.js +9 -46
- package/dist/output/html-render.js +73 -0
- package/dist/output/renderers.js +88 -58
- package/dist/output/shapes/curate.js +7 -3
- package/dist/output/shapes/distill.js +7 -3
- package/dist/output/shapes/env-list.js +18 -16
- package/dist/output/shapes/events.js +5 -4
- package/dist/output/shapes/helpers.js +19 -5
- package/dist/output/shapes/history.js +7 -3
- package/dist/output/shapes/passthrough.js +8 -11
- package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
- package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
- package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
- package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
- package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
- package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
- package/dist/output/shapes/registry-search.js +7 -3
- package/dist/output/shapes/registry.js +12 -0
- package/dist/output/shapes/search.js +7 -3
- package/dist/output/shapes/secret-list.js +18 -16
- package/dist/output/shapes/show.js +7 -3
- package/dist/output/shapes.js +55 -30
- package/dist/output/text/add.js +2 -3
- package/dist/output/text/clone.js +2 -3
- package/dist/output/text/config.js +2 -3
- package/dist/output/text/curate.js +4 -3
- package/dist/output/text/distill.js +2 -3
- package/dist/output/text/enable-disable.js +5 -4
- package/dist/output/text/env.js +13 -0
- package/dist/output/text/events.js +5 -4
- package/dist/output/text/feedback.js +4 -3
- package/dist/output/text/helpers.js +123 -40
- package/dist/output/text/history.js +2 -3
- package/dist/output/text/import.js +2 -3
- package/dist/output/text/index.js +2 -3
- package/dist/output/text/info.js +2 -3
- package/dist/output/text/init.js +2 -3
- package/dist/output/text/list.js +2 -3
- package/dist/output/text/proposal/producer.js +9 -0
- package/dist/output/text/proposal/proposal.js +13 -0
- package/dist/output/text/registry-commands.js +8 -7
- package/dist/output/text/registry.js +12 -0
- package/dist/output/text/remember.js +4 -3
- package/dist/output/text/remove.js +2 -3
- package/dist/output/text/save.js +2 -3
- package/dist/output/text/search.js +4 -3
- package/dist/output/text/show.js +4 -3
- package/dist/output/text/update.js +2 -3
- package/dist/output/text/upgrade.js +2 -3
- package/dist/output/text/wiki.js +12 -11
- package/dist/output/text/workflow.js +12 -10
- package/dist/output/text.js +66 -32
- package/dist/registry/build-index.js +11 -10
- package/dist/registry/factory.js +1 -1
- package/dist/registry/origin-resolve.js +1 -1
- package/dist/registry/providers/index.js +2 -2
- package/dist/registry/providers/skills-sh.js +91 -72
- package/dist/registry/providers/static-index.js +75 -52
- package/dist/registry/resolve.js +3 -3
- package/dist/runtime.js +242 -0
- package/dist/scripts/migrate-storage.js +1654 -683
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +254 -168
- package/dist/setup/detect.js +311 -9
- package/dist/setup/harness-config-import.js +6 -120
- package/dist/setup/setup.js +454 -43
- package/dist/sources/include.js +1 -1
- package/dist/sources/provider-factory.js +2 -2
- package/dist/sources/providers/filesystem.js +3 -3
- package/dist/sources/providers/git.js +9 -9
- package/dist/sources/providers/index.js +4 -4
- package/dist/sources/providers/npm.js +6 -6
- package/dist/sources/providers/provider-utils.js +13 -20
- package/dist/sources/providers/sync-from-ref.js +5 -5
- package/dist/sources/providers/tar-utils.js +2 -2
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +5 -5
- package/dist/sources/website-ingest.js +5 -5
- package/dist/storage/database.js +102 -0
- package/dist/storage/engines/sqlite-migrations.js +42 -0
- package/dist/storage/locations.js +25 -0
- package/dist/storage/repositories/index-db.js +43 -0
- package/dist/storage/repositories/workflow-runs-repository.js +141 -0
- package/dist/tasks/backends/cron.js +4 -4
- package/dist/tasks/backends/exec-utils.js +32 -0
- package/dist/tasks/backends/index.js +3 -3
- package/dist/tasks/backends/launchd.js +7 -14
- package/dist/tasks/backends/schtasks.js +7 -16
- package/dist/tasks/embedded.js +71 -0
- package/dist/tasks/parser.js +2 -2
- package/dist/tasks/resolveAkmBin.js +1 -1
- package/dist/tasks/runner.js +127 -31
- package/dist/tasks/schedule.js +1 -1
- package/dist/tasks/validator.js +7 -7
- package/dist/text-import-hook.mjs +51 -0
- package/dist/version.js +2 -1
- package/dist/wiki/wiki.js +7 -7
- package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
- package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
- package/dist/workflows/cli.js +1 -1
- package/dist/workflows/db.js +54 -32
- package/dist/workflows/parser.js +4 -4
- package/dist/workflows/renderer.js +5 -5
- package/dist/workflows/runtime/agent-identity.js +56 -0
- package/dist/workflows/runtime/checkin.js +57 -0
- package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
- package/dist/workflows/validate-summary.js +82 -0
- package/docs/README.md +1 -1
- package/docs/data-and-telemetry.md +6 -6
- package/package.json +17 -8
- package/dist/commands/add-cli.js +0 -279
- package/dist/commands/env.js +0 -213
- package/dist/integrations/agent/sdk-runner.js +0 -126
- package/dist/output/shapes/vault-list.js +0 -19
- package/dist/output/text/proposal-producer.js +0 -8
- package/dist/output/text/proposal.js +0 -12
- package/dist/output/text/vault.js +0 -16
- /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
- /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
- /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
- /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
- /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
- /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
- /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
- /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
|
@@ -0,0 +1,490 @@
|
|
|
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
|
+
* `akm proposal` command family (#225). Extracted verbatim from src/cli.ts
|
|
6
|
+
* (WS6) so the God Module shrinks; the `main.subCommands.proposal` key and
|
|
7
|
+
* every subcommand's args/output shape are byte-identical. Leaf handlers are
|
|
8
|
+
* migrated to `defineJsonCommand`, which wraps the body in `runWithJsonErrors`
|
|
9
|
+
* and emits the same JSON envelope (stdout/stderr/exit-code) as the inline
|
|
10
|
+
* `runWithJsonErrors` form it replaces.
|
|
11
|
+
*/
|
|
12
|
+
import { defineCommand } from "citty";
|
|
13
|
+
import { hasSubcommand, parsePositiveIntFlag } from "../../cli/parse-args.js";
|
|
14
|
+
import { defineJsonCommand, output, runWithJsonErrors } from "../../cli/shared.js";
|
|
15
|
+
import { resolveStashDir } from "../../core/common.js";
|
|
16
|
+
import { loadConfig } from "../../core/config/config.js";
|
|
17
|
+
import { UsageError } from "../../core/errors.js";
|
|
18
|
+
import { resolveTriageJudgmentRunner } from "../../integrations/agent/runner.js";
|
|
19
|
+
import { installLlmUsagePersistenceIfAbsent } from "../../llm/usage-persist.js";
|
|
20
|
+
import { withLlmStage } from "../../llm/usage-telemetry.js";
|
|
21
|
+
import { resolveImproveProfile } from "../improve/improve-profiles.js";
|
|
22
|
+
import { drainProposals } from "./drain.js";
|
|
23
|
+
import { resolveDrainPolicy } from "./drain-policies.js";
|
|
24
|
+
import { akmProposalAccept, akmProposalDiff, akmProposalList, akmProposalReject, akmProposalRevert, akmProposalShow, } from "./proposal.js";
|
|
25
|
+
function parseProposalStatus(raw) {
|
|
26
|
+
if (raw === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
const trimmed = raw.trim();
|
|
29
|
+
if (!trimmed)
|
|
30
|
+
return undefined;
|
|
31
|
+
if (trimmed === "pending" || trimmed === "accepted" || trimmed === "rejected" || trimmed === "reverted") {
|
|
32
|
+
return trimmed;
|
|
33
|
+
}
|
|
34
|
+
throw new UsageError(`Invalid --status value: "${raw}". Expected one of: pending, accepted, rejected, reverted.`, "INVALID_FLAG_VALUE");
|
|
35
|
+
}
|
|
36
|
+
const proposalListCommand = defineJsonCommand({
|
|
37
|
+
meta: { name: "list", description: "List proposal queue entries" },
|
|
38
|
+
args: {
|
|
39
|
+
status: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Filter by status (pending|accepted|rejected|reverted)",
|
|
42
|
+
},
|
|
43
|
+
ref: { type: "string", description: "Filter by asset ref (type:name)" },
|
|
44
|
+
type: { type: "string", description: "Filter by asset type" },
|
|
45
|
+
},
|
|
46
|
+
run({ args }) {
|
|
47
|
+
const status = parseProposalStatus(args.status);
|
|
48
|
+
const result = akmProposalList({
|
|
49
|
+
status,
|
|
50
|
+
ref: args.ref,
|
|
51
|
+
type: args.type,
|
|
52
|
+
includeArchive: status === "accepted" || status === "rejected" || status === "reverted",
|
|
53
|
+
});
|
|
54
|
+
output("proposal-list", result);
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const proposalAcceptCommand = defineJsonCommand({
|
|
58
|
+
meta: { name: "accept", description: "Accept a proposal and promote it into the stash" },
|
|
59
|
+
args: {
|
|
60
|
+
id: {
|
|
61
|
+
type: "positional",
|
|
62
|
+
description: "Proposal id (uuid / prefix) or asset ref (e.g. skill:akm-dream). Optional when --generator is provided.",
|
|
63
|
+
required: false,
|
|
64
|
+
},
|
|
65
|
+
target: { type: "string", description: "Override the write target by source name" },
|
|
66
|
+
// F-6 / #393: Batch accept by generator, diff size, or age.
|
|
67
|
+
generator: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "F-6: Bulk-accept all pending proposals from this generator (e.g. reflect, distill). Requires no positional id.",
|
|
70
|
+
},
|
|
71
|
+
"max-diff-lines": {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "F-6: When bulk-accepting, only accept proposals whose content is <= this many lines. Skips larger proposals.",
|
|
74
|
+
},
|
|
75
|
+
"older-than": {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "F-6: When bulk-accepting, only accept proposals created more than this many days ago (e.g. '7' for 7 days).",
|
|
78
|
+
},
|
|
79
|
+
"dry-run": {
|
|
80
|
+
type: "boolean",
|
|
81
|
+
description: "F-6: List proposals that would be bulk-accepted without accepting them.",
|
|
82
|
+
default: false,
|
|
83
|
+
},
|
|
84
|
+
yes: {
|
|
85
|
+
type: "boolean",
|
|
86
|
+
alias: "y",
|
|
87
|
+
description: "Skip confirmation prompt (required in non-interactive mode for bulk accept)",
|
|
88
|
+
default: false,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
async run({ args }) {
|
|
92
|
+
const generator = args.generator;
|
|
93
|
+
// F-6 / #393: Bulk-accept when --generator is provided without a positional id.
|
|
94
|
+
if (generator && !args.id) {
|
|
95
|
+
const { confirmDestructive } = await import("../../cli/confirm.js");
|
|
96
|
+
const confirmed = await confirmDestructive(`Bulk-accept all matching proposals from generator "${generator}"? This cannot be undone.`, { yes: args.yes === true || args["dry-run"] === true });
|
|
97
|
+
if (!confirmed) {
|
|
98
|
+
process.stderr.write("Aborted.\n");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const { listProposals } = await import("./validators/proposals.js");
|
|
102
|
+
const stashDir = resolveStashDir();
|
|
103
|
+
const rawMaxDiff = args["max-diff-lines"] ? Number.parseInt(String(args["max-diff-lines"]), 10) : undefined;
|
|
104
|
+
if (rawMaxDiff !== undefined && (Number.isNaN(rawMaxDiff) || rawMaxDiff < 0)) {
|
|
105
|
+
throw new UsageError("--max-diff-lines must be a non-negative integer", "INVALID_FLAG_VALUE");
|
|
106
|
+
}
|
|
107
|
+
const rawOlderThan = args["older-than"] ? Number.parseInt(String(args["older-than"]), 10) : undefined;
|
|
108
|
+
if (rawOlderThan !== undefined && (Number.isNaN(rawOlderThan) || rawOlderThan < 0)) {
|
|
109
|
+
throw new UsageError("--older-than must be a non-negative integer (days)", "INVALID_FLAG_VALUE");
|
|
110
|
+
}
|
|
111
|
+
const maxDiffLines = rawMaxDiff;
|
|
112
|
+
const olderThanMs = rawOlderThan !== undefined ? rawOlderThan * 86_400_000 : undefined;
|
|
113
|
+
const pending = listProposals(stashDir, { status: "pending" }).filter((p) => {
|
|
114
|
+
if (p.source !== generator)
|
|
115
|
+
return false;
|
|
116
|
+
if (maxDiffLines !== undefined) {
|
|
117
|
+
const lines = (p.payload.content ?? "").split("\n").length;
|
|
118
|
+
if (lines > maxDiffLines)
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (olderThanMs !== undefined) {
|
|
122
|
+
const age = Date.now() - new Date(p.createdAt).getTime();
|
|
123
|
+
if (age < olderThanMs)
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
});
|
|
128
|
+
const results = [];
|
|
129
|
+
for (const proposal of pending) {
|
|
130
|
+
if (args["dry-run"]) {
|
|
131
|
+
results.push({ id: proposal.id, ref: proposal.ref, source: proposal.source, dryRun: true });
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const result = await akmProposalAccept({ id: proposal.id, target: args.target });
|
|
135
|
+
results.push(result);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
output("proposal-accept-batch", { accepted: results.length, results, dryRun: args["dry-run"] });
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!args.id) {
|
|
142
|
+
throw new UsageError("Usage: akm proposal accept <id> OR akm proposal accept --generator <generator>", "MISSING_REQUIRED_ARGUMENT");
|
|
143
|
+
}
|
|
144
|
+
const result = await akmProposalAccept({ id: args.id, target: args.target });
|
|
145
|
+
output("proposal-accept", result);
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
const proposalRejectCommand = defineJsonCommand({
|
|
149
|
+
meta: { name: "reject", description: "Reject a proposal and record the reason" },
|
|
150
|
+
args: {
|
|
151
|
+
id: {
|
|
152
|
+
type: "positional",
|
|
153
|
+
description: "Proposal id (uuid / prefix) or asset ref (e.g. skill:akm-dream). Optional when --generator is provided.",
|
|
154
|
+
required: false,
|
|
155
|
+
},
|
|
156
|
+
reason: { type: "string", description: "Reason for rejection (required)" },
|
|
157
|
+
// F-6 / #393: Batch reject by generator, diff size, or age.
|
|
158
|
+
generator: {
|
|
159
|
+
type: "string",
|
|
160
|
+
description: "F-6: Bulk-reject all pending proposals from this generator (e.g. reflect, distill). Requires no positional id.",
|
|
161
|
+
},
|
|
162
|
+
"max-diff-lines": {
|
|
163
|
+
type: "string",
|
|
164
|
+
description: "F-6: When bulk-rejecting, only reject proposals whose content is <= this many lines. Skips larger proposals.",
|
|
165
|
+
},
|
|
166
|
+
"older-than": {
|
|
167
|
+
type: "string",
|
|
168
|
+
description: "F-6: When bulk-rejecting, only reject proposals created more than this many days ago (e.g. '7' for 7 days).",
|
|
169
|
+
},
|
|
170
|
+
"dry-run": {
|
|
171
|
+
type: "boolean",
|
|
172
|
+
description: "F-6: List proposals that would be bulk-rejected without rejecting them.",
|
|
173
|
+
default: false,
|
|
174
|
+
},
|
|
175
|
+
yes: {
|
|
176
|
+
type: "boolean",
|
|
177
|
+
alias: "y",
|
|
178
|
+
description: "Skip confirmation prompt (required in non-interactive mode)",
|
|
179
|
+
default: false,
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
async run({ args }) {
|
|
183
|
+
const generator = args.generator;
|
|
184
|
+
if (!args.reason || !String(args.reason).trim()) {
|
|
185
|
+
throw new UsageError("Usage: akm proposal reject <id> --reason '<reason>' OR akm proposal reject --generator <generator> --reason '<reason>'", "MISSING_REQUIRED_ARGUMENT");
|
|
186
|
+
}
|
|
187
|
+
// F-6 / #393: Bulk-reject when --generator is provided without a positional id.
|
|
188
|
+
if (generator && !args.id) {
|
|
189
|
+
const { confirmDestructive } = await import("../../cli/confirm.js");
|
|
190
|
+
const confirmed = await confirmDestructive(`Bulk-reject all matching proposals from generator "${generator}"? This cannot be undone.`, { yes: args.yes === true || args["dry-run"] === true });
|
|
191
|
+
if (!confirmed) {
|
|
192
|
+
process.stderr.write("Aborted.\n");
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const { listProposals } = await import("./validators/proposals.js");
|
|
196
|
+
const stashDir = resolveStashDir();
|
|
197
|
+
const rawMaxDiff = args["max-diff-lines"] ? Number.parseInt(String(args["max-diff-lines"]), 10) : undefined;
|
|
198
|
+
if (rawMaxDiff !== undefined && (Number.isNaN(rawMaxDiff) || rawMaxDiff < 0)) {
|
|
199
|
+
throw new UsageError("--max-diff-lines must be a non-negative integer", "INVALID_FLAG_VALUE");
|
|
200
|
+
}
|
|
201
|
+
const rawOlderThan = args["older-than"] ? Number.parseInt(String(args["older-than"]), 10) : undefined;
|
|
202
|
+
if (rawOlderThan !== undefined && (Number.isNaN(rawOlderThan) || rawOlderThan < 0)) {
|
|
203
|
+
throw new UsageError("--older-than must be a non-negative integer (days)", "INVALID_FLAG_VALUE");
|
|
204
|
+
}
|
|
205
|
+
const maxDiffLines = rawMaxDiff;
|
|
206
|
+
const olderThanMs = rawOlderThan !== undefined ? rawOlderThan * 86_400_000 : undefined;
|
|
207
|
+
const pending = listProposals(stashDir, { status: "pending" }).filter((p) => {
|
|
208
|
+
if (p.source !== generator)
|
|
209
|
+
return false;
|
|
210
|
+
if (maxDiffLines !== undefined) {
|
|
211
|
+
const lines = (p.payload.content ?? "").split("\n").length;
|
|
212
|
+
if (lines > maxDiffLines)
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
if (olderThanMs !== undefined) {
|
|
216
|
+
const age = Date.now() - new Date(p.createdAt).getTime();
|
|
217
|
+
if (age < olderThanMs)
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return true;
|
|
221
|
+
});
|
|
222
|
+
const results = [];
|
|
223
|
+
for (const proposal of pending) {
|
|
224
|
+
if (args["dry-run"]) {
|
|
225
|
+
results.push({ id: proposal.id, ref: proposal.ref, source: proposal.source, dryRun: true });
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
const result = akmProposalReject({ id: proposal.id, reason: String(args.reason) });
|
|
229
|
+
results.push(result);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
output("proposal-reject-batch", { rejected: results.length, results, dryRun: args["dry-run"] });
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (!args.id) {
|
|
236
|
+
throw new UsageError("Usage: akm proposal reject <id> --reason '<reason>' OR akm proposal reject --generator <generator> --reason '<reason>'", "MISSING_REQUIRED_ARGUMENT");
|
|
237
|
+
}
|
|
238
|
+
const { confirmDestructive } = await import("../../cli/confirm.js");
|
|
239
|
+
const confirmed = await confirmDestructive(`Reject proposal "${args.id}"? This cannot be undone.`, {
|
|
240
|
+
yes: args.yes === true,
|
|
241
|
+
});
|
|
242
|
+
if (!confirmed) {
|
|
243
|
+
process.stderr.write("Aborted.\n");
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const result = akmProposalReject({ id: args.id, reason: String(args.reason) });
|
|
247
|
+
output("proposal-reject", result);
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
const proposalDiffCommand = defineJsonCommand({
|
|
251
|
+
meta: { name: "diff", description: "Show the diff for a proposal (accepts full UUID, UUID prefix, or asset ref)" },
|
|
252
|
+
args: {
|
|
253
|
+
id: {
|
|
254
|
+
type: "positional",
|
|
255
|
+
description: "Proposal id (uuid / prefix) or asset ref (e.g. skill:akm-dream)",
|
|
256
|
+
required: true,
|
|
257
|
+
},
|
|
258
|
+
target: { type: "string", description: "Override the write target by source name" },
|
|
259
|
+
},
|
|
260
|
+
run({ args }) {
|
|
261
|
+
const result = akmProposalDiff({ id: args.id, target: args.target });
|
|
262
|
+
output("proposal-diff", result);
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
// Phase 6C (Advantage D6c): revert an accepted proposal.
|
|
266
|
+
//
|
|
267
|
+
// Exit codes (mapped by `runWithJsonErrors` from the typed errors thrown by
|
|
268
|
+
// `akmProposalRevert` / `revertProposal`):
|
|
269
|
+
// 0 — success; prior content restored.
|
|
270
|
+
// 1 — generic error (also used by `UsageError("INVALID_FLAG_VALUE")` and
|
|
271
|
+
// `UsageError("MISSING_REQUIRED_ARGUMENT")` when the proposal is not
|
|
272
|
+
// accepted, or no backup is available).
|
|
273
|
+
// 1 — `NotFoundError("FILE_NOT_FOUND")` when the proposal id does not resolve.
|
|
274
|
+
const proposalRevertCommand = defineJsonCommand({
|
|
275
|
+
meta: {
|
|
276
|
+
name: "revert",
|
|
277
|
+
description: "Revert an accepted proposal: restore the prior asset content from the backup captured at promotion time. " +
|
|
278
|
+
"Errors if the proposal is not accepted or has no backup (new-asset proposals leave no backup). " +
|
|
279
|
+
"Accepts the full proposal UUID or the asset ref. UUID prefixes are not supported for archived proposals — use the full UUID.",
|
|
280
|
+
},
|
|
281
|
+
args: {
|
|
282
|
+
id: {
|
|
283
|
+
type: "positional",
|
|
284
|
+
description: "Proposal id (full uuid) or asset ref (e.g. skill:akm-dream). UUID prefixes are not supported for archived proposals — use the full UUID.",
|
|
285
|
+
required: true,
|
|
286
|
+
},
|
|
287
|
+
target: { type: "string", description: "Override the write target by source name" },
|
|
288
|
+
},
|
|
289
|
+
async run({ args }) {
|
|
290
|
+
const result = await akmProposalRevert({
|
|
291
|
+
id: args.id,
|
|
292
|
+
target: args.target,
|
|
293
|
+
});
|
|
294
|
+
output("proposal-revert", result);
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
// `proposal show` (#225): show a single proposal with its validation findings.
|
|
298
|
+
// `akmProposalShow` already backs `akm show proposal <id>` (now deprecated); this
|
|
299
|
+
// is the canonical noun-group entry point.
|
|
300
|
+
const proposalShowCommand = defineJsonCommand({
|
|
301
|
+
meta: { name: "show", description: "Show a single proposal and its validation findings" },
|
|
302
|
+
args: {
|
|
303
|
+
id: {
|
|
304
|
+
type: "positional",
|
|
305
|
+
description: "Proposal id (uuid / prefix) or asset ref (e.g. skill:akm-dream)",
|
|
306
|
+
required: true,
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
run({ args }) {
|
|
310
|
+
const result = akmProposalShow({ id: args.id });
|
|
311
|
+
output("proposal-show", result);
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
const proposalDrainCommand = defineJsonCommand({
|
|
315
|
+
meta: {
|
|
316
|
+
name: "drain",
|
|
317
|
+
description: "Drain the standing pending proposal backlog using a deterministic triage policy",
|
|
318
|
+
},
|
|
319
|
+
args: {
|
|
320
|
+
policy: {
|
|
321
|
+
type: "string",
|
|
322
|
+
description: "Built-in preset (personal-stash|conservative|manual) or path to a policy file",
|
|
323
|
+
},
|
|
324
|
+
"dry-run": {
|
|
325
|
+
type: "boolean",
|
|
326
|
+
description: "List what would be accepted/rejected/deferred without writing.",
|
|
327
|
+
default: false,
|
|
328
|
+
},
|
|
329
|
+
yes: {
|
|
330
|
+
type: "boolean",
|
|
331
|
+
alias: "y",
|
|
332
|
+
description: "Skip confirmation prompt (required in non-interactive mode for promotion).",
|
|
333
|
+
default: false,
|
|
334
|
+
},
|
|
335
|
+
"max-accepts": {
|
|
336
|
+
type: "string",
|
|
337
|
+
description: "Hard per-run accept ceiling. Accepts beyond this are reported as skippedByCap.",
|
|
338
|
+
},
|
|
339
|
+
"max-diff-lines": {
|
|
340
|
+
type: "string",
|
|
341
|
+
description: "Defer (never promote) accepts whose proposed content exceeds this many lines.",
|
|
342
|
+
},
|
|
343
|
+
"older-than": {
|
|
344
|
+
type: "string",
|
|
345
|
+
description: "Only consider proposals created more than this many days ago.",
|
|
346
|
+
},
|
|
347
|
+
promote: {
|
|
348
|
+
type: "boolean",
|
|
349
|
+
description: "Promote (accept) matching proposals. Default is queue mode (stage only, no writes to assets).",
|
|
350
|
+
default: false,
|
|
351
|
+
},
|
|
352
|
+
judgment: {
|
|
353
|
+
type: "boolean",
|
|
354
|
+
description: "Opt into the judgment tier (llm by default; agent/sdk per config) for deferred items. No-op with a logged triage_deferred summary when no runner is configured.",
|
|
355
|
+
default: false,
|
|
356
|
+
},
|
|
357
|
+
profile: {
|
|
358
|
+
type: "string",
|
|
359
|
+
description: "Read the triage block (policy, applyMode, ceilings, judgment) from this improve profile.",
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
async run({ args }) {
|
|
363
|
+
const stashDir = resolveStashDir();
|
|
364
|
+
const cfg = loadConfig();
|
|
365
|
+
// Phase 2: read the triage block from the named improve profile. CLI flags
|
|
366
|
+
// always override config; config supplies defaults for any flag omitted.
|
|
367
|
+
const triageConfig = args.profile !== undefined ? resolveImproveProfile(args.profile, cfg).processes?.triage : undefined;
|
|
368
|
+
const policy = resolveDrainPolicy(args.policy ?? triageConfig?.policy);
|
|
369
|
+
const dryRun = args["dry-run"] === true;
|
|
370
|
+
const applyMode = args.promote === true ? "promote" : (triageConfig?.applyMode ?? "queue");
|
|
371
|
+
const maxAccepts = parsePositiveIntFlag(args["max-accepts"], "--max-accepts") ??
|
|
372
|
+
triageConfig?.maxAcceptsPerRun ??
|
|
373
|
+
25;
|
|
374
|
+
const maxDiffLines = parsePositiveIntFlag(args["max-diff-lines"], "--max-diff-lines") ??
|
|
375
|
+
triageConfig?.maxDiffLines;
|
|
376
|
+
const rawOlderThan = parsePositiveIntFlag(args["older-than"], "--older-than");
|
|
377
|
+
const olderThanMs = rawOlderThan !== undefined ? rawOlderThan * 86_400_000 : undefined;
|
|
378
|
+
// Promotion in promote mode is destructive (commits to git, no batch revert).
|
|
379
|
+
if (applyMode === "promote" && !dryRun) {
|
|
380
|
+
const { confirmDestructive } = await import("../../cli/confirm.js");
|
|
381
|
+
const confirmed = await confirmDestructive(`Drain and promote matching pending proposals under policy "${policy.name}"? Promotions commit to git and cannot be batch-reverted.`, { yes: args.yes === true });
|
|
382
|
+
if (!confirmed) {
|
|
383
|
+
process.stderr.write("Aborted.\n");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// `--older-than` is applied here as a pre-filter on excludeIds: ids that
|
|
388
|
+
// are too fresh are excluded so the engine never touches them. This reads
|
|
389
|
+
// the pending set once here; drainProposals reads the pending set again
|
|
390
|
+
// internally, so a future engine-level olderThan option could remove this
|
|
391
|
+
// second read (engine API owned by another agent — not changed here).
|
|
392
|
+
let excludeIds;
|
|
393
|
+
if (olderThanMs !== undefined) {
|
|
394
|
+
const { listProposals } = await import("./validators/proposals.js");
|
|
395
|
+
const now = Date.now();
|
|
396
|
+
excludeIds = new Set(listProposals(stashDir, { status: "pending" })
|
|
397
|
+
// Fail SAFE: exclude a proposal when its age cannot be computed
|
|
398
|
+
// (NaN createdAt) OR it is too fresh. An unparseable createdAt must
|
|
399
|
+
// never be treated as old enough to drain/promote.
|
|
400
|
+
.filter((proposal) => {
|
|
401
|
+
const age = now - new Date(proposal.createdAt).getTime();
|
|
402
|
+
return Number.isNaN(age) || age < olderThanMs;
|
|
403
|
+
})
|
|
404
|
+
.map((proposal) => proposal.id));
|
|
405
|
+
}
|
|
406
|
+
// Phase 3: resolve the judgment runner when --judgment is set. Default
|
|
407
|
+
// mode is llm; falls back to defaults.llm when the triage block sets
|
|
408
|
+
// neither mode nor profile (mirrors resolveValidationRunner). null when
|
|
409
|
+
// nothing is configured → the engine leaves deferred items unresolved and
|
|
410
|
+
// emits triage_deferred.
|
|
411
|
+
const judgment = args.judgment === true ? resolveTriageJudgmentRunner(triageConfig?.judgment, cfg) : null;
|
|
412
|
+
// #576: persist + attribute per-call LLM usage for the standalone drain
|
|
413
|
+
// path. `IfAbsent` keeps an enclosing `akm improve` sink in charge when
|
|
414
|
+
// drain runs as a sub-step; the disposer clears only a sink we installed.
|
|
415
|
+
const disposeDrainUsageSink = installLlmUsagePersistenceIfAbsent();
|
|
416
|
+
let result;
|
|
417
|
+
try {
|
|
418
|
+
result = await withLlmStage("drain", () => drainProposals({
|
|
419
|
+
stashDir,
|
|
420
|
+
policy,
|
|
421
|
+
applyMode,
|
|
422
|
+
maxAccepts,
|
|
423
|
+
dryRun,
|
|
424
|
+
...(maxDiffLines !== undefined ? { maxDiffLines } : {}),
|
|
425
|
+
...(excludeIds ? { excludeIds } : {}),
|
|
426
|
+
judgment,
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
finally {
|
|
430
|
+
disposeDrainUsageSink();
|
|
431
|
+
}
|
|
432
|
+
output("proposal-drain", {
|
|
433
|
+
schemaVersion: 1,
|
|
434
|
+
ok: true,
|
|
435
|
+
policy: policy.name,
|
|
436
|
+
applyMode,
|
|
437
|
+
dryRun,
|
|
438
|
+
promoted: result.promoted,
|
|
439
|
+
rejected: result.rejected,
|
|
440
|
+
deferred: result.deferred,
|
|
441
|
+
skippedByCap: result.skippedByCap,
|
|
442
|
+
});
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
// ── proposal noun group (#225 / 0.8 CLI stabilization) ────────────────────────
|
|
446
|
+
//
|
|
447
|
+
// `akm proposal <verb>` is the canonical grammar in 0.8. The flat verbs
|
|
448
|
+
// (`proposals`/`accept`/`reject`/`diff`/`revert`) remain as deprecated aliases
|
|
449
|
+
// that warn to stderr and delegate to the same command bodies; they are removed
|
|
450
|
+
// in 0.9.0. Bare `akm proposal` behaves as `proposal list` (mirrors `akm env`).
|
|
451
|
+
// Single source of truth: the routing set is derived from the subCommands keys
|
|
452
|
+
// (M10) so adding a subcommand can never silently desync from `hasSubcommand`.
|
|
453
|
+
const proposalSubCommands = {
|
|
454
|
+
list: proposalListCommand,
|
|
455
|
+
show: proposalShowCommand,
|
|
456
|
+
diff: proposalDiffCommand,
|
|
457
|
+
accept: proposalAcceptCommand,
|
|
458
|
+
reject: proposalRejectCommand,
|
|
459
|
+
revert: proposalRevertCommand,
|
|
460
|
+
drain: proposalDrainCommand,
|
|
461
|
+
};
|
|
462
|
+
const PROPOSAL_SUBCOMMAND_SET = new Set(Object.keys(proposalSubCommands));
|
|
463
|
+
export const proposalCommand = defineCommand({
|
|
464
|
+
meta: { name: "proposal", description: "Manage the proposal queue: list, show, diff, accept, reject, revert" },
|
|
465
|
+
args: {
|
|
466
|
+
status: {
|
|
467
|
+
type: "string",
|
|
468
|
+
description: "Filter by status (pending|accepted|rejected|reverted)",
|
|
469
|
+
},
|
|
470
|
+
ref: { type: "string", description: "Filter by asset ref (type:name)" },
|
|
471
|
+
type: { type: "string", description: "Filter by asset type" },
|
|
472
|
+
},
|
|
473
|
+
subCommands: proposalSubCommands,
|
|
474
|
+
run({ args }) {
|
|
475
|
+
return runWithJsonErrors(() => {
|
|
476
|
+
// citty runs the group body even after a subcommand; short-circuit so the
|
|
477
|
+
// default-to-list body only fires for bare `akm proposal [--status …]`.
|
|
478
|
+
if (hasSubcommand(args, PROPOSAL_SUBCOMMAND_SET))
|
|
479
|
+
return;
|
|
480
|
+
const status = parseProposalStatus(args.status);
|
|
481
|
+
const result = akmProposalList({
|
|
482
|
+
status,
|
|
483
|
+
ref: args.ref,
|
|
484
|
+
type: args.type,
|
|
485
|
+
includeArchive: status === "accepted" || status === "rejected" || status === "reverted",
|
|
486
|
+
});
|
|
487
|
+
output("proposal-list", result);
|
|
488
|
+
});
|
|
489
|
+
},
|
|
490
|
+
});
|
|
@@ -11,17 +11,28 @@
|
|
|
11
11
|
* fallback in the output layer — every shape is registered explicitly in
|
|
12
12
|
* `src/output/shapes.ts` and `src/output/text.ts`.
|
|
13
13
|
*/
|
|
14
|
-
import { resolveStashDir } from "
|
|
15
|
-
import { loadConfig } from "
|
|
16
|
-
import { UsageError } from "
|
|
17
|
-
import { appendEvent } from "
|
|
18
|
-
import { archiveProposal, createProposal, diffProposal, getProposal, isProposalSkipped, listProposals, promoteProposal, resolveProposalId, revertProposal, validateProposal, } from "
|
|
14
|
+
import { resolveStashDir } from "../../core/common.js";
|
|
15
|
+
import { loadConfig } from "../../core/config/config.js";
|
|
16
|
+
import { UsageError } from "../../core/errors.js";
|
|
17
|
+
import { appendEvent } from "../../core/events.js";
|
|
18
|
+
import { archiveProposal, createProposal, diffProposal, getProposal, isProposalSkipped, listProposals, promoteProposal, resolveProposalId, revertProposal, validateProposal, } from "./validators/proposals.js";
|
|
19
19
|
// ── Shared helpers ──────────────────────────────────────────────────────────
|
|
20
20
|
function resolveStash(stashDir) {
|
|
21
21
|
if (stashDir)
|
|
22
22
|
return stashDir;
|
|
23
23
|
return resolveStashDir();
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Thin in-process read of the pending proposal queue, used by the health HTML
|
|
27
|
+
* report builder (#582) so it never shells out to `akm proposal list`.
|
|
28
|
+
*
|
|
29
|
+
* Deliberately narrow (one optional arg, returns the storage-layer rows) so
|
|
30
|
+
* the parallel proposal-storage-to-SQLite consolidation only has to swap this
|
|
31
|
+
* one function's body.
|
|
32
|
+
*/
|
|
33
|
+
export function listPendingProposals(stashDir) {
|
|
34
|
+
return listProposals(resolveStash(stashDir), { status: "pending" });
|
|
35
|
+
}
|
|
25
36
|
export function akmProposalList(options = {}) {
|
|
26
37
|
const stash = resolveStash(options.stashDir);
|
|
27
38
|
// `--status accepted|rejected|reverted` implies archive-inclusion since the
|
|
@@ -141,7 +152,7 @@ export function akmProposalCreate(options) {
|
|
|
141
152
|
* (raised by `resolveProposalId` / `getProposal`).
|
|
142
153
|
* - Proposal is not `status === "accepted"` → `UsageError("INVALID_FLAG_VALUE")`
|
|
143
154
|
* with message `"only accepted proposals can be reverted ..."`.
|
|
144
|
-
* - No
|
|
155
|
+
* - No backup content on the record (new-asset proposals capture none) →
|
|
145
156
|
* `UsageError` with message `"no backup available for this proposal ..."`.
|
|
146
157
|
*
|
|
147
158
|
* On success, emits a `proposal_reverted` event for observability, mirroring
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
* `akm reflect`. `propose_invoked` is emitted at command entry.
|
|
14
14
|
*/
|
|
15
15
|
import fs from "node:fs";
|
|
16
|
-
import { parseAssetRef } from "
|
|
17
|
-
import { TYPE_DIRS } from "
|
|
18
|
-
import { resolveStashDir } from "
|
|
19
|
-
import { ConfigError, UsageError } from "
|
|
20
|
-
import { appendEvent } from "
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
16
|
+
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
17
|
+
import { TYPE_DIRS } from "../../core/asset/asset-spec.js";
|
|
18
|
+
import { resolveStashDir } from "../../core/common.js";
|
|
19
|
+
import { ConfigError, UsageError } from "../../core/errors.js";
|
|
20
|
+
import { appendEvent } from "../../core/events.js";
|
|
21
|
+
import { runAgent, } from "../../integrations/agent/index.js";
|
|
22
|
+
import { resolveProcessAgentProfile } from "../../integrations/agent/config.js";
|
|
23
|
+
import { buildProposePrompt, parseAgentProposalPayload } from "../../integrations/agent/prompts.js";
|
|
24
|
+
import { runAgentSdk } from "../../integrations/harnesses/opencode-sdk/index.js";
|
|
25
|
+
import { baseFailureFields, enoentHintMessage, isEnoentFailure, loadAgentConfigFromDisk, resolveAgentProfile, } from "../agent/agent-support.js";
|
|
26
|
+
import { createProposal, isProposalSkipped, } from "./validators/proposals.js";
|
|
27
27
|
function failureEnvelope(result, type, name, fallbackReason = "non_zero_exit") {
|
|
28
28
|
return {
|
|
29
29
|
...baseFailureFields(result, fallbackReason),
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
* assets where the absolute ceiling takes over.
|
|
59
59
|
*/
|
|
60
60
|
// ── Reflect-size guard ───────────────────────────────────────────────────────
|
|
61
|
-
import { parseFrontmatter } from "
|
|
62
|
-
import { detectTruncatedDescription, TRUNCATION_TRAILING_WORDS } from "
|
|
61
|
+
import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
62
|
+
import { detectTruncatedDescription, TRUNCATION_TRAILING_WORDS } from "../../../core/text-truncation.js";
|
|
63
63
|
// ── Description / when_to_use shape ─────────────────────────────────────────
|
|
64
64
|
export const HEADING_FRAGMENT_PATTERNS = [
|
|
65
65
|
/^for example\b/i,
|
|
@@ -115,7 +115,11 @@ export function isValidDescription(value, inputRef, options = {}) {
|
|
|
115
115
|
reason: `description has ${backtickCount} backticks (unbalanced); likely contains a malformed code fragment`,
|
|
116
116
|
};
|
|
117
117
|
if (/^when\b/i.test(v))
|
|
118
|
-
return {
|
|
118
|
+
return {
|
|
119
|
+
ok: false,
|
|
120
|
+
reason: "description starts with 'When' — that pattern belongs in when_to_use",
|
|
121
|
+
severity: "warn",
|
|
122
|
+
};
|
|
119
123
|
if (!options.skipRefTailCheck) {
|
|
120
124
|
const refTail = inputRef.split(":").pop()?.toLowerCase() ?? "";
|
|
121
125
|
if (refTail.length >= 6 && v.toLowerCase().includes(refTail) && v.length < refTail.length + 40)
|
|
@@ -299,6 +303,7 @@ const lessonContentQualityValidator = {
|
|
|
299
303
|
findings.push({
|
|
300
304
|
kind: "invalid-description",
|
|
301
305
|
message: `Lesson proposal ${proposal.id} (${proposal.ref}) has an invalid description: ${descCheck.reason}.`,
|
|
306
|
+
...(descCheck.severity ? { severity: descCheck.severity } : {}),
|
|
302
307
|
});
|
|
303
308
|
const wtuCheck = isValidWhenToUse(fm.when_to_use, proposal.ref);
|
|
304
309
|
if (!wtuCheck.ok)
|
|
@@ -1,10 +1,10 @@
|
|
|
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
|
-
import { parseAssetRef } from "
|
|
5
|
-
import { parseFrontmatter } from "
|
|
6
|
-
import { lintLessonContent } from "
|
|
7
|
-
import { defaultProposalQualityValidators } from "./proposal-quality-validators";
|
|
4
|
+
import { parseAssetRef } from "../../../core/asset/asset-ref.js";
|
|
5
|
+
import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
6
|
+
import { lintLessonContent } from "../../../core/lesson-lint.js";
|
|
7
|
+
import { defaultProposalQualityValidators } from "./proposal-quality-validators.js";
|
|
8
8
|
const genericProposalValidator = {
|
|
9
9
|
name: "generic-proposal-validator",
|
|
10
10
|
appliesTo: () => true,
|
|
@@ -65,5 +65,5 @@ export function runProposalValidators(proposal, validators = defaultProposalVali
|
|
|
65
65
|
if (ctx.stop)
|
|
66
66
|
break;
|
|
67
67
|
}
|
|
68
|
-
return { ok: findings.
|
|
68
|
+
return { ok: findings.every((f) => f.severity === "warn"), findings };
|
|
69
69
|
}
|