akm-opencode-v2 0.10.261106-beta → 0.10.261108-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/dist/index.js +62 -58
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -45,7 +45,8 @@ function readPluginVersion(moduleUrl, packageName) {
|
|
|
45
45
|
// ../claude/shared/ref-extraction.ts
|
|
46
46
|
import { existsSync, statSync } from "fs";
|
|
47
47
|
import path2 from "path";
|
|
48
|
-
var AKM_REF_STRICT = /^(?:[A-Za-z0-9@._+-]+\/\/)?(
|
|
48
|
+
var AKM_REF_STRICT = /^(?:[A-Za-z0-9@._+-]+\/\/)?[A-Za-z0-9@._+-]+(?:\/[A-Za-z0-9._+@-]+)*(?:#[A-Za-z0-9._~!$&'()*+,;=:@%/?-]+)?$/;
|
|
49
|
+
var CONCEPT_ROOT_REF = /^(?:[A-Za-z0-9@._+-]+\/\/)?(?:agents|commands|env|facts|instructions|knowledge|lessons|memories|scripts|secrets|sessions|skills|tasks|workflows)\/[A-Za-z0-9._/-]+(?:#[A-Za-z0-9._~!$&'()*+,;=:@%/?-]+)?$/;
|
|
49
50
|
var EDGE_PUNCTUATION = new Set([".", ",", ";", ":", "!", "?", "(", ")", "[", "]", "{", "}", "'", '"', "`"]);
|
|
50
51
|
function normalizeToken(token) {
|
|
51
52
|
let start = 0;
|
|
@@ -60,14 +61,39 @@ function extractAkmRefsFromString(text) {
|
|
|
60
61
|
const refs = new Set;
|
|
61
62
|
for (const token of text.split(/\s+/)) {
|
|
62
63
|
const normalized = normalizeToken(token);
|
|
63
|
-
if (normalized &&
|
|
64
|
+
if (normalized && CONCEPT_ROOT_REF.test(normalized))
|
|
64
65
|
refs.add(normalized);
|
|
65
66
|
}
|
|
66
67
|
return [...refs];
|
|
67
68
|
}
|
|
69
|
+
function akmRefFromValue(value) {
|
|
70
|
+
if (typeof value !== "string")
|
|
71
|
+
return;
|
|
72
|
+
const ref = value.trim();
|
|
73
|
+
return AKM_REF_STRICT.test(ref) && !ref.split("#", 1)[0].split("//").pop().split("/").some((s) => s === "." || s === "..") ? ref : undefined;
|
|
74
|
+
}
|
|
75
|
+
function refsOfAkmResult(result) {
|
|
76
|
+
if (!result || typeof result !== "object")
|
|
77
|
+
return [];
|
|
78
|
+
const r = result;
|
|
79
|
+
const entries = [r, ...Array.isArray(r.hits) ? r.hits : [], ...Array.isArray(r.items) ? r.items : []];
|
|
80
|
+
const refs = entries.map((e) => e && typeof e === "object" ? akmRefFromValue(e.ref) : undefined);
|
|
81
|
+
return [...new Set(refs.filter((ref) => !!ref))];
|
|
82
|
+
}
|
|
83
|
+
function extractRefsFromAkmOutput(text) {
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(text);
|
|
87
|
+
} catch {
|
|
88
|
+
parsed = undefined;
|
|
89
|
+
}
|
|
90
|
+
const refLines = [...text.matchAll(/^\s*ref:\s*(\S+)\s*$/gm)].map((m) => akmRefFromValue(m[1]));
|
|
91
|
+
return [...new Set([...refsOfAkmResult(parsed), ...refLines.filter((r) => !!r), ...extractAkmRefsFromString(text)])];
|
|
92
|
+
}
|
|
68
93
|
function normalizeCandidate(candidate) {
|
|
69
94
|
const canonical = normalizeToken(candidate);
|
|
70
|
-
|
|
95
|
+
const ref = akmRefFromValue(canonical);
|
|
96
|
+
if (!ref)
|
|
71
97
|
return null;
|
|
72
98
|
const withoutFragment = canonical.split("#", 1)[0] ?? "";
|
|
73
99
|
const separator = withoutFragment.indexOf("//");
|
|
@@ -103,37 +129,41 @@ var SCRIPT_EXTENSIONS = new Set([
|
|
|
103
129
|
".kts"
|
|
104
130
|
]);
|
|
105
131
|
function markdownFile(directPath) {
|
|
132
|
+
if (path2.basename(directPath).replace(/\.md$/, "") === "SKILL")
|
|
133
|
+
return false;
|
|
106
134
|
return isFile(`${directPath}.md`) || directPath.endsWith(".md") && isFile(directPath);
|
|
107
135
|
}
|
|
108
|
-
function isAsset(
|
|
109
|
-
if (
|
|
110
|
-
return
|
|
111
|
-
if (
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
136
|
+
function isAsset(directPath, ref) {
|
|
137
|
+
if (isFile(path2.join(directPath, "SKILL.md")) || markdownFile(directPath))
|
|
138
|
+
return true;
|
|
139
|
+
if (SCRIPT_EXTENSIONS.has(path2.extname(directPath).toLowerCase()) && isFile(directPath))
|
|
140
|
+
return true;
|
|
141
|
+
const folders = ref.split("/").slice(0, -1);
|
|
142
|
+
if (folders.includes("env")) {
|
|
143
|
+
if (isFile(`${directPath}.env`))
|
|
144
|
+
return true;
|
|
145
|
+
if (path2.basename(directPath) === "default" && isFile(path2.join(path2.dirname(directPath), ".env")))
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
if (folders.includes("tasks") && isFile(`${directPath}.yml`))
|
|
149
|
+
return true;
|
|
150
|
+
return folders.includes("secrets") && !/\.(lock|sensitive)$/.test(directPath) && isFile(directPath);
|
|
151
|
+
}
|
|
152
|
+
var LEGACY_PREFIXES = new Set(["scripts", "agents", "commands", "facts", "instructions", "knowledge", "lessons", "memories", "sessions"]);
|
|
123
153
|
function conceptExistsInAnyBundle(conceptId, bundleRoots) {
|
|
124
|
-
const
|
|
125
|
-
const rest = conceptId.slice(
|
|
154
|
+
const head = conceptId.split("/", 1)[0];
|
|
155
|
+
const rest = conceptId.slice(head.length + 1);
|
|
126
156
|
for (const root of bundleRoots) {
|
|
127
157
|
if (!root)
|
|
128
158
|
continue;
|
|
129
159
|
const resolvedRoot = path2.resolve(root);
|
|
160
|
+
const inside = (file) => file !== resolvedRoot && file.startsWith(`${resolvedRoot}${path2.sep}`);
|
|
130
161
|
const directPath = path2.resolve(resolvedRoot, conceptId);
|
|
131
|
-
if (directPath
|
|
132
|
-
continue;
|
|
133
|
-
if (isAsset(type, directPath))
|
|
162
|
+
if (inside(directPath) && isAsset(directPath, conceptId))
|
|
134
163
|
return true;
|
|
135
|
-
if (
|
|
136
|
-
|
|
164
|
+
if (LEGACY_PREFIXES.has(head) && rest.includes("/")) {
|
|
165
|
+
const legacyPath = path2.resolve(resolvedRoot, rest);
|
|
166
|
+
if (inside(legacyPath) && isAsset(legacyPath, rest))
|
|
137
167
|
return true;
|
|
138
168
|
}
|
|
139
169
|
}
|
|
@@ -802,35 +832,9 @@ function classifyToolFeedback(value) {
|
|
|
802
832
|
return "positive";
|
|
803
833
|
return;
|
|
804
834
|
}
|
|
805
|
-
function extractToolRefs(
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
if (typeof value !== "string")
|
|
809
|
-
return;
|
|
810
|
-
for (const ref of extractAkmRefsFromString(value))
|
|
811
|
-
refs.add(ref);
|
|
812
|
-
};
|
|
813
|
-
for (const key of ["ref", "package_ref"])
|
|
814
|
-
addMatches(args[key]);
|
|
815
|
-
if (output && typeof output === "object") {
|
|
816
|
-
const o = output;
|
|
817
|
-
addMatches(o.ref);
|
|
818
|
-
if (Array.isArray(o.hits)) {
|
|
819
|
-
for (const hit of o.hits) {
|
|
820
|
-
if (hit && typeof hit === "object")
|
|
821
|
-
addMatches(hit.ref);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
if (Array.isArray(o.items)) {
|
|
825
|
-
for (const item of o.items) {
|
|
826
|
-
if (item && typeof item === "object")
|
|
827
|
-
addMatches(item.ref);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
if (toolName === "akm_remember" && typeof o.ref === "string")
|
|
831
|
-
addMatches(o.ref);
|
|
832
|
-
}
|
|
833
|
-
return [...refs];
|
|
835
|
+
function extractToolRefs(args, output) {
|
|
836
|
+
const named = [args.ref, args.package_ref].map(akmRefFromValue).filter((ref) => !!ref);
|
|
837
|
+
return [...new Set([...named, ...refsOfAkmResult(output)])];
|
|
834
838
|
}
|
|
835
839
|
function extractAkmRefsFromAllArgs(args) {
|
|
836
840
|
if (!args || typeof args !== "object")
|
|
@@ -909,7 +913,7 @@ function createFeedbackTracker(options) {
|
|
|
909
913
|
const isAkmTool = input.tool.startsWith("akm_");
|
|
910
914
|
const candidateRefs = [...new Set([...extractAkmRefsFromAllArgs(input.args), ...extractAkmRefsFromString(input.outputText)])];
|
|
911
915
|
const parsed = isAkmTool ? parseToolOutput(input.outputText) : null;
|
|
912
|
-
const refs = isAkmTool && parsed ? extractToolRefs(input.
|
|
916
|
+
const refs = isAkmTool && parsed ? extractToolRefs(input.args, parsed) : validateRefCandidates(candidateRefs, [await input.getBundleDir() ?? ""]);
|
|
913
917
|
const failedCall = classifyToolFeedback(parsed) === "negative";
|
|
914
918
|
if (refs.length > 0) {
|
|
915
919
|
input.writeEvent({
|
|
@@ -929,7 +933,7 @@ function createFeedbackTracker(options) {
|
|
|
929
933
|
}
|
|
930
934
|
function observeAkmToolResult(input) {
|
|
931
935
|
const outcome = classifyToolFeedback(input.parsed);
|
|
932
|
-
const refs = extractToolRefs(input.
|
|
936
|
+
const refs = extractToolRefs(input.args, input.parsed);
|
|
933
937
|
const failedCall = outcome === "negative";
|
|
934
938
|
if (!failedCall)
|
|
935
939
|
noteRecentRefs(input.sessionID, refs);
|
|
@@ -3017,7 +3021,7 @@ ${NEED_MORE_CONTEXT_HINT}` : NEED_MORE_CONTEXT_HINT;
|
|
|
3017
3021
|
sessionID,
|
|
3018
3022
|
query: truncateLogText(decision.query, 200),
|
|
3019
3023
|
reason: decision.reason,
|
|
3020
|
-
refs:
|
|
3024
|
+
refs: extractRefsFromAkmOutput(curated ?? ""),
|
|
3021
3025
|
outcome: curated ? "ok" : "skipped"
|
|
3022
3026
|
});
|
|
3023
3027
|
});
|
|
@@ -3223,7 +3227,7 @@ ${st.hints}` : AKM_HINTS_PREFIX,
|
|
|
3223
3227
|
log(outcome.ok ? "info" : "error", outcome.ok ? "AKM tool call completed" : "AKM tool call failed", {
|
|
3224
3228
|
toolName: spec,
|
|
3225
3229
|
sessionID: context.sessionID,
|
|
3226
|
-
refs:
|
|
3230
|
+
refs: extractRefsFromAkmOutput(outcome.text),
|
|
3227
3231
|
...outcome.ok ? {} : { error: failureMessage(outcome.text) }
|
|
3228
3232
|
});
|
|
3229
3233
|
return outcome;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-opencode-v2",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.261108-beta",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenCode 2 plugin for AKM: the five AKM tools, automatic recall, and automatic session extraction. For OpenCode 1, use akm-opencode.",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"typecheck": "tsc -p tsconfig.json"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"akm-cli": "0.10.
|
|
42
|
+
"akm-cli": "0.10.261108-beta"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@opencode/plugin": "2.1.4",
|