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.
Files changed (2) hide show
  1. package/dist/index.js +62 -58
  2. 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@._+-]+\/\/)?(?:agents|commands|env|facts|instructions|knowledge|lessons|memories|scripts|secrets|sessions|skills|tasks|workflows)\/[A-Za-z0-9._/-]+(?:#[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 && AKM_REF_STRICT.test(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
- if (!AKM_REF_STRICT.test(canonical))
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(type, directPath) {
109
- if (type === "skills")
110
- return isFile(path2.join(directPath, "SKILL.md"));
111
- if (type === "scripts")
112
- return SCRIPT_EXTENSIONS.has(path2.extname(directPath).toLowerCase()) && isFile(directPath);
113
- if (type === "secrets")
114
- return !/\.(lock|sensitive)$/.test(directPath) && isFile(directPath);
115
- if (type === "tasks")
116
- return isFile(`${directPath}.yml`);
117
- if (type === "env") {
118
- return isFile(`${directPath}.env`) || path2.basename(directPath) === "default" && isFile(path2.join(path2.dirname(directPath), ".env"));
119
- }
120
- return markdownFile(directPath);
121
- }
122
- var OUTSIDE_THEIR_DIRECTORY = new Set(["scripts", "agents", "commands", "facts", "instructions", "knowledge", "lessons", "memories", "sessions"]);
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 type = conceptId.split("/", 1)[0];
125
- const rest = conceptId.slice(type.length + 1);
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 !== resolvedRoot && !directPath.startsWith(`${resolvedRoot}${path2.sep}`))
132
- continue;
133
- if (isAsset(type, directPath))
162
+ if (inside(directPath) && isAsset(directPath, conceptId))
134
163
  return true;
135
- if (OUTSIDE_THEIR_DIRECTORY.has(type) && rest.includes("/") && !rest.startsWith(`${type}/`)) {
136
- if (isAsset(type, path2.resolve(resolvedRoot, rest)))
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(toolName, args, output) {
806
- const refs = new Set;
807
- const addMatches = (value) => {
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.tool, input.args, parsed) : validateRefCandidates(candidateRefs, [await input.getBundleDir() ?? ""]);
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.tool, input.args, input.parsed);
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: extractAkmRefsFromString(curated ?? ""),
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: extractAkmRefsFromString(outcome.text),
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.261106-beta",
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.261106-beta"
42
+ "akm-cli": "0.10.261108-beta"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@opencode/plugin": "2.1.4",