akm-cli 0.9.0-rc.1 → 0.9.0-rc.2
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 +223 -9
- package/SECURITY.md +23 -24
- package/dist/assets/help/help-improve.md +10 -10
- package/dist/assets/hints/cli-hints-full.md +44 -10
- package/dist/assets/hints/cli-hints-short.md +6 -2
- package/dist/assets/{profiles → improve-strategies}/default.json +1 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +1 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +2 -3
- package/dist/assets/{profiles → improve-strategies}/reflect-distill.json +3 -4
- package/dist/assets/stash-skeleton/README.md +28 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +12 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +11 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +7 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +7 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +98 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +63 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +127 -0
- package/dist/assets/tasks/core/backup.yml +1 -0
- package/dist/assets/tasks/core/extract.yml +1 -0
- package/dist/assets/tasks/core/improve.yml +1 -0
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +1 -0
- package/dist/assets/tasks/graph-refresh-weekly.yml +4 -4
- package/dist/assets/templates/html/health.html +5 -1
- package/dist/cli/config-migrate.js +31 -138
- package/dist/cli/config-validate.js +10 -8
- package/dist/cli.js +48 -14
- package/dist/commands/agent/agent-dispatch.js +17 -16
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +5 -15
- package/dist/commands/backup-cli.js +54 -0
- package/dist/commands/config-cli.js +45 -159
- package/dist/commands/env/secret.js +8 -5
- package/dist/commands/health/checks.js +130 -83
- package/dist/commands/health/html-report.js +4 -0
- package/dist/commands/health/improve-metrics.js +30 -32
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +4 -0
- package/dist/commands/health/metrics.js +2 -1
- package/dist/commands/health/surfaces.js +5 -4
- package/dist/commands/health.js +1 -1
- package/dist/commands/improve/consolidate/chunking.js +2 -2
- package/dist/commands/improve/consolidate.js +28 -25
- package/dist/commands/improve/distill/promote-memory.js +5 -12
- package/dist/commands/improve/distill/quality-gate.js +5 -7
- package/dist/commands/improve/distill.js +16 -5
- package/dist/commands/improve/eligibility.js +22 -12
- package/dist/commands/improve/extract-cli.js +47 -19
- package/dist/commands/improve/extract.js +110 -62
- package/dist/commands/improve/improve-cli.js +38 -16
- package/dist/commands/improve/improve-result-file.js +30 -24
- package/dist/commands/improve/improve-strategies.js +137 -0
- package/dist/commands/improve/improve.js +60 -30
- package/dist/commands/improve/locks.js +66 -45
- package/dist/commands/improve/loop-stages.js +75 -33
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +12 -4
- package/dist/commands/improve/preparation.js +71 -73
- package/dist/commands/improve/procedural.js +3 -2
- package/dist/commands/improve/recombine.js +2 -1
- package/dist/commands/improve/reflect.js +119 -214
- package/dist/commands/improve/shared.js +11 -5
- package/dist/commands/lint/base-linter.js +152 -42
- package/dist/commands/mv-cli.js +809 -0
- package/dist/commands/proposal/proposal-cli.js +18 -8
- package/dist/commands/proposal/propose.js +64 -69
- package/dist/commands/read/knowledge.js +436 -4
- package/dist/commands/read/remember-cli.js +39 -2
- package/dist/commands/read/search-cli.js +6 -1
- package/dist/commands/registry-cli.js +29 -14
- package/dist/commands/remember.js +2 -0
- package/dist/commands/sources/init.js +13 -14
- package/dist/commands/sources/schema-repair.js +2 -4
- package/dist/commands/sources/source-add.js +62 -73
- package/dist/commands/sources/source-manage.js +50 -46
- package/dist/commands/sources/stash-cli.js +41 -4
- package/dist/commands/tasks/default-tasks.js +12 -12
- package/dist/commands/tasks/tasks-cli.js +7 -3
- package/dist/commands/tasks/tasks.js +113 -18
- package/dist/commands/wiki-cli.js +9 -10
- package/dist/core/asset/frontmatter.js +12 -2
- package/dist/core/common.js +5 -3
- package/dist/core/config/config-io.js +28 -17
- package/dist/core/config/config-schema.js +358 -66
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +98 -27
- package/dist/core/config/config.js +132 -266
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +32 -0
- package/dist/core/errors.js +2 -2
- package/dist/core/extra-params.js +61 -0
- package/dist/core/file-lock.js +201 -56
- package/dist/core/improve-result.js +178 -0
- package/dist/core/maintenance-barrier.js +119 -0
- package/dist/core/migration-backup.js +416 -0
- package/dist/core/paths.js +3 -0
- package/dist/core/redaction.js +358 -0
- package/dist/core/state/migrations.js +17 -2
- package/dist/core/state-db.js +44 -1
- package/dist/indexer/db/db.js +116 -1
- package/dist/indexer/graph/graph-extraction.js +28 -16
- package/dist/indexer/index-writer-lock.js +31 -24
- package/dist/indexer/index-written-assets.js +15 -6
- package/dist/indexer/indexer.js +47 -2
- package/dist/indexer/passes/memory-inference.js +10 -6
- package/dist/indexer/passes/metadata.js +250 -0
- package/dist/indexer/search/db-search.js +111 -44
- package/dist/indexer/search/fts-query.js +41 -0
- package/dist/indexer/search/ranking-contributors.js +48 -0
- package/dist/indexer/search/ranking.js +36 -23
- package/dist/indexer/search/search-fields.js +11 -1
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -52
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +202 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +7 -2
- package/dist/integrations/agent/profiles.js +6 -99
- package/dist/integrations/agent/runner-dispatch.js +76 -13
- package/dist/integrations/agent/runner.js +76 -207
- package/dist/integrations/agent/spawn.js +4 -6
- package/dist/integrations/harnesses/aider/agent-builder.js +2 -3
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -3
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +0 -2
- package/dist/integrations/harnesses/codex/agent-builder.js +2 -3
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +2 -3
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +2 -3
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +1 -24
- package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
- package/dist/integrations/harnesses/opencode/index.js +0 -6
- package/dist/integrations/harnesses/opencode-sdk/harness.js +1 -6
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
- package/dist/integrations/harnesses/openhands/agent-builder.js +2 -3
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +2 -3
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -32
- package/dist/integrations/lockfile.js +32 -21
- package/dist/llm/client.js +48 -14
- package/dist/llm/feature-gate.js +15 -47
- package/dist/llm/graph-extract.js +1 -1
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer-impl.js +1 -1
- package/dist/llm/usage-persist.js +4 -0
- package/dist/llm/usage-telemetry.js +35 -5
- package/dist/output/shapes/helpers.js +2 -1
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/helpers.js +3 -1
- package/dist/schemas/akm-config.json +11013 -8600
- package/dist/schemas/akm-task.json +87 -0
- package/dist/schemas/akm-workflow.json +57 -13
- package/dist/scripts/migrate-storage.js +8591 -509
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8623 -484
- package/dist/setup/detected-engines.js +142 -0
- package/dist/setup/engine-config.js +89 -0
- package/dist/setup/setup.js +236 -132
- package/dist/setup/steps/connection.js +61 -32
- package/dist/setup/steps/platforms.js +4 -4
- package/dist/setup/steps.js +3 -2
- package/dist/storage/database.js +13 -1
- package/dist/storage/engines/sqlite-migrations.js +1 -0
- package/dist/storage/repositories/improve-runs-repository.js +5 -5
- package/dist/storage/repositories/task-history-repository.js +78 -0
- package/dist/storage/repositories/workflow-runs-repository.js +9 -8
- package/dist/tasks/parser.js +138 -52
- package/dist/tasks/runner.js +71 -75
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/validator.js +11 -6
- package/dist/wiki/wiki.js +9 -8
- package/dist/workflows/authoring/workflow-program-template.yaml +3 -3
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/db.js +65 -13
- package/dist/workflows/exec/brief.js +18 -24
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +124 -65
- package/dist/workflows/exec/report.js +93 -33
- package/dist/workflows/exec/run-workflow.js +38 -25
- package/dist/workflows/exec/scheduler.js +12 -41
- package/dist/workflows/exec/step-work.js +91 -35
- package/dist/workflows/ir/compile.js +13 -26
- package/dist/workflows/ir/freeze.js +243 -0
- package/dist/workflows/ir/plan-hash.js +40 -5
- package/dist/workflows/ir/schema.js +542 -1
- package/dist/workflows/parser.js +7 -0
- package/dist/workflows/program/parser.js +132 -23
- package/dist/workflows/program/project.js +3 -4
- package/dist/workflows/program/schema.js +2 -2
- package/dist/workflows/resource-limits.js +20 -0
- package/dist/workflows/runtime/plan-classifier.js +187 -0
- package/dist/workflows/runtime/runs.js +54 -47
- package/dist/workflows/runtime/workflow-asset-loader.js +0 -22
- package/dist/workflows/validator.js +25 -0
- package/docs/data-and-telemetry.md +2 -2
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +5 -4
- package/docs/migration/v0.8-to-v0.9.md +401 -0
- package/package.json +4 -2
- package/schemas/akm-config.json +16638 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +372 -0
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/llm/call-ai.js +0 -62
- package/dist/setup/legacy-config.js +0 -106
- package/docs/README.md +0 -104
- /package/dist/assets/{profiles → improve-strategies}/catchup.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/consolidate.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/frequent.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/memory-focus.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/quick.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/recombine-only.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/synthesize.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/thorough.json +0 -0
|
@@ -8,105 +8,27 @@
|
|
|
8
8
|
* new config field is one line of Zod schema in `core/config-schema.ts` and
|
|
9
9
|
* zero lines here — the walker handles get/set/unset/coercion uniformly.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* - `akm config set embedding.ollamaOptions.numCtx` is sugar for
|
|
15
|
-
* `embedding.ollamaOptions.num_ctx` (camelCase ↔ snake_case bridge).
|
|
11
|
+
* `configVersion` is controlled by the config lifecycle. All execution
|
|
12
|
+
* settings use their canonical engine/strategy paths; retired aliases are not
|
|
13
|
+
* rewritten at this boundary.
|
|
16
14
|
* - `parseConfigValue` returns a Partial<AkmConfig> so it can be merged with
|
|
17
15
|
* the runtime config object via `mergeConfigValue`.
|
|
18
16
|
*/
|
|
19
17
|
import { defineGroupCommand, defineJsonCommand, output } from "../cli/shared.js";
|
|
20
18
|
import { resolveStashDir } from "../core/common.js";
|
|
21
|
-
import { DEFAULT_CONFIG,
|
|
19
|
+
import { DEFAULT_CONFIG, loadConfig, mutateConfig } from "../core/config/config.js";
|
|
22
20
|
import { configGet, configSet, configUnset, unknownKeyHint } from "../core/config/config-walker.js";
|
|
23
21
|
import { UsageError } from "../core/errors.js";
|
|
24
22
|
import { getCacheDir, getConfigPath, getDbPath, getDefaultStashDir } from "../core/paths.js";
|
|
25
|
-
// ── Legacy `llm.*` → `profiles.llm.<default>.*` aliasing ────────────────────
|
|
26
|
-
/**
|
|
27
|
-
* Map a legacy top-level `llm.<sub>` path onto the actual schema path. The
|
|
28
|
-
* default profile name is "default" when `defaults.llm` is unset.
|
|
29
|
-
*/
|
|
30
|
-
function rewriteLegacyLlmPath(config, key) {
|
|
31
|
-
if (key !== "llm" && !key.startsWith("llm."))
|
|
32
|
-
return key;
|
|
33
|
-
const sub = key === "llm" ? "" : key.slice("llm.".length);
|
|
34
|
-
const profileName = config.defaults?.llm ?? "default";
|
|
35
|
-
return sub ? `profiles.llm.${profileName}.${sub}` : `profiles.llm.${profileName}`;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Translate the legacy `embedding.ollamaOptions.numCtx` to the actual schema
|
|
39
|
-
* key `embedding.ollamaOptions.num_ctx`.
|
|
40
|
-
*/
|
|
41
|
-
function rewriteEmbeddingPath(key) {
|
|
42
|
-
if (key === "embedding.ollamaOptions.numCtx")
|
|
43
|
-
return "embedding.ollamaOptions.num_ctx";
|
|
44
|
-
return key;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Translate the deprecated `stashes` alias for `sources` (one-way: both read
|
|
48
|
-
* and write go through `sources`).
|
|
49
|
-
*/
|
|
50
|
-
function rewriteSourcesAlias(key) {
|
|
51
|
-
if (key === "stashes")
|
|
52
|
-
return "sources";
|
|
53
|
-
if (key.startsWith("stashes."))
|
|
54
|
-
return `sources.${key.slice("stashes.".length)}`;
|
|
55
|
-
return key;
|
|
56
|
-
}
|
|
57
|
-
function rewriteKey(config, key) {
|
|
58
|
-
let k = rewriteLegacyLlmPath(config, key);
|
|
59
|
-
k = rewriteEmbeddingPath(k);
|
|
60
|
-
k = rewriteSourcesAlias(k);
|
|
61
|
-
return k;
|
|
62
|
-
}
|
|
63
23
|
// ── Public API ──────────────────────────────────────────────────────────────
|
|
64
24
|
export function getConfigValue(config, key) {
|
|
65
|
-
|
|
66
|
-
const value = configGet(config, k);
|
|
67
|
-
if (k.split(".").at(-1) === "apiKey")
|
|
68
|
-
return null;
|
|
69
|
-
return omitApiKeysForOutput(value);
|
|
25
|
+
return redactConfigValue(configGet(config, key));
|
|
70
26
|
}
|
|
71
27
|
export function setConfigValue(config, key, rawValue) {
|
|
72
|
-
|
|
73
|
-
// env var the user typed (AKM_LLM_API_KEY) rather than the rewritten profile
|
|
74
|
-
// env var (AKM_PROFILE_DEFAULT_API_KEY) — both work at runtime, but the
|
|
75
|
-
// shorter name matches the user's mental model.
|
|
76
|
-
if (key === "llm.apiKey") {
|
|
77
|
-
throw new UsageError("apiKey cannot be persisted in config; export AKM_LLM_API_KEY instead. (key: llm.apiKey)", "INVALID_FLAG_VALUE", "Storing API keys in config.json leaks them through backups, logs, and version control. " +
|
|
78
|
-
"Use the corresponding environment variable. AKM reads it at request time.");
|
|
79
|
-
}
|
|
80
|
-
if (key === "embedding.apiKey") {
|
|
81
|
-
throw new UsageError("apiKey cannot be persisted in config; export AKM_EMBED_API_KEY instead. (key: embedding.apiKey)", "INVALID_FLAG_VALUE", "Storing API keys in config.json leaks them through backups, logs, and version control. " +
|
|
82
|
-
"Use the corresponding environment variable. AKM reads it at request time.");
|
|
83
|
-
}
|
|
84
|
-
const k = rewriteKey(config, key);
|
|
85
|
-
// Legacy ergonomic: `akm config set semanticSearchMode true|false`
|
|
86
|
-
let coerced = rawValue;
|
|
87
|
-
if (k === "semanticSearchMode") {
|
|
88
|
-
if (rawValue === "true")
|
|
89
|
-
coerced = "auto";
|
|
90
|
-
else if (rawValue === "false")
|
|
91
|
-
coerced = "off";
|
|
92
|
-
}
|
|
93
|
-
let next = configSet(config, k, coerced);
|
|
94
|
-
// Legacy ergonomic shim: when the user sets `llm.<field>` and no
|
|
95
|
-
// `defaults.llm` is set, point it at the freshly-created profile so the
|
|
96
|
-
// value actually takes effect at runtime.
|
|
97
|
-
if (key === "llm" || key.startsWith("llm.")) {
|
|
98
|
-
if (!next.defaults?.llm) {
|
|
99
|
-
next = {
|
|
100
|
-
...next,
|
|
101
|
-
defaults: { ...(next.defaults ?? {}), llm: "default" },
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return next;
|
|
28
|
+
return configSet(config, key, rawValue);
|
|
106
29
|
}
|
|
107
30
|
export function unsetConfigValue(config, key) {
|
|
108
|
-
|
|
109
|
-
return configUnset(config, k);
|
|
31
|
+
return configUnset(config, key);
|
|
110
32
|
}
|
|
111
33
|
/**
|
|
112
34
|
* Compatibility shim: returns a `Partial<AkmConfig>` containing just the
|
|
@@ -128,48 +50,21 @@ export function parseConfigValue(key, value) {
|
|
|
128
50
|
return patch;
|
|
129
51
|
}
|
|
130
52
|
export function listConfig(config) {
|
|
131
|
-
|
|
132
|
-
semanticSearchMode: config.semanticSearchMode,
|
|
133
|
-
registries: config.registries ?? DEFAULT_CONFIG.registries ?? [],
|
|
134
|
-
output: { ...(DEFAULT_CONFIG.output ?? {}), ...(config.output ?? {}) },
|
|
135
|
-
stashDir: config.stashDir ?? null,
|
|
136
|
-
installed: config.installed ?? [],
|
|
137
|
-
sources: getSources(config),
|
|
138
|
-
};
|
|
139
|
-
if (config.defaultWriteTarget)
|
|
140
|
-
result.defaultWriteTarget = config.defaultWriteTarget;
|
|
141
|
-
if (config.embedding)
|
|
142
|
-
result.embedding = config.embedding;
|
|
143
|
-
if (config.profiles)
|
|
144
|
-
result.profiles = config.profiles;
|
|
145
|
-
if (config.defaults)
|
|
146
|
-
result.defaults = config.defaults;
|
|
147
|
-
if (config.search)
|
|
148
|
-
result.search = config.search;
|
|
149
|
-
if (config.index)
|
|
150
|
-
result.index = config.index;
|
|
151
|
-
if (config.feedback)
|
|
152
|
-
result.feedback = config.feedback;
|
|
153
|
-
if (config.improve)
|
|
154
|
-
result.improve = config.improve;
|
|
155
|
-
if (config.archiveRetentionDays !== undefined)
|
|
156
|
-
result.archiveRetentionDays = config.archiveRetentionDays;
|
|
157
|
-
if (config.configVersion !== undefined)
|
|
158
|
-
result.configVersion = config.configVersion;
|
|
159
|
-
return omitApiKeysForOutput(result);
|
|
53
|
+
return redactConfigValue({ ...DEFAULT_CONFIG, ...config, sources: config.sources ?? [] });
|
|
160
54
|
}
|
|
161
|
-
function
|
|
55
|
+
function redactConfigValue(value) {
|
|
162
56
|
if (Array.isArray(value))
|
|
163
|
-
return value.map(
|
|
57
|
+
return value.map(redactConfigValue);
|
|
164
58
|
if (!value || typeof value !== "object")
|
|
165
59
|
return value;
|
|
166
|
-
const
|
|
60
|
+
const result = {};
|
|
167
61
|
for (const [key, child] of Object.entries(value)) {
|
|
168
|
-
if (key
|
|
169
|
-
|
|
170
|
-
|
|
62
|
+
if (key !== "apiKey" ||
|
|
63
|
+
(typeof child === "string" && /^\$(?:[A-Za-z_][A-Za-z0-9_]*|\{[A-Za-z_][A-Za-z0-9_]*\})$/.test(child))) {
|
|
64
|
+
result[key] = redactConfigValue(child);
|
|
65
|
+
}
|
|
171
66
|
}
|
|
172
|
-
return
|
|
67
|
+
return result;
|
|
173
68
|
}
|
|
174
69
|
export { unknownKeyHint };
|
|
175
70
|
// ── `akm config` command surface ────────────────────────────────────────────
|
|
@@ -190,25 +85,21 @@ function normalizeToggleTarget(target) {
|
|
|
190
85
|
throw new UsageError(`Unsupported target "${target}". Supported targets: skills.sh`);
|
|
191
86
|
}
|
|
192
87
|
function toggleSkillsShRegistry(enabled) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
registries.push({ url: SKILLS_SH_URL, name: SKILLS_SH_NAME, provider: SKILLS_SH_PROVIDER, enabled: true });
|
|
210
|
-
saveConfig({ ...config, registries });
|
|
211
|
-
return { changed: true, component: SKILLS_SH_NAME, enabled: true };
|
|
88
|
+
let changed = false;
|
|
89
|
+
mutateConfig((config) => {
|
|
90
|
+
const registries = (config.registries ?? DEFAULT_CONFIG.registries ?? []).map((registry) => ({ ...registry }));
|
|
91
|
+
const idx = registries.findIndex((registry) => registry.provider === SKILLS_SH_PROVIDER || registry.name === SKILLS_SH_NAME || registry.url === SKILLS_SH_URL);
|
|
92
|
+
if (idx >= 0) {
|
|
93
|
+
const wasEnabled = registries[idx].enabled !== false;
|
|
94
|
+
registries[idx].enabled = enabled;
|
|
95
|
+
changed = wasEnabled !== enabled;
|
|
96
|
+
return { ...config, registries };
|
|
97
|
+
}
|
|
98
|
+
registries.push({ url: SKILLS_SH_URL, name: SKILLS_SH_NAME, provider: SKILLS_SH_PROVIDER, enabled });
|
|
99
|
+
changed = true;
|
|
100
|
+
return { ...config, registries };
|
|
101
|
+
});
|
|
102
|
+
return { changed, component: SKILLS_SH_NAME, enabled };
|
|
212
103
|
}
|
|
213
104
|
function toggleComponent(targetRaw, enabled) {
|
|
214
105
|
const target = normalizeToggleTarget(targetRaw);
|
|
@@ -276,7 +167,11 @@ export const configCommand = defineGroupCommand({
|
|
|
276
167
|
set: defineJsonCommand({
|
|
277
168
|
meta: { name: "set", description: "Set a configuration value by key" },
|
|
278
169
|
args: {
|
|
279
|
-
key: {
|
|
170
|
+
key: {
|
|
171
|
+
type: "positional",
|
|
172
|
+
required: true,
|
|
173
|
+
description: "Config key (for example: embedding, engines.default)",
|
|
174
|
+
},
|
|
280
175
|
value: { type: "positional", required: true, description: "Config value" },
|
|
281
176
|
// #463: stable machine-friendly entry point for plugins / hooks.
|
|
282
177
|
// `--silent` suppresses the config dump on stdout so hook-driven
|
|
@@ -297,20 +192,19 @@ export const configCommand = defineGroupCommand({
|
|
|
297
192
|
},
|
|
298
193
|
run({ args }) {
|
|
299
194
|
if (args.layer && args.layer !== "user") {
|
|
300
|
-
throw new UsageError(`Unsupported --layer "${args.layer}". Only "user" is settable in 0.
|
|
195
|
+
throw new UsageError(`Unsupported --layer "${args.layer}". Only "user" is settable in 0.9.0.`, "INVALID_FLAG_VALUE");
|
|
301
196
|
}
|
|
302
197
|
// Use loadConfig (not loadUserConfig) so the project-config
|
|
303
198
|
// deprecation warning fires consistently with `akm config get`
|
|
304
199
|
// (#457). Effective merged shape is identical post-0.8.0.
|
|
305
|
-
const updated =
|
|
306
|
-
saveConfig(updated);
|
|
200
|
+
const updated = mutateConfig((current) => setConfigValue(current, args.key, args.value)).config;
|
|
307
201
|
if (!args.silent) {
|
|
308
202
|
output("config", listConfig(updated));
|
|
309
203
|
}
|
|
310
204
|
},
|
|
311
205
|
}),
|
|
312
206
|
unset: defineJsonCommand({
|
|
313
|
-
meta: { name: "unset", description: "Unset an optional configuration key or whole embedding/
|
|
207
|
+
meta: { name: "unset", description: "Unset an optional configuration key or whole embedding/engine section" },
|
|
314
208
|
args: {
|
|
315
209
|
key: { type: "positional", required: true, description: "Config key to unset" },
|
|
316
210
|
silent: {
|
|
@@ -326,10 +220,10 @@ export const configCommand = defineGroupCommand({
|
|
|
326
220
|
},
|
|
327
221
|
run({ args }) {
|
|
328
222
|
if (args.layer && args.layer !== "user") {
|
|
329
|
-
throw new UsageError(`Unsupported --layer "${args.layer}". Only "user" is settable in 0.
|
|
223
|
+
throw new UsageError(`Unsupported --layer "${args.layer}". Only "user" is settable in 0.9.0.`, "INVALID_FLAG_VALUE");
|
|
330
224
|
}
|
|
331
|
-
const
|
|
332
|
-
|
|
225
|
+
const result = mutateConfig((current) => unsetConfigValue(current, args.key), { absentNoop: true });
|
|
226
|
+
const updated = result.config;
|
|
333
227
|
if (!args.silent) {
|
|
334
228
|
output("config", listConfig(updated));
|
|
335
229
|
}
|
|
@@ -348,19 +242,11 @@ export const configCommand = defineGroupCommand({
|
|
|
348
242
|
migrate: defineJsonCommand({
|
|
349
243
|
meta: {
|
|
350
244
|
name: "migrate",
|
|
351
|
-
description: "
|
|
245
|
+
description: "Diagnose whether the user config is already in the current schema. Never writes or translates config.",
|
|
352
246
|
},
|
|
353
|
-
|
|
354
|
-
"dry-run": { type: "boolean", description: "Preview the migration result without writing.", default: false },
|
|
355
|
-
"print-diff": {
|
|
356
|
-
type: "boolean",
|
|
357
|
-
description: "Print a unified diff of old vs new config alongside the migration output.",
|
|
358
|
-
default: false,
|
|
359
|
-
},
|
|
360
|
-
},
|
|
361
|
-
async run({ args }) {
|
|
247
|
+
async run() {
|
|
362
248
|
const { runConfigMigrate } = await import("../cli/config-migrate.js");
|
|
363
|
-
await runConfigMigrate(
|
|
249
|
+
await runConfigMigrate();
|
|
364
250
|
},
|
|
365
251
|
}),
|
|
366
252
|
enable: defineJsonCommand({
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
import fs from "node:fs";
|
|
28
28
|
import path from "node:path";
|
|
29
29
|
import { writeFileAtomic } from "../../core/common.js";
|
|
30
|
-
import { probeLock, releaseLock, tryAcquireLockSync } from "../../core/file-lock.js";
|
|
30
|
+
import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync, } from "../../core/file-lock.js";
|
|
31
31
|
import { sleepSync } from "../../runtime.js";
|
|
32
32
|
// ── Write-lock helper ─────────────────────────────────────────────────────────
|
|
33
33
|
/**
|
|
@@ -39,10 +39,13 @@ import { sleepSync } from "../../runtime.js";
|
|
|
39
39
|
export function withSecretLock(secretPath, fn) {
|
|
40
40
|
const lockPath = `${secretPath}.lock`;
|
|
41
41
|
const deadline = Date.now() + 5000;
|
|
42
|
-
|
|
42
|
+
let ownership;
|
|
43
|
+
while (!ownership) {
|
|
44
|
+
ownership = tryAcquireLockSync(lockPath, createLockPayload());
|
|
45
|
+
if (ownership)
|
|
46
|
+
break;
|
|
43
47
|
const probe = probeLock(lockPath);
|
|
44
|
-
if (probe.state === "stale") {
|
|
45
|
-
releaseLock(lockPath);
|
|
48
|
+
if (probe.state === "stale" && reclaimStaleLock(lockPath, probe)) {
|
|
46
49
|
continue;
|
|
47
50
|
}
|
|
48
51
|
if (Date.now() > deadline) {
|
|
@@ -57,7 +60,7 @@ export function withSecretLock(secretPath, fn) {
|
|
|
57
60
|
return fn();
|
|
58
61
|
}
|
|
59
62
|
finally {
|
|
60
|
-
releaseLock(
|
|
63
|
+
releaseLock(ownership);
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
// ── Atomic byte write ──────────────────────────────────────────────────────────
|
|
@@ -2,111 +2,158 @@
|
|
|
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
4
|
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
5
6
|
import { loadConfig } from "../../core/config/config.js";
|
|
6
|
-
import {
|
|
7
|
+
import { resolveEngine } from "../../integrations/agent/engine-resolution.js";
|
|
8
|
+
import { resolveModel } from "../../integrations/agent/model-aliases.js";
|
|
7
9
|
import { TASK_FAIL_RATE_WARN, } from "./types.js";
|
|
8
10
|
const ACTIVE_RUN_WARN_MS = 15 * 60 * 1000;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
export function runAgentProbe() {
|
|
15
|
-
const config = loadConfig();
|
|
16
|
-
// v2: check profiles.agent first
|
|
17
|
-
if (config.profiles?.agent) {
|
|
18
|
-
const defaultName = config.defaults?.agent;
|
|
19
|
-
const profileCount = Object.keys(config.profiles.agent).length;
|
|
20
|
-
if (profileCount === 0) {
|
|
21
|
-
return {
|
|
22
|
-
name: "agent-profile",
|
|
23
|
-
kind: "deterministic",
|
|
24
|
-
status: "unknown",
|
|
25
|
-
confidence: "high",
|
|
26
|
-
message: "No agent profiles configured in profiles.agent.",
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
const profileName = defaultName ?? Object.keys(config.profiles.agent)[0];
|
|
30
|
-
const profile = config.profiles.agent[profileName];
|
|
11
|
+
export function runDefaultEngineProbe(deps = {}) {
|
|
12
|
+
const config = deps.loadConfig?.() ?? loadConfig();
|
|
13
|
+
const engineName = config.defaults?.engine ?? config.defaults?.llmEngine;
|
|
14
|
+
if (!engineName) {
|
|
31
15
|
return {
|
|
32
|
-
name: "
|
|
33
|
-
kind: "deterministic",
|
|
34
|
-
status: "pass",
|
|
35
|
-
confidence: "high",
|
|
36
|
-
message: `v2 agent profile "${profileName}" configured (platform: ${profile?.platform ?? "unknown"}).`,
|
|
37
|
-
evidence: { profile: profileName, platform: profile?.platform, profileCount },
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
if (!config.profiles?.agent && !config.defaults?.agent) {
|
|
41
|
-
return {
|
|
42
|
-
name: "agent-profile",
|
|
16
|
+
name: "default-engine",
|
|
43
17
|
kind: "deterministic",
|
|
44
18
|
status: "unknown",
|
|
45
19
|
confidence: "high",
|
|
46
|
-
message: "No
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
let profile;
|
|
50
|
-
try {
|
|
51
|
-
profile = requireAgentProfile(config);
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
return {
|
|
55
|
-
name: "agent-profile",
|
|
56
|
-
kind: "deterministic",
|
|
57
|
-
status: "warn",
|
|
58
|
-
confidence: "high",
|
|
59
|
-
message: error instanceof Error ? error.message : String(error),
|
|
20
|
+
message: "No default engine is configured.",
|
|
60
21
|
};
|
|
61
22
|
}
|
|
62
|
-
|
|
23
|
+
const configuredEngine = config.engines?.[engineName];
|
|
24
|
+
if (configuredEngine?.kind === "agent" && configuredEngine.platform === "opencode-sdk") {
|
|
25
|
+
let packageAvailable = false;
|
|
26
|
+
try {
|
|
27
|
+
const resolvePackage = deps.resolvePackage ?? ((name) => createRequire(import.meta.url).resolve(name));
|
|
28
|
+
resolvePackage("@opencode-ai/sdk");
|
|
29
|
+
packageAvailable = true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
packageAvailable = false;
|
|
33
|
+
}
|
|
34
|
+
const binary = configuredEngine.bin ?? "opencode";
|
|
35
|
+
const version = (deps.spawnSync ?? spawnSync)(binary, ["--version"], { encoding: "utf8", timeout: 5_000 });
|
|
36
|
+
const binaryAvailable = (version.status ?? 1) === 0;
|
|
37
|
+
const fallbackEngine = configuredEngine.llmEngine ?? config.defaults?.llmEngine;
|
|
38
|
+
let fallback;
|
|
39
|
+
let sdkRunner;
|
|
40
|
+
const resolve = deps.resolveEngine ?? resolveEngine;
|
|
41
|
+
try {
|
|
42
|
+
const resolved = resolve(engineName, config);
|
|
43
|
+
if (resolved.kind === "sdk")
|
|
44
|
+
sdkRunner = resolved;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
sdkRunner = undefined;
|
|
48
|
+
}
|
|
49
|
+
if (sdkRunner?.fallbackConnection) {
|
|
50
|
+
fallback = { kind: "llm", connection: sdkRunner.fallbackConnection };
|
|
51
|
+
}
|
|
52
|
+
else if (fallbackEngine) {
|
|
53
|
+
try {
|
|
54
|
+
const resolved = resolve(fallbackEngine, config);
|
|
55
|
+
if (resolved.kind === "llm")
|
|
56
|
+
fallback = resolved;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
fallback = undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const configuredModel = configuredEngine.model
|
|
63
|
+
? resolveModel(configuredEngine.model, "opencode-sdk", configuredEngine.modelAliases, config.modelAliases)
|
|
64
|
+
: undefined;
|
|
65
|
+
const effectiveModel = sdkRunner?.profile.model ?? configuredModel ?? fallback?.connection.model;
|
|
66
|
+
const missing = [
|
|
67
|
+
!packageAvailable ? "@opencode-ai/sdk package" : undefined,
|
|
68
|
+
!binaryAvailable ? `${binary} binary` : undefined,
|
|
69
|
+
!fallback ? "fallback LLM connection" : undefined,
|
|
70
|
+
!effectiveModel ? "effective SDK model" : undefined,
|
|
71
|
+
].filter((value) => value !== undefined);
|
|
63
72
|
return {
|
|
64
|
-
name: "
|
|
73
|
+
name: "default-engine",
|
|
65
74
|
kind: "deterministic",
|
|
66
|
-
status:
|
|
75
|
+
status: missing.length === 0 ? "pass" : "warn",
|
|
67
76
|
confidence: "high",
|
|
68
|
-
message:
|
|
69
|
-
? `SDK
|
|
70
|
-
: `SDK
|
|
71
|
-
evidence: {
|
|
77
|
+
message: missing.length === 0
|
|
78
|
+
? `SDK engine "${engineName}" is available.`
|
|
79
|
+
: `SDK engine "${engineName}" is incomplete: missing ${missing.join(", ")}.`,
|
|
80
|
+
evidence: {
|
|
81
|
+
engine: engineName,
|
|
82
|
+
platform: configuredEngine.platform,
|
|
83
|
+
runtimeKind: "sdk",
|
|
84
|
+
binary,
|
|
85
|
+
binaryAvailable,
|
|
86
|
+
package: "@opencode-ai/sdk",
|
|
87
|
+
packageAvailable,
|
|
88
|
+
model: effectiveModel ?? null,
|
|
89
|
+
configuredModel: configuredModel ?? null,
|
|
90
|
+
modelSource: configuredModel ? "sdk" : effectiveModel ? "fallback" : null,
|
|
91
|
+
fallbackEngine: fallbackEngine ?? null,
|
|
92
|
+
fallbackEndpoint: fallback?.connection.endpoint ?? null,
|
|
93
|
+
fallbackModel: fallback?.connection.model ?? null,
|
|
94
|
+
},
|
|
72
95
|
};
|
|
73
96
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
97
|
+
try {
|
|
98
|
+
const runner = (deps.resolveEngine ?? resolveEngine)(engineName, config);
|
|
99
|
+
if (runner.kind === "llm") {
|
|
100
|
+
return {
|
|
101
|
+
name: "default-engine",
|
|
102
|
+
kind: "deterministic",
|
|
103
|
+
status: "pass",
|
|
104
|
+
confidence: "high",
|
|
105
|
+
message: `LLM engine "${engineName}" is configured.`,
|
|
106
|
+
evidence: {
|
|
107
|
+
engine: engineName,
|
|
108
|
+
platform: null,
|
|
109
|
+
runtimeKind: "llm",
|
|
110
|
+
model: runner.connection.model,
|
|
111
|
+
endpoint: runner.connection.endpoint,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const profile = runner.profile;
|
|
116
|
+
if (runner.kind === "sdk")
|
|
117
|
+
throw new Error(`SDK engine "${engineName}" has no matching SDK config.`);
|
|
118
|
+
const version = (deps.spawnSync ?? spawnSync)(profile.bin, ["--version"], { encoding: "utf8", timeout: 5_000 });
|
|
119
|
+
if ((version.status ?? 1) !== 0) {
|
|
120
|
+
return {
|
|
121
|
+
name: "default-engine",
|
|
122
|
+
kind: "deterministic",
|
|
123
|
+
status: "warn",
|
|
124
|
+
confidence: "medium",
|
|
125
|
+
message: `Agent engine "${engineName}" was found but \`--version\` failed.`,
|
|
126
|
+
evidence: {
|
|
127
|
+
engine: engineName,
|
|
128
|
+
platform: profile.platform ?? null,
|
|
129
|
+
runtimeKind: "agent",
|
|
130
|
+
model: profile.model ?? null,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
77
134
|
return {
|
|
78
|
-
name: "
|
|
135
|
+
name: "default-engine",
|
|
79
136
|
kind: "deterministic",
|
|
80
|
-
status: "
|
|
137
|
+
status: "pass",
|
|
81
138
|
confidence: "high",
|
|
82
|
-
message: `
|
|
83
|
-
evidence: {
|
|
139
|
+
message: `Agent engine "${engineName}" is available.`,
|
|
140
|
+
evidence: {
|
|
141
|
+
engine: engineName,
|
|
142
|
+
platform: profile.platform ?? null,
|
|
143
|
+
runtimeKind: "agent",
|
|
144
|
+
model: profile.model ?? null,
|
|
145
|
+
},
|
|
84
146
|
};
|
|
85
147
|
}
|
|
86
|
-
|
|
87
|
-
if ((version.status ?? 1) !== 0) {
|
|
148
|
+
catch (error) {
|
|
88
149
|
return {
|
|
89
|
-
name: "
|
|
150
|
+
name: "default-engine",
|
|
90
151
|
kind: "deterministic",
|
|
91
152
|
status: "warn",
|
|
92
|
-
confidence: "
|
|
93
|
-
message:
|
|
94
|
-
evidence: {
|
|
95
|
-
profile: profile.name,
|
|
96
|
-
bin: profile.bin,
|
|
97
|
-
exitCode: version.status ?? null,
|
|
98
|
-
stderr: (version.stderr ?? "").trim(),
|
|
99
|
-
},
|
|
153
|
+
confidence: "high",
|
|
154
|
+
message: error instanceof Error ? error.message : String(error),
|
|
100
155
|
};
|
|
101
156
|
}
|
|
102
|
-
return {
|
|
103
|
-
name: "agent-profile",
|
|
104
|
-
kind: "deterministic",
|
|
105
|
-
status: "pass",
|
|
106
|
-
confidence: "high",
|
|
107
|
-
message: `Agent profile "${profile.name}" is available.`,
|
|
108
|
-
evidence: { profile: profile.name, bin: profile.bin, version: (version.stdout ?? "").trim() },
|
|
109
|
-
};
|
|
110
157
|
}
|
|
111
158
|
/**
|
|
112
159
|
* The ordered health-check registry. ORDER IS LOAD-BEARING: `akmHealth`
|
|
@@ -185,9 +232,9 @@ export const HEALTH_CHECKS = [
|
|
|
185
232
|
}),
|
|
186
233
|
},
|
|
187
234
|
{
|
|
188
|
-
name: "
|
|
235
|
+
name: "default-engine",
|
|
189
236
|
channel: "hard",
|
|
190
|
-
run: () =>
|
|
237
|
+
run: () => runDefaultEngineProbe(),
|
|
191
238
|
},
|
|
192
239
|
{
|
|
193
240
|
// C2 (13-bus-factor): the cron task-failure rate was computed and rendered
|
|
@@ -93,6 +93,8 @@ function reshapeRun(r) {
|
|
|
93
93
|
return {
|
|
94
94
|
id: r.id,
|
|
95
95
|
taskId: r.taskId ?? "manual",
|
|
96
|
+
strategy: r.strategy,
|
|
97
|
+
legacyProfile: r.legacyProfile,
|
|
96
98
|
startedAt: r.startedAt,
|
|
97
99
|
completedAt: r.completedAt,
|
|
98
100
|
wallTimeMs: wall,
|
|
@@ -295,6 +297,8 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
295
297
|
totalTokens: 0,
|
|
296
298
|
reasoningTokens: 0,
|
|
297
299
|
byStage: {},
|
|
300
|
+
byProcess: {},
|
|
301
|
+
byEngine: {},
|
|
298
302
|
};
|
|
299
303
|
const totalRuns = runs.length;
|
|
300
304
|
const failedRuns = runs.filter((r) => !r.ok).length;
|