@fro.bot/systematic 3.9.0 → 3.9.1

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.
package/dist/index.js CHANGED
@@ -14,125 +14,18 @@ import {
14
14
  loadConfig,
15
15
  loadConfigWithSources,
16
16
  parseFrontmatter
17
- } from "./index-hjbt4p5s.js";
17
+ } from "./index-y8nc60tz.js";
18
18
 
19
19
  // src/index.ts
20
20
  import { createHash as createHash5 } from "crypto";
21
21
  import fs11 from "fs";
22
22
  import path10 from "path";
23
- import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "url";
23
+ import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "url";
24
24
 
25
25
  // src/lib/bootstrap.ts
26
- import fs3 from "fs";
27
- import os from "os";
28
- import path2 from "path";
29
-
30
- // src/lib/skill-catalog.ts
31
- import { pathToFileURL } from "url";
32
-
33
- // src/lib/skill-loader.ts
34
26
  import fs2 from "fs";
27
+ import os from "os";
35
28
  import path from "path";
36
- var SKILL_PREFIX = "systematic:";
37
- var SKILL_DESCRIPTION_PREFIX = "(Systematic) ";
38
- function formatSkillCommandName(name2) {
39
- if (name2.includes(":")) {
40
- return name2;
41
- }
42
- return `${SKILL_PREFIX}${name2}`;
43
- }
44
- function formatSkillDescription(description, fallbackName) {
45
- const desc = description || `${fallbackName} skill`;
46
- if (desc.startsWith(SKILL_DESCRIPTION_PREFIX)) {
47
- return desc;
48
- }
49
- return `${SKILL_DESCRIPTION_PREFIX}${desc}`;
50
- }
51
- function wrapSkillTemplate(skillPath, body2) {
52
- const skillDir = path.dirname(skillPath);
53
- return `<skill-instruction>
54
- Base directory for this skill: ${skillDir}/
55
- File references (@path) in this skill are relative to this directory.
56
-
57
- ${body2.trim()}
58
- </skill-instruction>
59
-
60
- <user-request>
61
- $ARGUMENTS
62
- </user-request>`;
63
- }
64
- function extractSkillBody(wrappedTemplate) {
65
- const match = wrappedTemplate.match(/<skill-instruction>([\s\S]*?)<\/skill-instruction>/);
66
- return match ? match[1].trim() : wrappedTemplate;
67
- }
68
- function loadSkill(skillInfo) {
69
- try {
70
- const content = fs2.readFileSync(skillInfo.skillFile, "utf8");
71
- const { body: body2 } = parseFrontmatter(content);
72
- const wrappedTemplate = wrapSkillTemplate(skillInfo.skillFile, body2);
73
- return {
74
- name: skillInfo.name,
75
- prefixedName: formatSkillCommandName(skillInfo.name),
76
- description: formatSkillDescription(skillInfo.description, skillInfo.name),
77
- path: skillInfo.path,
78
- skillFile: skillInfo.skillFile,
79
- wrappedTemplate,
80
- disableModelInvocation: skillInfo.disableModelInvocation,
81
- userInvocable: skillInfo.userInvocable,
82
- subtask: skillInfo.subtask,
83
- agent: skillInfo.agent,
84
- model: skillInfo.model,
85
- argumentHint: skillInfo.argumentHint
86
- };
87
- } catch {
88
- return null;
89
- }
90
- }
91
-
92
- // src/lib/skill-catalog.ts
93
- function escapeXml(text) {
94
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
95
- }
96
- function buildCatalogEntries(options) {
97
- const { bundledSkillsDir, disabledSkills } = options;
98
- return findSkillsInDir(bundledSkillsDir).filter((s) => !disabledSkills.includes(s.name)).filter((s) => s.disableModelInvocation !== true).sort((a, b) => a.name.localeCompare(b.name)).map((s) => ({
99
- name: s.name,
100
- prefixedName: formatSkillCommandName(s.name),
101
- description: s.description,
102
- path: s.path,
103
- skillFile: s.skillFile
104
- }));
105
- }
106
- function renderCatalogVerbose(options) {
107
- const entries = buildCatalogEntries(options);
108
- if (entries.length === 0)
109
- return "";
110
- const skillLines = entries.flatMap((entry) => [
111
- " <skill>",
112
- ` <name>${escapeXml(entry.prefixedName)}</name>`,
113
- ` <description>${escapeXml(entry.description)}</description>`,
114
- ` <location>${pathToFileURL(entry.path).href}</location>`,
115
- " </skill>"
116
- ]);
117
- return ["<available_skills>", ...skillLines, "</available_skills>"].join(`
118
- `);
119
- }
120
- function renderCatalogCompact(options) {
121
- const entries = buildCatalogEntries(options);
122
- const heading = "## Available Systematic Skills";
123
- if (entries.length === 0) {
124
- return `${heading}
125
-
126
- No Systematic skills are currently available.`;
127
- }
128
- const bullets = entries.map((entry) => `- ${entry.prefixedName}: ${entry.description}`).join(`
129
- `);
130
- return `${heading}
131
-
132
- ${bullets}`;
133
- }
134
-
135
- // src/lib/bootstrap.ts
136
29
  var INTERNAL_AGENT_SIGNATURES = [
137
30
  "You are a title generator",
138
31
  "You are a helpful AI assistant tasked with summarizing conversations",
@@ -190,9 +83,9 @@ var applyBootstrapContent = (output, content) => {
190
83
  ${content}` : content;
191
84
  };
192
85
  function readHarnessProfile(bundledSkillsDir, name2) {
193
- const profilePath = path2.join(bundledSkillsDir, "using-systematic/references", `${name2}-profile.md`);
86
+ const profilePath = path.join(bundledSkillsDir, "using-systematic/references", `${name2}-profile.md`);
194
87
  try {
195
- return fs3.readFileSync(profilePath, "utf8");
88
+ return fs2.readFileSync(profilePath, "utf8");
196
89
  } catch (error) {
197
90
  console.error(`Failed to read harness profile ${profilePath}: ${error instanceof Error ? error.message : String(error)}`);
198
91
  return null;
@@ -216,25 +109,18 @@ function getBootstrapContent(config, deps) {
216
109
  if (!config.bootstrap.enabled)
217
110
  return null;
218
111
  if (config.bootstrap.file) {
219
- const customPath = config.bootstrap.file.startsWith("~/") ? path2.join(os.homedir(), config.bootstrap.file.slice(2)) : config.bootstrap.file;
220
- if (fs3.existsSync(customPath)) {
221
- return fs3.readFileSync(customPath, "utf8");
112
+ const customPath = config.bootstrap.file.startsWith("~/") ? path.join(os.homedir(), config.bootstrap.file.slice(2)) : config.bootstrap.file;
113
+ if (fs2.existsSync(customPath)) {
114
+ return fs2.readFileSync(customPath, "utf8");
222
115
  }
223
116
  }
224
- const usingSystematicPath = path2.join(bundledSkillsDir, "using-systematic/SKILL.md");
225
- if (!fs3.existsSync(usingSystematicPath))
117
+ const usingSystematicPath = path.join(bundledSkillsDir, "using-systematic/SKILL.md");
118
+ if (!fs2.existsSync(usingSystematicPath))
226
119
  return null;
227
- const fullContent = fs3.readFileSync(usingSystematicPath, "utf8");
120
+ const fullContent = fs2.readFileSync(usingSystematicPath, "utf8");
228
121
  const { body: body2 } = parseFrontmatter(fullContent);
229
122
  const content = body2.trim();
230
123
  const skillUsage = usageTemplate ?? getSkillUsageTemplate();
231
- const catalog = renderCatalogVerbose({
232
- bundledSkillsDir,
233
- disabledSkills: config.disabled_skills
234
- });
235
- const catalogSection = catalog.length > 0 ? `
236
-
237
- ${catalog}` : "";
238
124
  const profileSection = profileBlock === undefined ? "" : `
239
125
 
240
126
  ${profileBlock}`;
@@ -245,7 +131,7 @@ You have access to structured engineering workflows via the Systematic plugin.
245
131
 
246
132
  ${content}
247
133
 
248
- ${skillUsage}${profileSection}${catalogSection}
134
+ ${skillUsage}${profileSection}
249
135
  </SYSTEMATIC_WORKFLOWS>`;
250
136
  }
251
137
 
@@ -255,16 +141,16 @@ import os3 from "os";
255
141
  import path6 from "path";
256
142
 
257
143
  // src/lib/agent-overlays.ts
258
- import fs4 from "fs";
259
- import path4 from "path";
144
+ import fs3 from "fs";
145
+ import path3 from "path";
260
146
 
261
147
  // src/lib/source-model-defaults.ts
262
- import * as path3 from "path";
148
+ import * as path2 from "path";
263
149
  import { fileURLToPath } from "url";
264
150
  var __filename2 = fileURLToPath(import.meta.url);
265
- var __dirname2 = path3.dirname(__filename2);
266
- var packageRoot = path3.resolve(__dirname2, "..", "..");
267
- var bundledAgentsDir = path3.join(packageRoot, "agents");
151
+ var __dirname2 = path2.dirname(__filename2);
152
+ var packageRoot = path2.resolve(__dirname2, "..", "..");
153
+ var bundledAgentsDir = path2.join(packageRoot, "agents");
268
154
  var ProviderID = exports_external.union([
269
155
  exports_external.literal("vercel"),
270
156
  exports_external.literal("opencode"),
@@ -453,14 +339,14 @@ function buildBundledAgentInventory(agentsDir, disabledAgents) {
453
339
  const stemCategories = new Map;
454
340
  const disabledSet = new Set(disabledAgents);
455
341
  for (const category of categories) {
456
- for (const fileName of readMarkdownFiles(path4.join(agentsDir, category))) {
342
+ for (const fileName of readMarkdownFiles(path3.join(agentsDir, category))) {
457
343
  const key = fileName.replace(/\.md$/, "");
458
344
  const id = `${category}/${key}`;
459
345
  agentsByQualifiedId[id] = {
460
346
  id,
461
347
  key,
462
348
  category,
463
- file: path4.join(agentsDir, category, fileName),
349
+ file: path3.join(agentsDir, category, fileName),
464
350
  disabled: disabledSet.has(key) || disabledSet.has(id)
465
351
  };
466
352
  const existing = stemCategories.get(key) ?? [];
@@ -585,14 +471,14 @@ function validateOverlaySkills(overlay, skills, enabledSkills) {
585
471
  }
586
472
  function readCategoryDirs(agentsDir) {
587
473
  try {
588
- return fs4.readdirSync(agentsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
474
+ return fs3.readdirSync(agentsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
589
475
  } catch {
590
476
  return [];
591
477
  }
592
478
  }
593
479
  function readMarkdownFiles(categoryDir) {
594
480
  try {
595
- return fs4.readdirSync(categoryDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort();
481
+ return fs3.readdirSync(categoryDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort();
596
482
  } catch {
597
483
  return [];
598
484
  }
@@ -606,9 +492,9 @@ function throwConfigError(sourcePath, keyPath, message) {
606
492
 
607
493
  // src/lib/model-availability.ts
608
494
  import { createHash } from "crypto";
609
- import fs5 from "fs";
495
+ import fs4 from "fs";
610
496
  import os2 from "os";
611
- import path5 from "path";
497
+ import path4 from "path";
612
498
  function emptyAvailability() {
613
499
  return { status: "unknown", models: new Set };
614
500
  }
@@ -618,8 +504,8 @@ var MODELS_JSON_FILENAME = "models.json";
618
504
  var availabilityCache = new WeakMap;
619
505
  function resolveCacheDir() {
620
506
  const xdgCacheHome = process.env.XDG_CACHE_HOME?.trim();
621
- const cacheBase = xdgCacheHome && path5.isAbsolute(xdgCacheHome) ? xdgCacheHome : path5.join(os2.homedir(), ".cache");
622
- return path5.join(cacheBase, "opencode");
507
+ const cacheBase = xdgCacheHome && path4.isAbsolute(xdgCacheHome) ? xdgCacheHome : path4.join(os2.homedir(), ".cache");
508
+ return path4.join(cacheBase, "opencode");
623
509
  }
624
510
  function fastHash(input) {
625
511
  return createHash("sha1").update(input).digest("hex");
@@ -638,7 +524,7 @@ function isProviderRecord(value) {
638
524
  function readModelsFromCache(filePath) {
639
525
  let fd;
640
526
  try {
641
- fd = fs5.openSync(filePath, "r");
527
+ fd = fs4.openSync(filePath, "r");
642
528
  } catch {
643
529
  return null;
644
530
  }
@@ -646,7 +532,7 @@ function readModelsFromCache(filePath) {
646
532
  try {
647
533
  let stat;
648
534
  try {
649
- stat = fs5.fstatSync(fd);
535
+ stat = fs4.fstatSync(fd);
650
536
  } catch {
651
537
  return null;
652
538
  }
@@ -661,7 +547,7 @@ function readModelsFromCache(filePath) {
661
547
  const buffer = Buffer.alloc(stat.size);
662
548
  let bytesRead;
663
549
  try {
664
- bytesRead = fs5.readSync(fd, buffer, 0, stat.size, 0);
550
+ bytesRead = fs4.readSync(fd, buffer, 0, stat.size, 0);
665
551
  } catch {
666
552
  return null;
667
553
  }
@@ -670,7 +556,7 @@ function readModelsFromCache(filePath) {
670
556
  raw = buffer.toString("utf8");
671
557
  } finally {
672
558
  try {
673
- fs5.closeSync(fd);
559
+ fs4.closeSync(fd);
674
560
  } catch {}
675
561
  }
676
562
  if (raw.trim().length === 0)
@@ -697,14 +583,14 @@ function readFallbackCache() {
697
583
  const cacheDir = resolveCacheDir();
698
584
  const openCodeModelsUrl = process.env.OPENCODE_MODELS_URL?.trim();
699
585
  if (openCodeModelsUrl) {
700
- const urlDerivedPath = path5.join(cacheDir, `models-${fastHash(openCodeModelsUrl)}.json`);
586
+ const urlDerivedPath = path4.join(cacheDir, `models-${fastHash(openCodeModelsUrl)}.json`);
701
587
  const urlResult = readModelsFromCache(urlDerivedPath);
702
588
  if (urlResult !== null && urlResult.size > 0) {
703
589
  return { status: "cache", models: urlResult };
704
590
  }
705
591
  return emptyAvailability();
706
592
  }
707
- const defaultPath = path5.join(cacheDir, MODELS_JSON_FILENAME);
593
+ const defaultPath = path4.join(cacheDir, MODELS_JSON_FILENAME);
708
594
  const defaultResult = readModelsFromCache(defaultPath);
709
595
  if (defaultResult !== null && defaultResult.size > 0) {
710
596
  return { status: "cache", models: defaultResult };
@@ -769,6 +655,65 @@ async function getAvailableModels(client, options = {}) {
769
655
  });
770
656
  }
771
657
 
658
+ // src/lib/skill-loader.ts
659
+ import fs5 from "fs";
660
+ import path5 from "path";
661
+ var SKILL_PREFIX = "systematic:";
662
+ var SKILL_DESCRIPTION_PREFIX = "(Systematic) ";
663
+ function formatSkillCommandName(name2) {
664
+ if (name2.includes(":")) {
665
+ return name2;
666
+ }
667
+ return `${SKILL_PREFIX}${name2}`;
668
+ }
669
+ function formatSkillDescription(description, fallbackName) {
670
+ const desc = description || `${fallbackName} skill`;
671
+ if (desc.startsWith(SKILL_DESCRIPTION_PREFIX)) {
672
+ return desc;
673
+ }
674
+ return `${SKILL_DESCRIPTION_PREFIX}${desc}`;
675
+ }
676
+ function wrapSkillTemplate(skillPath, body2) {
677
+ const skillDir = path5.dirname(skillPath);
678
+ return `<skill-instruction>
679
+ Base directory for this skill: ${skillDir}/
680
+ File references (@path) in this skill are relative to this directory.
681
+
682
+ ${body2.trim()}
683
+ </skill-instruction>
684
+
685
+ <user-request>
686
+ $ARGUMENTS
687
+ </user-request>`;
688
+ }
689
+ function extractSkillBody(wrappedTemplate) {
690
+ const match = wrappedTemplate.match(/<skill-instruction>([\s\S]*?)<\/skill-instruction>/);
691
+ return match ? match[1].trim() : wrappedTemplate;
692
+ }
693
+ function loadSkill(skillInfo) {
694
+ try {
695
+ const content = fs5.readFileSync(skillInfo.skillFile, "utf8");
696
+ const { body: body2 } = parseFrontmatter(content);
697
+ const wrappedTemplate = wrapSkillTemplate(skillInfo.skillFile, body2);
698
+ return {
699
+ name: skillInfo.name,
700
+ prefixedName: formatSkillCommandName(skillInfo.name),
701
+ description: formatSkillDescription(skillInfo.description, skillInfo.name),
702
+ path: skillInfo.path,
703
+ skillFile: skillInfo.skillFile,
704
+ wrappedTemplate,
705
+ disableModelInvocation: skillInfo.disableModelInvocation,
706
+ userInvocable: skillInfo.userInvocable,
707
+ subtask: skillInfo.subtask,
708
+ agent: skillInfo.agent,
709
+ model: skillInfo.model,
710
+ argumentHint: skillInfo.argumentHint
711
+ };
712
+ } catch {
713
+ return null;
714
+ }
715
+ }
716
+
772
717
  // src/lib/config-handler.ts
773
718
  function isSystematicAgentConfig(agent) {
774
719
  const description = agent?.description;
@@ -13381,7 +13326,33 @@ function createOpencodeWorkflowGuard(options) {
13381
13326
  // src/lib/skill-resolver.ts
13382
13327
  import fs10 from "fs";
13383
13328
  import path9 from "path";
13384
- import { pathToFileURL as pathToFileURL2 } from "url";
13329
+ import { pathToFileURL } from "url";
13330
+
13331
+ // src/lib/skill-catalog.ts
13332
+ function buildCatalogEntries(options) {
13333
+ const { bundledSkillsDir, disabledSkills } = options;
13334
+ return findSkillsInDir(bundledSkillsDir).filter((s) => !disabledSkills.includes(s.name)).filter((s) => s.disableModelInvocation !== true).sort((a, b) => a.name.localeCompare(b.name)).map((s) => ({
13335
+ name: s.name,
13336
+ prefixedName: formatSkillCommandName(s.name),
13337
+ description: s.description
13338
+ }));
13339
+ }
13340
+ function renderCatalogCompact(options) {
13341
+ const entries = buildCatalogEntries(options);
13342
+ const heading = "## Available Systematic Skills";
13343
+ if (entries.length === 0) {
13344
+ return `${heading}
13345
+
13346
+ No Systematic skills are currently available.`;
13347
+ }
13348
+ const bullets = entries.map((entry) => `- ${entry.prefixedName}: ${entry.description}`).join(`
13349
+ `);
13350
+ return `${heading}
13351
+
13352
+ ${bullets}`;
13353
+ }
13354
+
13355
+ // src/lib/skill-resolver.ts
13385
13356
  function getAllSkills(options) {
13386
13357
  const { bundledSkillsDir, disabledSkills } = options;
13387
13358
  return findSkillsInDir(bundledSkillsDir).filter((s) => !disabledSkills.includes(s.name)).map((skillInfo) => loadSkill(skillInfo)).filter((s) => s !== null).sort((a, b) => a.name.localeCompare(b.name));
@@ -13417,7 +13388,7 @@ function buildSkillToolParameterHint(options) {
13417
13388
  function buildSkillContentOutput(matchedSkill) {
13418
13389
  const body2 = extractSkillBody(matchedSkill.wrappedTemplate);
13419
13390
  const dir = path9.dirname(matchedSkill.skillFile);
13420
- const base = pathToFileURL2(dir).href;
13391
+ const base = pathToFileURL(dir).href;
13421
13392
  const files = discoverSkillFiles(dir);
13422
13393
  const lines = [
13423
13394
  `<skill_content name="${matchedSkill.prefixedName}">`,
@@ -13469,6 +13440,7 @@ function discoverSkillFiles(dir, limit = 10) {
13469
13440
  return files.map((file) => ` <file>${file}</file>`).join(`
13470
13441
  `);
13471
13442
  }
13443
+
13472
13444
  // src/lib/skill-tool.ts
13473
13445
  function createSkillTool(options) {
13474
13446
  const { bundledSkillsDir, disabledSkills } = options;
@@ -13520,7 +13492,7 @@ var bundledSkillsDir = path10.join(packageRoot2, "skills");
13520
13492
  var bundledAgentsDir2 = path10.join(packageRoot2, "agents");
13521
13493
  var bundledCommandsDir = path10.join(packageRoot2, "commands");
13522
13494
  var packageJsonPath = path10.join(packageRoot2, "package.json");
13523
- var canonicalPackageSource = pathToFileURL3(fs11.realpathSync(packageRoot2)).href;
13495
+ var canonicalPackageSource = pathToFileURL2(fs11.realpathSync(packageRoot2)).href;
13524
13496
  var registrationSourceIdentity = createHash5("sha256").update(`systematic/opencode-registration-source/v1/${canonicalPackageSource}`).digest("hex");
13525
13497
  var getPackageVersion = () => {
13526
13498
  try {
@@ -1,15 +1,7 @@
1
- /**
2
- * Escape XML special characters (&, <, >) in text content.
3
- * Quotes are not escaped because catalog names and descriptions are rendered
4
- * as element text, not attribute values.
5
- */
6
- export declare function escapeXml(text: string): string;
7
1
  export interface CatalogEntry {
8
2
  name: string;
9
3
  prefixedName: string;
10
4
  description: string;
11
- path: string;
12
- skillFile: string;
13
5
  }
14
6
  export interface CatalogOptions {
15
7
  bundledSkillsDir: string;
@@ -21,11 +13,6 @@ export interface CatalogOptions {
21
13
  * Returns entries sorted by name.
22
14
  */
23
15
  export declare function buildCatalogEntries(options: CatalogOptions): CatalogEntry[];
24
- /**
25
- * Renders discoverable skills as native-style verbose XML for bootstrap content.
26
- * Returns empty string when no skills are available.
27
- */
28
- export declare function renderCatalogVerbose(options: CatalogOptions): string;
29
16
  /**
30
17
  * Renders discoverable skills as a compact markdown list for tool descriptions.
31
18
  * Always includes the heading; renders an explicit no-skills message when empty.
@@ -1,13 +1,7 @@
1
1
  import type { ToolDefinition } from '@opencode-ai/plugin';
2
- import type { SkillInfo } from './skills.js';
3
2
  export { discoverSkillFiles } from './skill-resolver.js';
4
3
  export interface SkillToolOptions {
5
4
  bundledSkillsDir: string;
6
5
  disabledSkills: string[];
7
6
  }
8
- /**
9
- * Formats skills as XML for tool description.
10
- * Uses indented format matching OpenCode's native skill tool.
11
- */
12
- export declare function formatSkillsXml(skills: SkillInfo[]): string;
13
7
  export declare function createSkillTool(options: SkillToolOptions): ToolDefinition;