akm-opencode 0.9.9202609021827 → 0.10.261002-beta
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/README.md +37 -16
- package/dist/index.js +3888 -0
- package/package.json +7 -8
- package/index.ts +0 -3646
- package/shared/akm-version.ts +0 -48
- package/shared/feedback-signals.ts +0 -113
- package/shared/memory-events.ts +0 -95
- package/shared/recall-policy.ts +0 -74
- package/shared/redaction.ts +0 -292
- package/shared/ref-extraction.ts +0 -120
- package/shared/state-files.ts +0 -98
- package/shared/vendor-semver.ts +0 -112
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# akm-opencode
|
|
2
2
|
|
|
3
|
-
OpenCode plugin for [AKM](https://github.com/itlackey/akm)
|
|
3
|
+
OpenCode **1.x** plugin for [AKM](https://github.com/itlackey/akm) `0.10.26101002-alpha`. It exposes exactly five public tools and uses lifecycle hooks to bring relevant AKM context into a session. Every AKM call goes through the public `akm` CLI (`--format json`); the plugin imports nothing from `akm-cli` internals. The `akm-cli` dependency is exact-pinned to the tested release, the same pin as `akm-opencode-v2`.
|
|
4
|
+
|
|
5
|
+
Using OpenCode 2.x? Install [`akm-opencode-v2`](../opencode-v2/README.md) instead. This package's name, entrypoint and single `AkmPlugin` export are unchanged, and `latest` stays on the OpenCode 1 plugin. Tested against OpenCode 1.18.34.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -12,32 +14,39 @@ Add the plugin to `opencode.json`:
|
|
|
12
14
|
}
|
|
13
15
|
```
|
|
14
16
|
|
|
17
|
+
The bare name resolves to the `latest` tag, which is still a stable 0.9 build. For the AKM 0.10 plugin use `"akm-opencode@next"` (the prerelease channel) or an exact version such as `"akm-opencode@0.10.261002-beta"`. To update, run `akm upgrade --next`, or close OpenCode and run `opencode plugin akm-opencode@<version> -g -f` with an exact version (a tag spec like `@next` reuses its cache directory and is not refreshed).
|
|
18
|
+
|
|
15
19
|
## Tools
|
|
16
20
|
|
|
17
21
|
| Tool | Description |
|
|
18
22
|
| --- | --- |
|
|
19
23
|
| `akm_search` | Search configured bundles or registries. `source` accepts `local`, `registry`, `all`, or a configured bundle name. |
|
|
20
|
-
| `akm_show` | Show a concept by `[bundle//]conceptId[#fragment]`.
|
|
24
|
+
| `akm_show` | Show a concept by `[bundle//]conceptId[#fragment]`. |
|
|
21
25
|
| `akm_curate` | Return ranked concepts for a task or topic. Optional `pack` is a token budget that returns the selected local assets' full content in one response; registry hits are omitted from packed items. |
|
|
22
26
|
| `akm_feedback` | Record positive or negative feedback for a concept. |
|
|
23
27
|
| `akm_remember` | Save durable knowledge as a searchable memory. |
|
|
24
28
|
|
|
25
|
-
`
|
|
29
|
+
All five tools run the bundled `akm` CLI as a subprocess (`akm search|show|curate ... --format json`, `akm feedback`, `akm remember`), through the helper shared with `akm-opencode-v2` ([`opencode-shared/akm-cli.ts`](../opencode-shared/akm-cli.ts)). Each call therefore pays the CLI's process start-up, about 0.4-0.5 s on akm-cli 0.9.30 (not re-measured on 0.10); the in-process library calls this replaced took 2-30 ms warm. Failures return structured results and are logged through OpenCode app logging.
|
|
26
30
|
|
|
27
31
|
Use the `ref` returned by search or curate directly with show or feedback. When `akm_curate.pack` is set, the response already includes packed local content, so a separate show call is only needed for omitted or registry-only hits. Concept IDs look like `skills/code-review`, `memories/release-retro`, or `team-playbook//knowledge/deploy#Rollback`.
|
|
28
32
|
|
|
33
|
+
Search and show
|
|
34
|
+
responses preserve canonical `ref` while exposing `selectedRef`, `parentRef`,
|
|
35
|
+
fragment ordinal/count and lines, previous/next refs, separate fragment/parent
|
|
36
|
+
estimates, and context truncation metadata.
|
|
37
|
+
|
|
29
38
|
## Lifecycle Hooks
|
|
30
39
|
|
|
31
40
|
The plugin subscribes to OpenCode lifecycle events. Hook failures are logged through OpenCode app logging and do not interrupt the TUI.
|
|
32
41
|
|
|
33
42
|
| Event | Behavior |
|
|
34
43
|
| --- | --- |
|
|
35
|
-
| `session.created` |
|
|
44
|
+
| `session.created` | Warms local data in the background, and prepares hints, an active-workflow summary, and curated context for the session. |
|
|
36
45
|
| `session.updated` | Backfills hints and the workflow summary for a session the plugin has not prepared yet. It does not re-run session-created work. |
|
|
37
|
-
| `chat.message` | Records feedback
|
|
46
|
+
| `chat.message` | Records feedback/memory intent, captures corrections, guardrails, preferences, explicit memories, and positive feedback, and can schedule non-blocking curation. High-confidence durable signals submit asynchronous AKM proposals; recurring task intent across distinct sessions can submit a skill proposal. Only the text you typed counts; the synthetic parts OpenCode adds (an attached file's contents, the Read call that fetched it) are ignored. |
|
|
38
47
|
| `experimental.chat.system.transform` | Injects the AKM guidance and cached curated context into the system prompt. The host rebuilds the system prompt on every request, so these blocks are re-injected each turn — including after a compaction — rather than once per session. |
|
|
39
48
|
| `tool.execute.before` | The format-declaration write gate (`AKM_WRITE_GATE`, `observe` by default). Blocks the first `edit`/`write` to an existing file that declares a format your bundle documents and hands the model the ref to read. Once per file per session, released unconditionally on the retry. |
|
|
40
|
-
| `tool.execute.after` | Tracks concepts used by AKM tools
|
|
49
|
+
| `tool.execute.after` | Tracks concepts used by AKM tools and checkpoints session observations. It also records what a file the session `read` declares about its own format, which is what the write gate above keys on. `write` is deliberately not a source: a file the session created is not one it needs the bundle to explain. The create itself is recorded on the write's `tool.execute.before` pass, so a later read-back of that file cannot re-arm the gate. |
|
|
41
50
|
| `shell.env` | Exposes `AKM_PROJECT`, `AKM_PLUGIN_VERSION`, and the resolved `AKM_BUNDLE_DIR` to shell tools. |
|
|
42
51
|
| `session.idle` | Runs interval-gated memory extraction (`AKM_AUTO_MEMORY=0` disables it). Fires after every turn, so it is rate-limited. |
|
|
43
52
|
| `session.compacted` | Records a post-compaction event. |
|
|
@@ -45,7 +54,13 @@ The plugin subscribes to OpenCode lifecycle events. Hook failures are logged thr
|
|
|
45
54
|
|
|
46
55
|
The session observation buffer that retrospective feedback reads from survives every non-terminal event: it is bounded by `AKM_SESSION_BUFFER_MAX_ENTRIES` and dropped only on `session.deleted`. Discarding it at `session.idle` would empty it between turns, so "thanks, that worked" would credit nothing in exactly the sessions that used the most assets.
|
|
47
56
|
|
|
48
|
-
Automatic feedback skips references that AKM reports as ineligible, and
|
|
57
|
+
Automatic feedback skips references that AKM reports as ineligible, and no `akm_*` tool call submits feedback on its own. A *successful* `akm_show` / `akm_search` / `akm_curate` only inspected a concept, which is not evidence that it helped, so on OpenCode the positive signal comes from a retrospective confirmation instead. A *failed* call (an error result, a timeout, an ambiguous ref) is not feedback on the asset either, and is only logged, as a warning that names its ref and the error: its ref is not remembered as used, so a later "thanks, that worked" does not credit it and a later correction does not blame it. Same rules as the Claude plugin, which applies them to the `akm` subcommand of a Bash invocation.
|
|
58
|
+
|
|
59
|
+
### Automatic learning proposals
|
|
60
|
+
|
|
61
|
+
The chat hook applies the same shared classifier and proposal contract as the Claude plugin. Redacted, high-confidence memories and behavioral signals are project-scoped and submitted with `akm proposal new` as `memory` or `instruction` proposals. The plugin never writes `AGENTS.md`, `CLAUDE.md`, command files, or rules; AKM's proposal queue owns review and all later state changes. Proposal authoring requires the AKM agent profile configured by `akm setup`; outcomes are recorded through OpenCode app logging and the durable proposal ledger.
|
|
62
|
+
|
|
63
|
+
Praise is captured as supporting evidence and can credit recently used assets, but it does not create an empty proposal. Repeated task intents are compared across sessions in the same project; the default threshold is three distinct sessions before a cross-platform `skill` proposal is submitted. A durable ledger suppresses exact and near-duplicate submissions across OpenCode restarts.
|
|
49
64
|
|
|
50
65
|
## Locking down destructive commands
|
|
51
66
|
|
|
@@ -64,14 +79,14 @@ Every kill switch below is opt-out and reads the same way: only the literal `0`
|
|
|
64
79
|
| Variable | Default | Purpose |
|
|
65
80
|
| --- | --- | --- |
|
|
66
81
|
| `AKM_BUNDLE_DIR` | unset | Absolute path to the AKM bundle root. When unset the plugin discovers it once per process by running `akm info`. The resolved value is re-exported to shell tools through the `shell.env` hook. |
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
82
|
+
| `AKM_OPENCODE_CLI` | unset | Absolute path to an `akm` executable to run instead of the `akm-cli` dependency, exec'd as-is. Used by the eval harness to substitute a deterministic shim. |
|
|
83
|
+
| `AKM_READ_TOOL_TIMEOUT` | `60` | Seconds allowed for one `akm_search` / `akm_show` / `akm_curate` call. |
|
|
69
84
|
| `AKM_AUTO_CURATE` | `1` | Set to `0` to disable automatic prompt curation. |
|
|
70
|
-
| `AKM_AUTO_FEEDBACK` | `1` | Set to `0` to disable automatic
|
|
71
|
-
| `
|
|
72
|
-
| `AKM_AUTO_MEMORY` | `1` | Set to `0` to disable
|
|
85
|
+
| `AKM_AUTO_FEEDBACK` | `1` | Set to `0` to disable automatic feedback: the retrospective ("that worked") confirmation. |
|
|
86
|
+
| `AKM_AUTO_LEARNING` | `1` | Set to `0` to disable prompt-signal capture and automatic learning/skill proposal submission. Native session extraction is controlled separately by `AKM_AUTO_MEMORY`. |
|
|
87
|
+
| `AKM_AUTO_MEMORY` | `1` | Set to `0` to disable interval-gated native-session extraction through `akm proposal extract`. The Claude plugin honours the same variable for its `SessionEnd` extraction. |
|
|
73
88
|
| `AKM_INDEX_ON_SESSION_END` | `1` | Set to `0` to skip the `akm index` refresh. It runs only on `session.deleted` — never on `session.idle`, which fires after every turn. |
|
|
74
|
-
| `AKM_WRITE_GATE` | `observe` | The format-declaration write gate (#99). When a file the session **read** declares a format your bundle documents — an `apiVersion:` namespace, a `yaml-language-server` pragma, a `$schema` key, an XML root namespace — and that asset has not been opened this session, the FIRST `edit`/`write` to that file is blocked once and the model is told which ref to read. It ships in `observe`: everything runs and the would-fire count lands in the ledger, but nothing is blocked. Set `enforce` to block, or `off`/`0` to disable it entirely. An unrecognized value is a configuration error — it logs one error per process and the gate refuses to run rather than guessing a default. It never fires on a file this session created: the create is recorded when it happens — a `write` to a path this session has not read, or an `edit` with an empty `oldString` — and that path stays insulated for the rest of the session, so reading back the model's own output does not re-arm it (ledger reason `session-created`). It
|
|
89
|
+
| `AKM_WRITE_GATE` | `observe` | The format-declaration write gate (#99). When a file the session **read** declares a format your bundle documents — an `apiVersion:` namespace, a `yaml-language-server` pragma, a `$schema` key, an XML root namespace — and that asset has not been opened this session, the FIRST `edit`/`write` to that file is blocked once and the model is told which ref to read. It ships in `observe`: everything runs and the would-fire count lands in the ledger, but nothing is blocked. Set `enforce` to block, or `off`/`0` to disable it entirely. An unrecognized value is a configuration error — it logs one error per process and the gate refuses to run rather than guessing a default. It never fires on a file this session created: the create is recorded when it happens — a `write` to a path this session has not read, or an `edit` with an empty `oldString` — and that path stays insulated for the rest of the session, so reading back the model's own output does not re-arm it (ledger reason `session-created`). It is inert on `apply_patch` (which carries no file path) — that case logs one warning per process rather than failing quietly. |
|
|
75
90
|
|
|
76
91
|
### Scope
|
|
77
92
|
|
|
@@ -92,13 +107,19 @@ The `agent`, `run`, and `project` dimensions come from OpenCode itself (the acti
|
|
|
92
107
|
| `AKM_CURATE_LIMIT` | `5` | Maximum curated results injected per prompt. |
|
|
93
108
|
| `AKM_CURATE_MIN_CHARS` | `16` | Minimum prompt length for automatic curation. |
|
|
94
109
|
| `AKM_CURATE_TIMEOUT` | `8` | Timeout in seconds for AKM calls made by hooks. |
|
|
110
|
+
| `AKM_CURATE_MIN_SCORE` | `0` (disabled) | Minimum per-item relevance score to keep a curated result. `0` preserves the long-standing behavior. Set it above `0` and items below the floor are dropped (no curated block at all when none survive), and the survivors are reordered so locally authored assets (lesson, memory, knowledge, skill, command, agent, instruction, fact, workflow, task, env, secret) come before imported website/wiki snapshots. Same contract as the Claude plugin's `AKM_CURATE_MIN_SCORE` — see `claude/README.md` for the full explanation, including why there is no universal default value. |
|
|
111
|
+
| `AKM_CURATE_TYPE` | unset | Passthrough for `akm curate --type`. |
|
|
95
112
|
| `AKM_CONTEXT_BUDGET_CHARS` | `4000` | Maximum length of the AKM text injected into the system prompt on one turn. Past it the block is truncated with a marker. |
|
|
96
113
|
| `AKM_PENDING_PROPOSAL_TIMEOUT` | `2` | Timeout in seconds for the pending-proposal count. Floored at 0.5s. |
|
|
97
114
|
| `AKM_SESSION_BUFFER_MAX_ENTRIES` | `200` | Per-session cap on buffered observations. Oldest entries are dropped first. |
|
|
98
115
|
| `AKM_EXTRACT_MIN_INTERVAL_MS` | `600000` | Minimum gap between `akm proposal extract` runs for one session. `session.idle` fires after every turn, so without this gate extraction would flood. |
|
|
99
116
|
| `AKM_PLUGIN_MAX_LOG_BYTES` | `1048576` | Size cap for each append-only state file under `$XDG_STATE_HOME/akm-opencode`. Past the cap the newest half is retained. |
|
|
100
117
|
| `AKM_AUTO_FEEDBACK_MIN_CONFIDENCE` | `0.6` | Minimum classifier confidence before automatic feedback is actually submitted. Raise it to submit less. |
|
|
101
|
-
| `
|
|
118
|
+
| `AKM_LEARNING_PROPOSAL_MIN_CONFIDENCE` | `0.75` | Minimum correction/preference confidence required before `akm proposal new` is dispatched. Lower-confidence evidence remains available to native session extraction. |
|
|
119
|
+
| `AKM_LEARNING_PROPOSAL_TIMEOUT_MS` | `600000` | Maximum runtime for one detached proposal-authoring child. |
|
|
120
|
+
| `AKM_AUTO_SKILL_PROPOSALS` | `1` | Set to `0` to retain correction/preference proposals but disable recurring-intent skill proposals. |
|
|
121
|
+
| `AKM_WORKFLOW_PROPOSAL_MIN_SESSIONS` | `3` | Distinct-session recurrence required before a skill proposal is submitted. Values below `2` fall back to `3`. |
|
|
122
|
+
| `AKM_RETROSPECTIVE_FEEDBACK_PATTERN` | positive confirmation phrases | Case-insensitive regex for retrospective confirmation. The default covers thanks/worked plus “exactly right,” “great approach,” “love it,” and “nailed it.” Retune it for other languages or project jargon. An invalid regex falls back to the default rather than failing the hook. |
|
|
102
123
|
| `AKM_RETROSPECTIVE_NEGATIVE_PATTERN` | `\b(wrong\|failed\|broken\|didn't work\|did not work\|bad)\b` | Case-insensitive regex that vetoes retrospective credit, so a mixed message ("thanks, but it did not work") is skipped rather than misread as praise. Same fallback behavior. |
|
|
103
124
|
|
|
104
125
|
### Redaction
|
|
@@ -115,8 +136,8 @@ These three are read once, when the plugin module is imported, so they must be s
|
|
|
115
136
|
|
|
116
137
|
1. Start with `akm_curate` for task-oriented discovery; set `pack` when you want the selected local content immediately.
|
|
117
138
|
2. Use `akm_search` when you know the concept name and need its exact ID.
|
|
118
|
-
3. Fetch
|
|
119
|
-
4. Record the
|
|
139
|
+
3. Fetch a concept with `akm_show` before relying on it.
|
|
140
|
+
4. Record whether the concept helped with `akm_feedback`; a failed akm call is not feedback on the asset.
|
|
120
141
|
5. Use `akm_remember` for durable knowledge that should be available to future sessions.
|
|
121
142
|
|
|
122
143
|
## Docs
|