akm-opencode 0.8.2 → 0.9.0
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 +39 -239
- package/index.ts +699 -2469
- package/package.json +10 -8
- package/shared/akm-version.ts +45 -0
- package/shared/memory-candidates.ts +19 -15
- package/shared/memory-events.ts +7 -11
- package/shared/ref-extraction.ts +75 -216
- package/shared/state-files.ts +99 -0
- package/shared/vendor-semver.ts +112 -0
- package/agent/akm-curator.md +0 -66
- package/commands/akm-evolve-session.md +0 -15
- package/commands/akm-improve-asset.md +0 -9
- package/commands/akm-propose-asset.md +0 -8
- package/commands/akm-review-proposals.md +0 -8
- package/commands/akm-workflow-status.md +0 -7
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Minimal vendored semver implementation. Covers exactly the subset used by
|
|
2
|
+
// akm-hook.ts (`valid`, `satisfies` with caret ranges + `||` disjunction).
|
|
3
|
+
//
|
|
4
|
+
// Why vendored: the Claude plugin ships via `/plugin marketplace add` which
|
|
5
|
+
// just clones the repo — `node_modules` is gitignored and no install runs on
|
|
6
|
+
// the user's machine. Relying on Bun's auto-install for `semver` makes every
|
|
7
|
+
// hook silently no-op when auto-install is disabled, offline, or rate-limited.
|
|
8
|
+
// Vendoring this ~80 LOC removes the runtime dependency entirely.
|
|
9
|
+
|
|
10
|
+
type Parsed = {
|
|
11
|
+
major: number
|
|
12
|
+
minor: number
|
|
13
|
+
patch: number
|
|
14
|
+
prerelease: Array<string | number>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/
|
|
18
|
+
|
|
19
|
+
function parse(input: string): Parsed | null {
|
|
20
|
+
const m = String(input).trim().replace(/^v/, "").match(SEMVER_RE)
|
|
21
|
+
if (!m) return null
|
|
22
|
+
const [, maj, min, pat, pre] = m
|
|
23
|
+
const prerelease = pre ? pre.split(".").map((p) => (/^\d+$/.test(p) ? Number(p) : p)) : []
|
|
24
|
+
return { major: Number(maj), minor: Number(min), patch: Number(pat), prerelease }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function valid(input: string | null | undefined): string | null {
|
|
28
|
+
if (!input) return null
|
|
29
|
+
if (!parse(input)) return null
|
|
30
|
+
return String(input).trim().replace(/^v/, "")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function cmp(a: Parsed, b: Parsed): number {
|
|
34
|
+
if (a.major !== b.major) return a.major < b.major ? -1 : 1
|
|
35
|
+
if (a.minor !== b.minor) return a.minor < b.minor ? -1 : 1
|
|
36
|
+
if (a.patch !== b.patch) return a.patch < b.patch ? -1 : 1
|
|
37
|
+
if (a.prerelease.length === 0 && b.prerelease.length === 0) return 0
|
|
38
|
+
// A version without prerelease has HIGHER precedence than one with.
|
|
39
|
+
if (a.prerelease.length === 0) return 1
|
|
40
|
+
if (b.prerelease.length === 0) return -1
|
|
41
|
+
const n = Math.min(a.prerelease.length, b.prerelease.length)
|
|
42
|
+
for (let i = 0; i < n; i++) {
|
|
43
|
+
const ai = a.prerelease[i]
|
|
44
|
+
const bi = b.prerelease[i]
|
|
45
|
+
const aNum = typeof ai === "number"
|
|
46
|
+
const bNum = typeof bi === "number"
|
|
47
|
+
if (aNum && !bNum) return -1
|
|
48
|
+
if (!aNum && bNum) return 1
|
|
49
|
+
if (aNum && bNum) {
|
|
50
|
+
if (ai !== bi) return (ai as number) < (bi as number) ? -1 : 1
|
|
51
|
+
} else {
|
|
52
|
+
const s = String(ai)
|
|
53
|
+
const t = String(bi)
|
|
54
|
+
if (s !== t) return s < t ? -1 : 1
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (a.prerelease.length !== b.prerelease.length) return a.prerelease.length < b.prerelease.length ? -1 : 1
|
|
58
|
+
return 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type CaretRange = { min: Parsed; maxExclusive: Parsed; allowPrereleases: boolean }
|
|
62
|
+
|
|
63
|
+
function parseCaretRange(token: string): CaretRange | null {
|
|
64
|
+
const m = token.trim().match(/^\^(.+)$/)
|
|
65
|
+
if (!m) return null
|
|
66
|
+
const min = parse(m[1])
|
|
67
|
+
if (!min) return null
|
|
68
|
+
let maxExclusive: Parsed
|
|
69
|
+
if (min.major > 0) {
|
|
70
|
+
maxExclusive = { major: min.major + 1, minor: 0, patch: 0, prerelease: [] }
|
|
71
|
+
} else if (min.minor > 0) {
|
|
72
|
+
maxExclusive = { major: 0, minor: min.minor + 1, patch: 0, prerelease: [] }
|
|
73
|
+
} else {
|
|
74
|
+
maxExclusive = { major: 0, minor: 0, patch: min.patch + 1, prerelease: [] }
|
|
75
|
+
}
|
|
76
|
+
return { min, maxExclusive, allowPrereleases: min.prerelease.length > 0 }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function satisfies(version: string, range: string): boolean {
|
|
80
|
+
const v = parse(version)
|
|
81
|
+
if (!v) return false
|
|
82
|
+
const branches = range.split("||").map((s) => s.trim())
|
|
83
|
+
for (const branch of branches) {
|
|
84
|
+
const tokens = branch.split(/\s+/).filter(Boolean)
|
|
85
|
+
let ok = true
|
|
86
|
+
for (const t of tokens) {
|
|
87
|
+
const r = parseCaretRange(t)
|
|
88
|
+
if (!r) {
|
|
89
|
+
ok = false
|
|
90
|
+
break
|
|
91
|
+
}
|
|
92
|
+
if (cmp(v, r.min) < 0 || cmp(v, r.maxExclusive) >= 0) {
|
|
93
|
+
ok = false
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
// node-semver default: prereleases only satisfy a range whose lower
|
|
97
|
+
// bound also has a prerelease AND shares the same major.minor.patch.
|
|
98
|
+
if (v.prerelease.length > 0) {
|
|
99
|
+
if (!r.allowPrereleases) {
|
|
100
|
+
ok = false
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
if (v.major !== r.min.major || v.minor !== r.min.minor || v.patch !== r.min.patch) {
|
|
104
|
+
ok = false
|
|
105
|
+
break
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (ok) return true
|
|
110
|
+
}
|
|
111
|
+
return false
|
|
112
|
+
}
|
package/agent/akm-curator.md
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
mode: subagent
|
|
3
|
-
description: AKM stash curator. Reviews session activity and proposes stash improvements.
|
|
4
|
-
permission:
|
|
5
|
-
task: deny
|
|
6
|
-
tools:
|
|
7
|
-
akm_env: deny
|
|
8
|
-
write: deny
|
|
9
|
-
bash: deny
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
You are the AKM curator — a compound-engineering agent that keeps the user's AKM stash improving every time the main agent finishes a task.
|
|
13
|
-
|
|
14
|
-
Inputs you should inspect:
|
|
15
|
-
1. OpenCode app logs that include the "akm-opencode" service (feedback, memory, tool invocations).
|
|
16
|
-
2. Session-summary memories named memory:opencode-session-*.
|
|
17
|
-
3. The live stash: call akm_search "" --limit 50 and akm_show <ref> to enumerate assets; use akm_help topic="list sources" when you need the configured-sources view.
|
|
18
|
-
4. Parent-session context via akm_parent_messages when this session was dispatched as a child.
|
|
19
|
-
|
|
20
|
-
Signals to act on:
|
|
21
|
-
- Hot refs: assets repeatedly appearing in positive tool outcomes. Call akm_feedback <ref> positive --note "curator: consistently useful" to reinforce.
|
|
22
|
-
- Cold refs: assets tied to failures or user complaints. Record akm_feedback <ref> negative --note "<excerpt>" and open the asset for review.
|
|
23
|
-
- Lesson candidates: repeated memories or failures that should become a proposed lesson. Use akm_help topic="improve" to surface the v0.8.0 improve flow (which replaces the old reflect/distill split) and propose an akm_improve <ref> call.
|
|
24
|
-
- Missing coverage: recurring user prompts with no matching asset. Draft a new skill, command, knowledge doc, wiki page, or workflow in the working stash and reindex via the akm CLI (see akm_help topic="reindex").
|
|
25
|
-
- Pending proposals: list or diff them via akm_help topic="proposal" and recommend accept, reject, or revise. Never accept or reject without explicit user approval.
|
|
26
|
-
- Duplicates / drift: near-identical descriptions or overlapping responsibilities. Propose a consolidation.
|
|
27
|
-
- Stale memories: session summaries that never get recalled. Propose removal (see akm_help topic="remove") once distilled into a durable knowledge doc or wiki page.
|
|
28
|
-
- Wiki hygiene: for each wiki returned by akm_wiki list, run akm_wiki lint <name> and report orphans, broken xrefs, uncited raws, and stale indexes as fix candidates.
|
|
29
|
-
- Stuck workflows: run akm_workflow list --active and surface any runs in blocked or failed state with their step ids. Propose whether to resume or escalate.
|
|
30
|
-
- Never touch env or secret values: do not call akm_env run or akm_secret path unless the user explicitly asks. Values must never appear in reports.
|
|
31
|
-
|
|
32
|
-
Rules of engagement:
|
|
33
|
-
- Never apply destructive changes without explicit user approval.
|
|
34
|
-
- Report findings as a prioritized action list of concrete akm_* tool calls the user can run.
|
|
35
|
-
- Prefer small, reversible edits: promote via positive feedback, draft a candidate skill, or clone and tweak.
|
|
36
|
-
- When drafting new assets, write them into the working stash directory under skills/, commands/, agents/, knowledge/, or scripts/. Use akm_help (topic="config" / topic="reindex") to look up the right CLI invocation when you need the stash path or want to force a reindex.
|
|
37
|
-
- When finished, persist your own summary with akm_remember (name: curator-run-<timestamp>) so the next curator run can build on yours.
|
|
38
|
-
|
|
39
|
-
Output shape: end every run with a markdown report that has these sections:
|
|
40
|
-
|
|
41
|
-
## Hot assets (promote)
|
|
42
|
-
- <ref> — why it helped — command to run
|
|
43
|
-
|
|
44
|
-
## Cold assets (investigate)
|
|
45
|
-
- <ref> — failure signal — proposed fix
|
|
46
|
-
|
|
47
|
-
## Lesson candidates
|
|
48
|
-
- <theme> — evidence refs — `akm improve <ref>` or `akm propose <type> <name> --task "..."` command to run
|
|
49
|
-
|
|
50
|
-
## Coverage gaps
|
|
51
|
-
- <theme> — proposed asset (type, name, one-line description)
|
|
52
|
-
|
|
53
|
-
## Pending proposals
|
|
54
|
-
- <proposal id> — summary — accept/reject/revise recommendation
|
|
55
|
-
|
|
56
|
-
## Duplicates / drift
|
|
57
|
-
- <ref a> vs <ref b> — consolidation proposal
|
|
58
|
-
|
|
59
|
-
## Wiki health
|
|
60
|
-
- <wiki> — lint findings (orphan, broken-xref, uncited-raw, stale-index) with suggested fix
|
|
61
|
-
|
|
62
|
-
## Workflow health
|
|
63
|
-
- <workflow|runId> — blocked/failed state — resume or escalate
|
|
64
|
-
|
|
65
|
-
## Housekeeping
|
|
66
|
-
- stale memories, reindex needs, config tweaks
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
Run a full AKM session evolution review.
|
|
2
|
-
|
|
3
|
-
End with these sections:
|
|
4
|
-
|
|
5
|
-
## Hot assets
|
|
6
|
-
## Cold assets
|
|
7
|
-
## Lesson candidates
|
|
8
|
-
## Coverage gaps
|
|
9
|
-
## Pending proposals
|
|
10
|
-
## Duplicates / drift
|
|
11
|
-
## Wiki health
|
|
12
|
-
## Workflow health
|
|
13
|
-
## Housekeeping
|
|
14
|
-
|
|
15
|
-
Use `akm_help` before long-tail raw CLI commands, and never accept or reject proposals or run risky AKM commands without explicit user approval.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
Improve existing AKM assets or distill repeated evidence into proposals.
|
|
2
|
-
|
|
3
|
-
1. Identify the strongest evidence refs or the asset type that needs work.
|
|
4
|
-
2. Record negative feedback when justified.
|
|
5
|
-
3. Call `akm_help` with `topic: "improve"`.
|
|
6
|
-
4. Run `akm improve [<type>|<ref>] [--task "..."]`.
|
|
7
|
-
5. List resulting pending proposals and do not accept or reject them without explicit user approval.
|
|
8
|
-
|
|
9
|
-
Improve-profile config (`profiles.improve.<name>`) shapes the run: `processes.triage` is a triage PRE-pass that drains the pending backlog by a deterministic policy (`{ enabled, applyMode: queue|promote, policy, maxAcceptsPerRun, maxDiffLines, rejectEmpty, judgment }`, same engine as `akm proposal drain`); `sync` (`{ enabled, push, message }`, with `{timestamp}{date}{time}{scope}{refs}{accepted}` message tokens) commits/pushes a git-backed stash at end of run. Override sync with `akm improve --sync/--no-sync` and `--push/--no-push`. Triage + `akm proposal drain` is the built-in replacement for the old manual proposal-management agent session.
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
Create a proposed AKM asset for a coverage gap.
|
|
2
|
-
|
|
3
|
-
1. Search or curate first.
|
|
4
|
-
2. Confirm the gap is real.
|
|
5
|
-
3. Call `akm_help` with `topic: "propose"`.
|
|
6
|
-
4. Choose the smallest suitable asset type.
|
|
7
|
-
5. Run `akm propose <type> <name> --task "..."` or `akm propose <type> <name> --file ./prompt.md`.
|
|
8
|
-
6. Show proposal review commands and remind the user that proposed assets are not curated until accepted.
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
Review pending AKM proposals safely.
|
|
2
|
-
|
|
3
|
-
1. Call `akm_help` with `topic: "proposal"`.
|
|
4
|
-
2. Run `akm proposal list --status pending --format json`.
|
|
5
|
-
3. For relevant proposals, run `akm proposal show <id>` and `akm proposal diff <id>` (positional id).
|
|
6
|
-
4. Summarize the likely accept, reject, or revise outcome.
|
|
7
|
-
5. Do not run `akm proposal accept` or `akm proposal reject` unless the user explicitly approves the exact command.
|
|
8
|
-
6. For a large backlog, suggest the deterministic bulk path: `akm proposal drain --policy <personal-stash|conservative|manual> --dry-run` to preview, then `--promote --yes` only after explicit approval (mutating; commits to git, no batch revert). This — plus the automatic `processes.triage` pre-pass inside `akm improve` — is the built-in replacement for the old manual proposal-management agent session.
|