akm-cli 0.8.0 → 0.8.1

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 (39) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/assets/profiles/default.json +15 -0
  3. package/dist/assets/profiles/graph-refresh.json +13 -0
  4. package/dist/assets/profiles/memory-focus.json +12 -0
  5. package/dist/assets/profiles/quick.json +15 -0
  6. package/dist/assets/profiles/thorough.json +15 -0
  7. package/dist/assets/tasks/graph-refresh-weekly.yml +10 -0
  8. package/dist/commands/consolidate.js +32 -11
  9. package/dist/commands/extract-prompt.js +14 -1
  10. package/dist/commands/health.js +69 -8
  11. package/dist/commands/improve-cli.js +1 -1
  12. package/dist/commands/improve-profiles.js +13 -59
  13. package/dist/commands/improve.js +38 -41
  14. package/dist/commands/info.js +23 -28
  15. package/dist/indexer/indexer.js +2 -2
  16. package/dist/llm/graph-extract.js +1 -1
  17. package/dist/output/cli-hints.js +2 -2
  18. package/dist/tasks/backends/launchd.js +1 -1
  19. package/dist/tasks/backends/schtasks.js +1 -1
  20. package/dist/wiki/wiki-templates.js +3 -3
  21. package/dist/wiki/wiki.js +1 -1
  22. package/dist/workflows/authoring.js +1 -1
  23. package/package.json +1 -1
  24. /package/dist/{tasks → assets}/backends/launchd-template.xml +0 -0
  25. /package/dist/{tasks → assets}/backends/schtasks-template.xml +0 -0
  26. /package/dist/{commands → assets}/help/help-accept.md +0 -0
  27. /package/dist/{commands → assets}/help/help-improve.md +0 -0
  28. /package/dist/{commands → assets}/help/help-proposals.md +0 -0
  29. /package/dist/{commands → assets}/help/help-propose.md +0 -0
  30. /package/dist/{commands → assets}/help/help-reject.md +0 -0
  31. /package/dist/{output → assets/hints}/cli-hints-full.md +0 -0
  32. /package/dist/{output → assets/hints}/cli-hints-short.md +0 -0
  33. /package/dist/{llm → assets}/prompts/extract-session.md +0 -0
  34. /package/dist/{llm → assets}/prompts/graph-extract-user-prompt.md +0 -0
  35. /package/dist/{wiki → assets/wiki}/index-template.md +0 -0
  36. /package/dist/{wiki → assets/wiki}/ingest-workflow-template.md +0 -0
  37. /package/dist/{wiki → assets/wiki}/log-template.md +0 -0
  38. /package/dist/{wiki → assets/wiki}/schema-template.md +0 -0
  39. /package/dist/{workflows → assets/workflows}/workflow-template.md +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,53 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [0.8.1] - 2026-06-05
8
+
9
+ ### Added
10
+
11
+ - **`graph-refresh` improve profile** — new built-in profile that runs a full-corpus
12
+ graph extraction pass across all stash files (all other improve processes disabled).
13
+ Use `akm improve --profile graph-refresh` for a weekly relationship rebuild.
14
+ Pairs with the new `graph-refresh-weekly` task template (`akm tasks add --template graph-refresh-weekly`).
15
+ - **`session-extraction` health advisory** — new heuristic advisory backed by real
16
+ `akmExtract` outcomes: warns when the session-extraction process ran but produced
17
+ zero proposals across ≥ 5 sessions, or recorded warnings. Replaces the vestigial
18
+ `session-log-failures` warn signal.
19
+ - **`improve.sessionExtraction` health metrics** — `sessionsScanned`, `sessionsExtracted`,
20
+ `sessionsSkipped`, `proposalsCreated`, `warnings`, `durationMs` now tracked and
21
+ visible in `akm health` reports.
22
+
23
+ ### Fixed
24
+
25
+ - **`akm info` indexStats** — `readIndexStats` errors are now surfaced and the resolved
26
+ DB path is passed correctly; `entryCount`, `hasEmbeddings`, and related fields are
27
+ no longer silently empty (#510).
28
+ - **Indexer timing fields** — `embedMs` and `ftsMs` in timing output had their
29
+ operands swapped, producing negative durations. Fixed (#516).
30
+ - **Incremental consolidation gate** — the `volumeTriggered` path bypassed the
31
+ incremental gate introduced in 0.8.0, causing consolidation to run on chunks it
32
+ had already processed in the same run. Fixed.
33
+ - **Improve budget exhaustion** — `improve.lock` was not released after budget
34
+ exhaustion, blocking subsequent runs until the lock TTL expired.
35
+ - **Consolidation chunk retry** — failed chunks are now retried once with a 2 s
36
+ backoff before being recorded as lost, reducing transient LLM errors from
37
+ propagating to `chunksFailed`.
38
+ - **`yieldRate` health metric** — `skippedAborted` refs were incorrectly counted in
39
+ `freshAttempts`, inflating the denominator and underreporting yield rate.
40
+ - **`session-log-failures` advisory** — demoted from `warn` to always `pass`
41
+ (informational only); the advisory was a raw regex counter with no LLM signal,
42
+ producing false positives on normal session content.
43
+
44
+ ### Refactored
45
+
46
+ - All runtime assets consolidated under `src/assets/` with `dist/assets/` mirroring
47
+ the layout exactly. Built-in improve profiles moved from in-source object literals
48
+ to embedded JSON files (`src/assets/profiles/*.json`). The `copy-assets.ts` build
49
+ step now uses a precise `src/assets/**/*` glob instead of a broad catch-all.
50
+ - Vestigial Phase 0 (`getExecutionLogCandidates` / `ERROR_PATTERNS`) removed from
51
+ the improve pipeline. This regex scan collected a metric count but never fed an
52
+ LLM; `akmExtract` (Phase 0.4) is the real session extraction pipeline.
53
+
7
54
  ## [0.8.0] - 2026-05-28
8
55
 
9
56
  ### Performance
@@ -0,0 +1,15 @@
1
+ {
2
+ "description": "Standard improve pass — all sub-processes, markdown asset types.",
3
+ "processes": {
4
+ "reflect": {
5
+ "enabled": true,
6
+ "allowedTypes": ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"]
7
+ },
8
+ "distill": { "enabled": true, "allowedTypes": ["memory"] },
9
+ "consolidate": { "enabled": true, "allowedTypes": ["memory"] },
10
+ "memoryInference": { "enabled": true },
11
+ "graphExtraction": { "enabled": true },
12
+ "triage": { "enabled": false, "applyMode": "queue", "policy": "personal-stash" }
13
+ },
14
+ "sync": { "enabled": true, "push": true }
15
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "description": "Full-corpus graph extraction pass — rebuilds graph relationships across all stash files. Disables all other sub-processes. Use via `akm improve --profile graph-refresh` or the embedded `graph-refresh-weekly` task.",
3
+ "processes": {
4
+ "reflect": { "enabled": false },
5
+ "distill": { "enabled": false },
6
+ "consolidate": { "enabled": false },
7
+ "memoryInference": { "enabled": false },
8
+ "graphExtraction": { "enabled": true, "fullScan": true },
9
+ "extract": { "enabled": false },
10
+ "triage": { "enabled": false }
11
+ },
12
+ "sync": { "enabled": true, "push": true }
13
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "description": "Memory and lesson improvement only — no distill, consolidate, or graphExtraction.",
3
+ "processes": {
4
+ "reflect": { "enabled": true, "allowedTypes": ["memory", "lesson"] },
5
+ "distill": { "enabled": false },
6
+ "consolidate": { "enabled": false },
7
+ "memoryInference": { "enabled": true },
8
+ "graphExtraction": { "enabled": false },
9
+ "triage": { "enabled": false }
10
+ },
11
+ "sync": { "enabled": false }
12
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "description": "Reflect-only pass — no distill, consolidate, memoryInference, or graphExtraction.",
3
+ "processes": {
4
+ "reflect": {
5
+ "enabled": true,
6
+ "allowedTypes": ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"]
7
+ },
8
+ "distill": { "enabled": false },
9
+ "consolidate": { "enabled": false },
10
+ "memoryInference": { "enabled": false },
11
+ "graphExtraction": { "enabled": false },
12
+ "triage": { "enabled": false }
13
+ },
14
+ "sync": { "enabled": false }
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "description": "All sub-processes enabled (currently identical to default; reserved for future divergence).",
3
+ "processes": {
4
+ "reflect": {
5
+ "enabled": true,
6
+ "allowedTypes": ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"]
7
+ },
8
+ "distill": { "enabled": true, "allowedTypes": ["memory"] },
9
+ "consolidate": { "enabled": true, "allowedTypes": ["memory"] },
10
+ "memoryInference": { "enabled": true },
11
+ "graphExtraction": { "enabled": true },
12
+ "triage": { "enabled": true, "applyMode": "queue" }
13
+ },
14
+ "sync": { "enabled": true, "push": true }
15
+ }
@@ -0,0 +1,10 @@
1
+ id: graph-refresh-weekly
2
+ description: >
3
+ Weekly full-corpus graph extraction pass. Rebuilds entity and relation
4
+ indexes across all stash files using the graph-refresh improve profile.
5
+ Complements the per-run improve pipeline which only extracts graph data
6
+ for files touched by actionable refs.
7
+ command: akm improve --profile graph-refresh
8
+ schedule: "0 3 * * 0"
9
+ enabled: false
10
+ timeout: 3600000
@@ -965,7 +965,7 @@ export async function akmConsolidate(opts = {}) {
965
965
  }
966
966
  warn(`[consolidate] chunk ${chunkIdx + 1}/${chunks.length} (${chunk.length} memories) …`);
967
967
  const userPrompt = buildChunkPrompt(sourceName, chunk, chunkIdx, chunks.length, bodyTruncation, pendingProposalBodyHashes);
968
- const raw = await tryLlmFeature("memory_consolidation", config, async () => {
968
+ let raw = await tryLlmFeature("memory_consolidation", config, async () => {
969
969
  if (!llmConfig)
970
970
  return { ok: false, error: "No LLM configured for consolidation" };
971
971
  try {
@@ -985,16 +985,37 @@ export async function akmConsolidate(opts = {}) {
985
985
  }
986
986
  }, { ok: false, error: `chunk ${chunkIdx + 1} failed` });
987
987
  if (!raw.ok) {
988
- warn(raw.error ?? `chunk ${chunkIdx + 1} failed`);
989
- warnings.push(raw.error ?? `chunk ${chunkIdx + 1} failed`);
990
- totalChunksProcessed++;
991
- totalChunksFailed++;
992
- // Account for the chunk's memories under the failed-chunk bucket.
993
- // judgedNoAction does NOT run on this path (it's after the success
994
- // guards) so without this the accounting invariant breaks on every
995
- // chunk-level transport/parse failure.
996
- failedChunkMemories += chunk.length;
997
- continue;
988
+ // Single retry with 2s backoff before recording chunk as lost.
989
+ // Recovers transient Shredder LM Studio timeouts without significantly
990
+ // extending run time. Only marks failed if both attempts fail.
991
+ await new Promise((r) => setTimeout(r, 2_000));
992
+ const retry = await tryLlmFeature("memory_consolidation", config, async () => {
993
+ if (!llmConfig)
994
+ return { ok: false, error: "No LLM configured for consolidation" };
995
+ try {
996
+ const content = await chatCompletion(llmConfig, [
997
+ { role: "system", content: CONSOLIDATE_SYSTEM_PROMPT },
998
+ { role: "user", content: userPrompt },
999
+ ], { responseSchema: CONSOLIDATE_PLAN_JSON_SCHEMA, enableThinking: false });
1000
+ return { ok: true, content };
1001
+ }
1002
+ catch (e) {
1003
+ return { ok: false, error: String(e) };
1004
+ }
1005
+ }, { ok: false, error: `chunk ${chunkIdx + 1} retry failed` });
1006
+ if (!retry.ok) {
1007
+ warn(retry.error ?? `chunk ${chunkIdx + 1} failed after retry`);
1008
+ warnings.push(retry.error ?? `chunk ${chunkIdx + 1} failed after retry`);
1009
+ totalChunksProcessed++;
1010
+ totalChunksFailed++;
1011
+ // Account for the chunk's memories under the failed-chunk bucket.
1012
+ // judgedNoAction does NOT run on this path (it's after the success
1013
+ // guards) so without this the accounting invariant breaks on every
1014
+ // chunk-level transport/parse failure.
1015
+ failedChunkMemories += chunk.length;
1016
+ continue;
1017
+ }
1018
+ raw = retry;
998
1019
  }
999
1020
  if (process.env.AKM_DEBUG_LLM) {
1000
1021
  const preview = (raw.content ?? "").slice(0, 500);
@@ -1,7 +1,20 @@
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 promptTemplate from "../llm/prompts/extract-session.md" with { type: "text" };
4
+ /**
5
+ * Prompt + schema for `akm extract <session>`.
6
+ *
7
+ * Mirrors the REFLECT_JSON_SCHEMA pattern: a strict JSON Schema describing
8
+ * the LLM output, plus a {@link buildExtractPrompt} helper that interpolates
9
+ * session data into the markdown template loaded from
10
+ * `src/assets/prompts/extract-session.md`.
11
+ *
12
+ * The schema is intentionally strict — providers with `supportsJsonSchema:
13
+ * true` enforce shape upstream, so the parser only has to handle the
14
+ * happy path. `additionalProperties: false` means any hallucinated keys
15
+ * the model emits get dropped before we parse.
16
+ */
17
+ import promptTemplate from "../assets/prompts/extract-session.md" with { type: "text" };
5
18
  /**
6
19
  * JSON Schema for the structured extract output. Passed to `chatCompletion`
7
20
  * when the configured LLM connection has `supportsJsonSchema: true`.
@@ -73,7 +73,6 @@ function createUnknownImproveMetrics() {
73
73
  },
74
74
  reflectsWithErrorContext: 0,
75
75
  coverageGapCount: 0,
76
- executionLogCandidateCount: 0,
77
76
  evalCasesWritten: 0,
78
77
  deadUrlCount: 0,
79
78
  memorySummary: { eligible: 0, derived: 0 },
@@ -130,6 +129,15 @@ function createUnknownImproveMetrics() {
130
129
  failures: 0,
131
130
  durationMs: 0,
132
131
  },
132
+ sessionExtraction: {
133
+ ran: false,
134
+ sessionsScanned: 0,
135
+ sessionsExtracted: 0,
136
+ sessionsSkipped: 0,
137
+ proposalsCreated: 0,
138
+ warnings: 0,
139
+ durationMs: 0,
140
+ },
133
141
  wallTime: {
134
142
  count: 0,
135
143
  medianMs: 0,
@@ -288,8 +296,6 @@ function projectRunMetrics(result) {
288
296
  metrics.reflectsWithErrorContext += toFiniteNumber(result.reflectsWithErrorContext);
289
297
  if (Array.isArray(result.coverageGaps))
290
298
  metrics.coverageGapCount += result.coverageGaps.length;
291
- if (Array.isArray(result.executionLogCandidates))
292
- metrics.executionLogCandidateCount += result.executionLogCandidates.length;
293
299
  metrics.evalCasesWritten += toFiniteNumber(result.evalCasesWritten);
294
300
  if (Array.isArray(result.deadUrls))
295
301
  metrics.deadUrlCount += result.deadUrls.length;
@@ -385,6 +391,18 @@ function projectRunMetrics(result) {
385
391
  }
386
392
  }
387
393
  metrics.graphExtraction.durationMs += toFiniteNumber(result.graphExtractionDurationMs);
394
+ if (Array.isArray(result.extract)) {
395
+ for (const e of result.extract) {
396
+ metrics.sessionExtraction.sessionsScanned += toFiniteNumber(e.sessionsProcessed);
397
+ metrics.sessionExtraction.sessionsSkipped += toFiniteNumber(e.sessionsSkipped);
398
+ if (Array.isArray(e.sessions)) {
399
+ metrics.sessionExtraction.sessionsExtracted += e.sessions.filter((s) => Array.isArray(s.proposalIds) && s.proposalIds.length > 0).length;
400
+ }
401
+ metrics.sessionExtraction.proposalsCreated += Array.isArray(e.proposals) ? e.proposals.length : 0;
402
+ metrics.sessionExtraction.warnings += Array.isArray(e.warnings) ? e.warnings.length : 0;
403
+ metrics.sessionExtraction.durationMs += toFiniteNumber(e.durationMs);
404
+ }
405
+ }
388
406
  return metrics;
389
407
  }
390
408
  /**
@@ -412,7 +430,9 @@ function finalizeImproveMetrics(metrics) {
412
430
  // `considered`/`written` for totals but are excluded from the rate so
413
431
  // they cannot drag it down. See ImproveHealthMetrics.memoryInference
414
432
  // jsdoc for the rationale.
415
- metrics.memoryInference.freshAttempts = Math.max(0, metrics.memoryInference.yieldEligibleConsidered - metrics.memoryInference.cacheHits);
433
+ metrics.memoryInference.freshAttempts = Math.max(0, metrics.memoryInference.yieldEligibleConsidered -
434
+ metrics.memoryInference.cacheHits -
435
+ metrics.memoryInference.skippedAborted);
416
436
  metrics.memoryInference.yieldRate =
417
437
  metrics.memoryInference.freshAttempts > 0
418
438
  ? roundRate(metrics.memoryInference.yieldEligibleWritten / metrics.memoryInference.freshAttempts)
@@ -423,6 +443,10 @@ function finalizeImproveMetrics(metrics) {
423
443
  metrics.graphExtraction.durationMs > 0;
424
444
  const cacheTotal = metrics.graphExtraction.cacheHits + metrics.graphExtraction.cacheMisses;
425
445
  metrics.graphExtraction.cacheHitRate = cacheTotal > 0 ? roundRate(metrics.graphExtraction.cacheHits / cacheTotal) : 0;
446
+ metrics.sessionExtraction.ran =
447
+ metrics.sessionExtraction.sessionsScanned > 0 ||
448
+ metrics.sessionExtraction.proposalsCreated > 0 ||
449
+ metrics.sessionExtraction.durationMs > 0;
426
450
  }
427
451
  /**
428
452
  * Merge per-row metrics from `src` into accumulator `dst`. All numeric fields
@@ -459,7 +483,6 @@ function mergeImproveMetrics(dst, src) {
459
483
  dst.actions.error += src.actions.error;
460
484
  dst.reflectsWithErrorContext += src.reflectsWithErrorContext;
461
485
  dst.coverageGapCount += src.coverageGapCount;
462
- dst.executionLogCandidateCount += src.executionLogCandidateCount;
463
486
  dst.evalCasesWritten += src.evalCasesWritten;
464
487
  dst.deadUrlCount += src.deadUrlCount;
465
488
  dst.memorySummary.eligible += src.memorySummary.eligible;
@@ -504,6 +527,12 @@ function mergeImproveMetrics(dst, src) {
504
527
  dst.graphExtraction.truncations += src.graphExtraction.truncations;
505
528
  dst.graphExtraction.failures += src.graphExtraction.failures;
506
529
  dst.graphExtraction.durationMs += src.graphExtraction.durationMs;
530
+ dst.sessionExtraction.sessionsScanned += src.sessionExtraction.sessionsScanned;
531
+ dst.sessionExtraction.sessionsExtracted += src.sessionExtraction.sessionsExtracted;
532
+ dst.sessionExtraction.sessionsSkipped += src.sessionExtraction.sessionsSkipped;
533
+ dst.sessionExtraction.proposalsCreated += src.sessionExtraction.proposalsCreated;
534
+ dst.sessionExtraction.warnings += src.sessionExtraction.warnings;
535
+ dst.sessionExtraction.durationMs += src.sessionExtraction.durationMs;
507
536
  }
508
537
  function loadImproveRunRows(db, since, until) {
509
538
  const sql = until
@@ -892,6 +921,8 @@ const INTERESTING_DELTA_PATHS = [
892
921
  "improve.memoryInference.skippedNoFacts",
893
922
  "improve.graphExtraction.cacheHitRate",
894
923
  "improve.graphExtraction.failures",
924
+ "improve.sessionExtraction.sessionsScanned",
925
+ "improve.sessionExtraction.proposalsCreated",
895
926
  "improve.wallTime.medianMs",
896
927
  "improve.wallTime.p95Ms",
897
928
  ];
@@ -1112,16 +1143,46 @@ export function akmHealth(options = {}) {
1112
1143
  catch {
1113
1144
  sessionLogEntries = [];
1114
1145
  }
1146
+ // session-log-failures: demoted to informational — the ERROR_PATTERNS regex
1147
+ // scans pre-LLM session text and produces false positives on diagnostic
1148
+ // conversation. It does not gate the real extraction pipeline (akmExtract).
1149
+ // Never triggers warn; kept for backward-compat visibility only.
1115
1150
  advisories.push({
1116
1151
  name: "session-log-failures",
1117
1152
  kind: "heuristic",
1118
- status: sessionLogEntries.length === 0 ? "pass" : "warn",
1119
- confidence: sessionLogEntries.length === 0 ? "low" : "medium",
1153
+ status: "pass",
1154
+ confidence: "low",
1120
1155
  message: sessionLogEntries.length === 0
1121
1156
  ? "No repeated external session-log failure patterns were detected."
1122
- : `${sessionLogEntries.length} repeated external session-log failure pattern(s) detected.`,
1157
+ : `${sessionLogEntries.length} raw session-log keyword match(es) detected (pre-LLM, informational only).`,
1123
1158
  evidence: { candidates: sessionLogEntries.slice(0, 5) },
1124
1159
  });
1160
+ const sx = improveSummary.sessionExtraction;
1161
+ const sxWarnReasons = [];
1162
+ if (sx.warnings > 0)
1163
+ sxWarnReasons.push(`${sx.warnings} harness error(s)`);
1164
+ if (sx.ran && sx.sessionsScanned >= 5 && sx.proposalsCreated === 0)
1165
+ sxWarnReasons.push("no proposals generated across scanned sessions");
1166
+ advisories.push({
1167
+ name: "session-extraction",
1168
+ kind: "heuristic",
1169
+ status: sxWarnReasons.length > 0 ? "warn" : "pass",
1170
+ confidence: sx.ran ? "medium" : "low",
1171
+ message: sx.ran
1172
+ ? sxWarnReasons.length > 0
1173
+ ? `Session extraction degraded: ${sxWarnReasons.join("; ")}.`
1174
+ : `Session extraction healthy: ${sx.sessionsScanned} scanned, ${sx.sessionsExtracted} extracted, ${sx.proposalsCreated} proposal(s) created.`
1175
+ : "Session extraction not active (feature disabled or no harness available).",
1176
+ evidence: {
1177
+ ran: sx.ran,
1178
+ sessionsScanned: sx.sessionsScanned,
1179
+ sessionsExtracted: sx.sessionsExtracted,
1180
+ sessionsSkipped: sx.sessionsSkipped,
1181
+ proposalsCreated: sx.proposalsCreated,
1182
+ warnings: sx.warnings,
1183
+ durationMs: sx.durationMs,
1184
+ },
1185
+ });
1125
1186
  const metrics = {
1126
1187
  taskFailRate: roundRate(taskFailRate),
1127
1188
  agentFailureRate: roundRate(agentFailureRate),
@@ -56,7 +56,7 @@ export const improveCommand = defineCommand({
56
56
  },
57
57
  profile: {
58
58
  type: "string",
59
- description: "Named improve profile from profiles.improve or built-in profiles (default, quick, thorough, memory-focus). Controls which sub-processes run and which asset types are processed.",
59
+ description: "Named improve profile from profiles.improve or built-in profiles (default, quick, thorough, memory-focus, graph-refresh). Controls which sub-processes run and which asset types are processed.",
60
60
  },
61
61
  sync: {
62
62
  type: "boolean",
@@ -1,6 +1,11 @@
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 profileDefault from "../assets/profiles/default.json" with { type: "json" };
5
+ import profileGraphRefresh from "../assets/profiles/graph-refresh.json" with { type: "json" };
6
+ import profileMemoryFocus from "../assets/profiles/memory-focus.json" with { type: "json" };
7
+ import profileQuick from "../assets/profiles/quick.json" with { type: "json" };
8
+ import profileThorough from "../assets/profiles/thorough.json" with { type: "json" };
4
9
  import { parseAssetRef } from "../core/asset-ref";
5
10
  import { warn } from "../core/warn";
6
11
  /** Profile name used as the final fallback when nothing else resolves. */
@@ -11,66 +16,15 @@ export const DEFAULT_ALLOWED_TYPES = {
11
16
  distill: ["memory"],
12
17
  consolidate: ["memory"],
13
18
  };
19
+ // Built-in profiles are loaded from embedded JSON files in src/assets/profiles/.
20
+ // To add a new profile: create a new .json file there, import it above, and add
21
+ // it to this map. No code change needed beyond those two steps.
14
22
  const BUILTIN_PROFILES = {
15
- default: {
16
- description: "Standard improve pass — all sub-processes, markdown asset types.",
17
- processes: {
18
- reflect: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.reflect },
19
- distill: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.distill },
20
- consolidate: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.consolidate },
21
- memoryInference: { enabled: true },
22
- graphExtraction: { enabled: true },
23
- // validation: deliberately undefined — third-tier classifier is opt-in.
24
- triage: { enabled: false, applyMode: "queue", policy: "personal-stash" },
25
- },
26
- sync: { enabled: true, push: true },
27
- },
28
- quick: {
29
- description: "Reflect-only pass — no distill, consolidate, memoryInference, or graphExtraction.",
30
- processes: {
31
- reflect: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.reflect },
32
- distill: { enabled: false },
33
- consolidate: { enabled: false },
34
- memoryInference: { enabled: false },
35
- graphExtraction: { enabled: false },
36
- triage: { enabled: false },
37
- },
38
- // Lightweight passes opt out of end-of-run sync: a reflect-only `quick`
39
- // run should not auto-commit/push the git-backed stash to its remote.
40
- // (The auto-sync gate in improve.ts treats an absent sync block as
41
- // ENABLED + push, so we set this explicitly to avoid a surprise push.)
42
- sync: { enabled: false },
43
- },
44
- thorough: {
45
- // Reserved for future divergence; for now behaviorally identical to
46
- // `default`. Documented here so callers picking `--profile thorough` do
47
- // not expect a different code path until we wire stricter limits in.
48
- description: "All sub-processes enabled (currently identical to default; reserved for future divergence).",
49
- processes: {
50
- reflect: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.reflect },
51
- distill: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.distill },
52
- consolidate: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.consolidate },
53
- memoryInference: { enabled: true },
54
- graphExtraction: { enabled: true },
55
- triage: { enabled: true, applyMode: "queue" },
56
- },
57
- sync: { enabled: true, push: true },
58
- },
59
- "memory-focus": {
60
- description: "Memory and lesson improvement only — no distill or consolidate.",
61
- processes: {
62
- reflect: { enabled: true, allowedTypes: ["memory", "lesson"] },
63
- distill: { enabled: false },
64
- consolidate: { enabled: false },
65
- memoryInference: { enabled: true },
66
- graphExtraction: { enabled: false },
67
- triage: { enabled: false },
68
- },
69
- // Limited pass opts out of end-of-run sync for the same reason as `quick`:
70
- // a memory/lesson-only run should not auto-commit/push the stash. Explicit
71
- // here because improve.ts treats an absent sync block as ENABLED + push.
72
- sync: { enabled: false },
73
- },
23
+ default: profileDefault,
24
+ quick: profileQuick,
25
+ thorough: profileThorough,
26
+ "memory-focus": profileMemoryFocus,
27
+ "graph-refresh": profileGraphRefresh,
74
28
  };
75
29
  /**
76
30
  * Default enabled-state for known improve processes when neither the user
@@ -25,7 +25,7 @@ import { resolveAssetPath } from "../indexer/path-resolver";
25
25
  import { getWritableStashDirs, resolveSourceEntries } from "../indexer/search-source";
26
26
  import { runStalenessDetectionPass } from "../indexer/staleness-detect";
27
27
  import { resolveImproveProcessRunnerFromProfile, resolveTriageJudgmentRunner } from "../integrations/agent/runner";
28
- import { getAvailableHarnesses, getExecutionLogCandidates } from "../integrations/session-logs";
28
+ import { getAvailableHarnesses } from "../integrations/session-logs";
29
29
  import { isLlmFeatureEnabled, isProcessEnabled } from "../llm/feature-gate";
30
30
  import { isGitBackedStash, resolveWritableOverride, saveGitStash } from "../sources/providers/git";
31
31
  import { akmConsolidate } from "./consolidate";
@@ -674,7 +674,12 @@ export async function akmImprove(options = {}) {
674
674
  // lives in the outer scope. It is always assigned at the top of the try.
675
675
  let eventsCtx = {};
676
676
  try {
677
- const budgetTimer = setTimeout(() => budgetAbortController.abort("improve budget exhausted"), budgetMs);
677
+ const budgetTimer = setTimeout(() => {
678
+ budgetAbortController.abort("improve budget exhausted");
679
+ // Grace period: let finally run to release improve.lock, then hard-exit
680
+ // to prevent the process outliving the task timeout window (lock-cascade fix).
681
+ setTimeout(() => process.exit(1), 5_000);
682
+ }, budgetMs);
678
683
  // Clear the timer when the run ends to avoid keeping the event loop alive.
679
684
  clearBudgetTimer = () => clearTimeout(budgetTimer);
680
685
  try {
@@ -798,9 +803,6 @@ export async function akmImprove(options = {}) {
798
803
  ...(preparation.lintSummary !== undefined ? { lintSummary: preparation.lintSummary } : {}),
799
804
  ...(preparation.memoryIndexHealth !== undefined ? { memoryIndexHealth: preparation.memoryIndexHealth } : {}),
800
805
  ...(preparation.coverageGaps.length > 0 ? { coverageGaps: preparation.coverageGaps } : {}),
801
- ...(preparation.executionLogCandidates.length > 0
802
- ? { executionLogCandidates: preparation.executionLogCandidates }
803
- : {}),
804
806
  ...(preparation.extract && preparation.extract.length > 0 ? { extract: preparation.extract } : {}),
805
807
  ...(primaryStashDir !== undefined ? { evalCasesWritten: countEvalCases(primaryStashDir) } : {}),
806
808
  ...(deadUrls !== undefined && deadUrls.length > 0 ? { deadUrls } : {}),
@@ -990,7 +992,6 @@ function emitImproveCompletedEvent(result, durations, eventsCtx) {
990
992
  reflectSkippedActions: actionCounts.reflectSkipped,
991
993
  reflectsWithErrorContext: result.reflectsWithErrorContext ?? 0,
992
994
  coverageGapCount: result.coverageGaps?.length ?? 0,
993
- executionLogCandidateCount: result.executionLogCandidates?.length ?? 0,
994
995
  evalCasesWritten: result.evalCasesWritten ?? 0,
995
996
  deadUrlCount: result.deadUrls?.length ?? 0,
996
997
  memoryEligible: result.memorySummary.eligible,
@@ -1047,15 +1048,6 @@ async function runImprovePreparationStage(args) {
1047
1048
  }
1048
1049
  }
1049
1050
  }
1050
- // Phase 0 — execution log synthesis
1051
- let executionLogCandidates = [];
1052
- try {
1053
- const logEntries = getExecutionLogCandidates(7);
1054
- executionLogCandidates = logEntries.filter((e) => e.isFailurePattern).map((e) => e.topic);
1055
- }
1056
- catch {
1057
- // best-effort
1058
- }
1059
1051
  // Phase 0.4 — session-extract pass.
1060
1052
  //
1061
1053
  // Reads native session files (claude-code JSONL, opencode storage tree)
@@ -1539,7 +1531,6 @@ async function runImprovePreparationStage(args) {
1539
1531
  cleanupWarnings,
1540
1532
  appliedCleanup,
1541
1533
  memoryIndexHealth,
1542
- executionLogCandidates,
1543
1534
  extract: extractResults,
1544
1535
  actionableRefs,
1545
1536
  signalBearingSet,
@@ -2111,13 +2102,16 @@ async function runImprovePostLoopStage(args) {
2111
2102
  // Tie consolidate proposals back to this improve invocation so
2112
2103
  // accept-rate-per-run aggregation works. Mirrors reflect/propose/extract.
2113
2104
  sourceRun: `consolidate-${Date.now()}`,
2114
- // Incremental consolidation: in steady state (not bootstrap, not volume-
2115
- // triggered) pass the last-consolidation timestamp so akmConsolidate skips
2116
- // chunks with no memory changed since then. Converts consolidation cost
2117
- // from O(pool) to O(changed clusters) the fix for the rising p95 tail
2118
- // where full-pool re-judging produced 5–10 min runs that promoted ~0.
2119
- // undefined full pass (bootstrap, or volume-triggered large-pool sweep).
2120
- incrementalSince: volumeTriggered ? undefined : lastConsolidateTs,
2105
+ // Incremental consolidation: pass the last-consolidation timestamp so
2106
+ // akmConsolidate skips chunks with no memory changed since then. Converts
2107
+ // consolidation cost from O(pool) to O(changed clusters) the fix for
2108
+ // the rising p95 tail where full-pool re-judging produced 5–10 min runs
2109
+ // that promoted ~0. undefined full pass on first-ever run (bootstrap).
2110
+ // volumeTriggered correctly forces the run past cooldown but must NOT
2111
+ // override incrementalSince the stash has ~1400 eligible memories so
2112
+ // volumeTriggered=true on every run, permanently forcing full 12-chunk
2113
+ // scans (~264s) instead of the intended 1-2 chunk incremental path (~44s).
2114
+ incrementalSince: lastConsolidateTs,
2121
2115
  maxChunkSize: improveProfile?.processes?.consolidate?.maxChunkSize,
2122
2116
  // Honor profile.autoAccept (already merged into options.autoAccept at the
2123
2117
  // top of akmImprove). The CLI parser always supplies 90 when --auto-accept
@@ -2287,25 +2281,25 @@ async function runImproveMaintenancePasses(args) {
2287
2281
  }
2288
2282
  const graphEnabled = isProcessEnabled("index", "graph_extraction", config);
2289
2283
  const graphExtractionDisabledByProfile = improveProfile?.processes?.graphExtraction?.enabled === false;
2284
+ const graphExtractionFullScan = improveProfile?.processes?.graphExtraction?.fullScan === true;
2290
2285
  // Build the set of refs actually touched this run.
2291
2286
  const touchedRefs = new Set();
2292
2287
  for (const r of args.actionableRefs)
2293
2288
  touchedRefs.add(r.ref);
2294
2289
  for (const r of memoryRefsForInference)
2295
2290
  touchedRefs.add(r);
2296
- // INVARIANT: graph extraction must never run on the full corpus from the
2297
- // improve post-loop. Full-corpus scans belong in `akm index`. We enforce
2298
- // this by ALWAYS passing `candidatePaths` (possibly an empty Set) to the
2299
- // extractor never `undefined`. With an empty Set, the extractor's
2300
- // filter (graph-extraction.ts ~L452) rejects every file and returns the
2301
- // empty result without scanning. The pass is still invoked so that the
2302
- // action is recorded, the D9 post-consolidation reindex still fires, and
2303
- // mock injection (graphExtractionFn) used by tests stays exercised.
2291
+ // INVARIANT: graph extraction normally runs only on files touched by
2292
+ // actionable refs (candidatePaths). Full-corpus scans are opt-in via
2293
+ // profile.processes.graphExtraction.fullScan = true (used by the
2294
+ // `graph-refresh` built-in profile and its weekly scheduled task).
2295
+ // The empty-Set fallback is intentional when no refs were touched —
2296
+ // the extractor's filter rejects every file and returns empty, keeping
2297
+ // the pass invoked so the action is recorded and tests stay exercised.
2304
2298
  if (graphExtractionDisabledByProfile) {
2305
2299
  info("[improve] graph extraction skipped (disabled by improve profile)");
2306
2300
  }
2307
2301
  else if (sources.length > 0 && graphEnabled) {
2308
- info("[improve] graph extraction starting");
2302
+ info(`[improve] graph extraction starting${graphExtractionFullScan ? " (full-corpus scan)" : ""}`);
2309
2303
  const extractionStart = Date.now();
2310
2304
  try {
2311
2305
  // D9: if consolidation ran but memory inference did not reindex, force a reindex
@@ -2325,15 +2319,18 @@ async function runImproveMaintenancePasses(args) {
2325
2319
  closeDatabase(db);
2326
2320
  db = openDatabase(getDbPath(), config.embedding?.dimension ? { embeddingDim: config.embedding.dimension } : undefined);
2327
2321
  }
2328
- // Resolve touched refs to absolute file paths. Empty Set is intentional
2329
- // when no refs were touched see INVARIANT above.
2330
- const candidatePaths = new Set();
2331
- if (primaryStashDir && touchedRefs.size > 0) {
2332
- const writableDirSet = new Set(getWritableStashDirs(primaryStashDir).map((d) => path.resolve(d)));
2333
- const resolved = await Promise.all([...touchedRefs].map((ref) => findAssetFilePath(ref, primaryStashDir, writableDirSet).catch(() => null)));
2334
- for (const p of resolved) {
2335
- if (typeof p === "string" && p.length > 0)
2336
- candidatePaths.add(p);
2322
+ // Resolve touched refs to absolute file paths. Skipped for fullScan
2323
+ // (candidatePaths stays undefined extractor processes all files).
2324
+ let candidatePaths;
2325
+ if (!graphExtractionFullScan) {
2326
+ candidatePaths = new Set();
2327
+ if (primaryStashDir && touchedRefs.size > 0) {
2328
+ const writableDirSet = new Set(getWritableStashDirs(primaryStashDir).map((d) => path.resolve(d)));
2329
+ const resolved = await Promise.all([...touchedRefs].map((ref) => findAssetFilePath(ref, primaryStashDir, writableDirSet).catch(() => null)));
2330
+ for (const p of resolved) {
2331
+ if (typeof p === "string" && p.length > 0)
2332
+ candidatePaths.add(p);
2333
+ }
2337
2334
  }
2338
2335
  }
2339
2336
  const progressHandler = (event) => {
@@ -46,8 +46,11 @@ export function assembleInfo(options) {
46
46
  ...(s.url ? { url: s.url } : {}),
47
47
  ...(s.enabled !== undefined ? { enabled: s.enabled } : {}),
48
48
  }));
49
- // Index stats
50
- const indexStats = readIndexStats(options?.dbPath);
49
+ // Index stats — resolve the DB path from config so info reads the same
50
+ // database that health and search use, rather than a bare getDbPath() call
51
+ // that ignores XDG_DATA_HOME or per-config overrides.
52
+ const resolvedDbPath = options?.dbPath ?? getDbPath();
53
+ const indexStats = readIndexStats(resolvedDbPath);
51
54
  return {
52
55
  schemaVersion: 1,
53
56
  version: pkgVersion,
@@ -64,38 +67,30 @@ export function assembleInfo(options) {
64
67
  indexStats,
65
68
  };
66
69
  }
67
- function readIndexStats(dbPath) {
68
- const resolvedPath = dbPath ?? getDbPath();
69
- // If no index file exists, return zeros
70
- if (!fs.existsSync(resolvedPath)) {
71
- return {
72
- entryCount: 0,
73
- lastBuiltAt: null,
74
- hasEmbeddings: false,
75
- vecAvailable: false,
76
- };
77
- }
70
+ function readIndexStats(resolvedPath) {
71
+ const EMPTY = {
72
+ entryCount: 0,
73
+ lastBuiltAt: null,
74
+ hasEmbeddings: false,
75
+ vecAvailable: false,
76
+ };
77
+ if (!fs.existsSync(resolvedPath))
78
+ return EMPTY;
78
79
  let db;
79
80
  try {
80
81
  db = openExistingDatabase(resolvedPath);
81
- const entryCount = getEntryCount(db);
82
- const lastBuiltAt = getMeta(db, "builtAt") ?? null;
83
- const vecAvailable = isVecAvailable(db);
84
- const hasEmbeddings = getMeta(db, "hasEmbeddings") === "1";
85
82
  return {
86
- entryCount,
87
- lastBuiltAt,
88
- hasEmbeddings,
89
- vecAvailable,
83
+ entryCount: getEntryCount(db),
84
+ lastBuiltAt: getMeta(db, "builtAt") ?? null,
85
+ hasEmbeddings: getMeta(db, "hasEmbeddings") === "1",
86
+ vecAvailable: isVecAvailable(db),
90
87
  };
91
88
  }
92
- catch {
93
- return {
94
- entryCount: 0,
95
- lastBuiltAt: null,
96
- hasEmbeddings: false,
97
- vecAvailable: false,
98
- };
89
+ catch (err) {
90
+ // Surface the error so operators can diagnose mismatches between
91
+ // `akm info` and `akm health` rather than silently returning zeros.
92
+ process.stderr.write(`[akm info] failed to read index stats from ${resolvedPath}: ${String(err)}\n`);
93
+ return EMPTY;
99
94
  }
100
95
  finally {
101
96
  if (db) {
@@ -322,8 +322,8 @@ export async function akmIndex(options) {
322
322
  totalMs: Date.now() - timing.t0,
323
323
  walkMs: timing.tWalkEnd - timing.tWalkStart,
324
324
  llmMs: timing.tLlmEnd - timing.tWalkEnd,
325
- embedMs: timing.tEmbedEnd - timing.tFtsEnd,
326
- ftsMs: timing.tFtsEnd - timing.tLlmEnd,
325
+ embedMs: timing.tEmbedEnd - timing.tLlmEnd,
326
+ ftsMs: timing.tFtsEnd - timing.tEmbedEnd,
327
327
  },
328
328
  ...(cleanResult !== undefined ? { clean: cleanResult } : {}),
329
329
  };
@@ -20,11 +20,11 @@
20
20
  * the connection via `resolveIndexPassLLM("graph", config)` and pass it
21
21
  * straight through.
22
22
  */
23
+ import userPromptTemplate from "../assets/prompts/graph-extract-user-prompt.md" with { type: "text" };
23
24
  import { toErrorMessage } from "../core/common";
24
25
  import { warn, warnVerbose } from "../core/warn";
25
26
  import { chatCompletion, parseEmbeddedJsonResponse } from "./client";
26
27
  import { tryLlmFeature } from "./feature-gate";
27
- import userPromptTemplate from "./prompts/graph-extract-user-prompt.md" with { type: "text" };
28
28
  /**
29
29
  * Separator token used between assets in a batch prompt.
30
30
  * Chosen to be visually clear and unlikely to appear verbatim in asset bodies.
@@ -10,6 +10,6 @@
10
10
  * `EMBEDDED_HINTS` (`--detail brief`, short reference, ~40 lines) and
11
11
  * `EMBEDDED_HINTS_FULL` (`--detail normal|full`, ~250 lines).
12
12
  */
13
- import EMBEDDED_HINTS_FULL from "./cli-hints-full.md" with { type: "text" };
14
- import EMBEDDED_HINTS from "./cli-hints-short.md" with { type: "text" };
13
+ import EMBEDDED_HINTS_FULL from "../assets/hints/cli-hints-full.md" with { type: "text" };
14
+ import EMBEDDED_HINTS from "../assets/hints/cli-hints-short.md" with { type: "text" };
15
15
  export { EMBEDDED_HINTS, EMBEDDED_HINTS_FULL };
@@ -24,12 +24,12 @@
24
24
  import fs from "node:fs";
25
25
  import os from "node:os";
26
26
  import path from "node:path";
27
+ import launchdTemplate from "../../assets/backends/launchd-template.xml" with { type: "text" };
27
28
  import { ConfigError } from "../../core/errors";
28
29
  import { getTaskLogDir } from "../../core/paths";
29
30
  import { resolveAkmInvocation } from "../resolveAkmBin";
30
31
  import { parseSchedule, translateToLaunchd } from "../schedule";
31
32
  import { escapeXml, spawnCommand } from "./exec-utils";
32
- import launchdTemplate from "./launchd-template.xml" with { type: "text" };
33
33
  export const LAUNCHD_LABEL_PREFIX = "com.akm.task.";
34
34
  export function LAUNCHD_BACKEND(options = {}) {
35
35
  const exec = options.exec ?? defaultLaunchdExec();
@@ -32,12 +32,12 @@
32
32
  import fs from "node:fs";
33
33
  import os from "node:os";
34
34
  import path from "node:path";
35
+ import schtasksTemplate from "../../assets/backends/schtasks-template.xml" with { type: "text" };
35
36
  import { ConfigError } from "../../core/errors";
36
37
  import { getTaskLogDir } from "../../core/paths";
37
38
  import { resolveAkmInvocation } from "../resolveAkmBin";
38
39
  import { parseSchedule, translateToSchtasks } from "../schedule";
39
40
  import { escapeXml, spawnCommand } from "./exec-utils";
40
- import schtasksTemplate from "./schtasks-template.xml" with { type: "text" };
41
41
  export const DEFAULT_FOLDER_PREFIX = "\\akm\\";
42
42
  export function SCHTASKS_BACKEND(options = {}) {
43
43
  const exec = options.exec ?? defaultSchtasksExec();
@@ -1,9 +1,9 @@
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 indexTemplate from "./index-template.md" with { type: "text" };
5
- import logTemplate from "./log-template.md" with { type: "text" };
6
- import schemaTemplate from "./schema-template.md" with { type: "text" };
4
+ import indexTemplate from "../assets/wiki/index-template.md" with { type: "text" };
5
+ import logTemplate from "../assets/wiki/log-template.md" with { type: "text" };
6
+ import schemaTemplate from "../assets/wiki/schema-template.md" with { type: "text" };
7
7
  export function buildSchemaMd(wikiName) {
8
8
  return schemaTemplate.replaceAll("{{WIKI_NAME}}", wikiName);
9
9
  }
package/dist/wiki/wiki.js CHANGED
@@ -44,13 +44,13 @@
44
44
  import fs from "node:fs";
45
45
  import path from "node:path";
46
46
  import { parse as yamlParse } from "yaml";
47
+ import ingestWorkflowTemplate from "../assets/wiki/ingest-workflow-template.md" with { type: "text" };
47
48
  import { akmSearch } from "../commands/search";
48
49
  import { isWithin, todayIso } from "../core/common";
49
50
  import { getSources, loadUserConfig, saveConfig } from "../core/config";
50
51
  import { NotFoundError, UsageError } from "../core/errors";
51
52
  import { parseFrontmatter, parseFrontmatterBlock } from "../core/frontmatter";
52
53
  import { resolveSourceEntries } from "../indexer/search-source";
53
- import ingestWorkflowTemplate from "./ingest-workflow-template.md" with { type: "text" };
54
54
  import { buildIndexMd, buildLogMd, buildSchemaMd } from "./wiki-templates";
55
55
  // ── Constants ───────────────────────────────────────────────────────────────
56
56
  export const WIKIS_SUBDIR = "wikis";
@@ -3,12 +3,12 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
+ import workflowTemplate from "../assets/workflows/workflow-template.md" with { type: "text" };
6
7
  import { resolveAssetPathFromName } from "../core/asset-spec";
7
8
  import { isWithin, resolveStashDir } from "../core/common";
8
9
  import { UsageError } from "../core/errors";
9
10
  import { warn } from "../core/warn";
10
11
  import { parseWorkflow } from "./parser";
11
- import workflowTemplate from "./workflow-template.md" with { type: "text" };
12
12
  const DEFAULT_WORKFLOW_TEMPLATE = renderWorkflowTemplate({
13
13
  title: "Example Workflow",
14
14
  firstStepTitle: "First Step",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "description": "akm (Agent Knowledge Management) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
6
6
  "keywords": [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes