akm-opencode-v2 0.10.261106-beta → 0.10.261107-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 +56 -60
  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,39 +129,35 @@ 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
+ }
123
152
  function conceptExistsInAnyBundle(conceptId, bundleRoots) {
124
- const type = conceptId.split("/", 1)[0];
125
- const rest = conceptId.slice(type.length + 1);
126
153
  for (const root of bundleRoots) {
127
154
  if (!root)
128
155
  continue;
129
156
  const resolvedRoot = path2.resolve(root);
157
+ const inside = (file) => file !== resolvedRoot && file.startsWith(`${resolvedRoot}${path2.sep}`);
130
158
  const directPath = path2.resolve(resolvedRoot, conceptId);
131
- if (directPath !== resolvedRoot && !directPath.startsWith(`${resolvedRoot}${path2.sep}`))
132
- continue;
133
- if (isAsset(type, directPath))
159
+ if (inside(directPath) && isAsset(directPath, conceptId))
134
160
  return true;
135
- if (OUTSIDE_THEIR_DIRECTORY.has(type) && rest.includes("/") && !rest.startsWith(`${type}/`)) {
136
- if (isAsset(type, path2.resolve(resolvedRoot, rest)))
137
- return true;
138
- }
139
161
  }
140
162
  return false;
141
163
  }
@@ -802,35 +824,9 @@ function classifyToolFeedback(value) {
802
824
  return "positive";
803
825
  return;
804
826
  }
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];
827
+ function extractToolRefs(args, output) {
828
+ const named = [args.ref, args.package_ref].map(akmRefFromValue).filter((ref) => !!ref);
829
+ return [...new Set([...named, ...refsOfAkmResult(output)])];
834
830
  }
835
831
  function extractAkmRefsFromAllArgs(args) {
836
832
  if (!args || typeof args !== "object")
@@ -909,7 +905,7 @@ function createFeedbackTracker(options) {
909
905
  const isAkmTool = input.tool.startsWith("akm_");
910
906
  const candidateRefs = [...new Set([...extractAkmRefsFromAllArgs(input.args), ...extractAkmRefsFromString(input.outputText)])];
911
907
  const parsed = isAkmTool ? parseToolOutput(input.outputText) : null;
912
- const refs = isAkmTool && parsed ? extractToolRefs(input.tool, input.args, parsed) : validateRefCandidates(candidateRefs, [await input.getBundleDir() ?? ""]);
908
+ const refs = isAkmTool && parsed ? extractToolRefs(input.args, parsed) : validateRefCandidates(candidateRefs, [await input.getBundleDir() ?? ""]);
913
909
  const failedCall = classifyToolFeedback(parsed) === "negative";
914
910
  if (refs.length > 0) {
915
911
  input.writeEvent({
@@ -929,7 +925,7 @@ function createFeedbackTracker(options) {
929
925
  }
930
926
  function observeAkmToolResult(input) {
931
927
  const outcome = classifyToolFeedback(input.parsed);
932
- const refs = extractToolRefs(input.tool, input.args, input.parsed);
928
+ const refs = extractToolRefs(input.args, input.parsed);
933
929
  const failedCall = outcome === "negative";
934
930
  if (!failedCall)
935
931
  noteRecentRefs(input.sessionID, refs);
@@ -3017,7 +3013,7 @@ ${NEED_MORE_CONTEXT_HINT}` : NEED_MORE_CONTEXT_HINT;
3017
3013
  sessionID,
3018
3014
  query: truncateLogText(decision.query, 200),
3019
3015
  reason: decision.reason,
3020
- refs: extractAkmRefsFromString(curated ?? ""),
3016
+ refs: extractRefsFromAkmOutput(curated ?? ""),
3021
3017
  outcome: curated ? "ok" : "skipped"
3022
3018
  });
3023
3019
  });
@@ -3223,7 +3219,7 @@ ${st.hints}` : AKM_HINTS_PREFIX,
3223
3219
  log(outcome.ok ? "info" : "error", outcome.ok ? "AKM tool call completed" : "AKM tool call failed", {
3224
3220
  toolName: spec,
3225
3221
  sessionID: context.sessionID,
3226
- refs: extractAkmRefsFromString(outcome.text),
3222
+ refs: extractRefsFromAkmOutput(outcome.text),
3227
3223
  ...outcome.ok ? {} : { error: failureMessage(outcome.text) }
3228
3224
  });
3229
3225
  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.261107-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.261107-beta"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@opencode/plugin": "2.1.4",