nexrall-code 0.5.94 → 0.5.95

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 +163 -38
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -11654,17 +11654,86 @@ var require_frontmatter = __commonJS({
11654
11654
  "use strict";
11655
11655
  Object.defineProperty(exports2, "__esModule", { value: true });
11656
11656
  exports2.parseFrontmatter = parseFrontmatter;
11657
+ var TOP_LEVEL_KEY = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/;
11658
+ var BLOCK_SCALAR = /^[|>][+-]?$/;
11659
+ function leadingIndent(line) {
11660
+ const m2 = /^[ \t]*/.exec(line);
11661
+ return m2 ? m2[0].length : 0;
11662
+ }
11663
+ function unquote(v) {
11664
+ return v.trim().replace(/^["']|["']$/g, "");
11665
+ }
11657
11666
  function parseFrontmatter(raw) {
11658
11667
  const m2 = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
11659
11668
  if (!m2)
11660
- return { meta: {}, body: raw.trim() };
11669
+ return { meta: {}, body: raw.trim(), ok: false };
11670
+ const lines = m2[1].split(/\r?\n/);
11661
11671
  const meta = {};
11662
- for (const line of m2[1].split(/\r?\n/)) {
11663
- const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
11664
- if (kv)
11665
- meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, "");
11672
+ let nested;
11673
+ let i2 = 0;
11674
+ while (i2 < lines.length) {
11675
+ const line = lines[i2];
11676
+ if (!line.trim() || leadingIndent(line) > 0) {
11677
+ i2++;
11678
+ continue;
11679
+ }
11680
+ const kv = TOP_LEVEL_KEY.exec(line.trim());
11681
+ if (!kv) {
11682
+ i2++;
11683
+ continue;
11684
+ }
11685
+ const key = kv[1].toLowerCase();
11686
+ const value = kv[2].trim();
11687
+ const continuation = [];
11688
+ let j = i2 + 1;
11689
+ while (j < lines.length && (leadingIndent(lines[j]) > 0 || !lines[j].trim())) {
11690
+ continuation.push(lines[j]);
11691
+ j++;
11692
+ }
11693
+ while (continuation.length && !continuation[continuation.length - 1].trim())
11694
+ continuation.pop();
11695
+ if (BLOCK_SCALAR.test(value)) {
11696
+ const folded = value.startsWith(">");
11697
+ const base = continuation.length ? leadingIndent(continuation.find((l2) => l2.trim()) ?? continuation[0]) : 0;
11698
+ const dedented = continuation.map((l2) => l2.trim() ? l2.slice(Math.min(base, leadingIndent(l2))) : "");
11699
+ meta[key] = folded ? dedented.join(" ").replace(/\s+/g, " ").trim() : dedented.join("\n").trim();
11700
+ } else if (value === "") {
11701
+ const looksNested = continuation.length > 0 && continuation.every((l2) => !l2.trim() || TOP_LEVEL_KEY.test(l2.trim()));
11702
+ if (looksNested && continuation.some((l2) => l2.trim())) {
11703
+ const map = {};
11704
+ for (const l2 of continuation) {
11705
+ const sub = TOP_LEVEL_KEY.exec(l2.trim());
11706
+ if (sub)
11707
+ map[sub[1].toLowerCase()] = unquote(sub[2]);
11708
+ }
11709
+ nested = nested ?? {};
11710
+ nested[key] = map;
11711
+ } else if (continuation.length) {
11712
+ meta[key] = [value, ...continuation.map((l2) => l2.trim())].filter(Boolean).join(" ").trim();
11713
+ } else {
11714
+ meta[key] = "";
11715
+ }
11716
+ } else {
11717
+ const looksNested = continuation.length > 0 && continuation.every((l2) => !l2.trim() || TOP_LEVEL_KEY.test(l2.trim()));
11718
+ if (continuation.length && !looksNested) {
11719
+ meta[key] = unquote([value, ...continuation.map((l2) => l2.trim())].join(" "));
11720
+ } else if (continuation.length && looksNested) {
11721
+ meta[key] = unquote(value);
11722
+ const map = {};
11723
+ for (const l2 of continuation) {
11724
+ const sub = TOP_LEVEL_KEY.exec(l2.trim());
11725
+ if (sub)
11726
+ map[sub[1].toLowerCase()] = unquote(sub[2]);
11727
+ }
11728
+ nested = nested ?? {};
11729
+ nested[key] = map;
11730
+ } else {
11731
+ meta[key] = unquote(value);
11732
+ }
11733
+ }
11734
+ i2 = j;
11666
11735
  }
11667
- return { meta, body: (m2[2] ?? "").trim() };
11736
+ return { meta, body: (m2[2] ?? "").trim(), nested, ok: true };
11668
11737
  }
11669
11738
  }
11670
11739
  });
@@ -11898,6 +11967,7 @@ var require_skills = __commonJS({
11898
11967
  };
11899
11968
  }();
11900
11969
  Object.defineProperty(exports2, "__esModule", { value: true });
11970
+ exports2.loadSkillsWithWarnings = loadSkillsWithWarnings;
11901
11971
  exports2.loadSkills = loadSkills2;
11902
11972
  exports2.findSkill = findSkill2;
11903
11973
  exports2.autoInvokableSkills = autoInvokableSkills;
@@ -11910,6 +11980,19 @@ var require_skills = __commonJS({
11910
11980
  var index_1 = require_plugins();
11911
11981
  var loader_1 = require_loader();
11912
11982
  var frontmatter_1 = require_frontmatter();
11983
+ var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
11984
+ "name",
11985
+ "description",
11986
+ "model",
11987
+ "mode",
11988
+ "disable-model-invocation",
11989
+ "user-invocable",
11990
+ "license",
11991
+ "compatibility",
11992
+ "metadata",
11993
+ "allowed-tools"
11994
+ ]);
11995
+ var SPEC_NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
11913
11996
  function parseBool(v, fallback) {
11914
11997
  if (v === void 0)
11915
11998
  return fallback;
@@ -11920,7 +12003,13 @@ var require_skills = __commonJS({
11920
12003
  return false;
11921
12004
  return fallback;
11922
12005
  }
11923
- function toSkill(meta, body, name, source2, dir) {
12006
+ function parseSpaceList(v) {
12007
+ if (!v)
12008
+ return void 0;
12009
+ const items = v.split(/[,\s]+/).map((t2) => t2.trim()).filter(Boolean);
12010
+ return items.length ? items : void 0;
12011
+ }
12012
+ function toSkill(meta, body, name, source2, dir, nested) {
11924
12013
  const model = ["turbo", "pro", "ultra"].find((x2) => x2 === (meta.model ?? "").toLowerCase());
11925
12014
  return {
11926
12015
  name,
@@ -11931,10 +12020,38 @@ var require_skills = __commonJS({
11931
12020
  source: source2,
11932
12021
  dir,
11933
12022
  disableModelInvocation: parseBool(meta["disable-model-invocation"], false),
11934
- userInvocable: parseBool(meta["user-invocable"], true)
12023
+ userInvocable: parseBool(meta["user-invocable"], true),
12024
+ license: meta.license || void 0,
12025
+ compatibility: meta.compatibility || void 0,
12026
+ metadata: nested?.metadata,
12027
+ allowedTools: parseSpaceList(meta["allowed-tools"])
11935
12028
  };
11936
12029
  }
11937
- function loadFlatCommandDir(dir, source2, into) {
12030
+ function validateSkillMeta(meta, rawName, effectiveName, dirName, file, warnings) {
12031
+ if (!warnings)
12032
+ return;
12033
+ if (!SPEC_NAME_RE.test(rawName)) {
12034
+ warnings.push({
12035
+ file,
12036
+ name: effectiveName,
12037
+ message: `name "${rawName}" does not match the agentskills.io convention (lowercase letters, numbers, and single hyphens only) \u2014 loaded anyway, but other clients reading this skill may warn or reject it.`
12038
+ });
12039
+ } else if (rawName.length > 64) {
12040
+ warnings.push({ file, name: effectiveName, message: `name is ${rawName.length} characters \u2014 the spec caps this at 64. Loaded anyway.` });
12041
+ }
12042
+ if (dirName !== void 0 && meta.name && meta.name.trim().toLowerCase() !== dirName.toLowerCase()) {
12043
+ warnings.push({
12044
+ file,
12045
+ name: effectiveName,
12046
+ message: `frontmatter name "${meta.name}" does not match its directory name "${dirName}" \u2014 the spec requires them to match for cross-client compatibility. Loaded anyway, using the frontmatter name.`
12047
+ });
12048
+ }
12049
+ const strayKeys = Object.keys(meta).filter((k) => !KNOWN_META_KEYS.has(k));
12050
+ if (strayKeys.length) {
12051
+ warnings.push({ file, name: effectiveName, message: `has unrecognised frontmatter key(s): ${strayKeys.join(", ")} \u2014 these are ignored.` });
12052
+ }
12053
+ }
12054
+ function loadFlatCommandDir(dir, source2, into, warnings) {
11938
12055
  let files;
11939
12056
  try {
11940
12057
  files = fs9.readdirSync(dir).filter((f3) => f3.endsWith(".md"));
@@ -11942,20 +12059,24 @@ var require_skills = __commonJS({
11942
12059
  return;
11943
12060
  }
11944
12061
  for (const file of files) {
12062
+ const full = path7.join(dir, file);
11945
12063
  try {
11946
- const raw = fs9.readFileSync(path7.join(dir, file), "utf-8");
12064
+ const raw = fs9.readFileSync(full, "utf-8");
11947
12065
  const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
11948
- const name = (meta.name || path7.basename(file, ".md")).trim().toLowerCase();
12066
+ const rawName = (meta.name || path7.basename(file, ".md")).trim();
12067
+ const name = rawName.toLowerCase();
11949
12068
  if (!name)
11950
12069
  continue;
11951
12070
  if (source2 !== "project" && into.has(name))
11952
12071
  continue;
12072
+ validateSkillMeta(meta, rawName, name, void 0, full, warnings);
11953
12073
  into.set(name, toSkill(meta, body, name, source2));
11954
- } catch {
12074
+ } catch (err) {
12075
+ warnings?.push({ file: full, name: path7.basename(file, ".md"), message: `could not be read or parsed (${err.message}) \u2014 skipped.` });
11955
12076
  }
11956
12077
  }
11957
12078
  }
11958
- function loadSkillDir(root, source2, into) {
12079
+ function loadSkillDir(root, source2, into, warnings) {
11959
12080
  let entries;
11960
12081
  try {
11961
12082
  entries = fs9.readdirSync(root, { withFileTypes: true });
@@ -11975,14 +12096,17 @@ var require_skills = __commonJS({
11975
12096
  }
11976
12097
  try {
11977
12098
  const raw = fs9.readFileSync(skillFile, "utf-8");
11978
- const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
11979
- const name = (meta.name || entry.name).trim().toLowerCase();
12099
+ const { meta, body, nested } = (0, frontmatter_1.parseFrontmatter)(raw);
12100
+ const rawName = (meta.name || entry.name).trim();
12101
+ const name = rawName.toLowerCase();
11980
12102
  if (!name)
11981
12103
  continue;
11982
12104
  if (source2 !== "project" && into.has(name))
11983
12105
  continue;
11984
- into.set(name, toSkill(meta, body, name, source2, skillDir));
11985
- } catch {
12106
+ validateSkillMeta(meta, rawName, name, entry.name, skillFile, warnings);
12107
+ into.set(name, toSkill(meta, body, name, source2, skillDir, nested));
12108
+ } catch (err) {
12109
+ warnings?.push({ file: skillFile, name: entry.name, message: `could not be read or parsed (${err.message}) \u2014 skipped.` });
11986
12110
  }
11987
12111
  }
11988
12112
  }
@@ -12022,21 +12146,27 @@ var require_skills = __commonJS({
12022
12146
  ].join("\n")
12023
12147
  }
12024
12148
  ];
12025
- function loadSkills2(workDir) {
12149
+ function loadSkillsWithWarnings(workDir) {
12026
12150
  const out = /* @__PURE__ */ new Map();
12027
- loadFlatCommandDir(path7.join(workDir, ".nexrall", "commands"), "project", out);
12028
- loadSkillDir(path7.join(workDir, ".nexrall", "skills"), "project", out);
12029
- loadFlatCommandDir(path7.join(os6.homedir(), ".nexrall", "commands"), "global", out);
12030
- loadSkillDir(path7.join(os6.homedir(), ".nexrall", "skills"), "global", out);
12151
+ const warnings = [];
12152
+ loadSkillDir(path7.join(workDir, ".agents", "skills"), "project", out, warnings);
12153
+ loadFlatCommandDir(path7.join(workDir, ".nexrall", "commands"), "project", out, warnings);
12154
+ loadSkillDir(path7.join(workDir, ".nexrall", "skills"), "project", out, warnings);
12155
+ loadFlatCommandDir(path7.join(os6.homedir(), ".nexrall", "commands"), "global", out, warnings);
12156
+ loadSkillDir(path7.join(os6.homedir(), ".nexrall", "skills"), "global", out, warnings);
12157
+ loadSkillDir(path7.join(os6.homedir(), ".agents", "skills"), "global", out, warnings);
12031
12158
  for (const dir of (0, index_1.pluginAssetDirs)(workDir, "commands"))
12032
- loadFlatCommandDir(dir, "plugin", out);
12159
+ loadFlatCommandDir(dir, "plugin", out, warnings);
12033
12160
  for (const dir of (0, index_1.pluginAssetDirs)(workDir, "skills"))
12034
- loadSkillDir(dir, "plugin", out);
12161
+ loadSkillDir(dir, "plugin", out, warnings);
12035
12162
  for (const skill of BUILTIN_SKILLS) {
12036
12163
  if (!out.has(skill.name))
12037
12164
  out.set(skill.name, skill);
12038
12165
  }
12039
- return [...out.values()];
12166
+ return { skills: [...out.values()], warnings };
12167
+ }
12168
+ function loadSkills2(workDir) {
12169
+ return loadSkillsWithWarnings(workDir).skills;
12040
12170
  }
12041
12171
  function findSkill2(skills, name) {
12042
12172
  const want = name.replace(/^\//, "").trim().toLowerCase();
@@ -101639,6 +101769,7 @@ var require_agentTypes = __commonJS({
101639
101769
  var path7 = __importStar(__require("path"));
101640
101770
  var os6 = __importStar(__require("os"));
101641
101771
  var index_1 = require_plugins();
101772
+ var frontmatter_1 = require_frontmatter();
101642
101773
  var VALID_MEMORY_SCOPES = ["project", "user", "local"];
101643
101774
  function parseMemoryScope(v) {
101644
101775
  const s2 = (v ?? "").trim().toLowerCase();
@@ -101992,18 +102123,6 @@ var require_agentTypes = __commonJS({
101992
102123
  "ultra",
101993
102124
  "fast"
101994
102125
  ];
101995
- function parseFrontmatter(raw) {
101996
- const m2 = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
101997
- if (!m2)
101998
- return { meta: {}, body: raw.trim(), ok: false };
101999
- const meta = {};
102000
- for (const line of m2[1].split(/\r?\n/)) {
102001
- const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
102002
- if (kv)
102003
- meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, "");
102004
- }
102005
- return { meta, body: (m2[2] ?? "").trim(), ok: true };
102006
- }
102007
102126
  function parseModel(v) {
102008
102127
  const raw = (v ?? "").trim();
102009
102128
  if (!raw)
@@ -102044,7 +102163,7 @@ var require_agentTypes = __commonJS({
102044
102163
  warnings.push({ file: full, agent: path7.basename(entry.name, ".md"), message: `could not be read (${err.message}) \u2014 this agent was skipped` });
102045
102164
  continue;
102046
102165
  }
102047
- const { meta, body, ok } = parseFrontmatter(raw);
102166
+ const { meta, body, ok } = (0, frontmatter_1.parseFrontmatter)(raw);
102048
102167
  const name = (meta.name || path7.basename(entry.name, ".md")).trim();
102049
102168
  if (!name)
102050
102169
  continue;
@@ -106956,6 +107075,9 @@ var require_trust = __commonJS({
106956
107075
  if (exists(".nexrall", "agents")) {
106957
107076
  signals.push(".nexrall/agents/ \u2014 custom sub-agent definitions");
106958
107077
  }
107078
+ if (exists(".agents", "skills")) {
107079
+ signals.push(".agents/skills/ \u2014 cross-client agent playbooks (agentskills.io convention)");
107080
+ }
106959
107081
  return signals;
106960
107082
  }
106961
107083
  }
@@ -143340,6 +143462,9 @@ function detectTrustSignals(dir) {
143340
143462
  if (exists(".nexrall", "agents")) {
143341
143463
  signals.push(".nexrall/agents/ \u2014 custom sub-agent definitions");
143342
143464
  }
143465
+ if (exists(".agents", "skills")) {
143466
+ signals.push(".agents/skills/ \u2014 cross-client agent playbooks (agentskills.io convention)");
143467
+ }
143343
143468
  return signals;
143344
143469
  }
143345
143470
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.94",
3
+ "version": "0.5.95",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",
@@ -41,7 +41,7 @@
41
41
  "release": "node build.js && node scripts/upload-release.cjs"
42
42
  },
43
43
  "dependencies": {
44
- "@nexrall/code-core": "^1.4.47",
44
+ "@nexrall/code-core": "^1.4.48",
45
45
  "chalk": "^5.3.0",
46
46
  "commander": "^12.0.0",
47
47
  "diff": "^5.2.0",