akm-cli 0.9.0-beta.5 → 0.9.0-beta.51

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 (221) hide show
  1. package/CHANGELOG.md +711 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/profiles/default.json +9 -4
  6. package/dist/assets/profiles/frequent.json +1 -1
  7. package/dist/assets/profiles/memory-focus.json +1 -1
  8. package/dist/assets/profiles/quick.json +1 -1
  9. package/dist/assets/profiles/synthesize.json +15 -0
  10. package/dist/assets/profiles/thorough.json +1 -1
  11. package/dist/assets/prompts/consolidate-system.md +23 -0
  12. package/dist/assets/prompts/contradiction-judge.md +33 -0
  13. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  14. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  15. package/dist/assets/prompts/extract-session.md +6 -2
  16. package/dist/assets/prompts/graph-extract-system.md +1 -0
  17. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  18. package/dist/assets/prompts/memory-infer-system.md +1 -0
  19. package/dist/assets/prompts/memory-infer-user.md +5 -0
  20. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  21. package/dist/assets/prompts/procedural-system.md +44 -0
  22. package/dist/assets/prompts/recombine-system.md +40 -0
  23. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  24. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  25. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  26. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  34. package/dist/assets/templates/html/health.html +281 -111
  35. package/dist/assets/wiki/ingest-workflow-template.md +38 -10
  36. package/dist/cli/parse-args.js +46 -1
  37. package/dist/cli/shared.js +28 -0
  38. package/dist/cli.js +27 -11
  39. package/dist/commands/agent/agent-dispatch.js +2 -2
  40. package/dist/commands/agent/agent-support.js +0 -7
  41. package/dist/commands/agent/contribute-cli.js +17 -4
  42. package/dist/commands/config-cli.js +18 -2
  43. package/dist/commands/env/child-env.js +47 -0
  44. package/dist/commands/env/env-cli.js +33 -26
  45. package/dist/commands/env/secret-cli.js +36 -22
  46. package/dist/commands/feedback-cli.js +15 -6
  47. package/dist/commands/graph/graph-cli.js +5 -13
  48. package/dist/commands/graph/graph.js +76 -72
  49. package/dist/commands/health/checks.js +49 -1
  50. package/dist/commands/health/html-report.js +422 -80
  51. package/dist/commands/health.js +386 -9
  52. package/dist/commands/improve/calibration.js +161 -0
  53. package/dist/commands/improve/consolidate/chunking.js +141 -0
  54. package/dist/commands/improve/consolidate/eligibility.js +81 -0
  55. package/dist/commands/improve/consolidate/merge.js +145 -0
  56. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  57. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  58. package/dist/commands/improve/consolidate.js +635 -660
  59. package/dist/commands/improve/dedup.js +482 -0
  60. package/dist/commands/improve/distill.js +159 -69
  61. package/dist/commands/improve/eligibility.js +434 -0
  62. package/dist/commands/improve/encoding-salience.js +205 -0
  63. package/dist/commands/improve/extract-cli.js +124 -2
  64. package/dist/commands/improve/extract-prompt.js +39 -2
  65. package/dist/commands/improve/extract-watch.js +140 -0
  66. package/dist/commands/improve/extract.js +389 -40
  67. package/dist/commands/improve/feedback-valence.js +54 -0
  68. package/dist/commands/improve/homeostatic.js +467 -0
  69. package/dist/commands/improve/improve-auto-accept.js +138 -7
  70. package/dist/commands/improve/improve-cli.js +36 -61
  71. package/dist/commands/improve/improve-profiles.js +14 -0
  72. package/dist/commands/improve/improve-result-file.js +14 -25
  73. package/dist/commands/improve/improve-session.js +58 -0
  74. package/dist/commands/improve/improve.js +485 -2498
  75. package/dist/commands/improve/locks.js +154 -0
  76. package/dist/commands/improve/loop-stages.js +1083 -0
  77. package/dist/commands/improve/memory/memory-contradiction-detect.js +23 -28
  78. package/dist/commands/improve/outcome-loop.js +256 -0
  79. package/dist/commands/improve/preparation.js +1966 -0
  80. package/dist/commands/improve/proactive-maintenance.js +115 -0
  81. package/dist/commands/improve/procedural.js +418 -0
  82. package/dist/commands/improve/recombine.js +850 -0
  83. package/dist/commands/improve/reflect-noise.js +0 -0
  84. package/dist/commands/improve/reflect.js +183 -40
  85. package/dist/commands/improve/salience.js +438 -0
  86. package/dist/commands/improve/triage.js +93 -0
  87. package/dist/commands/lint/agent-linter.js +19 -24
  88. package/dist/commands/lint/base-linter.js +173 -60
  89. package/dist/commands/lint/command-linter.js +19 -24
  90. package/dist/commands/lint/env-key-rules.js +38 -1
  91. package/dist/commands/lint/fact-linter.js +39 -0
  92. package/dist/commands/lint/index.js +31 -13
  93. package/dist/commands/lint/memory-linter.js +1 -1
  94. package/dist/commands/lint/registry.js +7 -2
  95. package/dist/commands/lint/task-linter.js +3 -3
  96. package/dist/commands/lint/workflow-linter.js +26 -1
  97. package/dist/commands/proposal/drain-policies.js +5 -0
  98. package/dist/commands/proposal/drain.js +43 -50
  99. package/dist/commands/proposal/proposal-cli.js +21 -31
  100. package/dist/commands/proposal/proposal.js +5 -0
  101. package/dist/commands/proposal/propose.js +7 -2
  102. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  103. package/dist/commands/proposal/validators/proposals.js +189 -63
  104. package/dist/commands/read/curate.js +414 -94
  105. package/dist/commands/read/knowledge.js +6 -3
  106. package/dist/commands/read/search-cli.js +9 -4
  107. package/dist/commands/read/search.js +10 -6
  108. package/dist/commands/read/show.js +86 -7
  109. package/dist/commands/sources/init.js +49 -17
  110. package/dist/commands/sources/installed-stashes.js +11 -3
  111. package/dist/commands/sources/schema-repair.js +43 -45
  112. package/dist/commands/sources/self-update.js +2 -2
  113. package/dist/commands/sources/source-add.js +7 -3
  114. package/dist/commands/sources/stash-cli.js +28 -40
  115. package/dist/commands/sources/stash-skeleton.js +23 -8
  116. package/dist/commands/tasks/tasks-cli.js +19 -27
  117. package/dist/commands/tasks/tasks.js +39 -11
  118. package/dist/commands/wiki-cli.js +21 -35
  119. package/dist/core/asset/asset-registry.js +3 -1
  120. package/dist/core/asset/asset-spec.js +18 -2
  121. package/dist/core/asset/frontmatter.js +166 -167
  122. package/dist/core/asset/markdown.js +8 -0
  123. package/dist/core/authoring-rules.js +92 -0
  124. package/dist/core/common.js +0 -5
  125. package/dist/core/config/config-migration.js +12 -11
  126. package/dist/core/config/config-schema.js +340 -56
  127. package/dist/core/config/config-types.js +3 -3
  128. package/dist/core/config/config.js +28 -7
  129. package/dist/core/events.js +3 -7
  130. package/dist/core/improve-types.js +11 -8
  131. package/dist/core/logs-db.js +10 -66
  132. package/dist/core/parse.js +36 -16
  133. package/dist/core/paths.js +3 -0
  134. package/dist/core/standards/resolve-standards-context.js +87 -0
  135. package/dist/core/standards/resolve-stash-standards.js +99 -0
  136. package/dist/core/standards/resolve-type-conventions.js +66 -0
  137. package/dist/core/state/migrations.js +714 -0
  138. package/dist/core/state-db.js +525 -474
  139. package/dist/indexer/db/db.js +439 -247
  140. package/dist/indexer/db/graph-db.js +129 -86
  141. package/dist/indexer/ensure-index.js +152 -17
  142. package/dist/indexer/graph/graph-boost.js +51 -41
  143. package/dist/indexer/graph/graph-extraction.js +218 -4
  144. package/dist/indexer/index-writer-lock.js +99 -0
  145. package/dist/indexer/indexer.js +123 -221
  146. package/dist/indexer/passes/dir-staleness.js +114 -0
  147. package/dist/indexer/passes/memory-inference.js +13 -5
  148. package/dist/indexer/passes/staleness-detect.js +2 -5
  149. package/dist/indexer/search/db-search.js +19 -6
  150. package/dist/indexer/search/ranking-contributors.js +22 -0
  151. package/dist/indexer/search/ranking.js +4 -0
  152. package/dist/indexer/search/search-source.js +17 -18
  153. package/dist/indexer/search/semantic-status.js +4 -0
  154. package/dist/indexer/walk/matchers.js +9 -0
  155. package/dist/integrations/agent/config.js +6 -53
  156. package/dist/integrations/agent/index.js +2 -18
  157. package/dist/integrations/agent/prompts.js +75 -9
  158. package/dist/integrations/agent/runner-dispatch.js +59 -0
  159. package/dist/integrations/harnesses/claude/session-log.js +11 -1
  160. package/dist/integrations/harnesses/index.js +2 -3
  161. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  162. package/dist/integrations/harnesses/opencode-sdk/index.js +2 -2
  163. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +0 -2
  164. package/dist/integrations/session-logs/index.js +16 -0
  165. package/dist/llm/client.js +45 -15
  166. package/dist/llm/embedder.js +42 -3
  167. package/dist/llm/embedders/deterministic.js +66 -0
  168. package/dist/llm/embedders/local.js +66 -2
  169. package/dist/llm/feature-gate.js +8 -4
  170. package/dist/llm/graph-extract.js +67 -44
  171. package/dist/llm/memory-infer-impl.js +138 -0
  172. package/dist/llm/memory-infer.js +1 -127
  173. package/dist/llm/metadata-enhance.js +44 -31
  174. package/dist/llm/structured-call.js +49 -0
  175. package/dist/migrate-storage-node.mjs +8 -0
  176. package/dist/output/context.js +5 -5
  177. package/dist/output/renderers.js +74 -2
  178. package/dist/output/shapes/curate.js +14 -2
  179. package/dist/output/shapes/passthrough.js +0 -1
  180. package/dist/output/text/helpers.js +16 -1
  181. package/dist/registry/providers/skills-sh.js +21 -147
  182. package/dist/registry/providers/static-index.js +15 -157
  183. package/dist/registry/resolve.js +22 -9
  184. package/dist/runtime.js +25 -1
  185. package/dist/scripts/migrate-storage.js +2617 -1961
  186. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +759 -510
  187. package/dist/setup/setup.js +29 -8
  188. package/dist/sources/include.js +6 -2
  189. package/dist/sources/providers/filesystem.js +0 -1
  190. package/dist/sources/providers/git-install.js +210 -0
  191. package/dist/sources/providers/git-provider.js +234 -0
  192. package/dist/sources/providers/git-stash.js +248 -0
  193. package/dist/sources/providers/git.js +10 -661
  194. package/dist/sources/providers/npm.js +2 -6
  195. package/dist/sources/providers/provider-utils.js +13 -7
  196. package/dist/sources/providers/sync-from-ref.js +9 -1
  197. package/dist/sources/providers/tar-utils.js +16 -8
  198. package/dist/sources/providers/website.js +9 -5
  199. package/dist/sources/website-ingest.js +187 -29
  200. package/dist/sources/wiki-fetchers/registry.js +53 -0
  201. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  202. package/dist/storage/database.js +45 -10
  203. package/dist/storage/managed-db.js +82 -0
  204. package/dist/storage/repositories/registry-cache.js +92 -0
  205. package/dist/storage/sqlite-pragmas.js +146 -0
  206. package/dist/tasks/backends/cron.js +1 -1
  207. package/dist/tasks/backends/launchd.js +1 -1
  208. package/dist/tasks/backends/schtasks.js +1 -1
  209. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  210. package/dist/tasks/runner.js +5 -13
  211. package/dist/text-import-hook.mjs +0 -0
  212. package/dist/wiki/wiki.js +37 -0
  213. package/dist/workflows/db.js +3 -4
  214. package/dist/workflows/runtime/runs.js +1 -117
  215. package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
  216. package/dist/workflows/validate-summary.js +2 -7
  217. package/docs/data-and-telemetry.md +3 -2
  218. package/docs/migration/release-notes/0.9.0.md +39 -0
  219. package/package.json +13 -11
  220. package/dist/commands/db-cli.js +0 -23
  221. package/dist/indexer/db/db-backup.js +0 -376
@@ -9,6 +9,9 @@ const TYPE_BOOST = {
9
9
  agent: 0.3,
10
10
  script: 0.2,
11
11
  knowledge: 0.22,
12
+ // Facts are authoritative, durable declarations about the stash — rank them
13
+ // alongside knowledge so they surface reliably when relevant.
14
+ fact: 0.22,
12
15
  memory: -0.02,
13
16
  };
14
17
  const MAX_BOOST_SUM = 3.0;
@@ -206,6 +209,24 @@ const lessonStrengthContributor = {
206
209
  return Math.min(0.3, 0.06 * strength);
207
210
  },
208
211
  };
212
+ /**
213
+ * Pinned-fact boost.
214
+ *
215
+ * Facts marked `pinned: true` form the small always-injected "core context"
216
+ * (see docs/design/fact-asset-type.md). The fact metadata contributor records
217
+ * a `pinned` search hint; here we give those facts a modest additive boost so
218
+ * the core outranks ordinary facts on otherwise-equal queries. Capped small so
219
+ * it cannot overpower an exact-name match.
220
+ */
221
+ const pinnedFactRankingContributor = {
222
+ name: "pinned-fact-ranking",
223
+ appliesTo(item) {
224
+ return item.entry.type === "fact" && (item.entry.searchHints?.includes("pinned") ?? false);
225
+ },
226
+ adjust() {
227
+ return 0.15;
228
+ },
229
+ };
209
230
  /**
210
231
  * Blend ratio for scoped vs. global utility signals.
211
232
  *
@@ -310,6 +331,7 @@ export const defaultRankingContributors = [
310
331
  graphRankingContributor,
311
332
  captureModeRankingContributor,
312
333
  lessonStrengthContributor,
334
+ pinnedFactRankingContributor,
313
335
  projectContextRankingContributor,
314
336
  ];
315
337
  export const defaultUtilityRankingContributors = [utilityRankingContributor];
@@ -20,6 +20,7 @@ export function normalizeFtsScores(results) {
20
20
  export function combineSearchScores(options) {
21
21
  const FTS_WEIGHT = 0.7;
22
22
  const VEC_WEIGHT = 0.3;
23
+ const excludeTypeSet = options.excludeTypes && options.excludeTypes.length > 0 ? new Set(options.excludeTypes) : null;
23
24
  const scored = [];
24
25
  const seenIds = new Set();
25
26
  for (const [id, { score: ftsScore, result }] of options.ftsScoreMap) {
@@ -42,6 +43,9 @@ export function combineSearchScores(options) {
42
43
  continue;
43
44
  if (options.typeFilter && found.entry.type !== options.typeFilter)
44
45
  continue;
46
+ // #627 — drop vector-only neighbors whose type is excluded on the default path.
47
+ if (excludeTypeSet?.has(found.entry.type))
48
+ continue;
45
49
  scored.push({
46
50
  id,
47
51
  entry: found.entry,
@@ -10,8 +10,6 @@ import { resolveSourceProviderFactory } from "../../sources/provider-factory.js"
10
10
  // before resolveEntryContentDir() runs.
11
11
  import "../../sources/providers/index.js";
12
12
  import { warn } from "../../core/warn.js";
13
- import { ensureGitMirror, getCachePaths, parseGitRepoUrl } from "../../sources/providers/git.js";
14
- import { ensureWebsiteMirror } from "../../sources/website-ingest.js";
15
13
  // Legacy "context-hub" / "github" type aliases are normalized to "git" at
16
14
  // config-load time (see src/config.ts), so this set only contains the canonical
17
15
  // type.
@@ -261,31 +259,32 @@ function isValidDirectory(dir) {
261
259
  export async function ensureSourceCaches(config, options) {
262
260
  const cfg = config ?? loadConfig();
263
261
  const force = options?.force === true;
264
- const entries = getSources(cfg);
265
- for (const entry of entries) {
266
- if (!GIT_STASH_TYPES.has(entry.type) || !entry.url || entry.enabled === false)
262
+ // Polymorphic refresh: walk every enabled source through its registered
263
+ // provider and call `sync()`. Every cache-backed kind (git, website, npm)
264
+ // refreshes the same way a bad source warns and is skipped without
265
+ // aborting the others. The git content/-subdir layout convention stays in
266
+ // resolveEntryContentDir.
267
+ for (const entry of getSources(cfg)) {
268
+ if (entry.enabled === false)
269
+ continue;
270
+ const factory = resolveSourceProviderFactory(entry.type);
271
+ if (!factory)
267
272
  continue;
273
+ let provider;
268
274
  try {
269
- const repo = parseGitRepoUrl(entry.url);
270
- const cachePaths = getCachePaths(repo.canonicalUrl);
271
- await ensureGitMirror(repo, cachePaths, {
272
- requireRepoDir: true,
273
- writable: entry.writable === true,
274
- force,
275
- });
275
+ provider = factory(entry);
276
276
  }
277
277
  catch (err) {
278
- warn(`Warning: failed to refresh git mirror for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
278
+ warn(`Warning: failed to construct ${entry.type} source provider for "${entry.name ?? entry.url ?? entry.path}": ${err instanceof Error ? err.message : String(err)}`);
279
+ continue;
279
280
  }
280
- }
281
- for (const entry of entries) {
282
- if (entry.type !== "website" || !entry.url || entry.enabled === false)
281
+ if (!provider.sync)
283
282
  continue;
284
283
  try {
285
- await ensureWebsiteMirror(entry, { requireStashDir: true, force });
284
+ await provider.sync({ force });
286
285
  }
287
286
  catch (err) {
288
- warn(`Warning: failed to refresh website stash for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
287
+ warn(`Warning: failed to refresh ${provider.kind} source "${provider.name}": ${err instanceof Error ? err.message : String(err)}`);
289
288
  }
290
289
  }
291
290
  }
@@ -4,8 +4,12 @@
4
4
  import fs from "node:fs";
5
5
  import { writeFileAtomic } from "../../core/common.js";
6
6
  import { getCacheDir, getSemanticStatusPath } from "../../core/paths.js";
7
+ import { DETERMINISTIC_EMBED_MODEL_ID, isDeterministicEmbedEnabled } from "../../llm/embedders/deterministic.js";
7
8
  import { DEFAULT_LOCAL_MODEL } from "../../llm/embedders/local.js";
8
9
  export function deriveSemanticProviderFingerprint(embedding) {
10
+ if (isDeterministicEmbedEnabled()) {
11
+ return `deterministic:${DETERMINISTIC_EMBED_MODEL_ID}`;
12
+ }
9
13
  if (embedding?.endpoint) {
10
14
  return `remote:${embedding.endpoint}|${embedding.model}|${embedding.dimension ?? "default"}`;
11
15
  }
@@ -78,6 +78,15 @@ const DIR_TYPE_MAP = [
78
78
  type: "session",
79
79
  test: (ext) => ext === ".md",
80
80
  },
81
+ {
82
+ // Durable stash-level facts live under `facts/<category>/<name>.md`.
83
+ // classifyByDirectory walks every ancestor dir, so nested category
84
+ // subdirs still match. Without this entry a fact file would fall through
85
+ // to classifyBySmartMd and be mistyped as `knowledge`.
86
+ dir: "facts",
87
+ type: "fact",
88
+ test: (ext) => ext === ".md",
89
+ },
81
90
  ];
82
91
  const COMMAND_PLACEHOLDER_RE = /\$ARGUMENTS|\$[123]\b/;
83
92
  // Files that should never be treated as the typed asset for the surrounding
@@ -1,9 +1,7 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { VALID_HARNESS_IDS } from "../../core/config/config.js";
5
4
  import { ConfigError } from "../../core/errors.js";
6
- import { warn } from "../../core/warn.js";
7
5
  import { BUILTIN_AGENT_PROFILE_NAMES, getBuiltinAgentProfile, listBuiltinAgentProfiles, } from "./profiles.js";
8
6
  /**
9
7
  * Default hard timeout for an agent CLI (60s — matches the value used in
@@ -41,7 +39,12 @@ export function resolveAgentProfile(name, overrides) {
41
39
  stdio: base.stdio,
42
40
  env: base.env,
43
41
  envPassthrough: base.envPassthrough,
44
- timeoutMs: base.timeoutMs,
42
+ // Honor a user-configured `profiles.agent.<name>.timeoutMs` override; fall
43
+ // back to the built-in profile's value. (Previously always used base, so
44
+ // the documented config override was silently ignored — callers had to pass
45
+ // a CLI flag like `--timeout-ms`.) runAgent (spawn.ts) reads profile.timeoutMs
46
+ // when no per-call timeout is supplied, so this makes the config knob work.
47
+ timeoutMs: overrides.timeoutMs ?? base.timeoutMs,
45
48
  parseOutput: base.parseOutput,
46
49
  ...(sdkMode ? { sdkMode: true } : {}),
47
50
  model: overrides.model ?? base.model,
@@ -134,53 +137,3 @@ export function listResolvedAgentProfiles(config) {
134
137
  }
135
138
  return resolved;
136
139
  }
137
- /**
138
- * Parse the v2 `profiles.agent` map (AgentProfileConfigV2 shape with required
139
- * `platform` field). Returns a map of profile name → AgentProfileConfigV2.
140
- */
141
- export function parseAgentProfilesMapV2(value) {
142
- if (typeof value !== "object" || value === null || Array.isArray(value))
143
- return undefined;
144
- const out = {};
145
- // Derives from the canonical harness-id source of truth (#565).
146
- const VALID_PLATFORMS = VALID_HARNESS_IDS;
147
- for (const [name, raw] of Object.entries(value)) {
148
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
149
- warn(`[akm] Ignoring profiles.agent["${name}"]: expected an object.`);
150
- continue;
151
- }
152
- const obj = raw;
153
- if (!VALID_PLATFORMS.includes(obj.platform)) {
154
- warn(`[akm] Ignoring profiles.agent["${name}"]: missing or invalid "platform" (must be one of: ${VALID_PLATFORMS.join(", ")}).`);
155
- continue;
156
- }
157
- const profile = {
158
- platform: obj.platform,
159
- };
160
- if (typeof obj.bin === "string" && obj.bin.trim())
161
- profile.bin = obj.bin.trim();
162
- if (Array.isArray(obj.args) && obj.args.every((a) => typeof a === "string")) {
163
- profile.args = obj.args;
164
- }
165
- if (typeof obj.workspace === "string" && obj.workspace.trim())
166
- profile.workspace = obj.workspace.trim();
167
- if (typeof obj.model === "string" && obj.model.trim())
168
- profile.model = obj.model.trim();
169
- out[name] = profile;
170
- }
171
- return Object.keys(out).length > 0 ? out : undefined;
172
- }
173
- /**
174
- * Stub kept for source-compat with callers that previously used the v1 agent
175
- * config parser. After 0.8.0 there is no separate `agent` block to parse — the
176
- * loaded `AkmConfig` already carries the agent data on `profiles.agent` and
177
- * `defaults.agent`. This function is a no-op alias for those callers.
178
- *
179
- * @deprecated v0.8.0 — the unified `AkmConfig` IS the agent config. Use the
180
- * profile/defaults accessors above instead.
181
- */
182
- export function parseAgentConfig(_value) {
183
- // No-op: there is no separate agent block in 0.8.0. Callers should pass
184
- // their loaded `AkmConfig` directly to `requireAgentProfile` etc.
185
- return undefined;
186
- }
@@ -1,26 +1,10 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * Internal entry point for the `agent` integration. CLI-only project — no
6
- * public exports map. Other akm modules import from this barrel for the
7
- * sake of grouping imports.
8
- *
9
- * Surface:
10
- * • Types: AgentProfile, AgentConfig, AgentRunResult, AgentFailureReason.
11
- * • Profiles: getBuiltinAgentProfile, listBuiltinAgentProfiles, BUILTIN_AGENT_PROFILE_NAMES.
12
- * • Config: parseAgentConfig, resolveProfileFromConfig, requireAgentProfile, listResolvedAgentProfiles, listAgentProfileNames.
13
- * • Spawn: runAgent. Builders: getCommandBuilder, AgentCommandBuilder, AgentDispatchRequest — platform-specific argv construction.
14
- * • Detection: detectAgentCliProfiles, pickDefaultAgentProfile, defaultWhich.
15
- */
16
- // The OpenCode SDK runner moved to its harness directory in #564
17
- // (`harnesses/opencode-sdk/`). Re-exported here so existing `agent/index`
18
- // import sites keep working.
19
- export { runAgentSdk } from "../harnesses/opencode-sdk/index.js";
20
4
  export { getCommandBuilder } from "./builders.js";
21
- export { DEFAULT_AGENT_TIMEOUT_MS, listAgentProfileNames, listResolvedAgentProfiles, parseAgentConfig, requireAgentProfile, resolveAgentProfile, resolveDefaultProfileName, resolveProfileFromConfig, } from "./config.js";
5
+ export { DEFAULT_AGENT_TIMEOUT_MS, listAgentProfileNames, listResolvedAgentProfiles, requireAgentProfile, resolveAgentProfile, resolveDefaultProfileName, resolveProfileFromConfig, } from "./config.js";
22
6
  export { defaultWhich, detectAgentCliProfiles, pickDefaultAgentProfile } from "./detect.js";
23
7
  export { listBuiltinModelAliases, resolveModel } from "./model-aliases.js";
24
8
  export { BUILTIN_AGENT_PROFILE_NAMES, getBuiltinAgentProfile, listBuiltinAgentProfiles, } from "./profiles.js";
25
- export { buildProposePrompt, buildReflectPrompt, buildSchemaRepairPrompt, extractDraftConfidence, parseAgentProposalPayload, stripJsonFences, } from "./prompts.js";
9
+ export { buildProposePrompt, buildReflectPrompt, buildSchemaRepairPrompt, extractDraftConfidence, parseAgentProposalPayload, } from "./prompts.js";
26
10
  export { runAgent } from "./spawn.js";
@@ -26,6 +26,7 @@
26
26
  * during validation. We carry it through if the agent supplies it.
27
27
  */
28
28
  import { TYPE_DIRS } from "../../core/asset/asset-spec.js";
29
+ import { authoringRulesForType, DESCRIPTION_MAX_CHARS, DESCRIPTION_MIN_CHARS, requiresDescription, } from "../../core/authoring-rules.js";
29
30
  import { parseEmbeddedJsonResponse, stripCodeFences, stripThinkBlocks } from "../../core/parse.js";
30
31
  /**
31
32
  * Per-asset-type frontmatter / authoring hints surfaced in the prompt so
@@ -41,8 +42,9 @@ const TYPE_HINTS = {
41
42
  memory: "memory assets are short factual notes the user wants persisted across sessions. Frontmatter usually includes `description`.",
42
43
  workflow: "workflow assets are markdown describing a multi-step process. Include `# <Title>` and ordered `## Step N` sections.",
43
44
  script: "script assets are executable text files. Include a shebang and minimal usage comment.",
44
- env: "env assets are `.env` files holding a group of related CONFIGURATION for an app/service (KEY=VALUE pairs, `#` comments) — URLs, flags, and any credentials it needs. Values may or may not be sensitive; all are protected (key names discoverable, values stay on disk). Inject with `akm env run env:<name> -- <cmd>` (the safe path values never reach stdout/your context); do NOT run `akm env export` and read its output, as that prints values. For a single sensitive value used on its own for authentication (token, key, cert) use a `secret` instead. Never echo values back to the user.",
45
+ env: "env assets are `.env` files holding a group of related CONFIGURATION for an app/service (KEY=VALUE pairs, `#` comments) — URLs, flags, and any credentials it needs. Values may or may not be sensitive; all are protected (key names discoverable, values stay on disk). Inject with `akm env run env:<name> -- <cmd>`; prefer `--clean` in agent contexts so the child starts from a minimal inherited environment. AKM itself does not print values, but the child command can print its environment, so do not run `env`, `printenv`, shell tracing, or similar diagnostics when secrets are in scope. For a single sensitive value used on its own for authentication (token, key, cert) use a `secret` instead. Never echo values back to the user.",
45
46
  wiki: "wiki assets are markdown reference pages with `# Title` and structured headings.",
47
+ fact: "fact assets are durable stash-level facts (personal/team/project details, coding conventions, stash-meta). Frontmatter SHOULD include `description` and a `category` (personal|team|project|convention|meta); set `pinned: true` only for the small always-injected core. Keep each fact short, high-signal, and self-contained — it is durable context, not an episodic note.",
46
48
  };
47
49
  function hintForType(type) {
48
50
  return TYPE_HINTS[type] ?? `assets of type "${type}" — produce sensible markdown with optional frontmatter.`;
@@ -109,6 +111,30 @@ export function extractDraftConfidence(stdout) {
109
111
  return undefined;
110
112
  return value;
111
113
  }
114
+ /**
115
+ * Whether the source asset content has a non-empty `description:` key in its
116
+ * YAML frontmatter. Used by {@link buildReflectPrompt} (#636) to decide whether
117
+ * to inject the synthesize-a-description instruction. Uses an inline regex to
118
+ * avoid pulling the full YAML parser into the prompt module (mirrors the
119
+ * existing inline frontmatter handling here).
120
+ */
121
+ function sourceHasNonEmptyDescription(assetContent) {
122
+ if (!assetContent)
123
+ return false;
124
+ const fmMatch = assetContent.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
125
+ if (!fmMatch)
126
+ return false;
127
+ const fmBlock = fmMatch[1] ?? "";
128
+ // Match a top-level `description:` line and capture its inline value.
129
+ const descMatch = fmBlock.match(/^description\s*:\s*(.*)$/m);
130
+ if (!descMatch)
131
+ return false;
132
+ const value = (descMatch[1] ?? "")
133
+ .trim()
134
+ .replace(/^['"]|['"]$/g, "")
135
+ .trim();
136
+ return value.length > 0;
137
+ }
112
138
  /**
113
139
  * Build the prompt for `akm reflect [ref]`. Asks the agent to review an
114
140
  * existing asset (plus any negative feedback / lint findings) and propose
@@ -155,6 +181,34 @@ export function buildReflectPrompt(input) {
155
181
  // ref is set but no feedback — explicitly constrain scope to schema compliance
156
182
  sections.push("No usage feedback recorded. Limit your proposal to schema and structural improvements only: missing required frontmatter fields, unclear `when_to_use`, ambiguous description, or broken formatting. Do not speculate about runtime weaknesses you have not observed.");
157
183
  }
184
+ if (input.standardsContext?.trim()) {
185
+ sections.push("Standards to follow (the rulebook for this target):");
186
+ sections.push(input.standardsContext.trim());
187
+ }
188
+ {
189
+ const resolvedType = input.type ?? (input.ref?.includes(":") ? input.ref.split(":")[0] : "");
190
+ const authoringRules = resolvedType ? authoringRulesForType(resolvedType) : "";
191
+ if (authoringRules) {
192
+ sections.push(authoringRules);
193
+ }
194
+ // #636 — synthesize-a-description instruction. Many source assets (notably
195
+ // scraped docs: `source`/`title`/`scraped`) carry frontmatter but NO
196
+ // `description`. Reflect echoes the source frontmatter, so the proposal
197
+ // inherits the missing description and the promote-time validator
198
+ // (isValidDescription, 20–400 chars) rejects it. The fix is at GENERATION
199
+ // time: when the source lacks a non-empty `description` and the type
200
+ // requires one, tell the model — unmissably — that it MUST author a valid
201
+ // `description`. (The validator/promote path is NOT changed: it must never
202
+ // fabricate content to pass itself.)
203
+ if (resolvedType && requiresDescription(resolvedType) && !sourceHasNonEmptyDescription(input.assetContent)) {
204
+ sections.push([
205
+ "REQUIRED — synthesize a `description` (the source asset has none):",
206
+ `- The source frontmatter does NOT include a non-empty \`description\`, but a ${resolvedType} asset REQUIRES one or the proposal will be rejected at promote time.`,
207
+ `- You MUST author a valid \`description\` in the proposal frontmatter: ${DESCRIPTION_MIN_CHARS}–${DESCRIPTION_MAX_CHARS} characters of plain-prose sentence summarizing what this asset is about.`,
208
+ '- Synthesize it from the asset\'s `title:` frontmatter, its first `# Heading`, or the opening body sentence. Do NOT copy a bare heading fragment (e.g. "Overview", "Named Page", "Key Insight") and do NOT emit a truncated phrase that ends on `:`/`;`/`,` or a hanging connector word.',
209
+ ].join("\n"));
210
+ }
211
+ }
158
212
  if (input.assetContent?.trim()) {
159
213
  // Cap at 12 000 chars to stay well under OS ARG_MAX when the prompt is
160
214
  // passed as a CLI argument to opencode/claude. Large assets (wiki snapshots,
@@ -288,6 +342,16 @@ export function buildProposePrompt(input) {
288
342
  for (const line of input.schemaHints)
289
343
  sections.push(`- ${line}`);
290
344
  }
345
+ if (input.standardsContext?.trim()) {
346
+ sections.push("Standards to follow (the rulebook for this target):");
347
+ sections.push(input.standardsContext.trim());
348
+ }
349
+ {
350
+ const authoringRules = authoringRulesForType(input.type);
351
+ if (authoringRules) {
352
+ sections.push(authoringRules);
353
+ }
354
+ }
291
355
  sections.push("Produce a single proposal that, if accepted, would land as the asset described above.");
292
356
  sections.push(input.draftFilePath ? fileWriteContract(input.draftFilePath) : RESPONSE_CONTRACT_JSON);
293
357
  return sections.join("\n\n");
@@ -304,6 +368,16 @@ export function buildSchemaRepairPrompt(input) {
304
368
  `while preserving all existing content.`);
305
369
  sections.push(`Target ref: ${input.ref}`);
306
370
  sections.push(`Schema requirements for ${input.type} assets: ${hintForType(input.type)}`);
371
+ if (input.standardsContext?.trim()) {
372
+ sections.push("Standards to follow (the rulebook for this target):");
373
+ sections.push(input.standardsContext.trim());
374
+ }
375
+ {
376
+ const authoringRules = authoringRulesForType(input.type);
377
+ if (authoringRules) {
378
+ sections.push(authoringRules);
379
+ }
380
+ }
307
381
  const CONTENT_CAP = 3000;
308
382
  const body = input.assetContent.trimEnd();
309
383
  const truncated = body.length > CONTENT_CAP;
@@ -370,11 +444,3 @@ export function parseAgentProposalPayload(stdout) {
370
444
  }
371
445
  return out;
372
446
  }
373
- /**
374
- * Strip `\`\`\`json … \`\`\`` fences and `<think>…</think>` reasoning blocks
375
- * from agent output. Thin wrapper around `core/parse` helpers, kept exported
376
- * for backward compatibility (re-exported from `integrations/agent/index.ts`).
377
- */
378
- export function stripJsonFences(text) {
379
- return stripCodeFences(stripThinkBlocks(text));
380
- }
@@ -0,0 +1,59 @@
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
+ /**
5
+ * X3 — the ONE dispatch seam for the {@link RunnerSpec} tagged union.
6
+ *
7
+ * The improve slice dispatches a `RunnerSpec` (`llm | agent | sdk`) in several
8
+ * places (`reflect.ts`, `proposal/drain.ts`, …). Before this module each site
9
+ * re-rolled the identical 3-arm switch and re-declared its own per-kind test
10
+ * seams (`chat`, `runAgentFn`, `runSdkFn`). `executeRunner` collapses that into
11
+ * one switch + one {@link RunnerSeams} object.
12
+ *
13
+ * Scoping (behavior-preserving):
14
+ * - The `agent` and `sdk` arms are byte-identical across call sites: invoke
15
+ * the profile runner (`runAgent` / `runOpencodeSdk`) with the per-call
16
+ * `RunAgentOptions` the caller passes. Those default runners live here so
17
+ * callers stop importing `runAgent` / `runOpencodeSdk` for dispatch. The
18
+ * `opts` (incl. any `timeoutMs`) is constructed by the caller and passed
19
+ * through unchanged, so each site keeps its exact option set.
20
+ * - The `llm` arm is irreducibly caller-specific (reflect wraps
21
+ * `runReflectViaLlm`, which returns reflect's iteration shape; drain wraps a
22
+ * plain `chatCompletion`). It is therefore a REQUIRED seam — there is no
23
+ * default `llm` handler — so neither caller's bespoke behavior is changed.
24
+ * - The `assertNever` exhaustiveness arm is kept so a 4th `RunnerSpec` kind is
25
+ * a compile error here instead of a silent runtime fall-through.
26
+ *
27
+ * The return type is {@link AgentRunResult} so a later `callStructured` layer
28
+ * (X2) can wrap `executeRunner` without changing this contract.
29
+ */
30
+ import { assertNever } from "../../core/assert.js";
31
+ import { runOpencodeSdk } from "../harnesses/opencode-sdk/index.js";
32
+ import { runAgent } from "./spawn.js";
33
+ /**
34
+ * Dispatch a {@link RunnerSpec} to its runner and return the raw
35
+ * {@link AgentRunResult}. `opts` is the {@link RunAgentOptions} for the profile
36
+ * (`agent` / `sdk`) arms; it is passed through unchanged so each caller keeps
37
+ * its exact option set (incl. any `timeoutMs` the caller chose to apply).
38
+ */
39
+ export async function executeRunner(spec, prompt, opts, seams = {}) {
40
+ switch (spec.kind) {
41
+ case "llm": {
42
+ if (!seams.llm) {
43
+ throw new Error("executeRunner: an `llm` runner requires a `seams.llm` handler (no default LLM dispatch).");
44
+ }
45
+ return seams.llm(spec, prompt);
46
+ }
47
+ case "agent": {
48
+ const run = seams.runAgent ?? runAgent;
49
+ return run(spec.profile, prompt, opts);
50
+ }
51
+ case "sdk": {
52
+ const run = seams.runSdk ?? runOpencodeSdk;
53
+ return run(spec.profile, prompt, opts);
54
+ }
55
+ default:
56
+ // Exhaustiveness arm: a 4th RunnerSpec kind becomes a compile error here.
57
+ return assertNever(spec);
58
+ }
59
+ }
@@ -107,6 +107,16 @@ export class ClaudeCodeProvider {
107
107
  isAvailable() {
108
108
  return fs.existsSync(claudeProjectsDir());
109
109
  }
110
+ /**
111
+ * Directory holding Claude Code's per-project session JSONL files
112
+ * (`~/.claude/projects`, honoring `AKM_CLAUDE_PROJECTS_DIR`). Returns `[]`
113
+ * when the directory does not exist on this machine. See {@link
114
+ * SessionLogHarness.watchRoots}.
115
+ */
116
+ watchRoots() {
117
+ const dir = claudeProjectsDir();
118
+ return fs.existsSync(dir) ? [dir] : [];
119
+ }
110
120
  *readEvents(input) {
111
121
  try {
112
122
  for (const jsonlPath of this.#walkJsonl(claudeProjectsDir())) {
@@ -298,7 +308,7 @@ export class ClaudeCodeProvider {
298
308
  const full = path.join(dir, entry.name);
299
309
  if (entry.isDirectory())
300
310
  yield* this.#walkJsonl(full);
301
- else if (entry.name.endsWith(".jsonl"))
311
+ else if (entry.name.endsWith(".jsonl") && entry.name !== "journal.jsonl")
302
312
  yield full;
303
313
  }
304
314
  }
@@ -60,9 +60,8 @@ const HARNESS_BY_ANY_ID = (() => {
60
60
  })();
61
61
  /**
62
62
  * Canonical, ordered list of valid harness / platform ids. The Zod
63
- * `AgentPlatformSchema` enum, the `AgentProfileConfigV2` platform union,
64
- * `parseAgentProfilesMapV2`'s membership check, and setup's `DetectedHarness`
65
- * union all derive from this so they cannot drift.
63
+ * `AgentPlatformSchema` enum, the `AgentProfileConfig` platform union, and
64
+ * setup's `DetectedHarness` union all derive from this so they cannot drift.
66
65
  */
67
66
  export const VALID_HARNESS_IDS = Object.freeze(HARNESS_REGISTRY.map((h) => h.id));
68
67
  /** Harnesses that expose readable native session logs. */