akm-cli 0.9.0-beta.11 → 0.9.0-beta.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +163 -0
- package/dist/assets/prompts/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +5 -1
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/templates/html/health.html +25 -27
- package/dist/cli.js +2 -2
- package/dist/commands/agent/contribute-cli.js +16 -3
- package/dist/commands/feedback-cli.js +48 -44
- package/dist/commands/health/html-report.js +140 -16
- package/dist/commands/health.js +277 -1
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/consolidate.js +595 -105
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill.js +119 -64
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +115 -1
- package/dist/commands/improve/extract-prompt.js +32 -1
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +210 -30
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/homeostatic.js +467 -0
- package/dist/commands/improve/improve-auto-accept.js +80 -7
- package/dist/commands/improve/improve-profiles.js +8 -0
- package/dist/commands/improve/improve.js +991 -61
- package/dist/commands/improve/memory/memory-contradiction-detect.js +23 -28
- package/dist/commands/improve/outcome-loop.js +256 -0
- package/dist/commands/improve/proactive-maintenance.js +9 -35
- package/dist/commands/improve/procedural.js +409 -0
- package/dist/commands/improve/recombine.js +488 -0
- package/dist/commands/improve/reflect.js +20 -1
- package/dist/commands/improve/related-sessions.js +120 -0
- package/dist/commands/improve/salience.js +386 -0
- package/dist/commands/improve/triage.js +95 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +34 -1
- package/dist/commands/lint/index.js +30 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +5 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/proposal/validators/proposals.js +4 -0
- package/dist/commands/read/curate.js +284 -86
- package/dist/commands/read/search-cli.js +7 -0
- package/dist/commands/read/search.js +1 -0
- package/dist/commands/sources/installed-stashes.js +5 -1
- package/dist/core/asset/frontmatter.js +166 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/config/config-schema.js +211 -3
- package/dist/core/config/config.js +2 -2
- package/dist/core/logs-db.js +4 -3
- package/dist/core/state-db.js +555 -29
- package/dist/indexer/db/db.js +250 -27
- package/dist/indexer/db/graph-db.js +81 -86
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/passes/memory-inference.js +10 -3
- package/dist/indexer/passes/staleness-detect.js +2 -5
- package/dist/indexer/search/db-search.js +15 -4
- package/dist/indexer/search/ranking.js +4 -0
- package/dist/integrations/harnesses/claude/session-log.js +10 -0
- package/dist/integrations/harnesses/opencode/session-log.js +9 -0
- package/dist/integrations/session-logs/index.js +16 -0
- package/dist/llm/embedder.js +27 -3
- package/dist/llm/embedders/local.js +66 -2
- package/dist/llm/graph-extract.js +2 -1
- package/dist/llm/memory-infer.js +4 -8
- package/dist/llm/metadata-enhance.js +9 -1
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/text/helpers.js +9 -0
- package/dist/runtime.js +25 -1
- package/dist/scripts/migrate-storage.js +1025 -567
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +435 -269
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/workflows/db.js +3 -4
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/data-and-telemetry.md +1 -0
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,169 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.9.0-beta.26] — 2026-06-20
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **#628 — configurable SQLite journal mode (`AKM_SQLITE_JOURNAL_MODE`) for network
|
|
14
|
+
filesystems.** AKM previously opened every database with `PRAGMA journal_mode = WAL`
|
|
15
|
+
unconditionally, which cannot run on a network filesystem (NFS/SMB/Azure Files) —
|
|
16
|
+
WAL's `-shm` shared-memory wal-index can't be `mmap`'d over a network mount. You can
|
|
17
|
+
now set `AKM_SQLITE_JOURNAL_MODE` to `WAL` (default), `DELETE`, or `TRUNCATE`, applied
|
|
18
|
+
at **all five** db openers (`state.db`, `index.db` ×2 paths, `workflow.db`, `logs.db`).
|
|
19
|
+
At the `WAL` default AKM auto-detects a network mount for the data dir and transparently
|
|
20
|
+
falls back to `DELETE` (rollback journal + `synchronous = FULL`) with a one-line warning;
|
|
21
|
+
invalid values warn once and fall back to `WAL`. **Default behavior is byte-identical.**
|
|
22
|
+
This lets the AKM database subtree live on a shared volume (e.g. Azure Files under
|
|
23
|
+
Azure Container Apps). New docs section "Hosting AKM databases on a network share
|
|
24
|
+
(NFS/SMB)" in `docs/configuration.md`.
|
|
25
|
+
|
|
26
|
+
## [0.9.0-beta.25] — 2026-06-19
|
|
27
|
+
|
|
28
|
+
Completes the recombine / extract-efficiency / graph thread. All new improve
|
|
29
|
+
passes are **opt-in (default off)**, so default behavior is unchanged.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **#606 — event-driven extract (`akm extract --watch`).** Opt-in watch mode: an
|
|
34
|
+
injectable, debounced watcher triggers extraction shortly after a session file
|
|
35
|
+
appears, with a clean `stop()` handle. The `8,28,48` cron remains the fallback;
|
|
36
|
+
no daemon is auto-launched.
|
|
37
|
+
- **#625 — recombine second pass (hypothesis → lesson).** The opt-in `recombine`
|
|
38
|
+
process (#609) now consumes `confirmThreshold` (default 2): a generalization
|
|
39
|
+
re-induced that many consecutive runs is promoted from a `type: hypothesis`
|
|
40
|
+
proposal to a `type: lesson` proposal through the normal queue + quality gate
|
|
41
|
+
(never a direct stash write). Hypotheses that stop recurring decay. Backed by a
|
|
42
|
+
new `recombine_hypotheses` table in `state.db`.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **#624 (P1) — graph storage decoupled from `entries.id`.** `graph_files` is
|
|
47
|
+
re-keyed on `(stash_root, file_path, body_hash)`, so extracted graph data now
|
|
48
|
+
**survives a reindex** of unchanged files instead of being cascade-wiped. The
|
|
49
|
+
upgrade is migrated in a **targeted, graph-only path** that preserves existing
|
|
50
|
+
graph data and leaves the entry index, embeddings, FTS, and LLM-enrichment cache
|
|
51
|
+
untouched — **no full index rebuild and no re-embed** on upgrade. (P2 priority-
|
|
52
|
+
ranked extraction and P3 lazy/on-demand extraction remain deferred.)
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- Graph re-key migration no longer triggers a destructive full-index rebuild: it
|
|
57
|
+
is a graph-scoped table migration (no `DB_VERSION` bump), and it **copies** the
|
|
58
|
+
existing graph rows into the new schema rather than dropping them.
|
|
59
|
+
- Test-suite `/tmp` hygiene: sandbox teardown now fires on `SIGINT`/`SIGTERM`/
|
|
60
|
+
`SIGHUP` (not just clean exit), and a `sweep:tmp` step reclaims stale `akm-*`
|
|
61
|
+
sandbox dirs left by force-killed workers — eliminating the tmpfs accumulation
|
|
62
|
+
that caused intermittent `EEXIST: epoll_ctl` test flakes.
|
|
63
|
+
|
|
64
|
+
## [0.9.0-beta.20] — 2026-06-18
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
|
|
68
|
+
- **`akm update --all` no longer fails for writable `github:` entries stored as `source:"git"`**. `updateRegistryEntry` was using `synced.source` (re-derived from the ref scheme as `"github"`) instead of the existing `entry.source`, causing the config validator to reject `writable:true` on every update cycle.
|
|
69
|
+
|
|
70
|
+
## [0.9.0-beta.19] — 2026-06-17
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- **`akm feedback` now completes in ~0.3s** (was 3+ minutes). Root cause: the command was calling `ensureIndex` with `mode: "blocking"` inside `withIndexWriterLease`, triggering a full reindex on every feedback call. Fix: removed the `ensureIndex` call entirely (feedback only needs the index to exist, not be current — a stale index is fine for ref lookup); removed the application-level writer lock (SQLite WAL + `busy_timeout=30s` handles concurrent access with `akm improve`); added a fast DB-exists guard with a clear error for first-time users.
|
|
75
|
+
- **`akm health --format html` now completes in ~11s** (was ~18s). Root cause: `akmHealth()` was called twice — once for the main result and once to get `deltas`. Fix: merged into a single call passing both `groupBy: "run"` and `windowCompare` together.
|
|
76
|
+
|
|
77
|
+
## [0.9.0-beta.18] — 2026-06-17
|
|
78
|
+
|
|
79
|
+
### Changed
|
|
80
|
+
|
|
81
|
+
- **Health report: Recent Runs table now shows all filtered runs in descending order** (newest first) instead of capping at the last 10.
|
|
82
|
+
- **Health report: Removed "Command Set Used" section.**
|
|
83
|
+
- **Health report: All timestamps now display in the viewer's local timezone** (chart axis labels, runs table, freshness line, executive summary, footer). Server-rendered ISO strings are wrapped in `<time data-iso>` elements and converted to local time by client-side JS on page load.
|
|
84
|
+
|
|
85
|
+
### Changed (migration required)
|
|
86
|
+
|
|
87
|
+
- **WS-2 outcome loop (#613) — default-off weight change (state.db migration 010).**
|
|
88
|
+
Every `akm improve` run now writes an `asset_outcome` row per processed asset
|
|
89
|
+
(state.db migration `010`) and computes a differential usefulness signal
|
|
90
|
+
(`outcome_score`) per ref. The outcome signal is persisted and visible in the
|
|
91
|
+
health report, but the **weight change is gated behind a config flag** (see
|
|
92
|
+
below). Ranking is unchanged from WS-1 by default.
|
|
93
|
+
|
|
94
|
+
**Opt-in weight change.** The WS-2 projection weights (`w_e=0.25, w_o=0.15,
|
|
95
|
+
w_r=0.60`) affect ranking only when you explicitly set
|
|
96
|
+
`improve.salience.outcomeWeightEnabled: true` in your `akm.yaml`. The default
|
|
97
|
+
(`false`) keeps WS-1 parity weights (`w_e=0.30, w_r=0.70`, `w_o=0`), so
|
|
98
|
+
existing users see no ranking change on upgrade.
|
|
99
|
+
|
|
100
|
+
**Part-V measurement gate.** Before enabling the weight change, run the Part-V
|
|
101
|
+
T0 baseline (`scripts/akm-eval` + `akm health`; confirm proactive accept
|
|
102
|
+
≥ 0.9× reactive; reversion ≤ 0.15; retrieval-delta ≥ 0; coverage not
|
|
103
|
+
regressed). That gate requires a running production stash and cannot be
|
|
104
|
+
exercised in CI. Once confirmed, set
|
|
105
|
+
`improve.salience.outcomeWeightEnabled: true` to activate the three-way split.
|
|
106
|
+
|
|
107
|
+
**Outcome loop mechanics.** `outcome_score` is a differential prediction-error
|
|
108
|
+
signal: `(retrieval_delta − expected_delta) − PENALTY × retrieval_delta × (1 −
|
|
109
|
+
accepted_change_rate) + valence`, tracked via an EMA (α=0.3). New rows are
|
|
110
|
+
warm-started from the utility EMA score (clipped to 0.3) so the signal is
|
|
111
|
+
non-zero from launch. A stash-wide diversity floor (10% of the max score) prevents
|
|
112
|
+
rare-but-correct assets from being permanently outcompeted. An inverted-proxy
|
|
113
|
+
tripwire (`corr(outcome_score, accepted_change_rate) < −0.3`) emits an
|
|
114
|
+
`outcome_proxy_inverted` health event when the signal degrades.
|
|
115
|
+
|
|
116
|
+
`review_pressure` is computed and persisted per asset but is **not yet wired into
|
|
117
|
+
the admission policy** — that is deferred to a later work stream per plan §Part-VI
|
|
118
|
+
#613. The column is present and populated; routing it into the consolidation-
|
|
119
|
+
selection filter is the next step.
|
|
120
|
+
|
|
121
|
+
- **WS-1 salience vector (#618) — default-on ranking change.** The eligibility sort
|
|
122
|
+
for all `akm improve` runs (whole-stash, type, and ref scope) has changed from
|
|
123
|
+
`combinedEligibilityScore = utility·0.7 + negativeOnlyRatio·0.3` to
|
|
124
|
+
`rankScore = (0.3·encodingSalience + 0.7·retrievalSalience) × sizePenalty`
|
|
125
|
+
(feedback valence and utility EMA dropped from ordering until WS-2 re-introduces
|
|
126
|
+
outcome salience). Assets are now ranked by retrieval frequency × recency × type
|
|
127
|
+
importance rather than by feedback magnitude. Because the old
|
|
128
|
+
`combinedEligibilityScore` ordering was never persisted, a forgetting comparison is
|
|
129
|
+
not possible on the first run; instead a one-time `improve_salience_first_run` marker
|
|
130
|
+
event is emitted to record the transition. On every subsequent run a stash-wide
|
|
131
|
+
`improve_salience_rank_change` drift report (including `stashSize`) is emitted so
|
|
132
|
+
rank movement under the new scoring can be tracked over time.
|
|
133
|
+
The Part-V measurement protocol (T0 baseline via `scripts/akm-eval` + health report,
|
|
134
|
+
throughput/quality gate) is deferred to the WS-2 milestone, when outcome salience
|
|
135
|
+
re-joins the projection and re-tuning is triggered.
|
|
136
|
+
|
|
137
|
+
## [0.9.0-beta.12] - 2026-06-15
|
|
138
|
+
|
|
139
|
+
Improve-tuning work streams (all **default-off / parity-preserving** — no behavior
|
|
140
|
+
change until explicitly enabled).
|
|
141
|
+
|
|
142
|
+
### Added
|
|
143
|
+
|
|
144
|
+
- **#617 — deterministic near-duplicate memory dedup** (`processes.consolidate.dedup`,
|
|
145
|
+
default off). A cheap no-LLM pre-pass in front of consolidation collapses obvious
|
|
146
|
+
duplicates — `.derived`+origin pairs and content twins (normalized content-hash
|
|
147
|
+
equality, or embedding cosine ≥ `cosineThreshold`, default 0.97). Each dropped
|
|
148
|
+
variant is archived + backed up before deletion; hot memories are never
|
|
149
|
+
collapsed; distinct-but-related memories fall through to the LLM.
|
|
150
|
+
- **#581 — judged-state cache for consolidation** (`processes.consolidate.judgedCache`,
|
|
151
|
+
default off). New state.db table (`consolidation_judged`) records each memory's
|
|
152
|
+
content hash + outcome when the LLM judges it; subsequent runs skip
|
|
153
|
+
judged-unchanged memories, converting coverage from O(time-window) to
|
|
154
|
+
O(changed/new) so a run can sweep the full corpus. Fails open; failed chunks
|
|
155
|
+
and dry-runs never poison the cache. (state.db migration `007`.)
|
|
156
|
+
- **#612 — auto-accept gate calibration** (`improve.calibration`, auto-tune default
|
|
157
|
+
off). Joins predicted gate confidence to realized accept/reject outcomes into a
|
|
158
|
+
reliability table + calibration gap, surfaced in `akm health` (+ summary rows in
|
|
159
|
+
the HTML report). Opt-in bounded threshold auto-tune nudges the accept threshold
|
|
160
|
+
within a configured band toward a target accept rate, logged via a
|
|
161
|
+
`calibration_autotune` event. (Replay-prioritization from prediction error is
|
|
162
|
+
deferred — it depends on the #610 replay budget, a 0.10 item.)
|
|
163
|
+
|
|
164
|
+
### Fixed
|
|
165
|
+
|
|
166
|
+
- **#614 — symmetric valence weighting** (`profiles.improve.*.symmetricValence`,
|
|
167
|
+
default off). The eligibility sort weighted feedback negative-only; when enabled
|
|
168
|
+
it uses a symmetric `|valence|` magnitude so strong positive and strong negative
|
|
169
|
+
feedback both drive attention (utility stays the dominant factor), routing
|
|
170
|
+
high-negative → fix and high-positive → reinforce lanes.
|
|
171
|
+
|
|
9
172
|
## [0.9.0-beta.11] - 2026-06-15
|
|
10
173
|
|
|
11
174
|
### Added
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
You are the akm consolidate assistant analyzing memory assets.
|
|
2
|
+
|
|
3
|
+
Rules:
|
|
4
|
+
1. MERGE: Two or more memories are substantially duplicated or closely related → propose merging. Return the primary ref to keep and secondary refs to delete. Do NOT include mergedContent — the merge will be executed in a separate step.
|
|
5
|
+
2. DELETE: Memory is clearly outdated, contradicted, or redundant → propose deletion. NEVER propose delete for memories annotated `(captureMode: hot)` — they are user-explicit and only the user can retire them. The downstream guard will refuse these regardless, so proposing them just wastes tokens.
|
|
6
|
+
3. PROMOTE: Memory expresses a stable, reusable fact suitable as a `knowledge:` asset → propose promotion. Do NOT delete the source memory. NEVER propose promote / merge / contradict for memories annotated `(already queued)` — they have a pending proposal whose body matches; a duplicate will be deterministically dropped, so proposing them just wastes tokens.
|
|
7
|
+
4. CONTRADICT: Two memories assert logically exclusive facts such that following BOTH simultaneously is impossible — not merely related or overlapping. You MUST cite the exact sentence from Memory A and the exact sentence from Memory B that are in direct conflict. If you cannot cite specific opposing sentences, use KEEP instead. Sharing a topic, tool, domain, or workflow stage is NOT sufficient. Only direct factual opposites qualify: opposing recommended commands, opposing boolean flags, opposing version numbers, or mutually exclusive instructions. Use confidence ≥ 0.92 only; omit the op entirely if below that threshold.
|
|
8
|
+
5. KEEP: Memory is unique and current → omit from output.
|
|
9
|
+
|
|
10
|
+
Return ONLY JSON (no prose, no code fences):
|
|
11
|
+
{
|
|
12
|
+
"operations": [
|
|
13
|
+
{ "op": "merge", "primary": "memory:<name>", "secondaries": ["memory:<name>", ...], "mergeStrategy": "synthesize", "confidence": 0.95 },
|
|
14
|
+
{ "op": "delete", "ref": "memory:<name>", "reason": "<brief reason>", "confidence": 0.90 },
|
|
15
|
+
{ "op": "promote", "ref": "memory:<name>", "knowledgeRef": "knowledge:<suggested-slug>", "reason": "<brief reason>", "description": "<one sentence describing the new knowledge asset>", "confidence": 0.92 },
|
|
16
|
+
{ "op": "contradict", "ref": "memory:<name>", "contradictedByRef": "memory:<name>", "reason": "<brief reason>", "confidence": 0.88 }
|
|
17
|
+
],
|
|
18
|
+
"warnings": ["<optional concerns>"]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
For every operation, emit a `confidence` field in [0, 1] expressing your certainty that the operation is correct and safe. Use 0.95+ only when evidence is unambiguous. Omit the field rather than guessing if you are uncertain.
|
|
22
|
+
|
|
23
|
+
When the merged content includes an `updated` frontmatter field, the value MUST be a real ISO date string (e.g. `updated: 2026-05-20`). NEVER emit `updated: today`, `updated: {today}`, `updated: {today: null}`, `updated: now`, or any other literal placeholder/template-variable. If you do not have a real source-of-truth date, OMIT the `updated` field entirely — the post-processor will not invent one for you.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
You are evaluating two derived memory entries to determine if they contain
|
|
2
|
+
directly contradictory factual claims about the same subject.
|
|
3
|
+
|
|
4
|
+
Memory A:
|
|
5
|
+
Ref: {{A_REF}}
|
|
6
|
+
Description: {{A_DESCRIPTION}}
|
|
7
|
+
Content:
|
|
8
|
+
```
|
|
9
|
+
{{A_BODY}}
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Memory B:
|
|
13
|
+
Ref: {{B_REF}}
|
|
14
|
+
Description: {{B_DESCRIPTION}}
|
|
15
|
+
Content:
|
|
16
|
+
```
|
|
17
|
+
{{B_BODY}}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Answer ONLY with valid JSON — no prose, no code fences:
|
|
21
|
+
{"contradicts": true|false, "confidence": 0.0, "reason": "<cite the exact opposing sentence from each memory, or explain why not contradicted>"}
|
|
22
|
+
|
|
23
|
+
A contradiction means the memories make LOGICALLY EXCLUSIVE claims: a practitioner
|
|
24
|
+
cannot follow BOTH simultaneously. The test: cite the exact sentence from Memory A
|
|
25
|
+
and the exact sentence from Memory B that are in direct conflict. If you cannot cite
|
|
26
|
+
specific opposing sentences, return false.
|
|
27
|
+
|
|
28
|
+
Sharing a topic, tool, domain, or workflow stage is NOT a contradiction. Only direct
|
|
29
|
+
factual opposites qualify: opposing recommended commands, opposing boolean flags,
|
|
30
|
+
opposing version numbers, or mutually exclusive instructions.
|
|
31
|
+
|
|
32
|
+
Set confidence ≥ 0.92 only when evidence is unambiguous. Use lower values when
|
|
33
|
+
uncertain — the caller will skip edges below 0.92.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
You are the akm `distill` distiller.
|
|
2
|
+
Given an asset and recent feedback events about it, produce a concise
|
|
3
|
+
*knowledge* markdown document capturing the durable, reusable facts.
|
|
4
|
+
Prefer stable guidance over narrative recap.
|
|
5
|
+
|
|
6
|
+
YOUR RESPONSE MUST START EXACTLY WITH `---` ON THE VERY FIRST LINE.
|
|
7
|
+
DO NOT output any prose, explanation, or code fences before or after.
|
|
8
|
+
|
|
9
|
+
Required output format:
|
|
10
|
+
---
|
|
11
|
+
description: <one-line summary of the knowledge asset>
|
|
12
|
+
tags: [<tag1>, <tag2>]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# <Title>
|
|
16
|
+
|
|
17
|
+
<body — structured markdown, durable facts only>
|
|
18
|
+
|
|
19
|
+
RULES:
|
|
20
|
+
- `description` MUST be a non-empty single-line string.
|
|
21
|
+
- Include a meaningful markdown body with a `# Title` heading.
|
|
22
|
+
- Output ONLY the knowledge file. No preamble, no code fences, no trailing prose.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
You are the akm `distill` distiller.
|
|
2
|
+
Given an asset and recent feedback events about it, produce a single
|
|
3
|
+
concise *lesson* an agent should remember next time it works on this
|
|
4
|
+
asset's domain.
|
|
5
|
+
|
|
6
|
+
YOUR RESPONSE MUST START EXACTLY WITH `---` ON THE VERY FIRST LINE.
|
|
7
|
+
DO NOT output any prose, explanation, or code fences before or after.
|
|
8
|
+
|
|
9
|
+
Required output format — copy this structure exactly:
|
|
10
|
+
---
|
|
11
|
+
description: <one complete sentence (ending with `.`) summarising what the lesson teaches>
|
|
12
|
+
when_to_use: <one complete sentence describing the concrete trigger condition>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<lesson body — plain markdown, 1–3 short paragraphs of practical guidance>
|
|
16
|
+
|
|
17
|
+
## description field (MANDATORY)
|
|
18
|
+
- A single complete sentence in present tense, 80-200 chars, NO markdown.
|
|
19
|
+
- Self-contained: a reviewer must understand the lesson from this field alone.
|
|
20
|
+
- DO NOT start with "When ", "If ", or a connector word — that belongs in when_to_use.
|
|
21
|
+
- DO NOT copy a section heading ("Key takeaways", "For example", "Key pitfalls").
|
|
22
|
+
- DO NOT begin with a numbered list marker, code fence, or markdown heading.
|
|
23
|
+
|
|
24
|
+
GOOD: "Always validate ref existence before promoting a memory to knowledge; missing refs surface as silent 404s during accept."
|
|
25
|
+
BAD: "Key pitfalls"
|
|
26
|
+
BAD: "When working with the akm CLI"
|
|
27
|
+
BAD: "For example, you might..."
|
|
28
|
+
BAD: "1. Check the file"
|
|
29
|
+
|
|
30
|
+
RULES:
|
|
31
|
+
- `when_to_use` MUST be a complete sentence describing a concrete trigger. Never write `When working with <asset-name>` — that is circular and useless.
|
|
32
|
+
- `description` and `when_to_use` MUST differ from each other.
|
|
33
|
+
- The lesson body MUST be non-empty markdown prose. Do NOT restate `description:` or `when_to_use:` inside the body (no `**description:** ...` or `**when_to_use:** ...` lines — the frontmatter is the only place those keys belong).
|
|
34
|
+
- Do NOT emit a second `---` fence after the opening frontmatter — there are exactly two `---` lines in the output, both belonging to the single frontmatter block at the top.
|
|
35
|
+
- Do NOT reproduce the source asset verbatim — distil what a caller needs to know.
|
|
36
|
+
- Output ONLY the lesson file. No preamble, no code fences, no trailing prose.
|
|
@@ -49,13 +49,17 @@ Respond with EXACTLY one JSON object matching this shape:
|
|
|
49
49
|
"when_to_use": "<one sentence 15-400 chars; REQUIRED only when type=lesson>",
|
|
50
50
|
"body": "<markdown body, 200-3000 chars typical>",
|
|
51
51
|
"confidence": <number 0.0-1.0>,
|
|
52
|
-
"evidence": "<one-line pointer to the moment in the session>"
|
|
52
|
+
"evidence": "<one-line pointer to the moment in the session>",
|
|
53
|
+
"orderedActions": ["<action-1>", "<action-2>", ...],
|
|
54
|
+
"outcomeData": "<one sentence describing the outcome of the action sequence>"
|
|
53
55
|
}
|
|
54
56
|
],
|
|
55
57
|
"rationale_if_empty": "<one sentence; REQUIRED when candidates is empty>"
|
|
56
58
|
}
|
|
57
59
|
```
|
|
58
60
|
|
|
61
|
+
`orderedActions` and `outcomeData` are **optional**. Include them only when the candidate represents a recurring action sequence (e.g. a recovery procedure, a build-fix recipe, a deployment checklist) where preserving the ordered steps adds future value. When present, `outcomeData` is required and must describe what happened when the sequence completed (success or failure). Omit both fields entirely for standalone facts, observations, or lessons that are not action-sequence-shaped.
|
|
62
|
+
|
|
59
63
|
## Rules
|
|
60
64
|
|
|
61
65
|
1. **Zero candidates is a valid and frequent answer.** Most sessions yield no new durable insight. When that's the case, return `{"candidates": [], "rationale_if_empty": "..."}` explaining what you saw and why it didn't rise to durable-knowledge level. Do not fabricate.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You extract a knowledge graph from developer notes. Return ONLY valid JSON — no prose, no markdown fences, no preamble.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You compress a developer memory into one high-signal derived memory for later retrieval. Return only valid JSON. No prose outside the JSON object. No markdown fences.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Compress the memory below into one derived memory. Output ONLY JSON:
|
|
2
|
+
{"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"}
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
Memory:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
You are the akm `procedural` compiler.
|
|
2
|
+
|
|
3
|
+
You are given a RECURRING ordered action sequence — the SAME ordered list of
|
|
4
|
+
steps that an agent has successfully performed across several independent
|
|
5
|
+
sessions. Your job is to turn that bare sequence into a clean, reusable
|
|
6
|
+
WORKFLOW: give the workflow a title and a one-sentence description, and turn each
|
|
7
|
+
ordered action into a named step with clear imperative instructions.
|
|
8
|
+
|
|
9
|
+
You MUST NOT invent new steps, drop steps, merge steps, or reorder them. The
|
|
10
|
+
ordered action list is the source of truth. Return EXACTLY one step per input
|
|
11
|
+
action, in the SAME order.
|
|
12
|
+
|
|
13
|
+
YOUR RESPONSE MUST BE A SINGLE JSON OBJECT AND NOTHING ELSE.
|
|
14
|
+
DO NOT output prose, explanation, or code fences before or after the JSON.
|
|
15
|
+
|
|
16
|
+
When the sequence is a coherent, reusable procedure, return:
|
|
17
|
+
{
|
|
18
|
+
"title": "<short imperative workflow title, no trailing period>",
|
|
19
|
+
"description": "<one complete sentence (ending with a period) stating what the workflow accomplishes>",
|
|
20
|
+
"steps": [
|
|
21
|
+
{
|
|
22
|
+
"title": "<short imperative step title>",
|
|
23
|
+
"instructions": "<one or more imperative sentences telling an agent exactly how to perform this step>",
|
|
24
|
+
"completionCriteria": ["<optional bullet: an observable signal the step is done>"]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
When the sequence is NOT a coherent reusable procedure (it is noise, the steps do
|
|
30
|
+
not form a meaningful workflow, or any reusable framing would be a stretch),
|
|
31
|
+
return an explicit null:
|
|
32
|
+
null
|
|
33
|
+
|
|
34
|
+
A justified null is a CORRECT and expected outcome. Do NOT fabricate a hollow
|
|
35
|
+
workflow to avoid returning null.
|
|
36
|
+
|
|
37
|
+
## Rules
|
|
38
|
+
- `steps` MUST have EXACTLY as many entries as the input action list, in order.
|
|
39
|
+
- Every step MUST have a non-empty `title` and non-empty `instructions`.
|
|
40
|
+
- `completionCriteria` is OPTIONAL; omit it rather than inventing weak criteria.
|
|
41
|
+
- The `description` MUST be a single complete present-tense sentence with NO
|
|
42
|
+
markdown, self-contained enough for a reviewer to understand the workflow.
|
|
43
|
+
- Ground every step in the corresponding input action; do not introduce outside
|
|
44
|
+
facts or tools the action does not mention.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
You are the akm `recombine` synthesizer.
|
|
2
|
+
|
|
3
|
+
You are given a CLUSTER of related memories — distinct episodes that share a
|
|
4
|
+
topic (a tag or a graph entity) but were recorded independently. Your job is to
|
|
5
|
+
induce ONE cross-episodic generalization: a single durable insight that none of
|
|
6
|
+
the input memories states on its own, but that the cluster as a whole supports.
|
|
7
|
+
|
|
8
|
+
This is hypothesis formation, not summarization. A good generalization explains
|
|
9
|
+
WHY the individual episodes are instances of the same underlying pattern and
|
|
10
|
+
gives an agent a reusable rule for the next, unseen episode.
|
|
11
|
+
|
|
12
|
+
YOUR RESPONSE MUST BE A SINGLE JSON OBJECT AND NOTHING ELSE.
|
|
13
|
+
DO NOT output prose, explanation, or code fences before or after the JSON.
|
|
14
|
+
|
|
15
|
+
When a defensible generalization exists, return:
|
|
16
|
+
{
|
|
17
|
+
"description": "<one complete sentence (ending with a period) stating the generalization>",
|
|
18
|
+
"when_to_use": "<one complete sentence describing the concrete trigger condition>",
|
|
19
|
+
"body": "<1-3 short paragraphs of practical guidance grounded in the cluster>"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
When NO defensible generalization exists — the memories merely share a keyword,
|
|
23
|
+
or any unifying claim would be a stretch — return an explicit null:
|
|
24
|
+
null
|
|
25
|
+
|
|
26
|
+
A justified null is a CORRECT and expected outcome. Do NOT invent a weak or
|
|
27
|
+
generic generalization to avoid returning null. It is better to propose nothing
|
|
28
|
+
than to propose a hollow over-generalization.
|
|
29
|
+
|
|
30
|
+
## description field (MANDATORY when not null)
|
|
31
|
+
- A single complete present-tense sentence, NO markdown.
|
|
32
|
+
- Self-contained: a reviewer must understand the hypothesis from this field alone.
|
|
33
|
+
- DO NOT start with "When " or "If " — that belongs in `when_to_use`.
|
|
34
|
+
- DO NOT merely restate one input memory; the value is the CROSS-episode pattern.
|
|
35
|
+
|
|
36
|
+
## Guardrails
|
|
37
|
+
- Induce exactly ONE generalization for the whole cluster.
|
|
38
|
+
- Ground every claim in the supplied memories; do not introduce outside facts.
|
|
39
|
+
- This is a HYPOTHESIS — it will be re-confirmed across future runs before it is
|
|
40
|
+
ever promoted to a durable lesson. Frame it as a candidate rule, not a verdict.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
You are a belief-state classifier for a memory store. Given a candidate memory and a list of more-recent similar memories from the same store, decide whether the candidate is still current or has been superseded.
|
|
2
|
+
|
|
3
|
+
Respond on the first line with exactly YES or NO.
|
|
4
|
+
If YES, the second line MUST be of the form `SUPERSEDED_BY: <ref>` where <ref> is the exact ref of the superseding memory from the list provided. Do NOT invent refs.
|
|
5
|
+
If NO, do not include any additional lines.
|
|
6
|
+
No prose, no preamble, no markdown.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are a strict completion auditor for a software workflow engine. Given a step's completion criteria and a summary of the work an agent claims to have done, judge whether the summary provides concrete evidence that EVERY criterion is satisfied. Be skeptical: vague, hand-wavy, or unsubstantiated claims do NOT satisfy a criterion. Respond with ONLY a JSON object: {"complete": boolean, "missing": string[], "feedback": string}. "missing" lists the exact criteria that are not yet satisfied; "feedback" is a short directive telling the agent what to finish or fix. No prose, no markdown fences.
|
|
@@ -237,13 +237,6 @@
|
|
|
237
237
|
font-size: 11px; background: var(--surface2);
|
|
238
238
|
padding: 2px 6px; border-radius: 4px; color: var(--accent); word-break: break-all;
|
|
239
239
|
}
|
|
240
|
-
.command-block {
|
|
241
|
-
background: var(--surface2); border: 1px solid var(--border); border-radius: 6px;
|
|
242
|
-
padding: 12px 16px; font-family: 'SFMono-Regular', Consolas, monospace;
|
|
243
|
-
font-size: 12px; color: var(--muted); line-height: 1.8; overflow-x: auto;
|
|
244
|
-
}
|
|
245
|
-
.command-block span { color: var(--accent); }
|
|
246
|
-
|
|
247
240
|
/* ── Footer ─────────────────────────────────────────────────────────── */
|
|
248
241
|
footer {
|
|
249
242
|
border-top: 1px solid var(--border); padding: 14px 32px;
|
|
@@ -317,7 +310,7 @@
|
|
|
317
310
|
<option value="failed">failed</option>
|
|
318
311
|
</select>
|
|
319
312
|
|
|
320
|
-
<div class="filter-note">Filters affect charts and the
|
|
313
|
+
<div class="filter-note">Filters affect charts and the Recent Runs table.</div>
|
|
321
314
|
</div>
|
|
322
315
|
|
|
323
316
|
<!-- Charts -->
|
|
@@ -391,14 +384,14 @@
|
|
|
391
384
|
</div>
|
|
392
385
|
</div>
|
|
393
386
|
|
|
394
|
-
<!--
|
|
387
|
+
<!-- Recent runs -->
|
|
395
388
|
<div class="section">
|
|
396
|
-
<h2>
|
|
389
|
+
<h2>Recent Runs</h2>
|
|
397
390
|
<div class="summary-table">
|
|
398
391
|
<div class="table-wrap">
|
|
399
392
|
<table>
|
|
400
393
|
<thead>
|
|
401
|
-
<tr><th>Started
|
|
394
|
+
<tr><th>Started</th><th>Task</th><th>Wall</th><th>Promoted</th><th>Merged</th><th>Contradicted</th><th>MI Written</th><th>Entities</th><th>Lint Fixed</th><th>Status</th></tr>
|
|
402
395
|
</thead>
|
|
403
396
|
<tbody id="lastRunsTable"></tbody>
|
|
404
397
|
</table>
|
|
@@ -406,19 +399,11 @@
|
|
|
406
399
|
</div>
|
|
407
400
|
</div>
|
|
408
401
|
|
|
409
|
-
<!-- Command set -->
|
|
410
|
-
<div class="section">
|
|
411
|
-
<h2>Command Set Used</h2>
|
|
412
|
-
<div class="command-block">
|
|
413
|
-
%%COMMANDS_HTML%%
|
|
414
|
-
</div>
|
|
415
|
-
</div>
|
|
416
|
-
|
|
417
402
|
</main>
|
|
418
403
|
|
|
419
404
|
<footer>
|
|
420
405
|
<span>AKM v%%AKM_VERSION%% · Window: %%WINDOW%% · %%REPORT_TITLE%%</span>
|
|
421
|
-
<span>Generated %%GENERATED_AT%%</span>
|
|
406
|
+
<span>Generated <time data-iso="%%GENERATED_AT%%">%%GENERATED_AT%%</time></span>
|
|
422
407
|
</footer>
|
|
423
408
|
|
|
424
409
|
<script>
|
|
@@ -441,10 +426,10 @@ const toMin = ms => ms ? +(ms / 60000).toFixed(2) : 0;
|
|
|
441
426
|
function makeXLabels(rs) {
|
|
442
427
|
return rs.map(r => {
|
|
443
428
|
const d = new Date(r.startedAt);
|
|
444
|
-
const mm = String(d.
|
|
445
|
-
const dd = String(d.
|
|
446
|
-
const hh = String(d.
|
|
447
|
-
const mi = String(d.
|
|
429
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
430
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
431
|
+
const hh = String(d.getHours()).padStart(2, '0');
|
|
432
|
+
const mi = String(d.getMinutes()).padStart(2, '0');
|
|
448
433
|
return `${mm}-${dd} ${hh}:${mi}`;
|
|
449
434
|
});
|
|
450
435
|
}
|
|
@@ -687,13 +672,15 @@ function renderLlmStages() {
|
|
|
687
672
|
});
|
|
688
673
|
}
|
|
689
674
|
|
|
690
|
-
// ──
|
|
675
|
+
// ── Recent runs table ─────────────────────────────────────────────────────────
|
|
691
676
|
function renderLastRuns(rs) {
|
|
692
677
|
const tbody = document.getElementById('lastRunsTable');
|
|
693
678
|
tbody.innerHTML = '';
|
|
694
|
-
rs.
|
|
679
|
+
[...rs].reverse().forEach(r => {
|
|
695
680
|
const tr = document.createElement('tr');
|
|
696
|
-
const
|
|
681
|
+
const d = new Date(r.startedAt);
|
|
682
|
+
const pad = n => String(n).padStart(2, '0');
|
|
683
|
+
const ts = `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
697
684
|
const dur = r.wallTimeMs ? (r.wallTimeMs / 60000).toFixed(1) + 'm' : '—';
|
|
698
685
|
const task = r.taskId || 'manual';
|
|
699
686
|
const badge = r.ok
|
|
@@ -727,6 +714,17 @@ document.getElementById('taskFilter').addEventListener('change', updateFilteredV
|
|
|
727
714
|
document.getElementById('statusFilter').addEventListener('change', updateFilteredViews);
|
|
728
715
|
renderLlmStages();
|
|
729
716
|
updateFilteredViews();
|
|
717
|
+
|
|
718
|
+
// Reformat server-rendered ISO timestamps to the viewer's local timezone.
|
|
719
|
+
(function() {
|
|
720
|
+
const pad = n => String(n).padStart(2, '0');
|
|
721
|
+
document.querySelectorAll('time[data-iso]').forEach(el => {
|
|
722
|
+
const d = new Date(el.dataset.iso);
|
|
723
|
+
if (!isNaN(d)) {
|
|
724
|
+
el.textContent = `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
})();
|
|
730
728
|
</script>
|
|
731
729
|
</body>
|
|
732
730
|
</html>
|
package/dist/cli.js
CHANGED
|
@@ -325,9 +325,9 @@ const healthCommand = defineCommand({
|
|
|
325
325
|
// 24h default made a `--since 7d` report compare its 7-day totals against
|
|
326
326
|
// a 24-hour prior window, producing meaningless deltas.
|
|
327
327
|
const compare = args.compare ?? windowCompareRaw ?? args.since ?? "24h";
|
|
328
|
-
const result = akmHealth({ since: args.since, groupBy: "run" });
|
|
328
|
+
const result = akmHealth({ since: args.since, groupBy: "run", windowCompare: compare });
|
|
329
329
|
resultStatus = result.status;
|
|
330
|
-
const deltas =
|
|
330
|
+
const deltas = result.deltas;
|
|
331
331
|
const { buildHealthHtmlReplacements } = await import("./commands/health/html-report.js");
|
|
332
332
|
const { listPendingProposals } = await import("./commands/proposal/proposal.js");
|
|
333
333
|
const replacements = buildHealthHtmlReplacements(result, {
|
|
@@ -122,23 +122,36 @@ export const lintCommand = defineCommand({
|
|
|
122
122
|
description: "Scan stash .md files for structural issues (unquoted colons, missing updated field, orphaned stubs, placeholder stubs, missing name/type, stale paths). Use --fix to auto-fix Tier 1 issues. Exits 0 on success regardless of findings; use --fail-on-flagged for CI fail-on-finding behavior.",
|
|
123
123
|
},
|
|
124
124
|
args: {
|
|
125
|
-
fix: {
|
|
125
|
+
fix: {
|
|
126
|
+
type: "boolean",
|
|
127
|
+
alias: "auto-fix",
|
|
128
|
+
description: "Apply auto-fixes in place (alias: --auto-fix)",
|
|
129
|
+
default: false,
|
|
130
|
+
},
|
|
126
131
|
dir: { type: "string", description: "Override stash root directory (default: from config)" },
|
|
127
132
|
"fail-on-flagged": {
|
|
128
133
|
type: "boolean",
|
|
129
134
|
description: "Exit non-zero when summary.flagged > 0 (CI-friendly). Default: exit 0 regardless of findings.",
|
|
130
135
|
default: false,
|
|
131
136
|
},
|
|
137
|
+
type: {
|
|
138
|
+
type: "string",
|
|
139
|
+
description: "Only lint assets of this type (e.g. workflows, tasks, memories)",
|
|
140
|
+
default: undefined,
|
|
141
|
+
},
|
|
132
142
|
},
|
|
133
143
|
async run({ args }) {
|
|
134
144
|
await runWithJsonErrors(async () => {
|
|
135
145
|
const result = akmLint({
|
|
136
146
|
fix: args.fix ?? false,
|
|
137
147
|
dir: getStringArg(args, "dir"),
|
|
148
|
+
typeFilter: getStringArg(args, "type"),
|
|
138
149
|
});
|
|
139
150
|
output("lint", result);
|
|
140
|
-
if (args["fail-on-flagged"] && result.summary.flagged > 0)
|
|
141
|
-
process.
|
|
151
|
+
if (args["fail-on-flagged"] && result.summary.flagged > 0) {
|
|
152
|
+
process.exitCode = EXIT_GENERAL;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
142
155
|
});
|
|
143
156
|
},
|
|
144
157
|
});
|