akm-cli 0.6.1 → 0.7.0-rc1
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} +620 -26
- package/dist/{commands → src/commands}/config-cli.js +5 -4
- package/dist/src/commands/distill.js +283 -0
- package/dist/src/commands/events.js +108 -0
- package/dist/src/commands/history.js +120 -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 +2 -1
- package/dist/{commands → src/commands}/remember.js +12 -0
- package/dist/{commands → src/commands}/search.js +74 -1
- package/dist/{commands → src/commands}/self-update.js +4 -3
- package/dist/{commands → src/commands}/show.js +44 -0
- 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 +175 -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 +113 -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 +73 -6
- package/dist/src/indexer/memory-inference.js +263 -0
- package/dist/{indexer → src/indexer}/metadata.js +111 -3
- 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 +221 -0
- package/dist/{integrations → src/integrations}/lockfile.js +0 -26
- package/dist/{llm → src/llm}/client.js +33 -2
- 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}/renderers.js +60 -1
- package/dist/src/output/shapes.js +516 -0
- package/dist/{output → src/output}/text.js +447 -4
- package/dist/{registry → src/registry}/build-index.js +14 -4
- package/dist/{registry → src/registry}/factory.js +0 -8
- package/dist/{registry → src/registry}/providers/static-index.js +3 -2
- package/dist/{registry → src/registry}/resolve.js +68 -2
- package/dist/{setup → src/setup}/setup.js +43 -5
- package/dist/{sources → src/sources}/providers/git.js +7 -15
- 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 +995 -0
- package/dist/tests/bench/cleanup-sigint.test.js +83 -0
- package/dist/tests/bench/cleanup.js +203 -0
- package/dist/tests/bench/cleanup.test.js +166 -0
- package/dist/tests/bench/cli.js +683 -0
- package/dist/tests/bench/cli.test.js +177 -0
- package/dist/tests/bench/compare.test.js +556 -0
- package/dist/tests/bench/corpus.js +314 -0
- package/dist/tests/bench/corpus.test.js +258 -0
- package/dist/tests/bench/driver.js +346 -0
- package/dist/tests/bench/driver.test.js +443 -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 +580 -0
- package/dist/tests/bench/evolve.test.js +616 -0
- package/dist/tests/bench/failure-modes.test.js +300 -0
- package/dist/tests/bench/feedback-integrity.test.js +456 -0
- package/dist/tests/bench/leakage.test.js +125 -0
- package/dist/tests/bench/learning-curve.test.js +133 -0
- package/dist/tests/bench/metrics.js +2319 -0
- package/dist/tests/bench/metrics.test.js +1144 -0
- package/dist/tests/bench/no-os-tmpdir-invariant.test.js +43 -0
- package/dist/tests/bench/report.js +1821 -0
- package/dist/tests/bench/report.test.js +989 -0
- package/dist/tests/bench/runner.js +536 -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 +41 -0
- package/dist/tests/bench/trajectory.js +116 -0
- package/dist/tests/bench/trajectory.test.js +127 -0
- package/dist/tests/bench/verifier.js +109 -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 +358 -0
- package/dist/tests/bench/workflow-spec.test.js +363 -0
- package/dist/tests/bench/workflow-trace.js +438 -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 +203 -0
- package/dist/tests/commands/events.test.js +370 -0
- package/dist/tests/commands/history.test.js +223 -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 +544 -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 +1398 -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 +88 -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 +559 -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 +217 -0
- package/dist/tests/output-shapes-unit.test.js +476 -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 +378 -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 +204 -0
- package/dist/tests/registry-resolve.test.js +126 -0
- package/dist/tests/registry-search.test.js +723 -0
- package/dist/tests/remember-frontmatter.test.js +380 -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 +268 -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 +221 -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 +377 -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/{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-add.js +0 -0
- /package/dist/{commands → src/commands}/source-clone.js +0 -0
- /package/dist/{commands → src/commands}/source-manage.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}/matchers.js +0 -0
- /package/dist/{indexer → src/indexer}/search-fields.js +0 -0
- /package/dist/{indexer → src/indexer}/search-source.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/{integrations → src/integrations}/github.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/remote.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}/cli-hints.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/{wiki → src/wiki}/wiki.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}/runs.js +0 -0
- /package/dist/{workflows → src/workflows}/schema.js +0 -0
- /package/dist/{workflows → src/workflows}/validator.js +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM helper for the `akm index` graph-extraction pass (#207).
|
|
3
|
+
*
|
|
4
|
+
* Given a single asset body (typically a `memory:` or `knowledge:` file),
|
|
5
|
+
* asks the configured LLM to surface the entities mentioned in it and the
|
|
6
|
+
* relations between them. The pass itself
|
|
7
|
+
* (`src/indexer/graph-extraction.ts`) is responsible for deciding which
|
|
8
|
+
* files to extract, persisting the resulting nodes/edges to a stash-local
|
|
9
|
+
* `graph.json` artifact, and feeding the artifact into the FTS5+boosts
|
|
10
|
+
* search pipeline as a single boost component.
|
|
11
|
+
*
|
|
12
|
+
* This module is intentionally tiny and stateless so tests can stub it via
|
|
13
|
+
* `mock.module("../src/llm/graph-extract", ...)` without hitting a network.
|
|
14
|
+
*
|
|
15
|
+
* Locked v1 contract (#208): the LLM connection always comes from the
|
|
16
|
+
* shared `akm.llm` block — never from a per-pass override. Callers obtain
|
|
17
|
+
* the connection via `resolveIndexPassLLM("graph", config)` and pass it
|
|
18
|
+
* straight through.
|
|
19
|
+
*/
|
|
20
|
+
import { toErrorMessage } from "../core/common";
|
|
21
|
+
import { warn } from "../core/warn";
|
|
22
|
+
import { chatCompletion, parseJsonResponse } from "./client";
|
|
23
|
+
/** Hard cap on body chars sent to the model. */
|
|
24
|
+
const MAX_BODY_CHARS = 4000;
|
|
25
|
+
/** Hard cap on entities returned per asset — guards against runaway LLM output. */
|
|
26
|
+
const MAX_ENTITIES_PER_ASSET = 32;
|
|
27
|
+
/** Hard cap on relations returned per asset. */
|
|
28
|
+
const MAX_RELATIONS_PER_ASSET = 32;
|
|
29
|
+
/** Hard timeout for the LLM call; an `akm index` run must not hang on a misbehaving endpoint. */
|
|
30
|
+
const LLM_TIMEOUT_MS = 30_000;
|
|
31
|
+
const SYSTEM_PROMPT = "You extract a knowledge graph from developer notes. Return only valid JSON. " + "No prose, no markdown fences.";
|
|
32
|
+
const USER_PROMPT_PREFIX = `Extract entities and relations from the asset body below.
|
|
33
|
+
|
|
34
|
+
Rules:
|
|
35
|
+
- Output ONLY a JSON object: {"entities": ["Entity One", ...], "relations": [{"from": "A", "to": "B", "type": "uses"}, ...]}.
|
|
36
|
+
- Entities are short, canonical noun phrases (project names, services, tools, people, file/dir names, technical concepts).
|
|
37
|
+
- Relations connect two entities that both appear in the entities array.
|
|
38
|
+
- "type" is a short verb phrase (e.g. "uses", "depends on", "owns", "documents"). Optional; omit when unsure.
|
|
39
|
+
- Drop pleasantries, meta-commentary, and timestamps.
|
|
40
|
+
- Limit to at most ${MAX_ENTITIES_PER_ASSET} entities and ${MAX_RELATIONS_PER_ASSET} relations per asset.
|
|
41
|
+
- Return {"entities": [], "relations": []} if the body has no extractable graph content.
|
|
42
|
+
|
|
43
|
+
Asset body:
|
|
44
|
+
`;
|
|
45
|
+
/**
|
|
46
|
+
* Extract entities and relations from a single asset body via the configured LLM.
|
|
47
|
+
*
|
|
48
|
+
* Returns `{entities: [], relations: []}` on any failure (timeout, invalid
|
|
49
|
+
* JSON, empty response). Errors are logged via `warn()` but never thrown — a
|
|
50
|
+
* failed extraction for one asset must not abort the rest of the index pass.
|
|
51
|
+
*/
|
|
52
|
+
export async function extractGraphFromBody(llmConfig, body) {
|
|
53
|
+
const empty = { entities: [], relations: [] };
|
|
54
|
+
const trimmedBody = body.trim();
|
|
55
|
+
if (!trimmedBody)
|
|
56
|
+
return empty;
|
|
57
|
+
const userPrompt = `${USER_PROMPT_PREFIX}${trimmedBody.slice(0, MAX_BODY_CHARS)}`;
|
|
58
|
+
let timeoutHandle;
|
|
59
|
+
try {
|
|
60
|
+
const raw = await Promise.race([
|
|
61
|
+
chatCompletion(llmConfig, [
|
|
62
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
63
|
+
{ role: "user", content: userPrompt },
|
|
64
|
+
], { maxTokens: 1024, temperature: 0.1 }),
|
|
65
|
+
new Promise((_, reject) => {
|
|
66
|
+
timeoutHandle = setTimeout(() => reject(new Error("graph extraction timed out")), LLM_TIMEOUT_MS);
|
|
67
|
+
}),
|
|
68
|
+
]);
|
|
69
|
+
if (!raw)
|
|
70
|
+
return empty;
|
|
71
|
+
const parsed = parseJsonResponse(raw);
|
|
72
|
+
if (!parsed) {
|
|
73
|
+
warn("graph extraction: invalid JSON response from LLM; skipping asset.");
|
|
74
|
+
return empty;
|
|
75
|
+
}
|
|
76
|
+
const entities = Array.isArray(parsed.entities)
|
|
77
|
+
? parsed.entities
|
|
78
|
+
.filter((e) => typeof e === "string")
|
|
79
|
+
.map((e) => e.trim())
|
|
80
|
+
.filter((e) => e.length > 0)
|
|
81
|
+
.slice(0, MAX_ENTITIES_PER_ASSET)
|
|
82
|
+
: [];
|
|
83
|
+
const entitySet = new Set(entities);
|
|
84
|
+
const relations = Array.isArray(parsed.relations)
|
|
85
|
+
? parsed.relations
|
|
86
|
+
.filter((r) => typeof r === "object" && r !== null && !Array.isArray(r))
|
|
87
|
+
.map((r) => ({
|
|
88
|
+
from: typeof r.from === "string" ? r.from.trim() : "",
|
|
89
|
+
to: typeof r.to === "string" ? r.to.trim() : "",
|
|
90
|
+
type: typeof r.type === "string" && r.type.trim() ? r.type.trim() : undefined,
|
|
91
|
+
}))
|
|
92
|
+
// Both endpoints must be non-empty AND mentioned in entities[];
|
|
93
|
+
// dangling relations are noise and inflate the boost component.
|
|
94
|
+
.filter((r) => r.from && r.to && entitySet.has(r.from) && entitySet.has(r.to))
|
|
95
|
+
.slice(0, MAX_RELATIONS_PER_ASSET)
|
|
96
|
+
: [];
|
|
97
|
+
return { entities, relations };
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
warn(`graph extraction failed: ${toErrorMessage(err)}`);
|
|
101
|
+
return empty;
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
if (timeoutHandle !== undefined)
|
|
105
|
+
clearTimeout(timeoutHandle);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-pass LLM config resolution for `akm index`.
|
|
3
|
+
*
|
|
4
|
+
* Locked v1 contract (#208):
|
|
5
|
+
* - There is exactly one provider/model configuration: `akm.llm`.
|
|
6
|
+
* - Every LLM-using pass inside `akm index` defaults to that block.
|
|
7
|
+
* - A pass can be opted out individually with `index.<passName>.llm = false`.
|
|
8
|
+
* - Any attempt to supply provider/model fields under `index.<passName>` is
|
|
9
|
+
* rejected at config-load time by `parseIndexConfig` in
|
|
10
|
+
* {@link ../core/config.ts} (`ConfigError("INVALID_CONFIG_FILE")`).
|
|
11
|
+
*
|
|
12
|
+
* Passes plug in by calling {@link resolveIndexPassLLM} with their pass
|
|
13
|
+
* name (e.g. `"memory"` for #201's memory-inference pass, `"graph"` for
|
|
14
|
+
* #207's graph-extraction pass). They do not read `config.llm` directly.
|
|
15
|
+
* This keeps the config surface small and the wiring uniform.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the {@link LlmConnectionConfig} a single index pass should use, or
|
|
19
|
+
* `undefined` when the pass should run without an LLM.
|
|
20
|
+
*
|
|
21
|
+
* Returns `undefined` if any of:
|
|
22
|
+
* - No top-level `akm.llm` block is configured.
|
|
23
|
+
* - The pass is explicitly opted out (`index.<passName>.llm === false`).
|
|
24
|
+
*
|
|
25
|
+
* Otherwise returns the shared `akm.llm` config. There is no per-pass
|
|
26
|
+
* provider override; that decision is locked by §9 of the v1 spec.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveIndexPassLLM(passName, config) {
|
|
29
|
+
if (!config.llm)
|
|
30
|
+
return undefined;
|
|
31
|
+
const passConfig = config.index?.[passName];
|
|
32
|
+
if (passConfig?.llm === false)
|
|
33
|
+
return undefined;
|
|
34
|
+
return config.llm;
|
|
35
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM helper for the `akm index` memory-inference pass (#201).
|
|
3
|
+
*
|
|
4
|
+
* Splits a single memory body into a list of atomic facts. The pass itself
|
|
5
|
+
* (in `src/indexer/memory-inference.ts`) is responsible for deciding which
|
|
6
|
+
* memories are pending, persisting the resulting atomic memories with the
|
|
7
|
+
* correct frontmatter (`inferred: true`, `source: <parent-ref>`), and
|
|
8
|
+
* marking the parent as processed for idempotency.
|
|
9
|
+
*
|
|
10
|
+
* This module is intentionally tiny and stateless so tests can stub it via
|
|
11
|
+
* `mock.module("../src/llm/memory-infer", ...)` without hitting a network.
|
|
12
|
+
*
|
|
13
|
+
* Locked v1 contract (#208): the LLM connection always comes from the
|
|
14
|
+
* shared `akm.llm` block — never from a per-pass override. Callers obtain
|
|
15
|
+
* the connection via `resolveIndexPassLLM("memory", config)` and pass it
|
|
16
|
+
* straight through.
|
|
17
|
+
*/
|
|
18
|
+
import { toErrorMessage } from "../core/common";
|
|
19
|
+
import { warn } from "../core/warn";
|
|
20
|
+
import { chatCompletion, parseJsonResponse } from "./client";
|
|
21
|
+
/** Hard cap on body chars sent to the model — pragmatic and matches `runLlmEnrich`. */
|
|
22
|
+
const MAX_BODY_CHARS = 4000;
|
|
23
|
+
/** Hard cap on the number of atomic facts returned per memory. */
|
|
24
|
+
const MAX_FACTS_PER_MEMORY = 16;
|
|
25
|
+
/** Hard timeout for the LLM call. The index run must not hang on a misbehaving endpoint. */
|
|
26
|
+
const LLM_TIMEOUT_MS = 30_000;
|
|
27
|
+
const SYSTEM_PROMPT = "You split a developer memory into atomic, self-contained facts. " +
|
|
28
|
+
"Return only valid JSON. No prose, no markdown fences.";
|
|
29
|
+
const USER_PROMPT_PREFIX = `Split the memory below into a JSON array of short, self-contained atomic facts.
|
|
30
|
+
|
|
31
|
+
Rules:
|
|
32
|
+
- Output ONLY a JSON object: {"facts": ["fact one", "fact two", ...]}.
|
|
33
|
+
- Each fact is a single complete sentence, decontextualized so it stands alone.
|
|
34
|
+
- Drop pleasantries, meta-commentary, and timestamps.
|
|
35
|
+
- Preserve technical specifics (names, versions, identifiers) verbatim.
|
|
36
|
+
- If the memory is already a single atomic fact, return it as the only entry.
|
|
37
|
+
- Limit to at most ${MAX_FACTS_PER_MEMORY} facts.
|
|
38
|
+
|
|
39
|
+
Memory:
|
|
40
|
+
`;
|
|
41
|
+
/**
|
|
42
|
+
* Split a single memory body into atomic facts via the configured LLM.
|
|
43
|
+
*
|
|
44
|
+
* Returns `[]` on any failure (timeout, invalid JSON, empty response). Errors
|
|
45
|
+
* are logged via `warn()` but never thrown — a failed split for one memory
|
|
46
|
+
* must not abort the rest of the index pass.
|
|
47
|
+
*/
|
|
48
|
+
export async function splitMemoryIntoAtomicFacts(llmConfig, body) {
|
|
49
|
+
const trimmedBody = body.trim();
|
|
50
|
+
if (!trimmedBody)
|
|
51
|
+
return [];
|
|
52
|
+
const userPrompt = `${USER_PROMPT_PREFIX}${trimmedBody.slice(0, MAX_BODY_CHARS)}`;
|
|
53
|
+
let timeoutHandle;
|
|
54
|
+
try {
|
|
55
|
+
const raw = await Promise.race([
|
|
56
|
+
chatCompletion(llmConfig, [
|
|
57
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
58
|
+
{ role: "user", content: userPrompt },
|
|
59
|
+
], { maxTokens: 768, temperature: 0.1 }),
|
|
60
|
+
new Promise((_, reject) => {
|
|
61
|
+
timeoutHandle = setTimeout(() => reject(new Error("memory inference timed out")), LLM_TIMEOUT_MS);
|
|
62
|
+
}),
|
|
63
|
+
]);
|
|
64
|
+
if (!raw)
|
|
65
|
+
return [];
|
|
66
|
+
const parsed = parseJsonResponse(raw);
|
|
67
|
+
if (!parsed || !Array.isArray(parsed.facts)) {
|
|
68
|
+
warn("memory inference: invalid JSON response from LLM; skipping memory.");
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
const facts = parsed.facts
|
|
72
|
+
.filter((f) => typeof f === "string")
|
|
73
|
+
.map((f) => f.trim())
|
|
74
|
+
.filter((f) => f.length > 0)
|
|
75
|
+
.slice(0, MAX_FACTS_PER_MEMORY);
|
|
76
|
+
return facts;
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
warn(`memory inference failed: ${toErrorMessage(err)}`);
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
if (timeoutHandle !== undefined)
|
|
84
|
+
clearTimeout(timeoutHandle);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -391,6 +391,64 @@ const wikiMdRenderer = {
|
|
|
391
391
|
}
|
|
392
392
|
},
|
|
393
393
|
};
|
|
394
|
+
// ── 4c. lesson-md ────────────────────────────────────────────────────────────
|
|
395
|
+
/**
|
|
396
|
+
* Renderer for the `lesson` asset type (v1 spec §13).
|
|
397
|
+
*
|
|
398
|
+
* Lessons are markdown files with required `description` and `when_to_use`
|
|
399
|
+
* frontmatter. The renderer projects both fields explicitly so consumers can
|
|
400
|
+
* decide whether to apply a lesson without reading the full body. Lint
|
|
401
|
+
* (see `src/core/lesson-lint.ts`) is the contract enforcer; the renderer is
|
|
402
|
+
* intentionally tolerant — a lesson missing required fields will still render
|
|
403
|
+
* its body so the user has something to work with while they fix the file.
|
|
404
|
+
*/
|
|
405
|
+
const lessonMdRenderer = {
|
|
406
|
+
name: "lesson-md",
|
|
407
|
+
buildShowResponse(ctx) {
|
|
408
|
+
const name = deriveName(ctx);
|
|
409
|
+
const parsed = parseFrontmatter(ctx.content());
|
|
410
|
+
const description = toStringOrUndefined(parsed.data.description);
|
|
411
|
+
const whenToUse = toStringOrUndefined(parsed.data.when_to_use);
|
|
412
|
+
const action = whenToUse
|
|
413
|
+
? `Apply this lesson when: ${whenToUse}`
|
|
414
|
+
: "Apply this lesson when its `when_to_use` trigger matches the current task.";
|
|
415
|
+
return {
|
|
416
|
+
type: "lesson",
|
|
417
|
+
name,
|
|
418
|
+
path: ctx.absPath,
|
|
419
|
+
action,
|
|
420
|
+
description,
|
|
421
|
+
content: parsed.content,
|
|
422
|
+
};
|
|
423
|
+
},
|
|
424
|
+
extractMetadata(entry, ctx) {
|
|
425
|
+
try {
|
|
426
|
+
const parsed = parseFrontmatter(ctx.content());
|
|
427
|
+
const fm = parsed.data;
|
|
428
|
+
const desc = toStringOrUndefined(fm.description);
|
|
429
|
+
if (desc && !entry.description) {
|
|
430
|
+
entry.description = desc;
|
|
431
|
+
entry.source = "frontmatter";
|
|
432
|
+
entry.confidence = 0.9;
|
|
433
|
+
}
|
|
434
|
+
const whenToUse = toStringOrUndefined(fm.when_to_use);
|
|
435
|
+
if (whenToUse) {
|
|
436
|
+
const hints = new Set(entry.searchHints ?? []);
|
|
437
|
+
hints.add(`when_to_use:${whenToUse}`);
|
|
438
|
+
entry.searchHints = Array.from(hints).filter(Boolean);
|
|
439
|
+
}
|
|
440
|
+
if (Array.isArray(fm.tags) && fm.tags.length > 0) {
|
|
441
|
+
const fmTags = fm.tags.filter((t) => typeof t === "string" && t.trim().length > 0);
|
|
442
|
+
if (fmTags.length > 0) {
|
|
443
|
+
entry.tags = Array.from(new Set([...(entry.tags ?? []), ...fmTags]));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
// Non-fatal: skip metadata extraction on parse error
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
};
|
|
394
452
|
// ── 5. memory-md ─────────────────────────────────────────────────────────────
|
|
395
453
|
const memoryMdRenderer = {
|
|
396
454
|
name: "memory-md",
|
|
@@ -560,6 +618,7 @@ const builtinRenderers = [
|
|
|
560
618
|
agentMdRenderer,
|
|
561
619
|
knowledgeMdRenderer,
|
|
562
620
|
wikiMdRenderer,
|
|
621
|
+
lessonMdRenderer,
|
|
563
622
|
memoryMdRenderer,
|
|
564
623
|
workflowMdRenderer,
|
|
565
624
|
scriptSourceRenderer,
|
|
@@ -575,4 +634,4 @@ export function registerBuiltinRenderers() {
|
|
|
575
634
|
}
|
|
576
635
|
}
|
|
577
636
|
// ── Named exports for testing ────────────────────────────────────────────────
|
|
578
|
-
export { agentMdRenderer, commandMdRenderer, INTERPRETER_MAP, knowledgeMdRenderer, memoryMdRenderer, SETUP_SIGNALS, scriptSourceRenderer, skillMdRenderer, vaultEnvRenderer, wikiMdRenderer, workflowMdRenderer, };
|
|
637
|
+
export { agentMdRenderer, commandMdRenderer, INTERPRETER_MAP, knowledgeMdRenderer, lessonMdRenderer, memoryMdRenderer, SETUP_SIGNALS, scriptSourceRenderer, skillMdRenderer, vaultEnvRenderer, wikiMdRenderer, workflowMdRenderer, };
|