akm-opencode 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 +63 -60
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -964,7 +964,8 @@ function shouldSubmitAutomaticFeedback(signal) {
964
964
  // ../claude/shared/ref-extraction.ts
965
965
  import { existsSync as existsSync3, statSync as statSync3 } from "fs";
966
966
  import path4 from "path";
967
- 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._~!$&'()*+,;=:@%/?-]+)?$/;
967
+ var AKM_REF_STRICT = /^(?:[A-Za-z0-9@._+-]+\/\/)?[A-Za-z0-9@._+-]+(?:\/[A-Za-z0-9._+@-]+)*(?:#[A-Za-z0-9._~!$&'()*+,;=:@%/?-]+)?$/;
968
+ 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._~!$&'()*+,;=:@%/?-]+)?$/;
968
969
  var EDGE_PUNCTUATION = new Set([".", ",", ";", ":", "!", "?", "(", ")", "[", "]", "{", "}", "'", '"', "`"]);
969
970
  function normalizeToken(token) {
970
971
  let start = 0;
@@ -979,14 +980,39 @@ function extractAkmRefsFromString(text) {
979
980
  const refs = new Set;
980
981
  for (const token of text.split(/\s+/)) {
981
982
  const normalized = normalizeToken(token);
982
- if (normalized && AKM_REF_STRICT.test(normalized))
983
+ if (normalized && CONCEPT_ROOT_REF.test(normalized))
983
984
  refs.add(normalized);
984
985
  }
985
986
  return [...refs];
986
987
  }
988
+ function akmRefFromValue(value) {
989
+ if (typeof value !== "string")
990
+ return;
991
+ const ref = value.trim();
992
+ return AKM_REF_STRICT.test(ref) && !ref.split("#", 1)[0].split("//").pop().split("/").some((s) => s === "." || s === "..") ? ref : undefined;
993
+ }
994
+ function refsOfAkmResult(result) {
995
+ if (!result || typeof result !== "object")
996
+ return [];
997
+ const r = result;
998
+ const entries = [r, ...Array.isArray(r.hits) ? r.hits : [], ...Array.isArray(r.items) ? r.items : []];
999
+ const refs = entries.map((e) => e && typeof e === "object" ? akmRefFromValue(e.ref) : undefined);
1000
+ return [...new Set(refs.filter((ref) => !!ref))];
1001
+ }
1002
+ function extractRefsFromAkmOutput(text) {
1003
+ let parsed;
1004
+ try {
1005
+ parsed = JSON.parse(text);
1006
+ } catch {
1007
+ parsed = undefined;
1008
+ }
1009
+ const refLines = [...text.matchAll(/^\s*ref:\s*(\S+)\s*$/gm)].map((m) => akmRefFromValue(m[1]));
1010
+ return [...new Set([...refsOfAkmResult(parsed), ...refLines.filter((r) => !!r), ...extractAkmRefsFromString(text)])];
1011
+ }
987
1012
  function normalizeCandidate(candidate) {
988
1013
  const canonical = normalizeToken(candidate);
989
- if (!AKM_REF_STRICT.test(canonical))
1014
+ const ref = akmRefFromValue(canonical);
1015
+ if (!ref)
990
1016
  return null;
991
1017
  const withoutFragment = canonical.split("#", 1)[0] ?? "";
992
1018
  const separator = withoutFragment.indexOf("//");
@@ -1022,37 +1048,41 @@ var SCRIPT_EXTENSIONS = new Set([
1022
1048
  ".kts"
1023
1049
  ]);
1024
1050
  function markdownFile(directPath) {
1051
+ if (path4.basename(directPath).replace(/\.md$/, "") === "SKILL")
1052
+ return false;
1025
1053
  return isFile(`${directPath}.md`) || directPath.endsWith(".md") && isFile(directPath);
1026
1054
  }
1027
- function isAsset(type, directPath) {
1028
- if (type === "skills")
1029
- return isFile(path4.join(directPath, "SKILL.md"));
1030
- if (type === "scripts")
1031
- return SCRIPT_EXTENSIONS.has(path4.extname(directPath).toLowerCase()) && isFile(directPath);
1032
- if (type === "secrets")
1033
- return !/\.(lock|sensitive)$/.test(directPath) && isFile(directPath);
1034
- if (type === "tasks")
1035
- return isFile(`${directPath}.yml`);
1036
- if (type === "env") {
1037
- return isFile(`${directPath}.env`) || path4.basename(directPath) === "default" && isFile(path4.join(path4.dirname(directPath), ".env"));
1038
- }
1039
- return markdownFile(directPath);
1040
- }
1041
- var OUTSIDE_THEIR_DIRECTORY = new Set(["scripts", "agents", "commands", "facts", "instructions", "knowledge", "lessons", "memories", "sessions"]);
1055
+ function isAsset(directPath, ref) {
1056
+ if (isFile(path4.join(directPath, "SKILL.md")) || markdownFile(directPath))
1057
+ return true;
1058
+ if (SCRIPT_EXTENSIONS.has(path4.extname(directPath).toLowerCase()) && isFile(directPath))
1059
+ return true;
1060
+ const folders = ref.split("/").slice(0, -1);
1061
+ if (folders.includes("env")) {
1062
+ if (isFile(`${directPath}.env`))
1063
+ return true;
1064
+ if (path4.basename(directPath) === "default" && isFile(path4.join(path4.dirname(directPath), ".env")))
1065
+ return true;
1066
+ }
1067
+ if (folders.includes("tasks") && isFile(`${directPath}.yml`))
1068
+ return true;
1069
+ return folders.includes("secrets") && !/\.(lock|sensitive)$/.test(directPath) && isFile(directPath);
1070
+ }
1071
+ var LEGACY_PREFIXES = new Set(["scripts", "agents", "commands", "facts", "instructions", "knowledge", "lessons", "memories", "sessions"]);
1042
1072
  function conceptExistsInAnyBundle(conceptId, bundleRoots) {
1043
- const type = conceptId.split("/", 1)[0];
1044
- const rest = conceptId.slice(type.length + 1);
1073
+ const head = conceptId.split("/", 1)[0];
1074
+ const rest = conceptId.slice(head.length + 1);
1045
1075
  for (const root of bundleRoots) {
1046
1076
  if (!root)
1047
1077
  continue;
1048
1078
  const resolvedRoot = path4.resolve(root);
1079
+ const inside = (file) => file !== resolvedRoot && file.startsWith(`${resolvedRoot}${path4.sep}`);
1049
1080
  const directPath = path4.resolve(resolvedRoot, conceptId);
1050
- if (directPath !== resolvedRoot && !directPath.startsWith(`${resolvedRoot}${path4.sep}`))
1051
- continue;
1052
- if (isAsset(type, directPath))
1081
+ if (inside(directPath) && isAsset(directPath, conceptId))
1053
1082
  return true;
1054
- if (OUTSIDE_THEIR_DIRECTORY.has(type) && rest.includes("/") && !rest.startsWith(`${type}/`)) {
1055
- if (isAsset(type, path4.resolve(resolvedRoot, rest)))
1083
+ if (LEGACY_PREFIXES.has(head) && rest.includes("/")) {
1084
+ const legacyPath = path4.resolve(resolvedRoot, rest);
1085
+ if (inside(legacyPath) && isAsset(legacyPath, rest))
1056
1086
  return true;
1057
1087
  }
1058
1088
  }
@@ -1101,35 +1131,9 @@ function classifyToolFeedback(value) {
1101
1131
  return "positive";
1102
1132
  return;
1103
1133
  }
1104
- function extractToolRefs(toolName, args, output) {
1105
- const refs = new Set;
1106
- const addMatches = (value) => {
1107
- if (typeof value !== "string")
1108
- return;
1109
- for (const ref of extractAkmRefsFromString(value))
1110
- refs.add(ref);
1111
- };
1112
- for (const key of ["ref", "package_ref"])
1113
- addMatches(args[key]);
1114
- if (output && typeof output === "object") {
1115
- const o = output;
1116
- addMatches(o.ref);
1117
- if (Array.isArray(o.hits)) {
1118
- for (const hit of o.hits) {
1119
- if (hit && typeof hit === "object")
1120
- addMatches(hit.ref);
1121
- }
1122
- }
1123
- if (Array.isArray(o.items)) {
1124
- for (const item of o.items) {
1125
- if (item && typeof item === "object")
1126
- addMatches(item.ref);
1127
- }
1128
- }
1129
- if (toolName === "akm_remember" && typeof o.ref === "string")
1130
- addMatches(o.ref);
1131
- }
1132
- return [...refs];
1134
+ function extractToolRefs(args, output) {
1135
+ const named = [args.ref, args.package_ref].map(akmRefFromValue).filter((ref) => !!ref);
1136
+ return [...new Set([...named, ...refsOfAkmResult(output)])];
1133
1137
  }
1134
1138
  function extractAkmRefsFromAllArgs(args) {
1135
1139
  if (!args || typeof args !== "object")
@@ -1208,7 +1212,7 @@ function createFeedbackTracker(options) {
1208
1212
  const isAkmTool = input.tool.startsWith("akm_");
1209
1213
  const candidateRefs = [...new Set([...extractAkmRefsFromAllArgs(input.args), ...extractAkmRefsFromString(input.outputText)])];
1210
1214
  const parsed = isAkmTool ? parseToolOutput(input.outputText) : null;
1211
- const refs = isAkmTool && parsed ? extractToolRefs(input.tool, input.args, parsed) : validateRefCandidates(candidateRefs, [await input.getBundleDir() ?? ""]);
1215
+ const refs = isAkmTool && parsed ? extractToolRefs(input.args, parsed) : validateRefCandidates(candidateRefs, [await input.getBundleDir() ?? ""]);
1212
1216
  const failedCall = classifyToolFeedback(parsed) === "negative";
1213
1217
  if (refs.length > 0) {
1214
1218
  input.writeEvent({
@@ -1228,7 +1232,7 @@ function createFeedbackTracker(options) {
1228
1232
  }
1229
1233
  function observeAkmToolResult(input) {
1230
1234
  const outcome = classifyToolFeedback(input.parsed);
1231
- const refs = extractToolRefs(input.tool, input.args, input.parsed);
1235
+ const refs = extractToolRefs(input.args, input.parsed);
1232
1236
  const failedCall = outcome === "negative";
1233
1237
  if (!failedCall)
1234
1238
  noteRecentRefs(input.sessionID, refs);
@@ -3233,8 +3237,7 @@ async function runCli(client, args, meta) {
3233
3237
  stdout,
3234
3238
  stderr: ""
3235
3239
  });
3236
- const parsed = safeJsonParse(stdout);
3237
- const refs = args[0] === "search" || args[0] === "curate" ? [...new Set([...parsed?.hits?.flatMap((hit) => hit.ref ? [hit.ref] : []) ?? [], ...extractAkmRefsFromString(stdout)])] : extractAkmRefsFromString(stdout);
3240
+ const refs = extractRefsFromAkmOutput(stdout);
3238
3241
  feedbackTracker.noteRecentRefs(meta.sessionID, refs);
3239
3242
  if (meta.toolName === "akm_search") {
3240
3243
  await emitWorkflowTelemetry(client, "info", "akm.search.invoked", {
@@ -3346,7 +3349,7 @@ async function runInProcess(client, operation, input, meta) {
3346
3349
  return result.output;
3347
3350
  }
3348
3351
  const output = result.output;
3349
- const refs = extractAkmRefsFromString(output);
3352
+ const refs = extractRefsFromAkmOutput(output);
3350
3353
  feedbackTracker.noteRecentRefs(meta.sessionID, refs);
3351
3354
  await writePluginLog(client, "info", "AKM read call completed", {
3352
3355
  subsystem: "akm",
@@ -3617,7 +3620,7 @@ ${block}`;
3617
3620
  (async () => {
3618
3621
  try {
3619
3622
  const curated = await runCurateForPrompt(logClient, decision.query, sessionID, directorySnapshot);
3620
- const refs = extractAkmRefsFromString(curated ?? "");
3623
+ const refs = extractRefsFromAkmOutput(curated ?? "");
3621
3624
  writeStructuredEvent({
3622
3625
  event: "prompt_recall",
3623
3626
  sessionId: sessionID,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-opencode",
3
- "version": "0.10.261106-beta",
3
+ "version": "0.10.261108-beta",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for AKM with discovery tools, feedback, memory, and automatic learning proposals from corrections and recurring workflows.",
6
6
  "keywords": [
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@opencode-ai/plugin": "^1.18.14",
41
- "akm-cli": "0.10.261106-beta"
41
+ "akm-cli": "0.10.261108-beta"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^26.1.1",