akm-cli 0.6.1 → 0.7.0
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 +66 -0
- package/dist/{cli.js → src/cli.js} +712 -34
- package/dist/{commands → src/commands}/config-cli.js +47 -4
- package/dist/src/commands/distill.js +283 -0
- package/dist/src/commands/events.js +108 -0
- package/dist/src/commands/history.js +191 -0
- package/dist/{commands → src/commands}/installed-stashes.js +1 -1
- package/dist/src/commands/proposal.js +119 -0
- package/dist/src/commands/propose.js +171 -0
- package/dist/src/commands/reflect.js +193 -0
- package/dist/{commands → src/commands}/registry-search.js +71 -7
- package/dist/{commands → src/commands}/remember.js +12 -0
- package/dist/{commands → src/commands}/search.js +104 -4
- package/dist/{commands → src/commands}/self-update.js +4 -3
- package/dist/{commands → src/commands}/show.js +73 -0
- package/dist/{commands → src/commands}/source-add.js +5 -1
- package/dist/{commands → src/commands}/source-manage.js +7 -1
- package/dist/{core → src/core}/asset-ref.js +5 -5
- package/dist/{core → src/core}/asset-spec.js +12 -0
- package/dist/{core → src/core}/common.js +1 -1
- package/dist/{core → src/core}/config.js +203 -121
- package/dist/{core → src/core}/errors.js +4 -0
- package/dist/src/core/events.js +239 -0
- package/dist/src/core/lesson-lint.js +86 -0
- package/dist/src/core/proposals.js +406 -0
- package/dist/src/core/warn.js +72 -0
- package/dist/{core → src/core}/write-source.js +80 -5
- package/dist/{indexer → src/indexer}/db-search.js +114 -24
- package/dist/{indexer → src/indexer}/db.js +76 -23
- package/dist/{indexer → src/indexer}/file-context.js +0 -3
- package/dist/src/indexer/graph-boost.js +179 -0
- package/dist/src/indexer/graph-extraction.js +212 -0
- package/dist/{indexer → src/indexer}/indexer.js +88 -7
- package/dist/{indexer → src/indexer}/matchers.js +1 -1
- package/dist/src/indexer/memory-inference.js +263 -0
- package/dist/{indexer → src/indexer}/metadata.js +111 -3
- package/dist/{indexer → src/indexer}/search-source.js +4 -2
- package/dist/src/integrations/agent/config.js +292 -0
- package/dist/src/integrations/agent/detect.js +94 -0
- package/dist/src/integrations/agent/index.js +17 -0
- package/dist/src/integrations/agent/profiles.js +65 -0
- package/dist/src/integrations/agent/prompts.js +167 -0
- package/dist/src/integrations/agent/spawn.js +272 -0
- package/dist/{integrations → src/integrations}/github.js +9 -3
- package/dist/{integrations → src/integrations}/lockfile.js +0 -26
- package/dist/{llm → src/llm}/client.js +33 -2
- package/dist/{llm → src/llm}/embedders/remote.js +37 -3
- package/dist/src/llm/feature-gate.js +108 -0
- package/dist/src/llm/graph-extract.js +107 -0
- package/dist/src/llm/index-passes.js +35 -0
- package/dist/src/llm/memory-infer.js +86 -0
- package/dist/{output → src/output}/cli-hints.js +15 -2
- package/dist/{output → src/output}/renderers.js +63 -2
- package/dist/src/output/shapes.js +523 -0
- package/dist/src/output/text.js +1116 -0
- package/dist/{registry → src/registry}/build-index.js +19 -8
- package/dist/{registry → src/registry}/factory.js +0 -8
- package/dist/{registry → src/registry}/providers/static-index.js +6 -3
- package/dist/{registry → src/registry}/resolve.js +68 -2
- package/dist/{setup → src/setup}/setup.js +52 -5
- package/dist/{sources → src/sources}/providers/git.js +7 -15
- package/dist/{wiki → src/wiki}/wiki.js +54 -6
- package/dist/{workflows → src/workflows}/runs.js +37 -3
- package/dist/tests/add-website-source.test.js +119 -0
- package/dist/tests/agent/agent-config-loader.test.js +70 -0
- package/dist/tests/agent/agent-config.test.js +221 -0
- package/dist/tests/agent/agent-detect.test.js +100 -0
- package/dist/tests/agent/agent-spawn.test.js +234 -0
- package/dist/tests/agent-output.test.js +186 -0
- package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +103 -0
- package/dist/tests/architecture/agent-spawn-seam.test.js +193 -0
- package/dist/tests/architecture/llm-stateless-seam.test.js +112 -0
- package/dist/tests/asset-ref.test.js +192 -0
- package/dist/tests/asset-registry.test.js +103 -0
- package/dist/tests/asset-spec.test.js +241 -0
- package/dist/tests/bench/attribution.test.js +996 -0
- package/dist/tests/bench/cleanup-sigint.test.js +83 -0
- package/dist/tests/bench/cleanup.js +234 -0
- package/dist/tests/bench/cleanup.test.js +166 -0
- package/dist/tests/bench/cli.js +1018 -0
- package/dist/tests/bench/cli.test.js +445 -0
- package/dist/tests/bench/compare.test.js +556 -0
- package/dist/tests/bench/corpus.js +317 -0
- package/dist/tests/bench/corpus.test.js +258 -0
- package/dist/tests/bench/doctor.js +525 -0
- package/dist/tests/bench/driver.js +401 -0
- package/dist/tests/bench/driver.test.js +584 -0
- package/dist/tests/bench/environment.js +233 -0
- package/dist/tests/bench/environment.test.js +199 -0
- package/dist/tests/bench/evolve-metrics.js +179 -0
- package/dist/tests/bench/evolve-metrics.test.js +187 -0
- package/dist/tests/bench/evolve.js +647 -0
- package/dist/tests/bench/evolve.test.js +624 -0
- package/dist/tests/bench/failure-modes.test.js +349 -0
- package/dist/tests/bench/feedback-integrity.test.js +457 -0
- package/dist/tests/bench/leakage.test.js +228 -0
- package/dist/tests/bench/learning-curve.test.js +134 -0
- package/dist/tests/bench/metrics.js +2395 -0
- package/dist/tests/bench/metrics.test.js +1150 -0
- package/dist/tests/bench/no-os-tmpdir-invariant.test.js +43 -0
- package/dist/tests/bench/opencode-config.js +194 -0
- package/dist/tests/bench/opencode-config.test.js +370 -0
- package/dist/tests/bench/report.js +1885 -0
- package/dist/tests/bench/report.test.js +1038 -0
- package/dist/tests/bench/run-config.js +355 -0
- package/dist/tests/bench/run-config.test.js +298 -0
- package/dist/tests/bench/run-curate-test.js +32 -0
- package/dist/tests/bench/run-failing-tasks.js +56 -0
- package/dist/tests/bench/run-full-bench.js +51 -0
- package/dist/tests/bench/run-items36-targeted.js +69 -0
- package/dist/tests/bench/run-nano-quick.js +42 -0
- package/dist/tests/bench/run-waveg-targeted.js +62 -0
- package/dist/tests/bench/runner.js +699 -0
- package/dist/tests/bench/runner.test.js +958 -0
- package/dist/tests/bench/search-bridge.test.js +331 -0
- package/dist/tests/bench/tmp.js +131 -0
- package/dist/tests/bench/trajectory.js +116 -0
- package/dist/tests/bench/trajectory.test.js +127 -0
- package/dist/tests/bench/verifier.js +114 -0
- package/dist/tests/bench/verifier.test.js +118 -0
- package/dist/tests/bench/workflow-evaluator.js +557 -0
- package/dist/tests/bench/workflow-evaluator.test.js +421 -0
- package/dist/tests/bench/workflow-spec.js +345 -0
- package/dist/tests/bench/workflow-spec.test.js +363 -0
- package/dist/tests/bench/workflow-trace.js +472 -0
- package/dist/tests/bench/workflow-trace.test.js +254 -0
- package/dist/tests/benchmark-search-quality.js +536 -0
- package/dist/tests/benchmark-suite.js +1441 -0
- package/dist/tests/capture-cli.test.js +112 -0
- package/dist/tests/cli-errors.test.js +204 -0
- package/dist/tests/commands/events.test.js +370 -0
- package/dist/tests/commands/history.test.js +418 -0
- package/dist/tests/commands/import.test.js +103 -0
- package/dist/tests/commands/proposal-cli.test.js +209 -0
- package/dist/tests/commands/reflect-propose-cli.test.js +333 -0
- package/dist/tests/commands/remember.test.js +97 -0
- package/dist/tests/commands/scope-flags.test.js +300 -0
- package/dist/tests/commands/search.test.js +537 -0
- package/dist/tests/commands/show-indexer-parity.test.js +117 -0
- package/dist/tests/commands/show.test.js +294 -0
- package/dist/tests/common.test.js +266 -0
- package/dist/tests/completions.test.js +142 -0
- package/dist/tests/config-cli.test.js +193 -0
- package/dist/tests/config-llm-features.test.js +139 -0
- package/dist/tests/config.test.js +569 -0
- package/dist/tests/contracts/migration-baseline.test.js +43 -0
- package/dist/tests/contracts/reflect-propose-envelope.test.js +139 -0
- package/dist/tests/contracts/spec-helpers.js +46 -0
- package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +228 -0
- package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +56 -0
- package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +34 -0
- package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +94 -0
- package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +39 -0
- package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +44 -0
- package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +47 -0
- package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +40 -0
- package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +58 -0
- package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +34 -0
- package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +75 -0
- package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +36 -0
- package/dist/tests/core/write-source.test.js +366 -0
- package/dist/tests/curate-command.test.js +87 -0
- package/dist/tests/db-scoring.test.js +201 -0
- package/dist/tests/db.test.js +654 -0
- package/dist/tests/distill-cli-flag.test.js +208 -0
- package/dist/tests/distill.test.js +515 -0
- package/dist/tests/docker-install.test.js +120 -0
- package/dist/tests/e2e.test.js +1419 -0
- package/dist/tests/embedder.test.js +340 -0
- package/dist/tests/embedding-model-config.test.js +379 -0
- package/dist/tests/feedback-command.test.js +172 -0
- package/dist/tests/file-context.test.js +552 -0
- package/dist/tests/fixtures/scripts/git/summarize-diff.js +9 -0
- package/dist/tests/fixtures/scripts/lint/eslint-check.js +7 -0
- package/dist/tests/fixtures/stashes/load.js +166 -0
- package/dist/tests/fixtures/stashes/load.test.js +97 -0
- package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +12 -0
- package/dist/tests/frontmatter.test.js +190 -0
- package/dist/tests/fts-field-weighting.test.js +254 -0
- package/dist/tests/fuzzy-search.test.js +230 -0
- package/dist/tests/git-provider-clone.test.js +45 -0
- package/dist/tests/github.test.js +161 -0
- package/dist/tests/graph-boost-ranking.test.js +305 -0
- package/dist/tests/graph-extraction.test.js +282 -0
- package/dist/tests/helpers/usage-events.js +8 -0
- package/dist/tests/index-pass-llm.test.js +161 -0
- package/dist/tests/indexer.test.js +570 -0
- package/dist/tests/info-command.test.js +166 -0
- package/dist/tests/init.test.js +69 -0
- package/dist/tests/install-script.test.js +246 -0
- package/dist/tests/integration/agent-real-profile.test.js +94 -0
- package/dist/tests/issue-36-repro.test.js +304 -0
- package/dist/tests/issues-191-194.test.js +160 -0
- package/dist/tests/lesson-lint.test.js +111 -0
- package/dist/tests/llm-client.test.js +115 -0
- package/dist/tests/llm-feature-gate.test.js +151 -0
- package/dist/tests/llm.test.js +139 -0
- package/dist/tests/lockfile.test.js +216 -0
- package/dist/tests/manifest.test.js +205 -0
- package/dist/tests/markdown.test.js +126 -0
- package/dist/tests/matchers-unit.test.js +189 -0
- package/dist/tests/memory-inference.test.js +299 -0
- package/dist/tests/merge-scoring.test.js +136 -0
- package/dist/tests/metadata.test.js +313 -0
- package/dist/tests/migration-help.test.js +89 -0
- package/dist/tests/origin-resolve.test.js +124 -0
- package/dist/tests/output-baseline.test.js +218 -0
- package/dist/tests/output-shapes-unit.test.js +478 -0
- package/dist/tests/parallel-search.test.js +272 -0
- package/dist/tests/parameter-metadata.test.js +365 -0
- package/dist/tests/paths.test.js +177 -0
- package/dist/tests/progressive-disclosure.test.js +280 -0
- package/dist/tests/proposals.test.js +279 -0
- package/dist/tests/proposed-quality.test.js +271 -0
- package/dist/tests/provider-registry.test.js +32 -0
- package/dist/tests/ranking-regression.test.js +548 -0
- package/dist/tests/reflect-propose.test.js +455 -0
- package/dist/tests/registry-build-index.test.js +394 -0
- package/dist/tests/registry-cli.test.js +290 -0
- package/dist/tests/registry-index-v2.test.js +430 -0
- package/dist/tests/registry-install.test.js +728 -0
- package/dist/tests/registry-providers/parity.test.js +189 -0
- package/dist/tests/registry-providers/skills-sh.test.js +309 -0
- package/dist/tests/registry-providers/static-index.test.js +238 -0
- package/dist/tests/registry-resolve.test.js +126 -0
- package/dist/tests/registry-search.test.js +923 -0
- package/dist/tests/remember-frontmatter.test.js +378 -0
- package/dist/tests/remember-unit.test.js +123 -0
- package/dist/tests/ripgrep-install.test.js +251 -0
- package/dist/tests/ripgrep-resolve.test.js +108 -0
- package/dist/tests/ripgrep.test.js +163 -0
- package/dist/tests/save-command.test.js +94 -0
- package/dist/tests/save-trust-qa-fixes.test.js +270 -0
- package/dist/tests/scoring-pipeline.test.js +648 -0
- package/dist/tests/search-include-proposed-cli.test.js +118 -0
- package/dist/tests/self-update.test.js +442 -0
- package/dist/tests/semantic-search-e2e.test.js +512 -0
- package/dist/tests/semantic-status.test.js +471 -0
- package/dist/tests/setup-run.integration.js +877 -0
- package/dist/tests/setup-wizard.test.js +198 -0
- package/dist/tests/setup.test.js +131 -0
- package/dist/tests/source-add.test.js +11 -0
- package/dist/tests/source-clone.test.js +254 -0
- package/dist/tests/source-manage.test.js +366 -0
- package/dist/tests/source-providers/filesystem.test.js +82 -0
- package/dist/tests/source-providers/git.test.js +252 -0
- package/dist/tests/source-providers/website.test.js +128 -0
- package/dist/tests/source-qa-fixes.test.js +286 -0
- package/dist/tests/source-registry.test.js +350 -0
- package/dist/tests/source-resolve.test.js +100 -0
- package/dist/tests/source-source.test.js +281 -0
- package/dist/tests/source.test.js +533 -0
- package/dist/tests/tar-utils-scan.test.js +73 -0
- package/dist/tests/toggle-components.test.js +73 -0
- package/dist/tests/usage-telemetry.test.js +265 -0
- package/dist/tests/utility-scoring.test.js +558 -0
- package/dist/tests/vault-load-error.test.js +78 -0
- package/dist/tests/vault-qa-fixes.test.js +194 -0
- package/dist/tests/vault.test.js +429 -0
- package/dist/tests/vector-search.test.js +608 -0
- package/dist/tests/walker.test.js +252 -0
- package/dist/tests/wave2-cluster-bc.test.js +228 -0
- package/dist/tests/wave2-cluster-d.test.js +180 -0
- package/dist/tests/wave2-cluster-e.test.js +179 -0
- package/dist/tests/wiki-qa-fixes.test.js +270 -0
- package/dist/tests/wiki.test.js +529 -0
- package/dist/tests/workflow-cli.test.js +271 -0
- package/dist/tests/workflow-markdown.test.js +171 -0
- package/dist/tests/workflow-path-escape.test.js +132 -0
- package/dist/tests/workflow-qa-fixes.test.js +395 -0
- package/dist/tests/workflows/indexer-rejection.test.js +213 -0
- package/docs/README.md +8 -0
- package/docs/migration/release-notes/0.7.0.md +244 -0
- package/package.json +2 -2
- package/dist/core/warn.js +0 -27
- package/dist/output/shapes.js +0 -212
- package/dist/output/text.js +0 -520
- /package/dist/{commands → src/commands}/completions.js +0 -0
- /package/dist/{commands → src/commands}/curate.js +0 -0
- /package/dist/{commands → src/commands}/info.js +0 -0
- /package/dist/{commands → src/commands}/init.js +0 -0
- /package/dist/{commands → src/commands}/install-audit.js +0 -0
- /package/dist/{commands → src/commands}/migration-help.js +0 -0
- /package/dist/{commands → src/commands}/source-clone.js +0 -0
- /package/dist/{commands → src/commands}/vault.js +0 -0
- /package/dist/{core → src/core}/asset-registry.js +0 -0
- /package/dist/{core → src/core}/frontmatter.js +0 -0
- /package/dist/{core → src/core}/markdown.js +0 -0
- /package/dist/{core → src/core}/paths.js +0 -0
- /package/dist/{indexer → src/indexer}/manifest.js +0 -0
- /package/dist/{indexer → src/indexer}/search-fields.js +0 -0
- /package/dist/{indexer → src/indexer}/semantic-status.js +0 -0
- /package/dist/{indexer → src/indexer}/usage-events.js +0 -0
- /package/dist/{indexer → src/indexer}/walker.js +0 -0
- /package/dist/{llm → src/llm}/embedder.js +0 -0
- /package/dist/{llm → src/llm}/embedders/cache.js +0 -0
- /package/dist/{llm → src/llm}/embedders/local.js +0 -0
- /package/dist/{llm → src/llm}/embedders/types.js +0 -0
- /package/dist/{llm → src/llm}/metadata-enhance.js +0 -0
- /package/dist/{output → src/output}/context.js +0 -0
- /package/dist/{registry → src/registry}/create-provider-registry.js +0 -0
- /package/dist/{registry → src/registry}/origin-resolve.js +0 -0
- /package/dist/{registry → src/registry}/providers/index.js +0 -0
- /package/dist/{registry → src/registry}/providers/skills-sh.js +0 -0
- /package/dist/{registry → src/registry}/providers/types.js +0 -0
- /package/dist/{registry → src/registry}/types.js +0 -0
- /package/dist/{setup → src/setup}/detect.js +0 -0
- /package/dist/{setup → src/setup}/ripgrep-install.js +0 -0
- /package/dist/{setup → src/setup}/ripgrep-resolve.js +0 -0
- /package/dist/{setup → src/setup}/steps.js +0 -0
- /package/dist/{sources → src/sources}/include.js +0 -0
- /package/dist/{sources → src/sources}/provider-factory.js +0 -0
- /package/dist/{sources → src/sources}/provider.js +0 -0
- /package/dist/{sources → src/sources}/providers/filesystem.js +0 -0
- /package/dist/{sources → src/sources}/providers/index.js +0 -0
- /package/dist/{sources → src/sources}/providers/install-types.js +0 -0
- /package/dist/{sources → src/sources}/providers/npm.js +0 -0
- /package/dist/{sources → src/sources}/providers/provider-utils.js +0 -0
- /package/dist/{sources → src/sources}/providers/sync-from-ref.js +0 -0
- /package/dist/{sources → src/sources}/providers/tar-utils.js +0 -0
- /package/dist/{sources → src/sources}/providers/website.js +0 -0
- /package/dist/{sources → src/sources}/resolve.js +0 -0
- /package/dist/{sources → src/sources}/types.js +0 -0
- /package/dist/{templates → src/templates}/wiki-templates.js +0 -0
- /package/dist/{version.js → src/version.js} +0 -0
- /package/dist/{workflows → src/workflows}/authoring.js +0 -0
- /package/dist/{workflows → src/workflows}/cli.js +0 -0
- /package/dist/{workflows → src/workflows}/db.js +0 -0
- /package/dist/{workflows → src/workflows}/document-cache.js +0 -0
- /package/dist/{workflows → src/workflows}/parser.js +0 -0
- /package/dist/{workflows → src/workflows}/renderer.js +0 -0
- /package/dist/{workflows → src/workflows}/schema.js +0 -0
- /package/dist/{workflows → src/workflows}/validator.js +0 -0
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* updated together with this builder.
|
|
9
9
|
*/
|
|
10
10
|
import fs from "node:fs";
|
|
11
|
-
import os from "node:os";
|
|
12
11
|
import path from "node:path";
|
|
13
12
|
import { fetchWithRetry, jsonWithByteCap } from "../core/common";
|
|
13
|
+
import { getCacheDir } from "../core/paths";
|
|
14
14
|
import { generateMetadataFlat, loadStashFile } from "../indexer/metadata";
|
|
15
15
|
import { walkStashFlat } from "../indexer/walker";
|
|
16
16
|
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github";
|
|
@@ -19,15 +19,16 @@ import { detectStashRoot } from "../sources/providers/provider-utils";
|
|
|
19
19
|
import { extractTarGzSecure } from "../sources/providers/tar-utils";
|
|
20
20
|
import { parseRegistryIndex } from "./providers/static-index";
|
|
21
21
|
const DEFAULT_NPM_REGISTRY_BASE = "https://registry.npmjs.org";
|
|
22
|
-
const DEFAULT_MANUAL_ENTRIES_PATH = path.resolve("manual-entries.json");
|
|
23
|
-
const DEFAULT_OUTPUT_PATH = path.resolve("index.json");
|
|
24
22
|
const REQUIRED_KEYWORDS = ["akm-stash"];
|
|
25
23
|
const GITHUB_TOPICS = ["akm-stash"];
|
|
26
24
|
const EXCLUDED_REPOS = new Set(["itlackey/akm"]);
|
|
27
25
|
const EXCLUDED_NPM_PACKAGES = new Set(["akm-cli"]);
|
|
28
26
|
const EMPTY_INSPECTION = {};
|
|
27
|
+
function getDefaultRegistryBuildDir() {
|
|
28
|
+
return path.join(getCacheDir(), "registry-build");
|
|
29
|
+
}
|
|
29
30
|
export async function buildRegistryIndex(options) {
|
|
30
|
-
const manualEntriesPath = path.resolve(options?.manualEntriesPath ??
|
|
31
|
+
const manualEntriesPath = path.resolve(options?.manualEntriesPath ?? path.join(getDefaultRegistryBuildDir(), "manual-entries.json"));
|
|
31
32
|
const npmRegistryBase = trimTrailingSlash(options?.npmRegistryBase ?? DEFAULT_NPM_REGISTRY_BASE);
|
|
32
33
|
const githubApiBase = trimTrailingSlash(options?.githubApiBase ?? GITHUB_API_BASE);
|
|
33
34
|
const [manualKits, npmKits, githubKits] = await Promise.all([
|
|
@@ -55,7 +56,7 @@ export async function buildRegistryIndex(options) {
|
|
|
55
56
|
};
|
|
56
57
|
}
|
|
57
58
|
export function writeRegistryIndex(index, outPath) {
|
|
58
|
-
const resolved = path.resolve(outPath ??
|
|
59
|
+
const resolved = path.resolve(outPath ?? path.join(getDefaultRegistryBuildDir(), "index.json"));
|
|
59
60
|
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
60
61
|
fs.writeFileSync(resolved, `${JSON.stringify(index, null, 2)}\n`, "utf8");
|
|
61
62
|
return resolved;
|
|
@@ -173,7 +174,16 @@ async function scanGithub(githubApiBase) {
|
|
|
173
174
|
return stashes;
|
|
174
175
|
}
|
|
175
176
|
async function inspectArchive(url, headers) {
|
|
176
|
-
|
|
177
|
+
// Route registry-build scratch space under the akm cache dir instead of
|
|
178
|
+
// the system temp directory. Long-running registry builds that crash
|
|
179
|
+
// mid-flight previously left orphan dirs under `/tmp`, which can fill
|
|
180
|
+
// the OS partition. Pinning to `${getCacheDir()}/registry-build/` keeps
|
|
181
|
+
// a single `rm -rf` of that directory sufficient to reclaim the space
|
|
182
|
+
// and leaves the operator's `/tmp` untouched. See #276 for the
|
|
183
|
+
// bench-tmp precedent and #284 for this non-bench rollout.
|
|
184
|
+
const tmpRoot = path.join(getCacheDir(), "registry-build");
|
|
185
|
+
fs.mkdirSync(tmpRoot, { recursive: true });
|
|
186
|
+
const tempDir = fs.mkdtempSync(path.join(tmpRoot, "build-"));
|
|
177
187
|
const archivePath = path.join(tempDir, "archive.tgz");
|
|
178
188
|
const extractDir = path.join(tempDir, "extract");
|
|
179
189
|
try {
|
|
@@ -283,7 +293,9 @@ async function loadManualEntries(manualEntriesPath) {
|
|
|
283
293
|
const parsed = parseRegistryIndex({ version: 3, updatedAt: new Date().toISOString(), stashes: candidateKits });
|
|
284
294
|
if (!parsed)
|
|
285
295
|
return [];
|
|
286
|
-
|
|
296
|
+
// Legacy `curated` flag on input entries is ignored (v1 spec §4.2). The
|
|
297
|
+
// builder no longer emits it on the resulting index.
|
|
298
|
+
return parsed.stashes.map((stash) => normalizeStash(stash));
|
|
287
299
|
}
|
|
288
300
|
catch {
|
|
289
301
|
return [];
|
|
@@ -327,7 +339,6 @@ function mergeEntries(a, b) {
|
|
|
327
339
|
author: a.author ?? b.author,
|
|
328
340
|
license: a.license ?? b.license,
|
|
329
341
|
latestVersion: a.latestVersion ?? b.latestVersion,
|
|
330
|
-
curated: a.curated || b.curated || undefined,
|
|
331
342
|
});
|
|
332
343
|
}
|
|
333
344
|
function mergeAssets(a, b) {
|
|
@@ -23,11 +23,3 @@ export function registerProvider(type, factory) {
|
|
|
23
23
|
export function resolveProviderFactory(type) {
|
|
24
24
|
return registry.resolve(type);
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Iterate over all registered registry providers. Used by the orchestrator
|
|
28
|
-
* (`src/commands/registry-search.ts`) to fan out queries through the same
|
|
29
|
-
* `RegistryProvider` interface regardless of provider kind.
|
|
30
|
-
*/
|
|
31
|
-
export function listProviderTypes() {
|
|
32
|
-
return registry.list();
|
|
33
|
-
}
|
|
@@ -190,7 +190,9 @@ export function parseRegistryIndex(data) {
|
|
|
190
190
|
if (typeof data !== "object" || data === null || Array.isArray(data))
|
|
191
191
|
return null;
|
|
192
192
|
const obj = data;
|
|
193
|
-
|
|
193
|
+
// Accept version 2 and 3 — both use the same stashes[] wire format.
|
|
194
|
+
// The live official registry currently publishes version 2.
|
|
195
|
+
if (typeof obj.version !== "number" || (obj.version !== 2 && obj.version !== 3))
|
|
194
196
|
return null;
|
|
195
197
|
if (typeof obj.updatedAt !== "string")
|
|
196
198
|
return null;
|
|
@@ -213,6 +215,9 @@ function parseStashEntry(raw) {
|
|
|
213
215
|
const source = asSource(obj.source);
|
|
214
216
|
if (!id || !name || !ref || !source)
|
|
215
217
|
return null;
|
|
218
|
+
// The legacy registry boolean `curated` is removed in v1. Legacy index JSON
|
|
219
|
+
// containing a `curated` key parses normally; the key is silently ignored
|
|
220
|
+
// (v1 spec §4.2, docs/migration/v1.md).
|
|
216
221
|
return {
|
|
217
222
|
id,
|
|
218
223
|
name,
|
|
@@ -226,7 +231,6 @@ function parseStashEntry(raw) {
|
|
|
226
231
|
author: asString(obj.author),
|
|
227
232
|
license: asString(obj.license),
|
|
228
233
|
latestVersion: asString(obj.latestVersion),
|
|
229
|
-
curated: obj.curated === true ? true : undefined,
|
|
230
234
|
};
|
|
231
235
|
}
|
|
232
236
|
// ── Scoring ─────────────────────────────────────────────────────────────────
|
|
@@ -294,7 +298,6 @@ function toSearchHit(stash, score, registryName) {
|
|
|
294
298
|
homepage: stash.homepage,
|
|
295
299
|
score: Math.round(score * 1000) / 1000,
|
|
296
300
|
metadata,
|
|
297
|
-
curated: stash.curated,
|
|
298
301
|
registryName,
|
|
299
302
|
};
|
|
300
303
|
}
|
|
@@ -4,7 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { fetchWithRetry, jsonWithByteCap } from "../core/common";
|
|
7
|
-
import { UsageError } from "../core/errors";
|
|
7
|
+
import { NotFoundError, UsageError } from "../core/errors";
|
|
8
8
|
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github";
|
|
9
9
|
/**
|
|
10
10
|
* Validate that a URL is safe to pass to git.
|
|
@@ -200,7 +200,7 @@ function tryParseLocalRef(rawRef, explicitPath) {
|
|
|
200
200
|
catch {
|
|
201
201
|
// Explicit paths (./foo, ../bar, /abs) should throw on missing
|
|
202
202
|
if (explicitPath) {
|
|
203
|
-
throw new
|
|
203
|
+
throw new NotFoundError(`Local path not found: ${resolvedPath}`, "FILE_NOT_FOUND", "Check the path exists and is readable.");
|
|
204
204
|
}
|
|
205
205
|
// Bare names that don't exist on disk — let caller fall through to npm/github
|
|
206
206
|
return undefined;
|
|
@@ -231,6 +231,71 @@ function isPathLikeRef(ref) {
|
|
|
231
231
|
}
|
|
232
232
|
return ref.includes("/") || ref.includes("\\");
|
|
233
233
|
}
|
|
234
|
+
/** Default public npm registry host. */
|
|
235
|
+
const DEFAULT_NPM_REGISTRY_HOST = "registry.npmjs.org";
|
|
236
|
+
/**
|
|
237
|
+
* Typed error raised when the npm registry returns a tarball URL on a host
|
|
238
|
+
* that is not the public registry or the operator-configured mirror. Carries
|
|
239
|
+
* a stable `.code` so callers (and JSON envelope output) can branch on it
|
|
240
|
+
* without parsing the message string.
|
|
241
|
+
*/
|
|
242
|
+
export class UntrustedNpmTarballError extends Error {
|
|
243
|
+
code = "UNTRUSTED_NPM_TARBALL";
|
|
244
|
+
_hint;
|
|
245
|
+
constructor(msg, hint) {
|
|
246
|
+
super(msg);
|
|
247
|
+
this.name = "UntrustedNpmTarballError";
|
|
248
|
+
this._hint = hint;
|
|
249
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
250
|
+
}
|
|
251
|
+
hint() {
|
|
252
|
+
return (this._hint ??
|
|
253
|
+
"Set AKM_NPM_REGISTRY to your private npm mirror's base URL if you install from a non-default registry.");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Resolve the set of npm registry hosts whose tarballs are considered trusted.
|
|
258
|
+
* Always includes the public npm registry, plus the host of an operator-set
|
|
259
|
+
* `AKM_NPM_REGISTRY` environment variable (if it parses to a valid URL).
|
|
260
|
+
*/
|
|
261
|
+
export function trustedNpmTarballHosts() {
|
|
262
|
+
const hosts = new Set([DEFAULT_NPM_REGISTRY_HOST]);
|
|
263
|
+
const override = process.env.AKM_NPM_REGISTRY?.trim();
|
|
264
|
+
if (override) {
|
|
265
|
+
try {
|
|
266
|
+
const overrideHost = new URL(override).hostname.toLowerCase();
|
|
267
|
+
if (overrideHost)
|
|
268
|
+
hosts.add(overrideHost);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
// Ignore unparseable overrides; the default host still applies.
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return hosts;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Validate that an npm tarball URL points at a trusted registry host. A
|
|
278
|
+
* compromised mirror could otherwise return an attacker-controlled
|
|
279
|
+
* `dist.tarball` field, redirecting installs to a third-party host.
|
|
280
|
+
*/
|
|
281
|
+
export function validateNpmTarballUrl(tarballUrl, packageRef) {
|
|
282
|
+
let url;
|
|
283
|
+
try {
|
|
284
|
+
url = new URL(tarballUrl);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
throw new UntrustedNpmTarballError(`npm package ${packageRef} returned an invalid tarball URL: ${tarballUrl}`);
|
|
288
|
+
}
|
|
289
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
290
|
+
throw new UntrustedNpmTarballError(`npm package ${packageRef} returned a tarball with disallowed scheme "${url.protocol}".`);
|
|
291
|
+
}
|
|
292
|
+
const trusted = trustedNpmTarballHosts();
|
|
293
|
+
const host = url.hostname.toLowerCase();
|
|
294
|
+
if (!trusted.has(host)) {
|
|
295
|
+
const allowed = Array.from(trusted).join(", ");
|
|
296
|
+
throw new UntrustedNpmTarballError(`npm package ${packageRef} returned a tarball URL on untrusted host "${host}" (allowed: ${allowed}).`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
234
299
|
async function resolveNpmArtifact(parsed) {
|
|
235
300
|
const encodedName = encodeURIComponent(parsed.packageName);
|
|
236
301
|
const metadata = await fetchJson(`https://registry.npmjs.org/${encodedName}`);
|
|
@@ -262,6 +327,7 @@ async function resolveNpmArtifact(parsed) {
|
|
|
262
327
|
if (!tarballUrl) {
|
|
263
328
|
throw new Error(`npm package ${parsed.packageName}@${resolvedVersion} does not expose a tarball URL.`);
|
|
264
329
|
}
|
|
330
|
+
validateNpmTarballUrl(tarballUrl, `${parsed.packageName}@${resolvedVersion}`);
|
|
265
331
|
const resolvedRevision = asString(dist.shasum) ?? asString(dist.integrity);
|
|
266
332
|
return {
|
|
267
333
|
id: parsed.id,
|
|
@@ -14,6 +14,7 @@ import { getDefaultStashDir } from "../core/paths";
|
|
|
14
14
|
import { closeDatabase, isVecAvailable, openDatabase } from "../indexer/db";
|
|
15
15
|
import { akmIndex } from "../indexer/indexer";
|
|
16
16
|
import { clearSemanticStatus, deriveSemanticProviderFingerprint, writeSemanticStatus, } from "../indexer/semantic-status";
|
|
17
|
+
import { detectAgentCliProfiles, pickDefaultAgentProfile, } from "../integrations/agent";
|
|
17
18
|
import { probeLlmCapabilities } from "../llm/client";
|
|
18
19
|
import { checkEmbeddingAvailability, DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "../llm/embedder";
|
|
19
20
|
import { detectAgentPlatforms, detectOllama } from "./detect";
|
|
@@ -282,8 +283,10 @@ async function stepOllama(current) {
|
|
|
282
283
|
mxbai: 1024,
|
|
283
284
|
minilm: 384,
|
|
284
285
|
bge: 384,
|
|
286
|
+
qwen3: 1024,
|
|
285
287
|
};
|
|
286
288
|
const guessedDim = Object.entries(knownDims).find(([k]) => embChoice.includes(k))?.[1] ?? 384;
|
|
289
|
+
p.note("Embedding dimension must match the model. Common values: 384 (BGE small), 768 (BGE base), 1024 (BGE large). Press Enter to accept the detected default.", "Embedding dimension");
|
|
287
290
|
const dimChoice = await prompt(() => p.text({
|
|
288
291
|
message: `Embedding dimension for ${embChoice}:`,
|
|
289
292
|
placeholder: String(guessedDim),
|
|
@@ -300,6 +303,14 @@ async function stepOllama(current) {
|
|
|
300
303
|
model: embChoice,
|
|
301
304
|
dimension: Number(dimChoice),
|
|
302
305
|
};
|
|
306
|
+
p.note([
|
|
307
|
+
"Recommended Qwen embedding models (modern, high context support):",
|
|
308
|
+
" • qwen3-embedding-0.6b — fast and lightweight (ollama pull qwen3-embedding-0.6b)",
|
|
309
|
+
" • qwen3-embedding-4b — higher quality (ollama pull qwen3-embedding-4b)",
|
|
310
|
+
"",
|
|
311
|
+
"For long documents (wiki pages, large files), set context length to avoid 400 errors:",
|
|
312
|
+
" akm config set embedding.contextLength 8192",
|
|
313
|
+
].join("\n"), "Embedding tips");
|
|
303
314
|
}
|
|
304
315
|
// else: undefined → use built-in local
|
|
305
316
|
// Surface Ollama details to the LLM step so it can offer Ollama as a preset.
|
|
@@ -313,7 +324,6 @@ const LLM_PRESETS = [
|
|
|
313
324
|
endpoint: "https://api.anthropic.com/v1/chat/completions",
|
|
314
325
|
defaultModel: "claude-sonnet-4-5",
|
|
315
326
|
hint: "beta OpenAI-compat layer; set AKM_LLM_API_KEY; override the model if the default is unavailable",
|
|
316
|
-
contextWindow: 200_000,
|
|
317
327
|
},
|
|
318
328
|
{
|
|
319
329
|
value: "openai",
|
|
@@ -321,7 +331,6 @@ const LLM_PRESETS = [
|
|
|
321
331
|
endpoint: "https://api.openai.com/v1/chat/completions",
|
|
322
332
|
defaultModel: "gpt-4o-mini",
|
|
323
333
|
hint: "AKM_LLM_API_KEY required",
|
|
324
|
-
contextWindow: 128_000,
|
|
325
334
|
},
|
|
326
335
|
{
|
|
327
336
|
value: "google",
|
|
@@ -329,7 +338,6 @@ const LLM_PRESETS = [
|
|
|
329
338
|
endpoint: "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
|
|
330
339
|
defaultModel: "gemini-2.0-flash",
|
|
331
340
|
hint: "OpenAI-compat endpoint, AKM_LLM_API_KEY required",
|
|
332
|
-
contextWindow: 1_000_000,
|
|
333
341
|
},
|
|
334
342
|
];
|
|
335
343
|
/**
|
|
@@ -431,7 +439,6 @@ export async function stepLlm(current, ollamaEndpoint, ollamaChatModels) {
|
|
|
431
439
|
model: model.trim() || preset.defaultModel,
|
|
432
440
|
temperature: 0.3,
|
|
433
441
|
maxTokens: 1024,
|
|
434
|
-
contextWindow: preset.contextWindow,
|
|
435
442
|
};
|
|
436
443
|
}
|
|
437
444
|
// Remind the user about API key placement. We do not offer a "store in config"
|
|
@@ -649,6 +656,29 @@ async function stepAgentPlatforms(current) {
|
|
|
649
656
|
}
|
|
650
657
|
return entries;
|
|
651
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* Detect installed agent CLIs and produce an updated `agent` config block
|
|
661
|
+
* with a sensible `default` (the first detected profile that the user has
|
|
662
|
+
* not already overridden).
|
|
663
|
+
*
|
|
664
|
+
* Pure-ish: file system / PATH probes are routed through `detectFn` so
|
|
665
|
+
* tests can drive the branches without touching the real PATH.
|
|
666
|
+
*
|
|
667
|
+
* @internal Exported for testing only.
|
|
668
|
+
*/
|
|
669
|
+
export function stepAgentCliDetection(current, detectFn = detectAgentCliProfiles) {
|
|
670
|
+
const detections = detectFn(current.agent);
|
|
671
|
+
const defaultName = pickDefaultAgentProfile(detections, current.agent?.default);
|
|
672
|
+
// No installed agents found and no existing config → leave block absent.
|
|
673
|
+
if (!defaultName && !current.agent) {
|
|
674
|
+
return { detections };
|
|
675
|
+
}
|
|
676
|
+
const agent = {
|
|
677
|
+
...(current.agent ?? {}),
|
|
678
|
+
...(defaultName ? { default: defaultName } : {}),
|
|
679
|
+
};
|
|
680
|
+
return { agent, detections };
|
|
681
|
+
}
|
|
652
682
|
// ── Main Wizard ─────────────────────────────────────────────────────────────
|
|
653
683
|
/**
|
|
654
684
|
* Build the canonical list of `SetupStep`s for the interactive wizard.
|
|
@@ -731,6 +761,23 @@ export function buildSetupSteps(options) {
|
|
|
731
761
|
ctx.apply({ sources: merged.length > 0 ? merged : undefined });
|
|
732
762
|
},
|
|
733
763
|
},
|
|
764
|
+
{
|
|
765
|
+
id: "agent-cli",
|
|
766
|
+
label: "Agent CLI",
|
|
767
|
+
nonInteractive: true,
|
|
768
|
+
async run(ctx) {
|
|
769
|
+
const result = stepAgentCliDetection(ctx.config);
|
|
770
|
+
const detected = result.detections.filter((d) => d.available);
|
|
771
|
+
if (detected.length > 0) {
|
|
772
|
+
p.log.info(`Detected agent CLIs: ${detected.map((d) => d.name).join(", ")}.` +
|
|
773
|
+
(result.agent?.default ? ` Default profile: ${result.agent.default}.` : ""));
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
p.log.info("No agent CLIs detected on PATH. Agent commands will be disabled until one is installed and `akm setup` is re-run.");
|
|
777
|
+
}
|
|
778
|
+
ctx.apply({ agent: result.agent });
|
|
779
|
+
},
|
|
780
|
+
},
|
|
734
781
|
];
|
|
735
782
|
return { steps, outcome };
|
|
736
783
|
}
|
|
@@ -771,7 +818,7 @@ export async function runSetupWizard() {
|
|
|
771
818
|
const embedding = newConfig.embedding;
|
|
772
819
|
const llm = newConfig.llm;
|
|
773
820
|
const registries = newConfig.registries;
|
|
774
|
-
const allStashes = newConfig.stashes ?? [];
|
|
821
|
+
const allStashes = newConfig.sources ?? newConfig.stashes ?? [];
|
|
775
822
|
// Confirm before saving
|
|
776
823
|
const effectiveRegistries = registries ?? DEFAULT_CONFIG.registries ?? [];
|
|
777
824
|
p.note([
|
|
@@ -6,6 +6,7 @@ import { resolveStashDir } from "../../core/common";
|
|
|
6
6
|
import { loadConfig } from "../../core/config";
|
|
7
7
|
import { ConfigError, UsageError } from "../../core/errors";
|
|
8
8
|
import { getRegistryCacheDir, getRegistryIndexCacheDir } from "../../core/paths";
|
|
9
|
+
import { sanitizeCommitMessage } from "../../core/write-source";
|
|
9
10
|
import { parseRegistryRef, resolveRegistryArtifact, validateGitRef, validateGitUrl } from "../../registry/resolve";
|
|
10
11
|
import { registerSourceProvider } from "../provider-factory";
|
|
11
12
|
import { applyAkmIncludeConfig, buildInstallCacheDir, copyDirectoryContents, detectStashRoot, isDirectory, isExpired, sanitizeString, } from "./provider-utils";
|
|
@@ -77,20 +78,6 @@ function getCachePaths(repoUrl) {
|
|
|
77
78
|
const key = createHash("sha256").update(repoUrl).digest("hex").slice(0, 16);
|
|
78
79
|
const cacheRoot = getRegistryIndexCacheDir();
|
|
79
80
|
const rootDir = path.join(cacheRoot, `git-${key}`);
|
|
80
|
-
// One-time silent migration: legacy `context-hub-${key}` directories were
|
|
81
|
-
// created for ALL git stashes (not just the andrewyng/context-hub repo). If
|
|
82
|
-
// the new path doesn't yet exist but the legacy one does, rename it in place
|
|
83
|
-
// so existing clones aren't silently invalidated. Failures are non-fatal —
|
|
84
|
-
// worst case the repo is re-cloned on the next refresh.
|
|
85
|
-
try {
|
|
86
|
-
const legacyRootDir = path.join(cacheRoot, `context-hub-${key}`);
|
|
87
|
-
if (!fs.existsSync(rootDir) && fs.existsSync(legacyRootDir)) {
|
|
88
|
-
fs.renameSync(legacyRootDir, rootDir);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
/* migration is best-effort */
|
|
93
|
-
}
|
|
94
81
|
return {
|
|
95
82
|
rootDir,
|
|
96
83
|
repoDir: path.join(rootDir, "repo"),
|
|
@@ -398,7 +385,12 @@ function parseGitRepoUrl(rawUrl) {
|
|
|
398
385
|
*/
|
|
399
386
|
export function saveGitStash(name, message, writableOverride) {
|
|
400
387
|
const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
401
|
-
|
|
388
|
+
// Sanitize the user-supplied message: strip CR/LF/NUL, collapse whitespace,
|
|
389
|
+
// clamp length. An attacker can otherwise pass `--message "subject\n\n\
|
|
390
|
+
// Co-Authored-By: someone-else"` and forge trailers in the commit log.
|
|
391
|
+
// Empty result falls back to the timestamped default.
|
|
392
|
+
const sanitized = message ? sanitizeCommitMessage(message) : "";
|
|
393
|
+
const commitMessage = sanitized || `akm save ${timestamp}`;
|
|
402
394
|
let repoDir;
|
|
403
395
|
let writable = false;
|
|
404
396
|
if (name) {
|
|
@@ -11,6 +11,32 @@
|
|
|
11
11
|
* Principle: "akm surfaces. The agent writes." akm owns lifecycle, raw-slug
|
|
12
12
|
* generation, structural lint, and `index.md` regeneration. The agent uses
|
|
13
13
|
* its native file tools for every other page operation.
|
|
14
|
+
*
|
|
15
|
+
* ## Canonical wiki content contract
|
|
16
|
+
*
|
|
17
|
+
* The three "infrastructure" files at the wiki root — `schema.md`, `index.md`,
|
|
18
|
+
* and `log.md` — are excluded from all user-facing content surfaces:
|
|
19
|
+
*
|
|
20
|
+
* | Surface | schema/index/log | raw/<slug>.md | <page>.md |
|
|
21
|
+
* | -------------------- | ---------------- | ------------- | --------- |
|
|
22
|
+
* | `wiki pages` | excluded | included | included |
|
|
23
|
+
* | scoped wiki search | excluded | included | included |
|
|
24
|
+
* | stash-wide FTS index | excluded | included | included |
|
|
25
|
+
* | `wiki lint` | excluded | tracked | tracked |
|
|
26
|
+
*
|
|
27
|
+
* `raw/` files are first-class addressable content (`wiki:<n>/raw/<slug>`),
|
|
28
|
+
* searchable, and listed. They are NOT authored pages — they are source
|
|
29
|
+
* material the agent turns into pages. `lint` tracks whether each raw file
|
|
30
|
+
* has been cited by a page's `sources:` frontmatter field.
|
|
31
|
+
*
|
|
32
|
+
* ## Regeneration contract
|
|
33
|
+
*
|
|
34
|
+
* `regenerateWikiIndex` / `regenerateAllWikiIndexes` apply ONLY to
|
|
35
|
+
* stash-owned wikis (directories under `<stashDir>/wikis/`). External wikis
|
|
36
|
+
* registered via `akm wiki register` are read-only caches; mutating their
|
|
37
|
+
* `index.md` would corrupt source-of-truth content that akm does not own.
|
|
38
|
+
* The indexer therefore calls `regenerateAllWikiIndexes(stashDir)` — which
|
|
39
|
+
* only iterates `<stashDir>/wikis/` — and never touches registered sources.
|
|
14
40
|
*/
|
|
15
41
|
import fs from "node:fs";
|
|
16
42
|
import path from "node:path";
|
|
@@ -496,10 +522,15 @@ function readPageFrontmatter(absPath) {
|
|
|
496
522
|
return out;
|
|
497
523
|
}
|
|
498
524
|
/**
|
|
499
|
-
* List
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
525
|
+
* List all addressable wiki content entries.
|
|
526
|
+
*
|
|
527
|
+
* Per the canonical wiki contract: `schema.md`, `index.md`, and `log.md` at
|
|
528
|
+
* the wiki root are infrastructure files and are excluded. Everything else —
|
|
529
|
+
* authored pages AND `raw/<slug>.md` sources — is included and addressable as
|
|
530
|
+
* `wiki:<name>/<rel-path-without-.md>`.
|
|
531
|
+
*
|
|
532
|
+
* Callers that need to distinguish authored pages from raw sources should
|
|
533
|
+
* check whether the returned `name` starts with `"raw/"`.
|
|
503
534
|
*/
|
|
504
535
|
export function listPages(stashDir, name) {
|
|
505
536
|
const wikiDir = resolveWikiSource(stashDir, name).path;
|
|
@@ -521,6 +552,10 @@ export function listPages(stashDir, name) {
|
|
|
521
552
|
* Uses `akmSearch({ type: "wiki" })` to reuse the full FTS5+boost pipeline,
|
|
522
553
|
* then drops hits that aren't inside `wikis/<name>/`. No parallel scorer.
|
|
523
554
|
*
|
|
555
|
+
* Per the canonical wiki contract: infrastructure files (`schema.md`,
|
|
556
|
+
* `index.md`, `log.md`) at the wiki root are excluded. `raw/<slug>.md`
|
|
557
|
+
* sources are included — they are first-class addressable content.
|
|
558
|
+
*
|
|
524
559
|
* When the index is absent (e.g. fresh stash), `akmSearch` falls back to its
|
|
525
560
|
* substring walker; hits still come through path-filtered here.
|
|
526
561
|
*/
|
|
@@ -795,7 +830,13 @@ export function lintWiki(stashDir, name) {
|
|
|
795
830
|
}
|
|
796
831
|
// ── Index regeneration ─────────────────────────────────────────────────────
|
|
797
832
|
/**
|
|
798
|
-
* Rebuild a wiki's `index.md` from its pages' frontmatter.
|
|
833
|
+
* Rebuild a stash-owned wiki's `index.md` from its pages' frontmatter.
|
|
834
|
+
*
|
|
835
|
+
* This function uses `resolveWikiDir` (not `resolveWikiSource`) so it only
|
|
836
|
+
* ever operates on the stash-owned path `<stashDir>/wikis/<name>/`. External
|
|
837
|
+
* wikis registered via `akm wiki register` are never regenerated here — they
|
|
838
|
+
* are read-only caches. See the canonical wiki contract at the top of this
|
|
839
|
+
* file for the full regeneration rule.
|
|
799
840
|
*
|
|
800
841
|
* Pages are grouped by `pageKind` (falling back to `uncategorised`) and
|
|
801
842
|
* listed alphabetically inside each group. If the wiki directory doesn't
|
|
@@ -861,7 +902,14 @@ export function regenerateWikiIndex(stashDir, name) {
|
|
|
861
902
|
}
|
|
862
903
|
}
|
|
863
904
|
/**
|
|
864
|
-
* Regenerate `index.md` for every wiki
|
|
905
|
+
* Regenerate `index.md` for every stash-owned wiki under `<stashDir>/wikis/`.
|
|
906
|
+
*
|
|
907
|
+
* Per the canonical wiki contract: regeneration applies ONLY to stash-owned
|
|
908
|
+
* wikis. External wikis registered via `akm wiki register` are read-only
|
|
909
|
+
* caches whose source-of-truth lives outside this stash; mutating their
|
|
910
|
+
* `index.md` would corrupt content that akm does not own. Those wikis
|
|
911
|
+
* therefore appear only in the FTS index (read), never in regeneration
|
|
912
|
+
* (write).
|
|
865
913
|
*
|
|
866
914
|
* Called from `akmIndex()` as a side effect after the FTS rebuild. Never
|
|
867
915
|
* throws; returns the list of wiki names that were regenerated.
|
|
@@ -3,6 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import { parseAssetRef } from "../core/asset-ref";
|
|
4
4
|
import { loadConfig } from "../core/config";
|
|
5
5
|
import { NotFoundError, UsageError } from "../core/errors";
|
|
6
|
+
import { appendEvent } from "../core/events";
|
|
6
7
|
import { getDbPath } from "../core/paths";
|
|
7
8
|
import { closeDatabase, openDatabase } from "../indexer/db";
|
|
8
9
|
import { resolveSourceEntries } from "../indexer/search-source";
|
|
@@ -32,7 +33,13 @@ export async function startWorkflowRun(ref, params = {}) {
|
|
|
32
33
|
insertStep.run(runId, step.id, step.title, step.instructions, step.completionCriteria ? JSON.stringify(step.completionCriteria) : null, step.sequenceIndex ?? 0);
|
|
33
34
|
}
|
|
34
35
|
})();
|
|
35
|
-
|
|
36
|
+
const result = getWorkflowStatus(runId);
|
|
37
|
+
appendEvent({
|
|
38
|
+
eventType: "workflow_started",
|
|
39
|
+
ref: ref,
|
|
40
|
+
metadata: { runId: result.run.id, title: result.run.workflowTitle },
|
|
41
|
+
});
|
|
42
|
+
return result;
|
|
36
43
|
}
|
|
37
44
|
finally {
|
|
38
45
|
closeWorkflowDatabase(workflowDb);
|
|
@@ -173,7 +180,16 @@ export function completeWorkflowStep(input) {
|
|
|
173
180
|
completed_at: state.completedAt,
|
|
174
181
|
};
|
|
175
182
|
})();
|
|
176
|
-
|
|
183
|
+
const detail = buildWorkflowRunDetail(updatedRun, refreshedSteps);
|
|
184
|
+
appendEvent({
|
|
185
|
+
eventType: "workflow_step_completed",
|
|
186
|
+
ref: detail.run.workflowRef,
|
|
187
|
+
metadata: { runId: input.runId, stepId: input.stepId, notes: input.notes },
|
|
188
|
+
});
|
|
189
|
+
if (detail.run.status === "completed") {
|
|
190
|
+
appendEvent({ eventType: "workflow_finished", ref: detail.run.workflowRef, metadata: { runId: input.runId } });
|
|
191
|
+
}
|
|
192
|
+
return detail;
|
|
177
193
|
}
|
|
178
194
|
finally {
|
|
179
195
|
closeWorkflowDatabase(workflowDb);
|
|
@@ -187,6 +203,9 @@ async function resolveRunSpecifier(db, specifier, params) {
|
|
|
187
203
|
}
|
|
188
204
|
return { run: explicitRun, autoStarted: false };
|
|
189
205
|
}
|
|
206
|
+
if (!specifier.includes(":")) {
|
|
207
|
+
throw new NotFoundError(`Workflow run "${specifier}" not found.`, "WORKFLOW_NOT_FOUND");
|
|
208
|
+
}
|
|
190
209
|
const parsed = parseAssetRef(specifier);
|
|
191
210
|
if (parsed.type !== "workflow") {
|
|
192
211
|
throw new UsageError(`Expected a workflow ref or workflow run id, got "${specifier}".`);
|
|
@@ -316,7 +335,7 @@ function resolveWorkflowEntryId(sourcePath, ref) {
|
|
|
316
335
|
function readWorkflowRun(db, runId) {
|
|
317
336
|
const run = db.prepare("SELECT * FROM workflow_runs WHERE id = ?").get(runId);
|
|
318
337
|
if (!run) {
|
|
319
|
-
throw new NotFoundError(`Workflow run not found
|
|
338
|
+
throw new NotFoundError(`Workflow run "${runId}" not found.`, "WORKFLOW_NOT_FOUND");
|
|
320
339
|
}
|
|
321
340
|
return run;
|
|
322
341
|
}
|
|
@@ -416,3 +435,18 @@ function parseJsonArray(value) {
|
|
|
416
435
|
}
|
|
417
436
|
return undefined;
|
|
418
437
|
}
|
|
438
|
+
export function getActiveWorkflowRun() {
|
|
439
|
+
try {
|
|
440
|
+
const workflowDb = openWorkflowDatabase();
|
|
441
|
+
const row = workflowDb
|
|
442
|
+
.query("SELECT id, current_step_id, workflow_ref FROM workflow_runs WHERE status IN ('active', 'blocked') ORDER BY updated_at DESC LIMIT 1")
|
|
443
|
+
.get();
|
|
444
|
+
closeWorkflowDatabase(workflowDb);
|
|
445
|
+
if (!row)
|
|
446
|
+
return null;
|
|
447
|
+
return { runId: row.id, stepId: row.current_step_id, workflowRef: row.workflow_ref };
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
return null; // fail-open: never crash show output due to DB error
|
|
451
|
+
}
|
|
452
|
+
}
|