@fro.bot/systematic 2.33.3 → 3.0.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.
Files changed (112) hide show
  1. package/ATTRIBUTIONS.md +1 -1
  2. package/agents/design/design-iterator.md +1 -0
  3. package/agents/research/best-practices-researcher.md +6 -8
  4. package/agents/review/pattern-recognition-specialist.md +1 -1
  5. package/agents/workflow/systematic-implementer.md +1 -0
  6. package/dist/cli.js +348 -53
  7. package/dist/{index-vyzhzvap.js → index-yskzf7vh.js} +587 -386
  8. package/dist/index.js +100 -155
  9. package/dist/lib/agent-overlays.d.ts +0 -1
  10. package/dist/lib/agent-resolver.d.ts +28 -0
  11. package/dist/lib/bootstrap.d.ts +8 -1
  12. package/dist/lib/bundled-names.d.ts +3 -3
  13. package/dist/lib/pi-delegate-session.d.ts +31 -0
  14. package/dist/lib/pi-delegate-tool.d.ts +42 -0
  15. package/dist/lib/removed-names.d.ts +3 -3
  16. package/dist/lib/setup.d.ts +36 -0
  17. package/dist/lib/skill-loader.d.ts +1 -2
  18. package/dist/lib/skill-resolver.d.ts +39 -0
  19. package/dist/lib/skill-tool.d.ts +2 -11
  20. package/dist/lib/skills.d.ts +1 -9
  21. package/dist/lib/source-model-defaults.d.ts +1 -1
  22. package/dist/pi.d.ts +7 -0
  23. package/dist/pi.js +3641 -0
  24. package/dist/schemas/systematic-config.schema.json +47 -242
  25. package/package.json +29 -6
  26. package/skills/ce-brainstorm/references/handoff.md +5 -26
  27. package/skills/ce-brainstorm/references/universal-brainstorming.md +0 -1
  28. package/skills/ce-compound/SKILL.md +8 -12
  29. package/skills/ce-ideate/references/post-ideation-workflow.md +21 -85
  30. package/skills/ce-plan/SKILL.md +6 -6
  31. package/skills/ce-plan/references/deepening-workflow.md +6 -7
  32. package/skills/ce-plan/references/plan-handoff.md +1 -17
  33. package/skills/ce-plan/references/universal-planning.md +0 -4
  34. package/skills/ce-review/SKILL.md +10 -19
  35. package/skills/ce-review/references/persona-catalog.md +2 -7
  36. package/skills/ce-review/references/review-output-template.md +0 -1
  37. package/skills/ce-work/SKILL.md +1 -1
  38. package/skills/deepen-plan/SKILL.md +6 -7
  39. package/skills/document-review/SKILL.md +2 -2
  40. package/skills/git-commit-push-pr/SKILL.md +1 -2
  41. package/skills/lfg/SKILL.md +1 -1
  42. package/skills/onboarding/SKILL.md +1 -12
  43. package/skills/orchestrating-subagents/SKILL.md +2 -2
  44. package/skills/slfg/SKILL.md +2 -3
  45. package/skills/test-browser/SKILL.md +2 -2
  46. package/skills/todos/SKILL.md +239 -0
  47. package/skills/writing-skills/SKILL.md +102 -1
  48. package/skills/{writing-systematic-skills → writing-skills}/references/foundation-conventions.md +5 -5
  49. package/agents/design/design-implementation-reviewer.md +0 -95
  50. package/agents/design/figma-design-sync.md +0 -173
  51. package/agents/docs/ankane-readme-writer.md +0 -51
  52. package/agents/review/cli-agent-readiness-reviewer.md +0 -418
  53. package/agents/review/data-integrity-guardian.md +0 -72
  54. package/agents/review/data-migration-expert.md +0 -99
  55. package/agents/review/dhh-rails-reviewer.md +0 -46
  56. package/agents/review/julik-frontend-races-reviewer.md +0 -49
  57. package/agents/review/kieran-python-reviewer.md +0 -47
  58. package/agents/review/kieran-rails-reviewer.md +0 -47
  59. package/agents/review/performance-oracle.md +0 -112
  60. package/agents/review/schema-drift-detector.md +0 -143
  61. package/agents/review/security-sentinel.md +0 -95
  62. package/agents/workflow/lint.md +0 -17
  63. package/dist/lib/converter.d.ts +0 -14
  64. package/skills/andrew-kane-gem-writer/SKILL.md +0 -184
  65. package/skills/andrew-kane-gem-writer/references/database-adapters.md +0 -231
  66. package/skills/andrew-kane-gem-writer/references/module-organization.md +0 -121
  67. package/skills/andrew-kane-gem-writer/references/rails-integration.md +0 -183
  68. package/skills/andrew-kane-gem-writer/references/resources.md +0 -119
  69. package/skills/andrew-kane-gem-writer/references/testing-patterns.md +0 -261
  70. package/skills/changelog/SKILL.md +0 -139
  71. package/skills/claude-permissions-optimizer/SKILL.md +0 -166
  72. package/skills/claude-permissions-optimizer/scripts/extract-commands.mjs +0 -655
  73. package/skills/claude-permissions-optimizer/scripts/normalize.mjs +0 -151
  74. package/skills/dhh-rails-style/SKILL.md +0 -186
  75. package/skills/dhh-rails-style/references/architecture.md +0 -653
  76. package/skills/dhh-rails-style/references/controllers.md +0 -303
  77. package/skills/dhh-rails-style/references/frontend.md +0 -510
  78. package/skills/dhh-rails-style/references/gems.md +0 -266
  79. package/skills/dhh-rails-style/references/models.md +0 -359
  80. package/skills/dhh-rails-style/references/testing.md +0 -338
  81. package/skills/dspy-ruby/SKILL.md +0 -737
  82. package/skills/dspy-ruby/assets/config-template.rb +0 -187
  83. package/skills/dspy-ruby/assets/module-template.rb +0 -300
  84. package/skills/dspy-ruby/assets/signature-template.rb +0 -221
  85. package/skills/dspy-ruby/references/core-concepts.md +0 -674
  86. package/skills/dspy-ruby/references/observability.md +0 -366
  87. package/skills/dspy-ruby/references/optimization.md +0 -603
  88. package/skills/dspy-ruby/references/providers.md +0 -418
  89. package/skills/dspy-ruby/references/toolsets.md +0 -502
  90. package/skills/every-style-editor/SKILL.md +0 -135
  91. package/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +0 -529
  92. package/skills/feature-video/SKILL.md +0 -383
  93. package/skills/gemini-imagegen/SKILL.md +0 -237
  94. package/skills/gemini-imagegen/requirements.txt +0 -2
  95. package/skills/gemini-imagegen/scripts/compose_images.py +0 -157
  96. package/skills/gemini-imagegen/scripts/edit_image.py +0 -144
  97. package/skills/gemini-imagegen/scripts/gemini_images.py +0 -263
  98. package/skills/gemini-imagegen/scripts/generate_image.py +0 -133
  99. package/skills/gemini-imagegen/scripts/multi_turn_chat.py +0 -216
  100. package/skills/generate_command/SKILL.md +0 -164
  101. package/skills/orchestrating-swarms/SKILL.md +0 -1723
  102. package/skills/proof/SKILL.md +0 -291
  103. package/skills/proof/references/hitl-review.md +0 -368
  104. package/skills/rclone/SKILL.md +0 -150
  105. package/skills/rclone/scripts/check_setup.sh +0 -60
  106. package/skills/setup/SKILL.md +0 -22
  107. package/skills/test-xcode/SKILL.md +0 -214
  108. package/skills/todo-create/SKILL.md +0 -109
  109. package/skills/todo-resolve/SKILL.md +0 -68
  110. package/skills/todo-triage/SKILL.md +0 -70
  111. package/skills/writing-systematic-skills/SKILL.md +0 -119
  112. /package/skills/{todo-create → todos}/assets/todo-template.md +0 -0
package/dist/index.js CHANGED
@@ -2,7 +2,6 @@
2
2
  import {
3
3
  AgentOverlaySchema,
4
4
  CategoryOverlaySchema,
5
- convertFileWithCache,
6
5
  exports_external,
7
6
  extractAgentFrontmatter,
8
7
  extractCommandFrontmatter,
@@ -15,15 +14,15 @@ import {
15
14
  loadConfigWithSources,
16
15
  parseFrontmatter,
17
16
  walkDir
18
- } from "./index-vyzhzvap.js";
17
+ } from "./index-yskzf7vh.js";
19
18
 
20
19
  // src/index.ts
21
- import fs6 from "fs";
20
+ import fs8 from "fs";
22
21
  import path9 from "path";
23
22
  import { fileURLToPath as fileURLToPath2 } from "url";
24
23
 
25
24
  // src/lib/bootstrap.ts
26
- import fs from "fs";
25
+ import fs2 from "fs";
27
26
  import os from "os";
28
27
  import path2 from "path";
29
28
 
@@ -31,6 +30,7 @@ import path2 from "path";
31
30
  import { pathToFileURL } from "url";
32
31
 
33
32
  // src/lib/skill-loader.ts
33
+ import fs from "fs";
34
34
  import path from "path";
35
35
  var SKILL_PREFIX = "systematic:";
36
36
  var SKILL_DESCRIPTION_PREFIX = "(Systematic) ";
@@ -66,10 +66,8 @@ function extractSkillBody(wrappedTemplate) {
66
66
  }
67
67
  function loadSkill(skillInfo) {
68
68
  try {
69
- const converted = convertFileWithCache(skillInfo.skillFile, "skill", {
70
- source: "bundled"
71
- });
72
- const { body } = parseFrontmatter(converted);
69
+ const content = fs.readFileSync(skillInfo.skillFile, "utf8");
70
+ const { body } = parseFrontmatter(content);
73
71
  const wrappedTemplate = wrapSkillTemplate(skillInfo.skillFile, body);
74
72
  return {
75
73
  name: skillInfo.name,
@@ -83,8 +81,7 @@ function loadSkill(skillInfo) {
83
81
  subtask: skillInfo.subtask,
84
82
  agent: skillInfo.agent,
85
83
  model: skillInfo.model,
86
- argumentHint: skillInfo.argumentHint,
87
- deprecated: skillInfo.deprecated
84
+ argumentHint: skillInfo.argumentHint
88
85
  };
89
86
  } catch {
90
87
  return null;
@@ -193,7 +190,7 @@ ${content}` : content;
193
190
  };
194
191
  function getSkillUsageTemplate() {
195
192
  return `**Skills naming:**
196
- - Systematic bundled skills use the \`systematic:\` prefix (e.g., \`systematic:setup\`)
193
+ - Systematic bundled skills use the \`systematic:\` prefix (e.g., \`systematic:onboarding\`)
197
194
  - Workflow skills with their own namespace keep it (e.g., \`ce:brainstorm\`)
198
195
  - Skills can also be invoked without prefix if unambiguous
199
196
 
@@ -205,22 +202,22 @@ function getSkillUsageTemplate() {
205
202
  Bundled skills ship with the Systematic plugin and are discoverable via \`systematic_skill\`.`;
206
203
  }
207
204
  function getBootstrapContent(config, deps) {
208
- const { bundledSkillsDir } = deps;
205
+ const { bundledSkillsDir, usageTemplate } = deps;
209
206
  if (!config.bootstrap.enabled)
210
207
  return null;
211
208
  if (config.bootstrap.file) {
212
209
  const customPath = config.bootstrap.file.startsWith("~/") ? path2.join(os.homedir(), config.bootstrap.file.slice(2)) : config.bootstrap.file;
213
- if (fs.existsSync(customPath)) {
214
- return fs.readFileSync(customPath, "utf8");
210
+ if (fs2.existsSync(customPath)) {
211
+ return fs2.readFileSync(customPath, "utf8");
215
212
  }
216
213
  }
217
214
  const usingSystematicPath = path2.join(bundledSkillsDir, "using-systematic/SKILL.md");
218
- if (!fs.existsSync(usingSystematicPath))
215
+ if (!fs2.existsSync(usingSystematicPath))
219
216
  return null;
220
- const fullContent = fs.readFileSync(usingSystematicPath, "utf8");
217
+ const fullContent = fs2.readFileSync(usingSystematicPath, "utf8");
221
218
  const { body } = parseFrontmatter(fullContent);
222
219
  const content = body.trim();
223
- const skillUsage = getSkillUsageTemplate();
220
+ const skillUsage = usageTemplate ?? getSkillUsageTemplate();
224
221
  const catalog = renderCatalogVerbose({
225
222
  bundledSkillsDir,
226
223
  disabledSkills: config.disabled_skills
@@ -240,11 +237,12 @@ ${skillUsage}${catalogSection}
240
237
  }
241
238
 
242
239
  // src/lib/config-handler.ts
240
+ import fs6 from "fs";
243
241
  import os3 from "os";
244
242
  import path7 from "path";
245
243
 
246
244
  // src/lib/agent-overlays.ts
247
- import fs2 from "fs";
245
+ import fs3 from "fs";
248
246
  import path4 from "path";
249
247
 
250
248
  // src/lib/source-model-defaults.ts
@@ -323,27 +321,6 @@ var SOURCE_CATEGORY_MODEL_DEFAULTS = {
323
321
  }
324
322
  ]
325
323
  },
326
- docs: {
327
- rationale: "Documentation and summarization tasks should start cheaper and faster; quality is sufficient at mid-tier models.",
328
- providers: [
329
- {
330
- provider: "github-copilot",
331
- models: [{ model: "gemini-3.1-pro-preview" }]
332
- },
333
- {
334
- provider: "openai",
335
- models: [{ model: "gpt-5.4-mini" }]
336
- },
337
- {
338
- provider: "anthropic",
339
- models: [{ model: "claude-haiku-4-5" }]
340
- },
341
- {
342
- provider: "opencode",
343
- models: [{ model: "claude-haiku-4-5" }]
344
- }
345
- ]
346
- },
347
324
  "document-review": {
348
325
  rationale: "Requirements and plan critique benefit from the strongest nuanced reasoning to surface non-obvious gaps and contradictions.",
349
326
  providers: [
@@ -507,22 +484,6 @@ function resolveAgentOverlaySet(overlays) {
507
484
  categoriesByKey: new Map(overlays.categories.map((overlay) => [overlay.key, overlay]))
508
485
  };
509
486
  }
510
- function inferBuiltInTemperature(name, description) {
511
- const sample = `${name} ${description ?? ""}`.toLowerCase();
512
- if (/(review|audit|security|sentinel|oracle|lint|verification|guardian)/.test(sample)) {
513
- return 0.1;
514
- }
515
- if (/(plan|planning|architecture|strategist|analysis|research)/.test(sample)) {
516
- return 0.2;
517
- }
518
- if (/(doc|readme|changelog|editor|writer)/.test(sample)) {
519
- return 0.3;
520
- }
521
- if (/(brainstorm|creative|ideate|design|concept)/.test(sample)) {
522
- return 0.6;
523
- }
524
- return 0.3;
525
- }
526
487
  function assertSourceCategoryModelCoverage(categories) {
527
488
  const missingCategories = categories.filter((category) => !Object.hasOwn(SOURCE_CATEGORY_MODEL_DEFAULTS, category));
528
489
  if (missingCategories.length > 0) {
@@ -611,14 +572,14 @@ function validateOverlaySkills(overlay, skills, enabledSkills) {
611
572
  }
612
573
  function readCategoryDirs(agentsDir) {
613
574
  try {
614
- return fs2.readdirSync(agentsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
575
+ return fs3.readdirSync(agentsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
615
576
  } catch {
616
577
  return [];
617
578
  }
618
579
  }
619
580
  function readMarkdownFiles(categoryDir) {
620
581
  try {
621
- return fs2.readdirSync(categoryDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort();
582
+ return fs3.readdirSync(categoryDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort();
622
583
  } catch {
623
584
  return [];
624
585
  }
@@ -631,7 +592,7 @@ function throwConfigError(sourcePath, keyPath, message) {
631
592
  }
632
593
 
633
594
  // src/lib/discovered-skills.ts
634
- import fs3 from "fs";
595
+ import fs4 from "fs";
635
596
  import path5 from "path";
636
597
  var SKILL_NAME_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
637
598
  function isValidSkillName(name) {
@@ -641,7 +602,7 @@ function findGitWorktreeRoot(startDir) {
641
602
  let current = path5.resolve(startDir);
642
603
  while (true) {
643
604
  try {
644
- if (fs3.existsSync(path5.join(current, ".git"))) {
605
+ if (fs4.existsSync(path5.join(current, ".git"))) {
645
606
  return current;
646
607
  }
647
608
  } catch {
@@ -661,7 +622,7 @@ function upWalk(targets, start, stop) {
661
622
  for (const target of targets) {
662
623
  const candidate = path5.join(current, target);
663
624
  try {
664
- if (fs3.existsSync(candidate)) {
625
+ if (fs4.existsSync(candidate)) {
665
626
  results.push(candidate);
666
627
  }
667
628
  } catch {}
@@ -692,7 +653,7 @@ function globSkillFiles(rootDir, subdirNames) {
692
653
  for (const subdirName of subdirNames) {
693
654
  const scanRoot = path5.join(rootDir, subdirName);
694
655
  try {
695
- if (!fs3.existsSync(scanRoot))
656
+ if (!fs4.existsSync(scanRoot))
696
657
  continue;
697
658
  const entries = walkDir(scanRoot, {
698
659
  maxDepth: 10,
@@ -708,7 +669,7 @@ function globSkillFiles(rootDir, subdirNames) {
708
669
  function toDiscoveredSkill(skillPath, rootId) {
709
670
  let content;
710
671
  try {
711
- content = fs3.readFileSync(skillPath, "utf8");
672
+ content = fs4.readFileSync(skillPath, "utf8");
712
673
  } catch {
713
674
  return;
714
675
  }
@@ -757,7 +718,7 @@ function discoverSkills(options) {
757
718
 
758
719
  // src/lib/model-availability.ts
759
720
  import { createHash } from "crypto";
760
- import fs4 from "fs";
721
+ import fs5 from "fs";
761
722
  import os2 from "os";
762
723
  import path6 from "path";
763
724
  function emptyAvailability() {
@@ -789,7 +750,7 @@ function isProviderRecord(value) {
789
750
  function readModelsFromCache(filePath) {
790
751
  let fd;
791
752
  try {
792
- fd = fs4.openSync(filePath, "r");
753
+ fd = fs5.openSync(filePath, "r");
793
754
  } catch {
794
755
  return null;
795
756
  }
@@ -797,7 +758,7 @@ function readModelsFromCache(filePath) {
797
758
  try {
798
759
  let stat;
799
760
  try {
800
- stat = fs4.fstatSync(fd);
761
+ stat = fs5.fstatSync(fd);
801
762
  } catch {
802
763
  return null;
803
764
  }
@@ -812,7 +773,7 @@ function readModelsFromCache(filePath) {
812
773
  const buffer = Buffer.alloc(stat.size);
813
774
  let bytesRead;
814
775
  try {
815
- bytesRead = fs4.readSync(fd, buffer, 0, stat.size, 0);
776
+ bytesRead = fs5.readSync(fd, buffer, 0, stat.size, 0);
816
777
  } catch {
817
778
  return null;
818
779
  }
@@ -821,7 +782,7 @@ function readModelsFromCache(filePath) {
821
782
  raw = buffer.toString("utf8");
822
783
  } finally {
823
784
  try {
824
- fs4.closeSync(fd);
785
+ fs5.closeSync(fd);
825
786
  } catch {}
826
787
  }
827
788
  if (raw.trim().length === 0)
@@ -956,10 +917,7 @@ function formatAgentDescription(name, description) {
956
917
  }
957
918
  function loadAgentAsConfig(agentInfo) {
958
919
  try {
959
- const converted = convertFileWithCache(agentInfo.file, "agent", {
960
- source: "bundled",
961
- agentMode: "subagent"
962
- });
920
+ const content = fs6.readFileSync(agentInfo.file, "utf8");
963
921
  const {
964
922
  description,
965
923
  prompt,
@@ -974,7 +932,7 @@ function loadAgentAsConfig(agentInfo) {
974
932
  steps,
975
933
  hidden,
976
934
  permission
977
- } = extractAgentFrontmatter(converted);
935
+ } = extractAgentFrontmatter(content);
978
936
  const config = {
979
937
  description: formatAgentDescription(agentInfo.name, description),
980
938
  prompt
@@ -1008,11 +966,9 @@ function loadAgentAsConfig(agentInfo) {
1008
966
  }
1009
967
  function loadCommandAsConfig(commandInfo) {
1010
968
  try {
1011
- const converted = convertFileWithCache(commandInfo.file, "command", {
1012
- source: "bundled"
1013
- });
1014
- const { name, description, agent, model, subtask } = extractCommandFrontmatter(converted);
1015
- const { body } = parseFrontmatter(converted);
969
+ const content = fs6.readFileSync(commandInfo.file, "utf8");
970
+ const { name, description, agent, model, subtask } = extractCommandFrontmatter(content);
971
+ const { body } = parseFrontmatter(content);
1016
972
  const cleanName = commandInfo.name.replace(/^\//, "");
1017
973
  const baseDescription = description || `${name || cleanName} command`;
1018
974
  const wrappedTemplate = `<command-instruction>
@@ -1080,7 +1036,6 @@ function applyAgentOverlays(config, agentInfo, overlays, availabilitySet) {
1080
1036
  if (hasPermissionOverlay && isRecord(config.permission)) {
1081
1037
  addPermissionRules(permissionRules, config.permission);
1082
1038
  }
1083
- result.temperature = result.temperature ?? inferBuiltInTemperature(agentInfo.name, result.description);
1084
1039
  applySourceModelDefault(result, agentInfo, availabilitySet);
1085
1040
  applyAgentOverlay(result, categoryOverlay?.value, permissionRules);
1086
1041
  applyAgentOverlay(result, exactOverlay?.value, permissionRules);
@@ -1329,10 +1284,66 @@ function normalizePath(path8) {
1329
1284
  }
1330
1285
 
1331
1286
  // src/lib/skill-tool.ts
1332
- import fs5 from "fs";
1287
+ import { tool } from "@opencode-ai/plugin/tool";
1288
+
1289
+ // src/lib/skill-resolver.ts
1290
+ import fs7 from "fs";
1333
1291
  import path8 from "path";
1334
1292
  import { pathToFileURL as pathToFileURL2 } from "url";
1335
- import { tool } from "@opencode-ai/plugin/tool";
1293
+ function getAllSkills(options) {
1294
+ const { bundledSkillsDir, disabledSkills } = options;
1295
+ 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));
1296
+ }
1297
+ function resolveSkill(options, requestedName) {
1298
+ const normalizedName = requestedName.startsWith("systematic:") ? requestedName.slice("systematic:".length) : requestedName;
1299
+ const skills = getAllSkills(options);
1300
+ const matchedSkill = skills.find((s) => s.name === normalizedName);
1301
+ if (!matchedSkill) {
1302
+ const availableSystematic = buildCatalogEntries(options).map((s) => s.prefixedName);
1303
+ throw new Error(`Skill "${requestedName}" not found. Available systematic skills: ${availableSystematic.join(", ")}`);
1304
+ }
1305
+ return matchedSkill;
1306
+ }
1307
+ function buildSkillToolDescription(options) {
1308
+ const catalog = renderCatalogCompact(options);
1309
+ return `Load a specialized skill that provides domain-specific instructions and workflows.
1310
+
1311
+ When you recognize that a task matches one of the available skills listed below, use this tool to load the full skill instructions.
1312
+
1313
+ The skill will inject detailed instructions, workflows, and access to bundled resources (scripts, references, templates) into the conversation context.
1314
+
1315
+ Tool output includes a \`<skill_content name="...">\` block with the loaded content.
1316
+
1317
+ ${catalog}`;
1318
+ }
1319
+ function buildSkillToolParameterHint(options) {
1320
+ const entries = buildCatalogEntries(options);
1321
+ const examples = entries.slice(0, 3).map((s) => `'${s.prefixedName}'`).join(", ");
1322
+ const hint = examples.length > 0 ? ` (e.g., ${examples}, ...)` : "";
1323
+ return `The name of the skill from available_skills${hint}`;
1324
+ }
1325
+ function buildSkillContentOutput(matchedSkill) {
1326
+ const body = extractSkillBody(matchedSkill.wrappedTemplate);
1327
+ const dir = path8.dirname(matchedSkill.skillFile);
1328
+ const base = pathToFileURL2(dir).href;
1329
+ const files = discoverSkillFiles(dir);
1330
+ const lines = [
1331
+ `<skill_content name="${matchedSkill.prefixedName}">`,
1332
+ `# Skill: ${matchedSkill.prefixedName}`,
1333
+ "",
1334
+ body.trim(),
1335
+ "",
1336
+ `Base directory for this skill: ${base}`,
1337
+ "Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.",
1338
+ "Note: file list is sampled."
1339
+ ];
1340
+ if (files) {
1341
+ lines.push("", "<skill_files>", files, "</skill_files>");
1342
+ }
1343
+ lines.push("</skill_content>");
1344
+ return { output: lines.join(`
1345
+ `), dir };
1346
+ }
1336
1347
  function discoverSkillFiles(dir, limit = 10) {
1337
1348
  const files = [];
1338
1349
  function shouldSkipDirectory(name) {
@@ -1354,7 +1365,7 @@ function discoverSkillFiles(dir, limit = 10) {
1354
1365
  if (files.length >= limit)
1355
1366
  return;
1356
1367
  try {
1357
- const entries = fs5.readdirSync(currentDir, { withFileTypes: true });
1368
+ const entries = fs7.readdirSync(currentDir, { withFileTypes: true });
1358
1369
  for (const entry of entries) {
1359
1370
  if (files.length >= limit)
1360
1371
  break;
@@ -1366,46 +1377,11 @@ function discoverSkillFiles(dir, limit = 10) {
1366
1377
  return files.map((file) => ` <file>${file}</file>`).join(`
1367
1378
  `);
1368
1379
  }
1369
- function formatDeprecationMessage(name, deprecated) {
1370
- let msg = `[systematic] skill "${name}" is deprecated since ${deprecated.since}; will be removed in ${deprecated.removal}.`;
1371
- if (deprecated.replacement) {
1372
- msg += ` Replacement: ${deprecated.replacement}`;
1373
- if (!deprecated.replacement.endsWith(".")) {
1374
- msg += ".";
1375
- }
1376
- }
1377
- if (deprecated.reason) {
1378
- msg += ` Reason: ${deprecated.reason}`;
1379
- if (!deprecated.reason.endsWith(".")) {
1380
- msg += ".";
1381
- }
1382
- }
1383
- return msg;
1384
- }
1380
+ // src/lib/skill-tool.ts
1385
1381
  function createSkillTool(options) {
1386
1382
  const { bundledSkillsDir, disabledSkills } = options;
1387
- const warnedSkills = new Set;
1388
- const getAllSkills = () => {
1389
- 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));
1390
- };
1391
- const buildDescription = () => {
1392
- const catalog = renderCatalogCompact({ bundledSkillsDir, disabledSkills });
1393
- return `Load a specialized skill that provides domain-specific instructions and workflows.
1394
-
1395
- When you recognize that a task matches one of the available skills listed below, use this tool to load the full skill instructions.
1396
-
1397
- The skill will inject detailed instructions, workflows, and access to bundled resources (scripts, references, templates) into the conversation context.
1398
-
1399
- Tool output includes a \`<skill_content name="...">\` block with the loaded content.
1400
-
1401
- ${catalog}`;
1402
- };
1403
- const buildParameterHint = () => {
1404
- const entries = buildCatalogEntries({ bundledSkillsDir, disabledSkills });
1405
- const examples = entries.slice(0, 3).map((s) => `'${s.prefixedName}'`).join(", ");
1406
- const hint = examples.length > 0 ? ` (e.g., ${examples}, ...)` : "";
1407
- return `The name of the skill from available_skills${hint}`;
1408
- };
1383
+ const buildDescription = () => buildSkillToolDescription({ bundledSkillsDir, disabledSkills });
1384
+ const buildParameterHint = () => buildSkillToolParameterHint({ bundledSkillsDir, disabledSkills });
1409
1385
  let cachedDescription = null;
1410
1386
  let cachedParameterHint = null;
1411
1387
  return tool({
@@ -1425,24 +1401,8 @@ ${catalog}`;
1425
1401
  },
1426
1402
  async execute(args, context) {
1427
1403
  const requestedName = args.name;
1428
- const normalizedName = requestedName.startsWith("systematic:") ? requestedName.slice("systematic:".length) : requestedName;
1429
- const skills = getAllSkills();
1430
- const matchedSkill = skills.find((s) => s.name === normalizedName);
1431
- if (!matchedSkill) {
1432
- const availableSystematic = buildCatalogEntries({
1433
- bundledSkillsDir,
1434
- disabledSkills
1435
- }).map((s) => s.prefixedName);
1436
- throw new Error(`Skill "${requestedName}" not found. Available systematic skills: ${availableSystematic.join(", ")}`);
1437
- }
1438
- if (matchedSkill.deprecated && !warnedSkills.has(matchedSkill.name)) {
1439
- console.warn(formatDeprecationMessage(matchedSkill.name, matchedSkill.deprecated));
1440
- warnedSkills.add(matchedSkill.name);
1441
- }
1442
- const body = extractSkillBody(matchedSkill.wrappedTemplate);
1443
- const dir = path8.dirname(matchedSkill.skillFile);
1444
- const base = pathToFileURL2(dir).href;
1445
- const files = discoverSkillFiles(dir);
1404
+ const matchedSkill = resolveSkill({ bundledSkillsDir, disabledSkills }, requestedName);
1405
+ const { output, dir } = buildSkillContentOutput(matchedSkill);
1446
1406
  await context.ask({
1447
1407
  permission: "skill",
1448
1408
  patterns: [matchedSkill.prefixedName],
@@ -1456,22 +1416,7 @@ ${catalog}`;
1456
1416
  dir
1457
1417
  }
1458
1418
  });
1459
- const output = [
1460
- `<skill_content name="${matchedSkill.prefixedName}">`,
1461
- `# Skill: ${matchedSkill.prefixedName}`,
1462
- "",
1463
- body.trim(),
1464
- "",
1465
- `Base directory for this skill: ${base}`,
1466
- "Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.",
1467
- "Note: file list is sampled."
1468
- ];
1469
- if (files) {
1470
- output.push("", "<skill_files>", files, "</skill_files>");
1471
- }
1472
- output.push("</skill_content>");
1473
- return output.join(`
1474
- `);
1419
+ return output;
1475
1420
  }
1476
1421
  });
1477
1422
  }
@@ -1485,9 +1430,9 @@ var bundledCommandsDir = path9.join(packageRoot2, "commands");
1485
1430
  var packageJsonPath = path9.join(packageRoot2, "package.json");
1486
1431
  var getPackageVersion = () => {
1487
1432
  try {
1488
- if (!fs6.existsSync(packageJsonPath))
1433
+ if (!fs8.existsSync(packageJsonPath))
1489
1434
  return "unknown";
1490
- const content = fs6.readFileSync(packageJsonPath, "utf8");
1435
+ const content = fs8.readFileSync(packageJsonPath, "utf8");
1491
1436
  const parsed = JSON.parse(content);
1492
1437
  return parsed.version ?? "unknown";
1493
1438
  } catch {
@@ -41,5 +41,4 @@ export interface ResolvedAgentOverlaySet {
41
41
  export declare function buildBundledAgentInventory(agentsDir: string, disabledAgents: string[]): BundledAgentInventory;
42
42
  export declare function validateAgentOverlays({ inventory, overlays, nativeAgents, enabledSkills, }: ValidateAgentOverlaysOptions): ValidatedAgentOverlays;
43
43
  export declare function resolveAgentOverlaySet(overlays: ValidatedAgentOverlays): ResolvedAgentOverlaySet;
44
- export declare function inferBuiltInTemperature(name: string, description?: string): number;
45
44
  export declare function assertSourceCategoryModelCoverage(categories: string[]): void;
@@ -0,0 +1,28 @@
1
+ /** Runtime persona catalog for `systematic_delegate`. Pi has no agent-discovery of its own, so category is dropped when flattening `agents/<category>/<name>.md` into this catalog. */
2
+ /** A single resolved persona entry in the flattened catalog. */
3
+ export interface AgentCatalogEntry {
4
+ /** Flat persona name (category dropped). */
5
+ name: string;
6
+ /** Human-readable description, used in tool description/parameter hints. */
7
+ description: string;
8
+ /** Full persona system-prompt body (frontmatter stripped). */
9
+ body: string;
10
+ /** Raw comma-separated `tools:` frontmatter value, if declared. Undefined = not declared. */
11
+ toolsSource: string | undefined;
12
+ }
13
+ /** Fails closed if the same persona name appears under more than one category. */
14
+ export declare function buildAgentCatalog(agentsDir: string): AgentCatalogEntry[];
15
+ /** Resolves a persona by exact flat name from the catalog. */
16
+ export declare function resolveAgent(catalog: AgentCatalogEntry[], name: string): AgentCatalogEntry | undefined;
17
+ /**
18
+ * Deterministic, bounded description of available personas for the delegation
19
+ * tool's description/parameter hint text. Stable ordering (catalog is pre-sorted).
20
+ */
21
+ export declare function renderAgentCatalogCompact(catalog: AgentCatalogEntry[]): string;
22
+ /** Pi built-in read-only tool names used as the default allowlist. */
23
+ export declare const DEFAULT_READONLY_TOOLS: readonly ["read", "grep", "find", "ls"];
24
+ export interface ResolvedToolAllowlist {
25
+ tools: string[];
26
+ }
27
+ /** Undeclared tools use the read-only default; unknown or `Task` declarations fail closed. */
28
+ export declare function resolveToolAllowlist(toolsSource: string | undefined): ResolvedToolAllowlist;
@@ -20,5 +20,12 @@ export declare const applyBootstrapContent: (output: {
20
20
  }, content: string) => void;
21
21
  export interface BootstrapDeps {
22
22
  bundledSkillsDir: string;
23
+ usageTemplate?: string;
23
24
  }
24
- export declare function getBootstrapContent(config: SystematicConfig, deps: BootstrapDeps): string | null;
25
+ type BootstrapContentConfig = Pick<SystematicConfig, 'bootstrap' | 'disabled_skills'>;
26
+ /** Safely computes default bootstrap content, returning null instead of throwing. */
27
+ export declare function computeBootstrapContentSafe(deps: BootstrapDeps, reportError?: (error: unknown) => void): string | null;
28
+ /** Composes an existing system prompt with nullable bootstrap content; returns null when bootstrapContent is null. */
29
+ export declare function composeSystemPromptWithBootstrap(existingSystemPrompt: string, bootstrapContent: string | null): string | null;
30
+ export declare function getBootstrapContent(config: BootstrapContentConfig, deps: BootstrapDeps): string | null;
31
+ export {};
@@ -1,5 +1,5 @@
1
- export declare const BUNDLED_AGENT_NAMES: readonly ["adversarial-document-reviewer", "adversarial-reviewer", "agent-native-reviewer", "ankane-readme-writer", "api-contract-reviewer", "architecture-strategist", "best-practices-researcher", "bug-reproduction-validator", "cli-agent-readiness-reviewer", "cli-readiness-reviewer", "code-simplicity-reviewer", "coherence-reviewer", "correctness-reviewer", "data-integrity-guardian", "data-migration-expert", "data-migrations-reviewer", "deployment-verification-agent", "design-implementation-reviewer", "design-iterator", "design-lens-reviewer", "dhh-rails-reviewer", "feasibility-reviewer", "figma-design-sync", "framework-docs-researcher", "git-history-analyzer", "issue-intelligence-analyst", "julik-frontend-races-reviewer", "kieran-python-reviewer", "kieran-rails-reviewer", "kieran-typescript-reviewer", "learnings-researcher", "lint", "maintainability-reviewer", "pattern-recognition-specialist", "performance-oracle", "performance-reviewer", "pr-comment-resolver", "previous-comments-reviewer", "product-lens-reviewer", "project-standards-reviewer", "reliability-reviewer", "repo-research-analyst", "schema-drift-detector", "scope-guardian-reviewer", "security-lens-reviewer", "security-reviewer", "security-sentinel", "slack-researcher", "spec-flow-analyzer", "systematic-implementer", "testing-reviewer"];
2
- export declare const BUNDLED_AGENT_QUALIFIED_IDS: readonly ["design/design-implementation-reviewer", "design/design-iterator", "design/figma-design-sync", "docs/ankane-readme-writer", "document-review/adversarial-document-reviewer", "document-review/coherence-reviewer", "document-review/design-lens-reviewer", "document-review/feasibility-reviewer", "document-review/product-lens-reviewer", "document-review/scope-guardian-reviewer", "document-review/security-lens-reviewer", "research/best-practices-researcher", "research/framework-docs-researcher", "research/git-history-analyzer", "research/issue-intelligence-analyst", "research/learnings-researcher", "research/repo-research-analyst", "research/slack-researcher", "review/adversarial-reviewer", "review/agent-native-reviewer", "review/api-contract-reviewer", "review/architecture-strategist", "review/cli-agent-readiness-reviewer", "review/cli-readiness-reviewer", "review/code-simplicity-reviewer", "review/correctness-reviewer", "review/data-integrity-guardian", "review/data-migration-expert", "review/data-migrations-reviewer", "review/deployment-verification-agent", "review/dhh-rails-reviewer", "review/julik-frontend-races-reviewer", "review/kieran-python-reviewer", "review/kieran-rails-reviewer", "review/kieran-typescript-reviewer", "review/maintainability-reviewer", "review/pattern-recognition-specialist", "review/performance-oracle", "review/performance-reviewer", "review/previous-comments-reviewer", "review/project-standards-reviewer", "review/reliability-reviewer", "review/schema-drift-detector", "review/security-reviewer", "review/security-sentinel", "review/testing-reviewer", "workflow/bug-reproduction-validator", "workflow/lint", "workflow/pr-comment-resolver", "workflow/spec-flow-analyzer", "workflow/systematic-implementer"];
3
- export declare const BUNDLED_SKILL_NAMES: readonly ["agent-browser", "agent-native-architecture", "agent-native-audit", "andrew-kane-gem-writer", "ce:brainstorm", "ce:compound", "ce:compound-refresh", "ce:ideate", "ce:plan", "ce:review", "ce:work", "changelog", "claude-permissions-optimizer", "compound-docs", "deepen-plan", "deploy-docs", "dhh-rails-style", "document-review", "dspy-ruby", "every-style-editor", "feature-video", "frontend-design", "gemini-imagegen", "generate_command", "git-clean-gone-branches", "git-commit", "git-commit-push-pr", "git-worktree", "lfg", "onboarding", "orchestrating-subagents", "orchestrating-swarms", "proof", "rclone", "report-bug-ce", "reproduce-bug", "resolve-pr-feedback", "setup", "slfg", "test-browser", "test-driven-development", "test-xcode", "todo-create", "todo-resolve", "todo-triage", "using-systematic", "writing-skills", "writing-systematic-skills"];
1
+ export declare const BUNDLED_AGENT_NAMES: readonly ["adversarial-document-reviewer", "adversarial-reviewer", "agent-native-reviewer", "api-contract-reviewer", "architecture-strategist", "best-practices-researcher", "bug-reproduction-validator", "cli-readiness-reviewer", "code-simplicity-reviewer", "coherence-reviewer", "correctness-reviewer", "data-migrations-reviewer", "deployment-verification-agent", "design-iterator", "design-lens-reviewer", "feasibility-reviewer", "framework-docs-researcher", "git-history-analyzer", "issue-intelligence-analyst", "kieran-typescript-reviewer", "learnings-researcher", "maintainability-reviewer", "pattern-recognition-specialist", "performance-reviewer", "pr-comment-resolver", "previous-comments-reviewer", "product-lens-reviewer", "project-standards-reviewer", "reliability-reviewer", "repo-research-analyst", "scope-guardian-reviewer", "security-lens-reviewer", "security-reviewer", "slack-researcher", "spec-flow-analyzer", "systematic-implementer", "testing-reviewer"];
2
+ export declare const BUNDLED_AGENT_QUALIFIED_IDS: readonly ["design/design-iterator", "document-review/adversarial-document-reviewer", "document-review/coherence-reviewer", "document-review/design-lens-reviewer", "document-review/feasibility-reviewer", "document-review/product-lens-reviewer", "document-review/scope-guardian-reviewer", "document-review/security-lens-reviewer", "research/best-practices-researcher", "research/framework-docs-researcher", "research/git-history-analyzer", "research/issue-intelligence-analyst", "research/learnings-researcher", "research/repo-research-analyst", "research/slack-researcher", "review/adversarial-reviewer", "review/agent-native-reviewer", "review/api-contract-reviewer", "review/architecture-strategist", "review/cli-readiness-reviewer", "review/code-simplicity-reviewer", "review/correctness-reviewer", "review/data-migrations-reviewer", "review/deployment-verification-agent", "review/kieran-typescript-reviewer", "review/maintainability-reviewer", "review/pattern-recognition-specialist", "review/performance-reviewer", "review/previous-comments-reviewer", "review/project-standards-reviewer", "review/reliability-reviewer", "review/security-reviewer", "review/testing-reviewer", "workflow/bug-reproduction-validator", "workflow/pr-comment-resolver", "workflow/spec-flow-analyzer", "workflow/systematic-implementer"];
3
+ export declare const BUNDLED_SKILL_NAMES: readonly ["agent-browser", "agent-native-architecture", "agent-native-audit", "ce:brainstorm", "ce:compound", "ce:compound-refresh", "ce:ideate", "ce:plan", "ce:review", "ce:work", "compound-docs", "deepen-plan", "deploy-docs", "document-review", "frontend-design", "git-clean-gone-branches", "git-commit", "git-commit-push-pr", "git-worktree", "lfg", "onboarding", "orchestrating-subagents", "report-bug-ce", "reproduce-bug", "resolve-pr-feedback", "slfg", "test-browser", "test-driven-development", "todos", "using-systematic", "writing-skills"];
4
4
  export type BundledAgentName = (typeof BUNDLED_AGENT_NAMES)[number];
5
5
  export type BundledSkillName = (typeof BUNDLED_SKILL_NAMES)[number];
@@ -0,0 +1,31 @@
1
+ /** Real Pi session-construction for `systematic_delegate`. `noExtensions: true` is the structural depth-1 boundary preventing the child from re-registering `systematic_delegate` or any project/user extension. */
2
+ import type { CreateAgentSessionOptions, CreateAgentSessionResult, ResourceLoader } from '@earendil-works/pi-coding-agent';
3
+ import { DefaultResourceLoader, SessionManager } from '@earendil-works/pi-coding-agent';
4
+ /** `DefaultResourceLoaderOptions` is not exported by the package, so it's derived from the constructor's parameter type. */
5
+ type DefaultResourceLoaderOptions = ConstructorParameters<typeof DefaultResourceLoader>[0];
6
+ import type { CreateDelegateSession } from './pi-delegate-tool.js';
7
+ export interface PiDelegateSessionRuntime {
8
+ getAgentDir: () => string;
9
+ createResourceLoader: (options: DefaultResourceLoaderOptions) => ResourceLoader & {
10
+ reload: () => Promise<void>;
11
+ };
12
+ createInMemorySessionManager: (cwd: string) => ReturnType<typeof SessionManager.inMemory>;
13
+ createAgentSession: (options: CreateAgentSessionOptions) => Promise<CreateAgentSessionResult>;
14
+ }
15
+ export declare const REAL_PI_DELEGATE_SESSION_RUNTIME: PiDelegateSessionRuntime;
16
+ export declare function buildDelegateResourceLoaderOptions(options: {
17
+ cwd: string;
18
+ agentDir: string;
19
+ systemPromptOverride: string;
20
+ }): DefaultResourceLoaderOptions;
21
+ export declare function buildDelegateAgentSessionOptions(options: {
22
+ cwd: string;
23
+ agentDir: string;
24
+ model: CreateAgentSessionOptions['model'];
25
+ allowedToolNames: string[];
26
+ resourceLoader: ResourceLoader;
27
+ sessionManager: ReturnType<typeof SessionManager.inMemory>;
28
+ }): CreateAgentSessionOptions;
29
+ export declare function createDelegateSessionWith(runtime: PiDelegateSessionRuntime): CreateDelegateSession;
30
+ export declare const createRealPiDelegateSession: CreateDelegateSession;
31
+ export {};
@@ -0,0 +1,42 @@
1
+ /** Pi-specific adapter factory for the `systematic_delegate` tool. `noExtensions: true` is the structural depth-1 boundary; max turns is fixed at 20. */
2
+ import type { ExtensionContext, ToolDefinition } from '@earendil-works/pi-coding-agent';
3
+ import { Type } from 'typebox';
4
+ import { type AgentCatalogEntry } from './agent-resolver.js';
5
+ /** Fixed, non-configurable delegation bounds (LOCKED). */
6
+ export declare const MAX_DELEGATE_TURNS = 20;
7
+ export declare const DELEGATE_TOOL_NAME = "systematic_delegate";
8
+ export declare const DELEGATE_EXECUTION_MODE: "sequential";
9
+ export type DelegateOutcome = 'completed' | 'turn_limit' | 'aborted' | 'failed';
10
+ /** The parent session's model, narrowed to always-defined (validated before use). */
11
+ export type DelegateParentModel = NonNullable<ExtensionContext['model']>;
12
+ export interface DelegateToolDetails {
13
+ persona: string;
14
+ turnCount: number;
15
+ outcome: DelegateOutcome;
16
+ }
17
+ export interface DelegateSessionLike {
18
+ subscribe(listener: (event: {
19
+ type: string;
20
+ }) => void): () => void;
21
+ prompt(text: string): Promise<void>;
22
+ abort(): Promise<void>;
23
+ dispose(): void;
24
+ getLastAssistantText(): string | undefined;
25
+ }
26
+ export type CreateDelegateSession = (options: {
27
+ agentName: string;
28
+ model: DelegateParentModel;
29
+ cwd: string;
30
+ systemPromptOverride: string;
31
+ allowedToolNames: string[];
32
+ }) => Promise<DelegateSessionLike>;
33
+ export interface PiDelegateToolDeps {
34
+ catalog: AgentCatalogEntry[];
35
+ createDelegateSession: CreateDelegateSession;
36
+ }
37
+ export declare function createPiDelegateTool(deps: PiDelegateToolDeps): ToolDefinition<ReturnType<typeof buildDelegateParametersSchema>, DelegateToolDetails>;
38
+ declare function buildDelegateParametersSchema(catalog: AgentCatalogEntry[]): Type.TObject<{
39
+ agent: Type.TString;
40
+ task: Type.TString;
41
+ }>;
42
+ export {};
@@ -7,9 +7,9 @@
7
7
  * configs that disabled them before removal. Genuinely-unknown names (typos,
8
8
  * names that never existed) are still rejected by strict validation.
9
9
  *
10
- * This list is empty until v3 populates it with the first removed names.
11
- * Add a name here only when the corresponding skill or agent directory is
12
- * actually deleted from the repo.
10
+ * These lists were populated as of v3, which removed a batch of bundled
11
+ * skills and agents. Add a name here only when the corresponding skill or
12
+ * agent directory is actually deleted from the repo.
13
13
  */
14
14
  export declare const REMOVED_BUNDLED_SKILL_NAMES: readonly string[];
15
15
  export declare const REMOVED_BUNDLED_AGENT_NAMES: readonly string[];