akm-cli 0.8.7 → 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 +428 -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} +48 -36
- 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
package/dist/setup/detect.js
CHANGED
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
* a result object describing what was found.
|
|
9
9
|
*/
|
|
10
10
|
import fs from "node:fs";
|
|
11
|
+
import os from "node:os";
|
|
11
12
|
import path from "node:path";
|
|
13
|
+
import { defaultWhich } from "../integrations/agent/detect.js";
|
|
14
|
+
import { SESSION_LOG_HARNESSES } from "../integrations/harnesses/index.js";
|
|
15
|
+
import { spawn } from "../runtime.js";
|
|
16
|
+
import { detectHarnessConfigs } from "./harness-config-import.js";
|
|
12
17
|
// ── Ollama Detection ────────────────────────────────────────────────────────
|
|
13
18
|
const OLLAMA_BASE = "http://localhost:11434";
|
|
14
19
|
/**
|
|
@@ -41,7 +46,7 @@ export async function detectOllama() {
|
|
|
41
46
|
}
|
|
42
47
|
// CLI fallback
|
|
43
48
|
try {
|
|
44
|
-
const proc =
|
|
49
|
+
const proc = spawn(["ollama", "list"], {
|
|
45
50
|
stdout: "pipe",
|
|
46
51
|
stderr: "pipe",
|
|
47
52
|
});
|
|
@@ -92,14 +97,24 @@ export async function detectLMStudio() {
|
|
|
92
97
|
return result;
|
|
93
98
|
}
|
|
94
99
|
// ── Agent Platform Detection ────────────────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Setup stash-source candidates, derived from the unified harness registry
|
|
102
|
+
* (#567).
|
|
103
|
+
*
|
|
104
|
+
* BEHAVIOUR FIX: the old hardcoded list named 6 harnesses (Claude Code,
|
|
105
|
+
* OpenCode, Continue, Codeium/Windsurf, Cursor, Codex CLI) but only the first
|
|
106
|
+
* two have a session-log provider. Selecting any of the other four added a
|
|
107
|
+
* filesystem stash source that was never indexed/extracted — a silent no-op
|
|
108
|
+
* "detection trap" (sessions never reached the improve pipeline).
|
|
109
|
+
*
|
|
110
|
+
* The registry is now the single source of which harnesses are real stash
|
|
111
|
+
* sources: a candidate must (a) have `capabilities.sessionLogs === true` AND
|
|
112
|
+
* (b) declare a `setupDetectionDir`. `SESSION_LOG_HARNESSES` already filters by
|
|
113
|
+
* (a), so dropping the four dead options is automatic — they aren't even in the
|
|
114
|
+
* registry. Adding a new session-log harness with a detection dir makes it
|
|
115
|
+
* appear here with no edit to this file.
|
|
116
|
+
*/
|
|
117
|
+
const AGENT_PLATFORMS = SESSION_LOG_HARNESSES.filter((h) => h.setupDetectionDir).map((h) => ({ name: h.displayName, relPath: h.setupDetectionDir }));
|
|
103
118
|
/**
|
|
104
119
|
* Scan the user's home directory for known agent platform config directories.
|
|
105
120
|
* Supports both HOME (Unix) and USERPROFILE (Windows).
|
|
@@ -121,3 +136,290 @@ export function detectAgentPlatforms() {
|
|
|
121
136
|
path: path.join(home, p.relPath),
|
|
122
137
|
}));
|
|
123
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Map of env var NAME → { provider, kind }. The key NAMES are the only thing
|
|
141
|
+
* this module ever inspects from `process.env`; values are never touched.
|
|
142
|
+
*/
|
|
143
|
+
const PROVIDER_ENV_VARS = [
|
|
144
|
+
{ name: "ANTHROPIC_API_KEY", provider: "anthropic", kind: "apiKey" },
|
|
145
|
+
{ name: "OPENAI_API_KEY", provider: "openai", kind: "apiKey" },
|
|
146
|
+
{ name: "GEMINI_API_KEY", provider: "gemini", kind: "apiKey" },
|
|
147
|
+
{ name: "GOOGLE_API_KEY", provider: "gemini", kind: "apiKey" },
|
|
148
|
+
{ name: "GROQ_API_KEY", provider: "groq", kind: "apiKey" },
|
|
149
|
+
{ name: "OLLAMA_HOST", provider: "ollama", kind: "endpoint" },
|
|
150
|
+
{ name: "OLLAMA_BASE_URL", provider: "ollama", kind: "endpoint" },
|
|
151
|
+
{ name: "LM_STUDIO_BASE_URL", provider: "lmstudio", kind: "endpoint" },
|
|
152
|
+
{ name: "LM_STUDIO_API_BASE", provider: "lmstudio", kind: "endpoint" },
|
|
153
|
+
{ name: "LMSTUDIO_BASE_URL", provider: "lmstudio", kind: "endpoint" },
|
|
154
|
+
{ name: "LMSTUDIO_API_BASE", provider: "lmstudio", kind: "endpoint" },
|
|
155
|
+
{ name: "AKM_LLM_API_KEY", provider: "akm-llm", kind: "apiKey" },
|
|
156
|
+
{ name: "AKM_LLM_ENDPOINT", provider: "akm-llm", kind: "endpoint" },
|
|
157
|
+
{ name: "AKM_LLM_BASE_URL", provider: "akm-llm", kind: "endpoint" },
|
|
158
|
+
];
|
|
159
|
+
/**
|
|
160
|
+
* Scan `process.env` for the presence of known provider configuration env var
|
|
161
|
+
* NAMES and return inferred providers.
|
|
162
|
+
*
|
|
163
|
+
* Pure function — no network, no filesystem. It reads only whether each known
|
|
164
|
+
* key is *defined and non-empty*; it never reads, returns, or logs the value.
|
|
165
|
+
*
|
|
166
|
+
* @param envSource Env to inspect. Defaults to `process.env`. Tests inject a
|
|
167
|
+
* fake env so a real API key is never required.
|
|
168
|
+
* @returns Inferred providers, each carrying the env var NAME only.
|
|
169
|
+
*/
|
|
170
|
+
export function scanProviderEnvVars(envSource = process.env) {
|
|
171
|
+
const results = [];
|
|
172
|
+
for (const entry of PROVIDER_ENV_VARS) {
|
|
173
|
+
const value = envSource[entry.name];
|
|
174
|
+
const present = typeof value === "string" && value.trim().length > 0;
|
|
175
|
+
if (!present)
|
|
176
|
+
continue;
|
|
177
|
+
results.push({ provider: entry.provider, envVar: entry.name, kind: entry.kind });
|
|
178
|
+
}
|
|
179
|
+
return results;
|
|
180
|
+
}
|
|
181
|
+
/** Default endpoints probed in addition to any harness-config base URLs. */
|
|
182
|
+
const DEFAULT_LOCAL_ENDPOINTS = [
|
|
183
|
+
{ baseUrl: "http://localhost:11434", label: "Ollama" },
|
|
184
|
+
{ baseUrl: "http://localhost:1234", label: "LM Studio" },
|
|
185
|
+
{ baseUrl: "http://localhost:8080", label: "Local (8080)" },
|
|
186
|
+
];
|
|
187
|
+
/**
|
|
188
|
+
* Pick a sensible default model from a list via a name heuristic.
|
|
189
|
+
*
|
|
190
|
+
* Preference order: an explicit "instruct" variant, then the longest name
|
|
191
|
+
* (a rough proxy for the larger / more-capable variant), then the first.
|
|
192
|
+
* Returns `undefined` for an empty list.
|
|
193
|
+
*/
|
|
194
|
+
export function pickDefaultModel(models) {
|
|
195
|
+
const cleaned = models.filter((m) => typeof m === "string" && m.trim().length > 0);
|
|
196
|
+
if (cleaned.length === 0)
|
|
197
|
+
return undefined;
|
|
198
|
+
const instruct = cleaned.filter((m) => /instruct/i.test(m));
|
|
199
|
+
const pool = instruct.length > 0 ? instruct : cleaned;
|
|
200
|
+
// Prefer the longest name as a proxy for the larger/most-specific variant,
|
|
201
|
+
// breaking ties by sort order for determinism.
|
|
202
|
+
return [...pool].sort((a, b) => b.length - a.length || a.localeCompare(b))[0];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Probe a single OpenAI-compatible `/v1/models` endpoint.
|
|
206
|
+
*
|
|
207
|
+
* Tolerant of failure: any network/timeout/parse error yields an
|
|
208
|
+
* `available: false` result rather than throwing.
|
|
209
|
+
*/
|
|
210
|
+
export async function probeLocalEndpoint(baseUrl, label, timeoutMs = 2000) {
|
|
211
|
+
const result = { baseUrl, label, available: false, models: [] };
|
|
212
|
+
const url = `${baseUrl.replace(/\/$/, "")}/v1/models`;
|
|
213
|
+
try {
|
|
214
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
215
|
+
if (response.ok) {
|
|
216
|
+
const data = (await response.json());
|
|
217
|
+
if (Array.isArray(data.data)) {
|
|
218
|
+
result.models = data.data
|
|
219
|
+
.map((m) => (typeof m.id === "string" ? m.id : ""))
|
|
220
|
+
.filter(Boolean)
|
|
221
|
+
.sort();
|
|
222
|
+
result.available = true;
|
|
223
|
+
result.defaultModel = pickDefaultModel(result.models);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Endpoint down/unreachable — leave available=false.
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Probe the default local endpoints (Ollama 11434, LM Studio 1234, generic
|
|
234
|
+
* 8080) plus any base URLs found in imported harness configs.
|
|
235
|
+
*
|
|
236
|
+
* Never throws: every endpoint being down yields a list of unavailable
|
|
237
|
+
* results, not an error.
|
|
238
|
+
*
|
|
239
|
+
* @param harnessBaseUrls Extra base URLs to probe (e.g. from harness configs).
|
|
240
|
+
*/
|
|
241
|
+
export async function detectLocalServers(harnessBaseUrls = []) {
|
|
242
|
+
const endpoints = [...DEFAULT_LOCAL_ENDPOINTS];
|
|
243
|
+
for (const raw of harnessBaseUrls) {
|
|
244
|
+
if (typeof raw !== "string" || raw.trim().length === 0)
|
|
245
|
+
continue;
|
|
246
|
+
// Strip a trailing /v1 (and trailing slash) so we probe consistently.
|
|
247
|
+
const baseUrl = raw.replace(/\/$/, "").replace(/\/v1$/, "");
|
|
248
|
+
if (!endpoints.some((e) => e.baseUrl === baseUrl)) {
|
|
249
|
+
endpoints.push({ baseUrl, label: `Harness (${baseUrl})` });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return Promise.all(endpoints.map((e) => probeLocalEndpoint(e.baseUrl, e.label)));
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Suggest stash directories, ranked (lower rank = higher priority).
|
|
256
|
+
*
|
|
257
|
+
* Sources, in priority order:
|
|
258
|
+
* 1. An existing config `stashDir` (always rank 0 — no-op / keep current).
|
|
259
|
+
* 2. A `akm/` or `agent-stash/` directory in the CWD git repo.
|
|
260
|
+
* 3. `~/akm` then `~/.akm` when they already exist.
|
|
261
|
+
*
|
|
262
|
+
* Pure function — filesystem reads only, no network. Tests inject `cwd`/`home`.
|
|
263
|
+
*/
|
|
264
|
+
export function detectStashDir(opts) {
|
|
265
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
266
|
+
const home = opts?.home ?? os.homedir();
|
|
267
|
+
const suggestions = [];
|
|
268
|
+
const seen = new Set();
|
|
269
|
+
const push = (p, reason, rank) => {
|
|
270
|
+
const abs = path.resolve(p);
|
|
271
|
+
if (seen.has(abs))
|
|
272
|
+
return;
|
|
273
|
+
seen.add(abs);
|
|
274
|
+
suggestions.push({ path: abs, reason, rank });
|
|
275
|
+
};
|
|
276
|
+
if (opts?.existingStashDir?.trim()) {
|
|
277
|
+
push(opts.existingStashDir, "existing config stashDir", 0);
|
|
278
|
+
}
|
|
279
|
+
// CWD git repo containing akm/ or agent-stash/
|
|
280
|
+
const repoRoot = findGitRepoRoot(cwd);
|
|
281
|
+
if (repoRoot) {
|
|
282
|
+
for (const dirName of ["akm", "agent-stash"]) {
|
|
283
|
+
const candidate = path.join(repoRoot, dirName);
|
|
284
|
+
try {
|
|
285
|
+
if (fs.statSync(candidate).isDirectory()) {
|
|
286
|
+
push(candidate, `git repo contains ${dirName}/`, 1);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// not present
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// ~/akm then ~/.akm when they exist
|
|
295
|
+
if (home) {
|
|
296
|
+
for (const dirName of ["akm", ".akm"]) {
|
|
297
|
+
const candidate = path.join(home, dirName);
|
|
298
|
+
try {
|
|
299
|
+
if (fs.statSync(candidate).isDirectory()) {
|
|
300
|
+
push(candidate, `${dirName} exists in home`, 2);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// not present
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return suggestions.sort((a, b) => a.rank - b.rank);
|
|
309
|
+
}
|
|
310
|
+
/** Walk up from `start` looking for a directory containing `.git`. */
|
|
311
|
+
function findGitRepoRoot(start) {
|
|
312
|
+
let dir = path.resolve(start);
|
|
313
|
+
for (;;) {
|
|
314
|
+
try {
|
|
315
|
+
if (fs.existsSync(path.join(dir, ".git")))
|
|
316
|
+
return dir;
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
// ignore
|
|
320
|
+
}
|
|
321
|
+
const parent = path.dirname(dir);
|
|
322
|
+
if (parent === dir)
|
|
323
|
+
return undefined;
|
|
324
|
+
dir = parent;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Detect the best agent harness in priority order:
|
|
329
|
+
* 1. OpenCode SDK resolvable via `import('@opencode-ai/sdk')`.
|
|
330
|
+
* 2. `opencode` binary on PATH.
|
|
331
|
+
* 3. `claude` binary on PATH.
|
|
332
|
+
* 4. none.
|
|
333
|
+
*
|
|
334
|
+
* Pure aside from the dynamic import resolution (which performs no network).
|
|
335
|
+
*/
|
|
336
|
+
export async function detectHarness(whichFn = defaultWhich) {
|
|
337
|
+
try {
|
|
338
|
+
await import("@opencode-ai/sdk");
|
|
339
|
+
return "opencode-sdk";
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
// SDK not installed — fall through to bin probes.
|
|
343
|
+
}
|
|
344
|
+
if (whichFn("opencode"))
|
|
345
|
+
return "opencode";
|
|
346
|
+
if (whichFn("claude"))
|
|
347
|
+
return "claude";
|
|
348
|
+
return "none";
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Run the full environment-detection pipeline once and return a single typed
|
|
352
|
+
* result. Orchestrates env-var scan, harness config import, harness selection,
|
|
353
|
+
* local-server probes, and stash-dir suggestions.
|
|
354
|
+
*
|
|
355
|
+
* SAFETY: No API key VALUE is ever read, stored, logged, or returned — only
|
|
356
|
+
* env var NAMES. Tolerant of every detector failing.
|
|
357
|
+
*
|
|
358
|
+
* @param opts.existingStashDir Current config stashDir (no-op suggestion).
|
|
359
|
+
* @param opts.envSource Env to scan. Defaults to `process.env`.
|
|
360
|
+
* @param opts.whichFn Binary lookup. Tests inject a stub.
|
|
361
|
+
*/
|
|
362
|
+
export async function detectEnvironment(opts) {
|
|
363
|
+
const envSource = opts?.envSource ?? process.env;
|
|
364
|
+
const whichFn = opts?.whichFn ?? defaultWhich;
|
|
365
|
+
let harnessConfigs = [];
|
|
366
|
+
try {
|
|
367
|
+
harnessConfigs = detectHarnessConfigs();
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
harnessConfigs = [];
|
|
371
|
+
}
|
|
372
|
+
const harnessBaseUrls = harnessConfigs.map((c) => c.baseUrl).filter((u) => typeof u === "string");
|
|
373
|
+
const [harness, localServers] = await Promise.all([
|
|
374
|
+
detectHarness(whichFn),
|
|
375
|
+
detectLocalServers(harnessBaseUrls).catch(() => []),
|
|
376
|
+
]);
|
|
377
|
+
let agentPlatforms = [];
|
|
378
|
+
try {
|
|
379
|
+
agentPlatforms = detectAgentPlatforms();
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
agentPlatforms = [];
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
harness,
|
|
386
|
+
providers: scanProviderEnvVars(envSource),
|
|
387
|
+
harnessConfigs,
|
|
388
|
+
localServers,
|
|
389
|
+
stashSuggestions: detectStashDir({
|
|
390
|
+
existingStashDir: opts?.existingStashDir,
|
|
391
|
+
cwd: opts?.cwd,
|
|
392
|
+
home: opts?.home,
|
|
393
|
+
}),
|
|
394
|
+
agentPlatforms,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Render a compact, human-readable "Detected environment" summary block.
|
|
399
|
+
* Contains env var NAMES only — never any value.
|
|
400
|
+
*/
|
|
401
|
+
export function renderDetectionSummary(env) {
|
|
402
|
+
const lines = ["Detected environment:"];
|
|
403
|
+
lines.push(` Harness: ${env.harness}`);
|
|
404
|
+
const liveServers = env.localServers.filter((s) => s.available);
|
|
405
|
+
if (liveServers.length > 0) {
|
|
406
|
+
lines.push(` Local servers: ${liveServers
|
|
407
|
+
.map((s) => `${s.label}${s.defaultModel ? ` (${s.defaultModel})` : ""}`)
|
|
408
|
+
.join(", ")}`);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
lines.push(" Local servers: none reachable");
|
|
412
|
+
}
|
|
413
|
+
if (env.providers.length > 0) {
|
|
414
|
+
// NAMES only — never values.
|
|
415
|
+
lines.push(` Provider keys: ${env.providers.map((p) => `${p.provider}:${p.envVar}`).join(", ")}`);
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
lines.push(" Provider keys: none in environment");
|
|
419
|
+
}
|
|
420
|
+
const topStash = env.stashSuggestions[0];
|
|
421
|
+
if (topStash) {
|
|
422
|
+
lines.push(` Stash suggest: ${topStash.path} (${topStash.reason})`);
|
|
423
|
+
}
|
|
424
|
+
return lines.join("\n");
|
|
425
|
+
}
|
|
@@ -17,126 +17,12 @@
|
|
|
17
17
|
* further by reading and parsing the harness config. They serve different
|
|
18
18
|
* purposes and should not be deduplicated.
|
|
19
19
|
*/
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// ── Claude Code Importer ─────────────────────────────────────────────────────
|
|
27
|
-
/**
|
|
28
|
-
* Imports LLM config from a Claude Code installation.
|
|
29
|
-
*
|
|
30
|
-
* Claude Code stores settings in `~/.claude/settings.json` or `~/.claude.json`.
|
|
31
|
-
* The model field may appear at the root or under `env.ANTHROPIC_MODEL`.
|
|
32
|
-
* The API key is always `ANTHROPIC_API_KEY`.
|
|
33
|
-
*/
|
|
34
|
-
const claudeCodeImporter = {
|
|
35
|
-
harnessName: "Claude Code",
|
|
36
|
-
detect() {
|
|
37
|
-
const home = homeDir();
|
|
38
|
-
// Claude Code is installed if the ~/.claude/ directory exists
|
|
39
|
-
return fs.existsSync(path.join(home, ".claude"));
|
|
40
|
-
},
|
|
41
|
-
importConfig() {
|
|
42
|
-
const home = homeDir();
|
|
43
|
-
// Try ~/.claude/settings.json, then ~/.claude.json
|
|
44
|
-
const candidates = [path.join(home, ".claude", "settings.json"), path.join(home, ".claude.json")];
|
|
45
|
-
for (const filePath of candidates) {
|
|
46
|
-
try {
|
|
47
|
-
if (!fs.existsSync(filePath))
|
|
48
|
-
continue;
|
|
49
|
-
const raw = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
50
|
-
// Claude Code settings: model may be at root or nested under env
|
|
51
|
-
const envBlock = raw.env;
|
|
52
|
-
const model = typeof raw.model === "string"
|
|
53
|
-
? raw.model
|
|
54
|
-
: typeof envBlock?.ANTHROPIC_MODEL === "string"
|
|
55
|
-
? String(envBlock.ANTHROPIC_MODEL)
|
|
56
|
-
: undefined;
|
|
57
|
-
return {
|
|
58
|
-
harnessName: "Claude Code",
|
|
59
|
-
provider: "anthropic",
|
|
60
|
-
model: model ?? "claude-sonnet-4-5",
|
|
61
|
-
apiKeyEnvVar: "ANTHROPIC_API_KEY",
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// try next candidate
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
// ~/.claude exists but no readable settings — still return basic Anthropic config
|
|
69
|
-
return {
|
|
70
|
-
harnessName: "Claude Code",
|
|
71
|
-
provider: "anthropic",
|
|
72
|
-
model: "claude-sonnet-4-5",
|
|
73
|
-
apiKeyEnvVar: "ANTHROPIC_API_KEY",
|
|
74
|
-
};
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
// ── OpenCode Importer ────────────────────────────────────────────────────────
|
|
78
|
-
/**
|
|
79
|
-
* Imports LLM config from an OpenCode installation.
|
|
80
|
-
*
|
|
81
|
-
* OpenCode stores config in `~/.config/opencode/config.json` or
|
|
82
|
-
* `~/.opencode/config.json`. Its schema has a `providers` array and a
|
|
83
|
-
* `model` field. API keys in providers appear as `$ENV_VAR_NAME` references.
|
|
84
|
-
*/
|
|
85
|
-
const openCodeImporter = {
|
|
86
|
-
harnessName: "OpenCode",
|
|
87
|
-
detect() {
|
|
88
|
-
const home = homeDir();
|
|
89
|
-
return fs.existsSync(path.join(home, ".config", "opencode")) || fs.existsSync(path.join(home, ".opencode"));
|
|
90
|
-
},
|
|
91
|
-
importConfig() {
|
|
92
|
-
const home = homeDir();
|
|
93
|
-
const candidates = [
|
|
94
|
-
path.join(home, ".config", "opencode", "config.json"),
|
|
95
|
-
path.join(home, ".opencode", "config.json"),
|
|
96
|
-
path.join(process.cwd(), ".opencode", "config.json"),
|
|
97
|
-
];
|
|
98
|
-
for (const filePath of candidates) {
|
|
99
|
-
try {
|
|
100
|
-
if (!fs.existsSync(filePath))
|
|
101
|
-
continue;
|
|
102
|
-
const raw = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
103
|
-
// OpenCode config shape: { model?: string, providers?: Array<{id, apiKey, baseUrl, ...}> }
|
|
104
|
-
const model = typeof raw.model === "string" ? raw.model : undefined;
|
|
105
|
-
// Extract provider info from the first entry in the providers array
|
|
106
|
-
let provider;
|
|
107
|
-
let baseUrl;
|
|
108
|
-
let apiKeyEnvVar;
|
|
109
|
-
const providers = Array.isArray(raw.providers) ? raw.providers : [];
|
|
110
|
-
if (providers.length > 0) {
|
|
111
|
-
const first = providers[0];
|
|
112
|
-
provider = typeof first?.id === "string" ? first.id : undefined;
|
|
113
|
-
baseUrl =
|
|
114
|
-
typeof first?.baseUrl === "string"
|
|
115
|
-
? first.baseUrl
|
|
116
|
-
: typeof first?.base_url === "string"
|
|
117
|
-
? first.base_url
|
|
118
|
-
: undefined;
|
|
119
|
-
// apiKey is an env var reference like "$OPENAI_API_KEY" — extract the var name
|
|
120
|
-
const apiKeyVal = typeof first?.apiKey === "string" ? first.apiKey : "";
|
|
121
|
-
if (apiKeyVal.startsWith("$")) {
|
|
122
|
-
apiKeyEnvVar = apiKeyVal.slice(1);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return {
|
|
126
|
-
harnessName: "OpenCode",
|
|
127
|
-
provider,
|
|
128
|
-
model,
|
|
129
|
-
baseUrl,
|
|
130
|
-
apiKeyEnvVar,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
// try next candidate
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return null;
|
|
138
|
-
},
|
|
139
|
-
};
|
|
20
|
+
import { claudeCodeImporter } from "../integrations/harnesses/claude/config-import.js";
|
|
21
|
+
import { openCodeImporter } from "../integrations/harnesses/opencode/config-import.js";
|
|
22
|
+
// The Claude Code importer was migrated to its harness directory in #563
|
|
23
|
+
// (`harnesses/claude/config-import.ts`) and the OpenCode importer in #564
|
|
24
|
+
// (`harnesses/opencode/config-import.ts`). Both are imported back into
|
|
25
|
+
// HARNESS_CONFIG_IMPORTERS below so detection order is unchanged.
|
|
140
26
|
// ── Registry ─────────────────────────────────────────────────────────────────
|
|
141
27
|
/**
|
|
142
28
|
* Registry of all supported harness config importers.
|