akm-cli 0.7.5 → 0.8.0-rc.6

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.
Files changed (236) hide show
  1. package/{.github/CHANGELOG.md → CHANGELOG.md} +113 -2
  2. package/README.md +20 -4
  3. package/SECURITY.md +93 -0
  4. package/dist/cli/config-migrate.js +144 -0
  5. package/dist/cli/config-validate.js +39 -0
  6. package/dist/cli/confirm.js +73 -0
  7. package/dist/cli/parse-args.js +133 -0
  8. package/dist/cli.js +1995 -551
  9. package/dist/commands/agent-dispatch.js +110 -0
  10. package/dist/commands/agent-support.js +68 -0
  11. package/dist/commands/completions.js +3 -0
  12. package/dist/commands/config-cli.js +130 -534
  13. package/dist/commands/consolidate.js +1531 -0
  14. package/dist/commands/curate.js +44 -3
  15. package/dist/commands/db-cli.js +23 -0
  16. package/dist/commands/distill-promotion-policy.js +660 -0
  17. package/dist/commands/distill.js +990 -75
  18. package/dist/commands/eval-cases.js +43 -0
  19. package/dist/commands/events.js +5 -23
  20. package/dist/commands/graph.js +477 -0
  21. package/dist/commands/health.js +400 -0
  22. package/dist/commands/help/help-accept.md +9 -0
  23. package/dist/commands/help/help-improve.md +77 -0
  24. package/dist/commands/help/help-proposals.md +15 -0
  25. package/dist/commands/help/help-propose.md +17 -0
  26. package/dist/commands/help/help-reject.md +8 -0
  27. package/dist/commands/history.js +54 -46
  28. package/dist/commands/improve-profiles.js +146 -0
  29. package/dist/commands/improve-result-file.js +103 -0
  30. package/dist/commands/improve.js +2175 -0
  31. package/dist/commands/info.js +5 -2
  32. package/dist/commands/init.js +50 -2
  33. package/dist/commands/installed-stashes.js +102 -139
  34. package/dist/commands/knowledge.js +136 -0
  35. package/dist/commands/lint/agent-linter.js +49 -0
  36. package/dist/commands/lint/base-linter.js +479 -0
  37. package/dist/commands/lint/command-linter.js +49 -0
  38. package/dist/commands/lint/default-linter.js +16 -0
  39. package/dist/commands/lint/index.js +183 -0
  40. package/dist/commands/lint/knowledge-linter.js +16 -0
  41. package/dist/commands/lint/markdown-insertion.js +343 -0
  42. package/dist/commands/lint/memory-linter.js +61 -0
  43. package/dist/commands/lint/registry.js +36 -0
  44. package/dist/commands/lint/skill-linter.js +45 -0
  45. package/dist/commands/lint/task-linter.js +50 -0
  46. package/dist/commands/lint/types.js +4 -0
  47. package/dist/commands/lint/vault-key-rules.js +139 -0
  48. package/dist/commands/lint/workflow-linter.js +56 -0
  49. package/dist/commands/lint.js +4 -0
  50. package/dist/commands/migration-help.js +5 -2
  51. package/dist/commands/proposal.js +66 -12
  52. package/dist/commands/propose.js +86 -31
  53. package/dist/commands/reflect.js +1119 -73
  54. package/dist/commands/registry-search.js +5 -2
  55. package/dist/commands/remember.js +69 -6
  56. package/dist/commands/schema-repair.js +203 -0
  57. package/dist/commands/search.js +115 -14
  58. package/dist/commands/self-update.js +3 -0
  59. package/dist/commands/show.js +144 -25
  60. package/dist/commands/source-add.js +17 -45
  61. package/dist/commands/source-clone.js +3 -0
  62. package/dist/commands/source-manage.js +14 -19
  63. package/dist/commands/tasks.js +438 -0
  64. package/dist/commands/url-checker.js +42 -0
  65. package/dist/commands/vault.js +130 -77
  66. package/dist/core/action-contributors.js +28 -0
  67. package/dist/core/asset-ref.js +7 -0
  68. package/dist/core/asset-registry.js +7 -16
  69. package/dist/core/asset-serialize.js +88 -0
  70. package/dist/core/asset-spec.js +22 -0
  71. package/dist/core/common.js +157 -0
  72. package/dist/core/concurrent.js +25 -0
  73. package/dist/core/config-io.js +347 -0
  74. package/dist/core/config-migration.js +625 -0
  75. package/dist/core/config-schema.js +501 -0
  76. package/dist/core/config-sources.js +108 -0
  77. package/dist/core/config-types.js +4 -0
  78. package/dist/core/config-walker.js +337 -0
  79. package/dist/core/config.js +327 -987
  80. package/dist/core/errors.js +40 -19
  81. package/dist/core/events.js +91 -138
  82. package/dist/core/file-lock.js +104 -0
  83. package/dist/core/frontmatter.js +3 -6
  84. package/dist/core/lesson-lint.js +3 -0
  85. package/dist/core/markdown.js +20 -0
  86. package/dist/core/memory-belief.js +62 -0
  87. package/dist/core/memory-contradiction-detect.js +274 -0
  88. package/dist/core/memory-improve.js +806 -0
  89. package/dist/core/parse.js +158 -0
  90. package/dist/core/paths.js +326 -14
  91. package/dist/core/proposal-quality-validators.js +364 -0
  92. package/dist/core/proposal-validators.js +69 -0
  93. package/dist/core/proposals.js +498 -42
  94. package/dist/core/state-db.js +927 -0
  95. package/dist/core/text-truncation.js +107 -0
  96. package/dist/core/time.js +54 -0
  97. package/dist/core/warn.js +62 -1
  98. package/dist/core/write-source.js +3 -0
  99. package/dist/indexer/db-backup.js +391 -0
  100. package/dist/indexer/db-search.js +152 -253
  101. package/dist/indexer/db.js +933 -103
  102. package/dist/indexer/ensure-index.js +64 -0
  103. package/dist/indexer/file-context.js +3 -0
  104. package/dist/indexer/graph-boost.js +376 -101
  105. package/dist/indexer/graph-db.js +391 -0
  106. package/dist/indexer/graph-dedup.js +95 -0
  107. package/dist/indexer/graph-extraction.js +550 -124
  108. package/dist/indexer/index-context.js +4 -0
  109. package/dist/indexer/indexer.js +506 -291
  110. package/dist/indexer/llm-cache.js +47 -0
  111. package/dist/indexer/manifest.js +3 -0
  112. package/dist/indexer/matchers.js +148 -160
  113. package/dist/indexer/memory-inference.js +99 -74
  114. package/dist/indexer/metadata-contributors.js +29 -0
  115. package/dist/indexer/metadata.js +255 -196
  116. package/dist/indexer/path-resolver.js +92 -0
  117. package/dist/indexer/project-context.js +192 -0
  118. package/dist/indexer/ranking-contributors.js +331 -0
  119. package/dist/indexer/ranking.js +81 -0
  120. package/dist/indexer/search-fields.js +5 -9
  121. package/dist/indexer/search-hit-enrichers.js +111 -0
  122. package/dist/indexer/search-source.js +44 -10
  123. package/dist/indexer/semantic-status.js +5 -16
  124. package/dist/indexer/staleness-detect.js +447 -0
  125. package/dist/indexer/usage-events.js +12 -9
  126. package/dist/indexer/walker.js +28 -0
  127. package/dist/integrations/agent/builders.js +135 -0
  128. package/dist/integrations/agent/config.js +122 -230
  129. package/dist/integrations/agent/detect.js +3 -0
  130. package/dist/integrations/agent/index.js +7 -13
  131. package/dist/integrations/agent/model-aliases.js +55 -0
  132. package/dist/integrations/agent/profiles.js +70 -5
  133. package/dist/integrations/agent/prompts.js +150 -74
  134. package/dist/integrations/agent/runner.js +151 -0
  135. package/dist/integrations/agent/sdk-runner.js +126 -0
  136. package/dist/integrations/agent/spawn.js +118 -23
  137. package/dist/integrations/github.js +3 -0
  138. package/dist/integrations/lockfile.js +32 -69
  139. package/dist/integrations/session-logs/index.js +68 -0
  140. package/dist/integrations/session-logs/providers/claude-code.js +59 -0
  141. package/dist/integrations/session-logs/providers/opencode.js +55 -0
  142. package/dist/integrations/session-logs/types.js +4 -0
  143. package/dist/llm/call-ai.js +62 -0
  144. package/dist/llm/client.js +72 -124
  145. package/dist/llm/embedder.js +3 -19
  146. package/dist/llm/embedders/cache.js +3 -7
  147. package/dist/llm/embedders/local.js +3 -0
  148. package/dist/llm/embedders/remote.js +20 -8
  149. package/dist/llm/embedders/types.js +3 -7
  150. package/dist/llm/feature-gate.js +89 -48
  151. package/dist/llm/graph-extract.js +676 -70
  152. package/dist/llm/index-passes.js +9 -23
  153. package/dist/llm/memory-infer.js +52 -71
  154. package/dist/llm/metadata-enhance.js +42 -29
  155. package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
  156. package/dist/output/cli-hints-full.md +281 -0
  157. package/dist/output/cli-hints-short.md +65 -0
  158. package/dist/output/cli-hints.js +5 -318
  159. package/dist/output/context.js +3 -0
  160. package/dist/output/renderers.js +223 -256
  161. package/dist/output/shapes.js +150 -105
  162. package/dist/output/text.js +318 -30
  163. package/dist/registry/build-index.js +3 -0
  164. package/dist/registry/create-provider-registry.js +3 -0
  165. package/dist/registry/factory.js +3 -0
  166. package/dist/registry/origin-resolve.js +3 -0
  167. package/dist/registry/providers/index.js +3 -0
  168. package/dist/registry/providers/skills-sh.js +70 -49
  169. package/dist/registry/providers/static-index.js +53 -48
  170. package/dist/registry/providers/types.js +3 -24
  171. package/dist/registry/resolve.js +11 -16
  172. package/dist/registry/types.js +3 -0
  173. package/dist/scripts/migrate-storage.js +17307 -0
  174. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8900 -0
  175. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  176. package/dist/setup/detect.js +3 -0
  177. package/dist/setup/ripgrep-install.js +3 -0
  178. package/dist/setup/ripgrep-resolve.js +3 -0
  179. package/dist/setup/setup.js +775 -37
  180. package/dist/setup/steps.js +3 -15
  181. package/dist/sources/include.js +3 -0
  182. package/dist/sources/provider-factory.js +5 -12
  183. package/dist/sources/provider.js +3 -20
  184. package/dist/sources/providers/filesystem.js +19 -23
  185. package/dist/sources/providers/git.js +7 -5
  186. package/dist/sources/providers/index.js +3 -0
  187. package/dist/sources/providers/install-types.js +3 -13
  188. package/dist/sources/providers/npm.js +3 -4
  189. package/dist/sources/providers/provider-utils.js +3 -0
  190. package/dist/sources/providers/sync-from-ref.js +3 -11
  191. package/dist/sources/providers/tar-utils.js +3 -0
  192. package/dist/sources/providers/website.js +18 -22
  193. package/dist/sources/resolve.js +3 -0
  194. package/dist/sources/types.js +3 -0
  195. package/dist/sources/website-ingest.js +7 -0
  196. package/dist/tasks/backends/cron.js +203 -0
  197. package/dist/tasks/backends/exec-utils.js +28 -0
  198. package/dist/tasks/backends/index.js +24 -0
  199. package/dist/tasks/backends/launchd-template.xml +19 -0
  200. package/dist/tasks/backends/launchd.js +187 -0
  201. package/dist/tasks/backends/schtasks-template.xml +29 -0
  202. package/dist/tasks/backends/schtasks.js +215 -0
  203. package/dist/tasks/parser.js +211 -0
  204. package/dist/tasks/resolveAkmBin.js +87 -0
  205. package/dist/tasks/runner.js +458 -0
  206. package/dist/tasks/schedule.js +211 -0
  207. package/dist/tasks/schema.js +15 -0
  208. package/dist/tasks/validator.js +62 -0
  209. package/dist/version.js +3 -0
  210. package/dist/wiki/index-template.md +12 -0
  211. package/dist/wiki/ingest-workflow-template.md +54 -0
  212. package/dist/wiki/log-template.md +8 -0
  213. package/dist/wiki/schema-template.md +61 -0
  214. package/dist/wiki/wiki-templates.js +15 -0
  215. package/dist/wiki/wiki.js +13 -61
  216. package/dist/workflows/authoring.js +8 -25
  217. package/dist/workflows/cli.js +3 -0
  218. package/dist/workflows/db.js +140 -10
  219. package/dist/workflows/document-cache.js +3 -10
  220. package/dist/workflows/parser.js +3 -0
  221. package/dist/workflows/renderer.js +11 -3
  222. package/dist/workflows/runs.js +62 -91
  223. package/dist/workflows/schema.js +3 -0
  224. package/dist/workflows/scope-key.js +3 -0
  225. package/dist/workflows/validator.js +4 -8
  226. package/dist/workflows/workflow-template.md +24 -0
  227. package/docs/README.md +9 -2
  228. package/docs/data-and-telemetry.md +225 -0
  229. package/docs/migration/release-notes/0.7.0.md +1 -1
  230. package/docs/migration/release-notes/0.7.5.md +2 -2
  231. package/docs/migration/release-notes/0.8.0.md +48 -0
  232. package/docs/migration/v0.7-to-v0.8.md +1307 -0
  233. package/package.json +20 -8
  234. package/.github/LICENSE +0 -374
  235. package/dist/commands/install-audit.js +0 -381
  236. package/dist/templates/wiki-templates.js +0 -100
@@ -1,35 +1,21 @@
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
- */
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { getDefaultLlmConfig, getIndexPassConfig } from "../core/config";
17
5
  /**
18
6
  * Resolve the {@link LlmConnectionConfig} a single index pass should use, or
19
7
  * `undefined` when the pass should run without an LLM.
20
8
  *
21
9
  * Returns `undefined` if any of:
22
- * - No top-level `akm.llm` block is configured.
10
+ * - No default LLM profile is configured.
23
11
  * - 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
12
  */
28
13
  export function resolveIndexPassLLM(passName, config) {
29
- if (!config.llm)
14
+ const llm = getDefaultLlmConfig(config);
15
+ if (!llm)
30
16
  return undefined;
31
- const passConfig = config.index?.[passName];
17
+ const passConfig = getIndexPassConfig(config.index, passName);
32
18
  if (passConfig?.llm === false)
33
19
  return undefined;
34
- return config.llm;
20
+ return llm;
35
21
  }
@@ -1,3 +1,6 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
1
4
  /**
2
5
  * LLM helper for the `akm index` memory-inference pass (#201).
3
6
  *
@@ -18,32 +21,14 @@
18
21
  import { toErrorMessage } from "../core/common";
19
22
  import { warn } from "../core/warn";
20
23
  import { chatCompletion, parseEmbeddedJsonResponse } from "./client";
24
+ import { tryLlmFeature } from "./feature-gate";
21
25
  /** Hard cap on body chars sent to the model — pragmatic and matches `runLlmEnrich`. */
22
26
  const MAX_BODY_CHARS = 4000;
23
27
  const SYSTEM_PROMPT = "You compress a developer memory into one high-signal derived memory for later retrieval. " +
24
28
  "Return only valid JSON. No prose outside the JSON object. No markdown fences.";
25
- const USER_PROMPT_PREFIX = `Compress the memory below into one concise, information-dense derived memory.
26
-
27
- Rules:
28
- - Output ONLY a JSON object with exactly these keys: {"title": string, "description": string, "tags": string[], "searchHints": string[], "content": string}.
29
- - ` +
30
- '"title"' +
31
- ` is a short, descriptive title for the derived memory.
32
- - ` +
33
- '"description"' +
34
- ` is one sentence explaining why this derived memory matters.
35
- - ` +
36
- '"tags"' +
37
- ` contains 3-8 specific keywords.
38
- - ` +
39
- '"searchHints"' +
40
- ` contains 3-6 natural-language retrieval phrases.
41
- - ` +
42
- '"content"' +
43
- ` must be compact markdown that preserves the reusable insight, root cause, fix, constraints, and applicability conditions when present.
44
- - Prefer 2-4 short sections with informative headings over long prose.
45
- - Omit timestamps, verification-only metrics, pleasantries, and session-specific chatter unless they are essential to applying the insight later.
46
- - Preserve technical specifics (names, versions, identifiers, selectors, file paths, config keys) verbatim.
29
+ const USER_PROMPT_PREFIX = `Compress the memory below into one derived memory. Output ONLY JSON:
30
+ {"title":"short title string","description":"one sentence summary string","tags":["tag1","tag2"],"searchHints":["search phrase 1","search phrase 2"],"content":"2-3 sentence compressed body preserving key facts verbatim"}
31
+ Rules: be specific, no vague generalizations, preserve key facts (names/versions/paths/config keys verbatim), merge related points, 3-8 tags, 3-6 searchHints. The content field must be a plain string with 2-3 sentences.
47
32
 
48
33
  Memory:
49
34
  `;
@@ -51,67 +36,63 @@ Memory:
51
36
  * Compress a single memory body into one derived memory via the configured LLM.
52
37
  *
53
38
  * Returns `undefined` on any failure (timeout, invalid JSON, empty response).
54
- * Errors
55
- * are logged via `warn()` but never thrown — a failed split for one memory
39
+ * Errors are logged via `warn()` but never thrown — a failed split for one memory
56
40
  * must not abort the rest of the index pass.
41
+ *
42
+ * Routes through `tryLlmFeature("memory_inference", ...)` so the feature gate
43
+ * and onFallback hook are honoured uniformly (Fix C5).
57
44
  */
58
- export async function compressMemoryToDerivedMemory(llmConfig, body, signal) {
45
+ export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akmConfig, onFallback) {
59
46
  const trimmedBody = body.trim();
60
47
  if (!trimmedBody)
61
48
  return undefined;
62
49
  const userPrompt = `${USER_PROMPT_PREFIX}${trimmedBody.slice(0, MAX_BODY_CHARS)}`;
63
- let timeoutHandle;
64
- try {
65
- const raw = await Promise.race([
66
- chatCompletion(llmConfig, [
50
+ return tryLlmFeature("memory_inference", akmConfig, async () => {
51
+ try {
52
+ const raw = await chatCompletion(llmConfig, [
67
53
  { role: "system", content: SYSTEM_PROMPT },
68
54
  { role: "user", content: userPrompt },
69
55
  ], {
70
- maxTokens: llmConfig.maxTokens ?? 4096,
71
56
  temperature: 0.1,
72
- timeoutMs: llmConfig.timeoutMs ?? 120_000,
57
+ timeoutMs: llmConfig.timeoutMs,
73
58
  signal,
74
- }),
75
- new Promise((_, reject) => {
76
- timeoutHandle = setTimeout(() => reject(new Error("memory inference timed out")), llmConfig.timeoutMs ?? 120_000);
77
- }),
78
- ]);
79
- if (!raw)
80
- return undefined;
81
- const parsed = parseEmbeddedJsonResponse(raw);
82
- if (!parsed) {
83
- warn("memory inference: invalid JSON response from LLM; skipping memory.");
84
- return undefined;
59
+ });
60
+ if (!raw)
61
+ return undefined;
62
+ const parsed = parseEmbeddedJsonResponse(raw);
63
+ if (!parsed) {
64
+ warn("memory inference: invalid JSON response from LLM; skipping memory.");
65
+ return undefined;
66
+ }
67
+ const title = typeof parsed.title === "string" ? parsed.title.trim() : "";
68
+ const description = typeof parsed.description === "string" ? parsed.description.trim() : "";
69
+ const content = typeof parsed.content === "string" ? parsed.content.trim() : "";
70
+ const tags = Array.isArray(parsed.tags)
71
+ ? parsed.tags
72
+ .filter((t) => typeof t === "string")
73
+ .map((t) => t.trim())
74
+ .filter(Boolean)
75
+ .slice(0, 8)
76
+ : [];
77
+ const searchHints = Array.isArray(parsed.searchHints)
78
+ ? parsed.searchHints
79
+ .filter((h) => typeof h === "string")
80
+ .map((h) => h.trim())
81
+ .filter(Boolean)
82
+ .slice(0, 6)
83
+ : [];
84
+ if (!title || !description || !content || tags.length === 0 || searchHints.length === 0) {
85
+ warn("memory inference: incomplete derived memory payload from LLM; skipping memory.");
86
+ return undefined;
87
+ }
88
+ return { title, description, tags, searchHints, content };
85
89
  }
86
- const title = typeof parsed.title === "string" ? parsed.title.trim() : "";
87
- const description = typeof parsed.description === "string" ? parsed.description.trim() : "";
88
- const content = typeof parsed.content === "string" ? parsed.content.trim() : "";
89
- const tags = Array.isArray(parsed.tags)
90
- ? parsed.tags
91
- .filter((t) => typeof t === "string")
92
- .map((t) => t.trim())
93
- .filter(Boolean)
94
- .slice(0, 8)
95
- : [];
96
- const searchHints = Array.isArray(parsed.searchHints)
97
- ? parsed.searchHints
98
- .filter((h) => typeof h === "string")
99
- .map((h) => h.trim())
100
- .filter(Boolean)
101
- .slice(0, 6)
102
- : [];
103
- if (!title || !description || !content || tags.length === 0 || searchHints.length === 0) {
104
- warn("memory inference: incomplete derived memory payload from LLM; skipping memory.");
90
+ catch (err) {
91
+ warn(`memory inference failed: ${toErrorMessage(err)}`);
105
92
  return undefined;
106
93
  }
107
- return { title, description, tags, searchHints, content };
108
- }
109
- catch (err) {
110
- warn(`memory inference failed: ${toErrorMessage(err)}`);
111
- return undefined;
112
- }
113
- finally {
114
- if (timeoutHandle !== undefined)
115
- clearTimeout(timeoutHandle);
116
- }
94
+ }, undefined, {
95
+ timeoutMs: llmConfig.timeoutMs,
96
+ onFallback,
97
+ });
117
98
  }
@@ -1,25 +1,28 @@
1
- /**
2
- * LLM-driven metadata enhancement for stash entries.
3
- *
4
- * Split out of `llm.ts` so the higher-level workflow (prompting the LLM to
5
- * improve descriptions/tags/searchHints) lives separately from the low-level
6
- * transport client in `client.ts`.
7
- */
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
4
  import { chatCompletion, parseJsonResponse } from "./client";
5
+ import { tryLlmFeature } from "./feature-gate";
9
6
  const SYSTEM_PROMPT = `You are a metadata generator for a developer asset registry. Given a script/skill/command/agent entry, generate improved metadata. Respond with ONLY valid JSON, no markdown fencing.`;
10
7
  /**
11
8
  * Use an LLM to enhance a stash entry's metadata: improve description,
12
9
  * generate searchHints, and suggest tags.
10
+ *
11
+ * When `akmConfig` is provided, routes through
12
+ * `tryLlmFeature("metadata_enhance", ...)` so the feature gate is honoured and
13
+ * errors are swallowed to `{}`. When `akmConfig` is `undefined` the gate is
14
+ * bypassed entirely — the LLM call runs unconditionally and errors propagate to
15
+ * the caller (pre-gate behaviour, used by direct callers such as tests).
13
16
  */
14
- export async function enhanceMetadata(config, entry, fileContent, signal) {
17
+ export async function enhanceMetadata(config, entry, fileContent, signal, akmConfig) {
15
18
  const contextParts = [`Name: ${entry.name}`, `Type: ${entry.type}`];
16
19
  if (entry.description)
17
20
  contextParts.push(`Current description: ${entry.description}`);
18
21
  if (entry.tags?.length)
19
22
  contextParts.push(`Current tags: ${entry.tags.join(", ")}`);
20
23
  if (fileContent) {
21
- // Limit content to first 2000 chars to stay within token limits
22
- const truncated = fileContent.length > 2000 ? `${fileContent.slice(0, 2000)}\n... (truncated)` : fileContent;
24
+ // Limit content to first 4000 chars to stay within token limits (matches other modules)
25
+ const truncated = fileContent.length > 4000 ? `${fileContent.slice(0, 4000)}\n... (truncated)` : fileContent;
23
26
  contextParts.push(`File content:\n${truncated}`);
24
27
  }
25
28
  const userPrompt = `${contextParts.join("\n")}
@@ -30,24 +33,34 @@ Generate improved metadata for this ${entry.type}. Return JSON with these fields
30
33
  - "tags": an array of 3-8 relevant keyword tags
31
34
 
32
35
  Return ONLY the JSON object, no explanation.`;
33
- const raw = await chatCompletion(config, [
34
- { role: "system", content: SYSTEM_PROMPT },
35
- { role: "user", content: userPrompt },
36
- ], { signal });
37
- const parsed = parseJsonResponse(raw);
38
- if (!parsed)
39
- return {};
40
- const result = {};
41
- if (typeof parsed.description === "string" && parsed.description) {
42
- result.description = parsed.description;
43
- }
44
- if (Array.isArray(parsed.searchHints)) {
45
- result.searchHints = parsed.searchHints
46
- .filter((s) => typeof s === "string" && s.trim().length > 0)
47
- .slice(0, 8);
48
- }
49
- if (Array.isArray(parsed.tags)) {
50
- result.tags = parsed.tags.filter((s) => typeof s === "string" && s.trim().length > 0).slice(0, 10);
36
+ const runLlm = async () => {
37
+ const raw = await chatCompletion(config, [
38
+ { role: "system", content: SYSTEM_PROMPT },
39
+ { role: "user", content: userPrompt },
40
+ ], { signal });
41
+ const parsed = parseJsonResponse(raw);
42
+ if (!parsed)
43
+ return {};
44
+ const result = {};
45
+ if (typeof parsed.description === "string" && parsed.description) {
46
+ result.description = parsed.description;
47
+ }
48
+ if (Array.isArray(parsed.searchHints)) {
49
+ result.searchHints = parsed.searchHints
50
+ .filter((s) => typeof s === "string" && s.trim().length > 0)
51
+ .slice(0, 8);
52
+ }
53
+ if (Array.isArray(parsed.tags)) {
54
+ result.tags = parsed.tags.filter((s) => typeof s === "string" && s.trim().length > 0).slice(0, 10);
55
+ }
56
+ return result;
57
+ };
58
+ // When no akmConfig is provided, bypass the feature gate entirely: run the
59
+ // LLM call directly and let errors propagate to the caller (pre-gate
60
+ // behaviour). When akmConfig is present, honour the feature flag and swallow
61
+ // errors to {} via tryLlmFeature.
62
+ if (akmConfig === undefined) {
63
+ return runLlm();
51
64
  }
52
- return result;
65
+ return tryLlmFeature("metadata_enhance", akmConfig, runLlm, {}, { timeoutMs: config.timeoutMs });
53
66
  }
@@ -0,0 +1,35 @@
1
+ Extract entities and relations from the asset body below.
2
+
3
+ Rules:
4
+ - Output ONLY a JSON object: {"entities": ["Entity One", ...], "relations": [{"from": "A", "to": "B", "type": "uses"}, ...]}.
5
+ - Entities are short, canonical noun phrases (project names, services, tools, people, file/dir names, technical concepts).
6
+ - Relations connect two entities that both appear in the entities array.
7
+ - "type" is a short verb phrase (e.g. "uses", "depends on", "owns", "documents"). Optional; omit when unsure.
8
+ - Drop pleasantries, meta-commentary, and timestamps.
9
+ - Limit to at most {{MAX_ENTITIES}} entities and {{MAX_RELATIONS}} relations per asset.
10
+ - Return {"entities": [], "relations": []} if the body has no extractable graph content.
11
+ - DO NOT return markdown code blocks, ONLY valid JSON objects.
12
+
13
+ Examples:
14
+
15
+ Input:
16
+ ## Deployment Notes
17
+ The auth-service uses PostgreSQL for user sessions. It depends on the redis-cache
18
+ for rate limiting. The terraform-provisioner deploys everything to the prod cluster.
19
+ Owner: @alice.
20
+
21
+ Output:
22
+ {"entities":["auth-service","PostgreSQL","redis-cache","terraform-provisioner","prod cluster","@alice"],"relations":[{"from":"auth-service","to":"PostgreSQL","type":"uses"},{"from":"auth-service","to":"redis-cache","type":"depends on"},{"from":"terraform-provisioner","to":"prod cluster","type":"deploys"},{"from":"terraform-provisioner","to":"auth-service","type":"deploys"},{"from":"@alice","to":"auth-service","type":"owns"}]}
23
+
24
+ Input:
25
+ ## Meeting: API Redesign
26
+ Discussed moving from REST to GraphQL. The frontend team will use Apollo Client.
27
+ Backend needs to implement resolvers. Timeline: Q2.
28
+
29
+ Output:
30
+ {"entities":["REST","GraphQL","Apollo Client","frontend team","backend","resolvers","Q2"],"relations":[{"from":"frontend team","to":"Apollo Client","type":"uses"},{"from":"backend","to":"resolvers","type":"implements"},{"from":"frontend team","to":"GraphQL","type":"migrates to"}]}
31
+
32
+ ===============
33
+
34
+ Request:
35
+
@@ -0,0 +1,281 @@
1
+ # akm CLI — Full Reference
2
+
3
+ You have access to a searchable library of scripts, skills, commands, agents, knowledge documents, workflows, wikis, and memories via `akm`. Search your sources first before writing something from scratch.
4
+
5
+ ## Search
6
+
7
+ ```sh
8
+ akm search "<query>" # Search all sources
9
+ akm curate "<task>" # Curate the best matches for a task
10
+ akm search "<query>" --type workflow # Filter by asset type
11
+ akm search "<query>" --source both # Also search registries
12
+ akm search "<query>" --source registry # Search registries only
13
+ akm search "<query>" --limit 10 # Limit results
14
+ akm search "<query>" --detail full # Include scores, paths, timing
15
+ ```
16
+
17
+ | Flag | Values | Default |
18
+ | --- | --- | --- |
19
+ | `--type` | `skill`, `command`, `agent`, `knowledge`, `workflow`, `script`, `memory`, `vault`, `wiki`, `any` | `any` |
20
+ | `--source` | `stash`, `registry`, `both` | `stash` |
21
+ | `--limit` | number | `20` |
22
+ | `--format` | `json`, `jsonl`, `text`, `yaml` | `json` |
23
+ | `--detail` | `brief`, `normal`, `full`, `summary`, `agent` | `brief` |
24
+ | `--for-agent` | boolean (deprecated — use `--detail agent`) | `false` |
25
+
26
+ ## Curate
27
+
28
+ Combine search + follow-up hints into a dense summary for a task or prompt.
29
+
30
+ ```sh
31
+ akm curate "plan a release" # Pick top matches across asset types
32
+ akm curate "deploy a Bun app" --limit 3 # Keep the summary shorter
33
+ akm curate "review architecture" --type workflow # Restrict to one asset type
34
+ ```
35
+
36
+ ## Show
37
+
38
+ Display an asset by ref. Knowledge assets support view modes as positional arguments.
39
+
40
+ ```sh
41
+ akm show script:deploy.sh # Show script (returns run command)
42
+ akm show skill:code-review # Show skill (returns full content)
43
+ akm show command:release # Show command (returns template)
44
+ akm show agent:architect # Show agent (returns system prompt)
45
+ akm show workflow:ship-release # Show parsed workflow steps
46
+ akm show knowledge:guide toc # Table of contents
47
+ akm show knowledge:guide section "Auth" # Specific section
48
+ akm show knowledge:guide lines 10 30 # Line range
49
+ akm show knowledge:my-doc # Show content (local or remote)
50
+ ```
51
+
52
+ | Type | Key fields returned |
53
+ | --- | --- |
54
+ | script | `run`, `setup`, `cwd` |
55
+ | skill | `content` (full SKILL.md) |
56
+ | command | `template`, `description`, `parameters` |
57
+ | agent | `prompt`, `description`, `modelHint`, `toolPolicy` |
58
+ | knowledge | `content` (with view modes: `full`, `toc`, `frontmatter`, `section`, `lines`) |
59
+ | workflow | `workflowTitle`, `workflowParameters`, `steps` |
60
+ | memory | `content` (recalled context) |
61
+ | vault | `keys`, `comments` |
62
+ | wiki | `content` (same view modes as knowledge). For any wiki task, run `akm wiki list`. To ingest sources, `akm wiki ingest <name>` dispatches the configured agent (defaults.agent or `--profile`) to execute the ingest workflow. |
63
+
64
+ ## Capture Knowledge While You Work
65
+
66
+ ```sh
67
+ akm remember "Deployment needs VPN access" # Record a memory in your stash
68
+ akm remember --name release-retro < notes.md # Save multiline memory from stdin
69
+ akm remember "note" --target my-other-stash # Route write to a named writable stash source
70
+ akm import ./docs/auth-flow.md # Import a file as knowledge
71
+ akm import - --name scratch-notes < notes.md # Import stdin as a knowledge doc
72
+ akm import https://example.com/docs/auth # Fetch one URL and import it as knowledge
73
+ akm import ./doc.md --target my-other-stash # Route import to a named writable stash source
74
+ akm workflow create ship-release # Create a workflow asset in the stash
75
+ akm workflow validate workflows/foo.md # Validate a workflow file or ref; lists every error
76
+ akm workflow next workflow:ship-release # Start or resume the next workflow step
77
+ akm feedback skill:code-review --positive # Record that an asset helped
78
+ akm feedback agent:reviewer --negative # Record that an asset missed the mark
79
+ akm feedback memory:deployment-notes --positive # Works for memories too
80
+ akm feedback vault:prod --positive # Records vault feedback without surfacing values
81
+ ```
82
+
83
+ Use `akm feedback` whenever an asset materially helps or fails so future search
84
+ ranking can learn from actual usage.
85
+
86
+ ## Wikis
87
+
88
+ Multi-wiki knowledge bases (Karpathy-style). A stash-owned wiki lives at
89
+ `<stashDir>/wikis/<name>/`; external directories or repos can also be registered
90
+ as first-class wikis. akm owns lifecycle + raw-slug + lint + index regeneration
91
+ for stash-owned wikis; page edits use your native Read/Write/Edit tools.
92
+
93
+ ```sh
94
+ akm wiki list # List wikis (name, pages, raws, last-modified)
95
+ akm wiki create research # Scaffold a new wiki
96
+ akm wiki register ics-docs ~/code/ics-documentation # Register an external wiki
97
+ akm wiki show research # Path, description, counts, last 3 log entries
98
+ akm wiki pages research # Page refs + descriptions (excludes schema/index/log; includes raw/)
99
+ akm wiki search research "attention" # Scoped search (equivalent to --type wiki --wiki research)
100
+ akm wiki stash research ./paper.md # Copy source into raw/<slug>.md (never overwrites)
101
+ akm wiki stash research https://example.com/paper # Fetch one URL into raw/<slug>.md
102
+ akm wiki stash research ./paper.md --target my-stash # Route write to a named writable stash source
103
+ echo "..." | akm wiki stash research - # stdin form
104
+ akm wiki lint research # Structural checks: orphans, broken xrefs, uncited raws, stale index
105
+ akm wiki ingest research # Dispatch defaults.agent to run the ingest workflow on this wiki
106
+ akm wiki ingest research --profile claude --model sonnet # Override profile and model
107
+ akm wiki ingest research --timeout-ms 600000 # Override agent CLI timeout (default: profile setting)
108
+ akm wiki remove research --force # Delete pages/schema/index/log; preserves raw/
109
+ akm wiki remove research --force --with-sources # Full nuke, including raw/
110
+ ```
111
+
112
+ **For any wiki task, start with `akm wiki list`. Then `akm wiki ingest <name>`
113
+ dispatches the configured agent (defaults.agent or `--profile`) to execute
114
+ the wiki's ingest workflow end-to-end — schema read, source dedup, search,
115
+ page create/update, log entry, lint, reindex.** Wiki pages are also addressable as
116
+ `wiki:<name>/<page-path>` and show up in stash-wide `akm search` as
117
+ `type: wiki`. Files under `raw/` and the wiki root infrastructure files
118
+ `schema.md`, `index.md`, and `log.md` are not indexed and do not appear in
119
+ search results. No `--llm` anywhere — akm never reasons about page content.
120
+
121
+ ## Vaults
122
+
123
+ Encrypted-at-rest key/value stores for secrets. Each vault is a `.env`-format
124
+ file at `<stashDir>/vaults/<name>.env`.
125
+
126
+ ```sh
127
+ akm vault create prod # Create a new vault
128
+ akm vault set prod DB_URL postgres://... # Set a key (or KEY=VALUE combined form)
129
+ akm vault set prod DB_URL=postgres://... # Combined KEY=VALUE form also works
130
+ akm vault unset prod DB_URL # Remove a key
131
+ akm vault list # List all vaults across all stashes with key names
132
+ akm vault path vault:prod # Print the vault file path for shell loading
133
+ akm vault run vault:prod -- env # Run one command with all vault vars injected
134
+ akm vault run vault:prod/DB_URL -- printenv DB_URL # Inject one key for one command
135
+ ```
136
+
137
+ ## Workflows
138
+
139
+ Step-based workflows stored as `<stashDir>/workflows/<name>.md`.
140
+
141
+ Ref-based workflow commands are scoped to the current project/worktree/directory,
142
+ so one active run does not block unrelated directories from starting the same
143
+ workflow. Direct run-id commands still target the exact run.
144
+
145
+ ```sh
146
+ akm workflow template # Print a starter workflow template
147
+ akm workflow create ship-release # Scaffold a new workflow asset
148
+ akm workflow start workflow:ship-release # Start a new run in the current scope
149
+ akm workflow next workflow:ship-release # Advance to the next step (or auto-start) in the current scope
150
+ akm workflow complete <run-id> # Mark a step complete and advance
151
+ akm workflow status <run-id> # Show the exact run by id
152
+ akm workflow resume <run-id> # Resume a blocked or failed run
153
+ akm workflow list # List workflow runs in the current scope
154
+ ```
155
+
156
+ ## Clone
157
+
158
+ Copy an asset to the working stash or a custom destination for editing.
159
+
160
+ ```sh
161
+ akm clone <ref> # Clone to working stash
162
+ akm clone <ref> --name new-name # Rename on clone
163
+ akm clone <ref> --dest ./project/.claude # Clone to custom location
164
+ akm clone <ref> --force # Overwrite existing
165
+ akm clone "npm:@scope/pkg//script:deploy.sh" # Clone from remote package
166
+ ```
167
+
168
+ When `--dest` is provided, `akm init` is not required first.
169
+
170
+ ## Save
171
+
172
+ Commit local changes in a git-backed stash. Behaviour adapts automatically:
173
+
174
+ - **Not a git repo** — no-op (silent skip)
175
+ - **Git repo, no remote** — stage and commit only (the default stash always falls here)
176
+ - **Git repo, has remote, not writable** — stage and commit only
177
+ - **Git repo, has remote, `writable: true`** — stage, commit, and push
178
+
179
+ ```sh
180
+ akm save # Save primary stash (timestamp message)
181
+ akm save -m "Add deploy skill" # Save with explicit message
182
+ akm save my-skills # Save a named writable git stash
183
+ akm save my-skills -m "Update patterns" # Save named stash with message
184
+ ```
185
+
186
+ The `--writable` flag on `akm add` opts a remote git stash into push-on-save:
187
+
188
+ ```sh
189
+ akm add git@github.com:org/skills.git --provider git --name my-skills --writable
190
+ ```
191
+
192
+ ## Add & Manage Sources
193
+
194
+ ```sh
195
+ akm add <ref> # Add a source
196
+ akm add @scope/stash # From npm (managed)
197
+ akm add owner/repo # From GitHub (managed)
198
+ akm add ./path/to/local/stash # Local directory
199
+ akm add git@github.com:org/repo.git --provider git --name my-skills --writable
200
+ akm enable skills.sh # Enable the skills.sh registry
201
+ akm disable skills.sh # Disable the skills.sh registry
202
+ akm list # List all sources
203
+ akm list --kind managed # List managed sources only
204
+ akm remove <target> # Remove by id, ref, path, or name
205
+ akm update --all # Update all managed sources
206
+ akm update <target> --force # Force re-download
207
+ ```
208
+
209
+ ## Registries
210
+
211
+ ```sh
212
+ akm registry list # List configured registries
213
+ akm registry add <url> # Add a registry
214
+ akm registry add <url> --name my-team # Add with label
215
+ akm registry add <url> --provider skills-sh # Specify provider type
216
+ akm registry remove <url-or-name> # Remove a registry
217
+ akm registry search "<query>" # Search all registries
218
+ akm registry search "<query>" --assets # Include asset-level results
219
+ akm registry build-index # Build the default cache-backed index.json
220
+ akm registry build-index --out dist/index.json # Build to a custom path
221
+ ```
222
+
223
+ ## Configuration
224
+
225
+ ```sh
226
+ akm config list # Show current config
227
+ akm config get <key> # Read a value
228
+ akm config set <key> <value> # Set a value
229
+ akm config unset <key> # Remove a key
230
+ akm config path --all # Show all config paths
231
+ ```
232
+
233
+ ## Other Commands
234
+
235
+ ```sh
236
+ akm init # Initialize working stash
237
+ akm index # Rebuild search index (metadata enrichment when configured)
238
+ akm index --full # Full reindex (metadata enrichment when configured)
239
+ akm list # List all sources
240
+ akm upgrade # Upgrade akm using its install method
241
+ akm upgrade --check # Check for updates
242
+ akm help migrate 0.6.0 # Print migration notes for a release (or: latest)
243
+ akm hints # Print this reference
244
+ akm completions # Print bash completion script
245
+ akm completions --install # Install completions
246
+ ```
247
+
248
+ ## Proposals & Improvement (0.8.0+)
249
+
250
+ ```sh
251
+ akm improve <ref> # Propose improvement for an asset
252
+ akm proposals # List pending proposals
253
+ akm show proposal <id> # Render the proposal body
254
+ akm diff <ref-or-id> # Diff by ref, UUID, or 8-char prefix (proposal positional optional)
255
+ akm diff skill:akm-dream # Diff by asset ref
256
+ akm accept 7c115132 # Accept by UUID prefix
257
+ akm accept <id> --target team-stash # Accept to a named writable stash source
258
+ akm reject skill:my-skill --reason "not ready" # Reject by asset ref
259
+ akm reject <id> --reason "..." # Archive with a reason
260
+ ```
261
+
262
+ Per-task `timeoutMs`: task markdown frontmatter may set `timeoutMs: null` to
263
+ disable the agent kill timer for long-running local-model tasks, or a number
264
+ (milliseconds) to override `config.agent.timeoutMs` for that task only.
265
+
266
+ ## Output Control
267
+
268
+ All commands accept `--format` and `--detail` flags:
269
+
270
+ - `--format json` (default) — structured JSON
271
+ - `--format jsonl` — one JSON object per line (streaming-friendly)
272
+ - `--format text` — human-readable plain text
273
+ - `--format yaml` — YAML output
274
+ - `--detail brief` (default) — compact output
275
+ - `--detail normal` — adds tags, refs, origins
276
+ - `--detail full` — includes scores, paths, timing, debug info
277
+ - `--detail summary` — metadata only (no content/template/prompt), under 200 tokens
278
+ - `--detail agent` — agent-optimized output: strips non-actionable fields
279
+ - `--for-agent` — deprecated alias for `--detail agent`
280
+
281
+ Run `akm -h` or `akm <command> -h` for per-command help.