akm-opencode 0.10.261105-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 +57 -62
  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,39 +1048,35 @@ 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
+ }
1042
1071
  function conceptExistsInAnyBundle(conceptId, bundleRoots) {
1043
- const type = conceptId.split("/", 1)[0];
1044
- const rest = conceptId.slice(type.length + 1);
1045
1072
  for (const root of bundleRoots) {
1046
1073
  if (!root)
1047
1074
  continue;
1048
1075
  const resolvedRoot = path4.resolve(root);
1076
+ const inside = (file) => file !== resolvedRoot && file.startsWith(`${resolvedRoot}${path4.sep}`);
1049
1077
  const directPath = path4.resolve(resolvedRoot, conceptId);
1050
- if (directPath !== resolvedRoot && !directPath.startsWith(`${resolvedRoot}${path4.sep}`))
1051
- continue;
1052
- if (isAsset(type, directPath))
1078
+ if (inside(directPath) && isAsset(directPath, conceptId))
1053
1079
  return true;
1054
- if (OUTSIDE_THEIR_DIRECTORY.has(type) && rest.includes("/") && !rest.startsWith(`${type}/`)) {
1055
- if (isAsset(type, path4.resolve(resolvedRoot, rest)))
1056
- return true;
1057
- }
1058
1080
  }
1059
1081
  return false;
1060
1082
  }
@@ -1101,35 +1123,9 @@ function classifyToolFeedback(value) {
1101
1123
  return "positive";
1102
1124
  return;
1103
1125
  }
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];
1126
+ function extractToolRefs(args, output) {
1127
+ const named = [args.ref, args.package_ref].map(akmRefFromValue).filter((ref) => !!ref);
1128
+ return [...new Set([...named, ...refsOfAkmResult(output)])];
1133
1129
  }
1134
1130
  function extractAkmRefsFromAllArgs(args) {
1135
1131
  if (!args || typeof args !== "object")
@@ -1208,7 +1204,7 @@ function createFeedbackTracker(options) {
1208
1204
  const isAkmTool = input.tool.startsWith("akm_");
1209
1205
  const candidateRefs = [...new Set([...extractAkmRefsFromAllArgs(input.args), ...extractAkmRefsFromString(input.outputText)])];
1210
1206
  const parsed = isAkmTool ? parseToolOutput(input.outputText) : null;
1211
- const refs = isAkmTool && parsed ? extractToolRefs(input.tool, input.args, parsed) : validateRefCandidates(candidateRefs, [await input.getBundleDir() ?? ""]);
1207
+ const refs = isAkmTool && parsed ? extractToolRefs(input.args, parsed) : validateRefCandidates(candidateRefs, [await input.getBundleDir() ?? ""]);
1212
1208
  const failedCall = classifyToolFeedback(parsed) === "negative";
1213
1209
  if (refs.length > 0) {
1214
1210
  input.writeEvent({
@@ -1228,7 +1224,7 @@ function createFeedbackTracker(options) {
1228
1224
  }
1229
1225
  function observeAkmToolResult(input) {
1230
1226
  const outcome = classifyToolFeedback(input.parsed);
1231
- const refs = extractToolRefs(input.tool, input.args, input.parsed);
1227
+ const refs = extractToolRefs(input.args, input.parsed);
1232
1228
  const failedCall = outcome === "negative";
1233
1229
  if (!failedCall)
1234
1230
  noteRecentRefs(input.sessionID, refs);
@@ -3233,8 +3229,7 @@ async function runCli(client, args, meta) {
3233
3229
  stdout,
3234
3230
  stderr: ""
3235
3231
  });
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);
3232
+ const refs = extractRefsFromAkmOutput(stdout);
3238
3233
  feedbackTracker.noteRecentRefs(meta.sessionID, refs);
3239
3234
  if (meta.toolName === "akm_search") {
3240
3235
  await emitWorkflowTelemetry(client, "info", "akm.search.invoked", {
@@ -3346,7 +3341,7 @@ async function runInProcess(client, operation, input, meta) {
3346
3341
  return result.output;
3347
3342
  }
3348
3343
  const output = result.output;
3349
- const refs = extractAkmRefsFromString(output);
3344
+ const refs = extractRefsFromAkmOutput(output);
3350
3345
  feedbackTracker.noteRecentRefs(meta.sessionID, refs);
3351
3346
  await writePluginLog(client, "info", "AKM read call completed", {
3352
3347
  subsystem: "akm",
@@ -3617,7 +3612,7 @@ ${block}`;
3617
3612
  (async () => {
3618
3613
  try {
3619
3614
  const curated = await runCurateForPrompt(logClient, decision.query, sessionID, directorySnapshot);
3620
- const refs = extractAkmRefsFromString(curated ?? "");
3615
+ const refs = extractRefsFromAkmOutput(curated ?? "");
3621
3616
  writeStructuredEvent({
3622
3617
  event: "prompt_recall",
3623
3618
  sessionId: sessionID,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-opencode",
3
- "version": "0.10.261105-beta",
3
+ "version": "0.10.261107-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.261105-beta"
41
+ "akm-cli": "0.10.261107-beta"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^26.1.1",