@krtclcdy/tersio 2.25.5 → 2.25.7

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 (231) hide show
  1. package/README.md +42 -38
  2. package/dashboard/dist/index.html +15 -15
  3. package/dist/bench/code-out/pfull-0/debounce.js +13 -0
  4. package/dist/bench/code-out/pfull-0/deepClone.js +18 -0
  5. package/dist/bench/code-out/pfull-0/groupBy.js +15 -0
  6. package/dist/bench/code-out/pfull-1/debounce.js +13 -0
  7. package/dist/bench/code-out/pfull-1/deepClone.js +17 -0
  8. package/dist/bench/code-out/pfull-1/groupBy.js +14 -0
  9. package/dist/bench/code-out/pfull-2/debounce.js +13 -0
  10. package/dist/bench/code-out/pfull-2/deepClone.js +18 -0
  11. package/dist/bench/code-out/pfull-2/groupBy.js +15 -0
  12. package/dist/bench/code-out/pfull-3/debounce.js +12 -0
  13. package/dist/bench/code-out/pfull-3/deepClone.js +17 -0
  14. package/dist/bench/code-out/pfull-3/groupBy.js +7 -0
  15. package/dist/bench/code-out/pfull-4/debounce.js +10 -0
  16. package/dist/bench/code-out/pfull-4/deepClone.js +17 -0
  17. package/dist/bench/code-out/pfull-4/groupBy.js +14 -0
  18. package/dist/bench/code-out/plite-0/debounce.js +13 -0
  19. package/dist/bench/code-out/plite-0/deepClone.js +18 -0
  20. package/dist/bench/code-out/plite-0/groupBy.js +15 -0
  21. package/dist/bench/code-out/plite-1/debounce.js +13 -0
  22. package/dist/bench/code-out/plite-1/deepClone.js +17 -0
  23. package/dist/bench/code-out/plite-1/groupBy.js +11 -0
  24. package/dist/bench/code-out/plite-2/debounce.js +12 -0
  25. package/dist/bench/code-out/plite-2/deepClone.js +17 -0
  26. package/dist/bench/code-out/plite-2/groupBy.js +14 -0
  27. package/dist/bench/code-out/plite-3/debounce.js +12 -0
  28. package/dist/bench/code-out/plite-3/deepClone.js +17 -0
  29. package/dist/bench/code-out/plite-3/groupBy.js +14 -0
  30. package/dist/bench/code-out/plite-4/debounce.js +12 -0
  31. package/dist/bench/code-out/plite-4/deepClone.js +17 -0
  32. package/dist/bench/code-out/plite-4/groupBy.js +14 -0
  33. package/dist/bench/code-out/pultra-0/debounce.js +12 -0
  34. package/dist/bench/code-out/pultra-0/deepClone.js +20 -0
  35. package/dist/bench/code-out/pultra-0/groupBy.js +14 -0
  36. package/dist/bench/code-out/pultra-1/debounce.js +10 -0
  37. package/dist/bench/code-out/pultra-1/deepClone.js +17 -0
  38. package/dist/bench/code-out/pultra-1/groupBy.js +7 -0
  39. package/dist/bench/code-out/pultra-2/debounce.js +13 -0
  40. package/dist/bench/code-out/pultra-2/deepClone.js +18 -0
  41. package/dist/bench/code-out/pultra-2/groupBy.js +15 -0
  42. package/dist/bench/code-out/pultra-3/debounce.js +13 -0
  43. package/dist/bench/code-out/pultra-3/deepClone.js +18 -0
  44. package/dist/bench/code-out/pultra-3/groupBy.js +15 -0
  45. package/dist/bench/code-out/pultra-4/debounce.js +13 -0
  46. package/dist/bench/code-out/pultra-4/deepClone.js +18 -0
  47. package/dist/bench/code-out/pultra-4/groupBy.js +15 -0
  48. package/dist/bench/code-out/run-0/debounce.js +12 -0
  49. package/dist/bench/code-out/run-0/deepClone.js +17 -0
  50. package/dist/bench/code-out/run-0/groupBy.js +14 -0
  51. package/dist/bench/code-out/run-1/debounce.js +13 -0
  52. package/dist/bench/code-out/run-1/deepClone.js +15 -0
  53. package/dist/bench/code-out/run-1/groupBy.js +13 -0
  54. package/dist/bench/code-out/run-2/debounce.js +12 -0
  55. package/dist/bench/code-out/run-2/deepClone.js +18 -0
  56. package/dist/bench/code-out/run-2/groupBy.js +13 -0
  57. package/dist/bench/code-out/run-3/debounce.js +12 -0
  58. package/dist/bench/code-out/run-3/deepClone.js +18 -0
  59. package/dist/bench/code-out/run-3/groupBy.js +14 -0
  60. package/dist/bench/code-out/run-4/debounce.js +12 -0
  61. package/dist/bench/code-out/run-4/deepClone.js +16 -0
  62. package/dist/bench/code-out/run-4/groupBy.js +13 -0
  63. package/dist/cli/backups.js +7 -14
  64. package/dist/cli/banner.js +3 -3
  65. package/dist/cli/common.js +29 -13
  66. package/dist/cli/currency.js +1 -1
  67. package/dist/cli/dashboard.js +66 -35
  68. package/dist/cli/doctor-fix.js +204 -19
  69. package/dist/cli/doctor.js +263 -40
  70. package/dist/cli/hosts.js +25 -6
  71. package/dist/cli/install.js +150 -49
  72. package/dist/cli/interactive.js +3 -3
  73. package/dist/cli/manifest.js +109 -9
  74. package/dist/cli/profile.js +13 -8
  75. package/dist/cli/rtk-wiring.js +1 -1
  76. package/dist/cli/settings.js +20 -5
  77. package/dist/cli/uninstall.js +4 -4
  78. package/dist/cli/update.js +6 -7
  79. package/dist/cli/usage.js +6 -9
  80. package/dist/extensions/caveman/index.js +10 -4
  81. package/dist/extensions/caveman/rule-lite.md +77 -0
  82. package/dist/extensions/caveman/rule-megacave-lite.md +59 -0
  83. package/dist/extensions/caveman/rule-megacave-ultra.md +59 -0
  84. package/dist/extensions/caveman/rule-megacave.md +5 -3
  85. package/dist/extensions/caveman/rule-ultra.md +4 -4
  86. package/dist/extensions/caveman/rule.md +55 -45
  87. package/dist/extensions/combo/index.js +85 -25
  88. package/dist/extensions/commands/index.js +1 -1
  89. package/dist/extensions/pstack/index.js +121 -0
  90. package/dist/extensions/pstack/rule.md +141 -0
  91. package/dist/extensions/rtk/filter.js +0 -1
  92. package/dist/extensions/rtk/session.js +20 -12
  93. package/dist/extensions/server.js +90 -6
  94. package/dist/extensions/shared/agent-stats.js +214 -278
  95. package/dist/extensions/shared/brand-marks.js +33 -34
  96. package/dist/extensions/shared/carbon.js +1 -1
  97. package/dist/extensions/shared/plugin-settings.js +11 -6
  98. package/dist/extensions/shared/pricing.js +8 -16
  99. package/dist/extensions/shared/rtk-eligible.js +84 -0
  100. package/dist/extensions/shared/rtk-gain.js +5 -10
  101. package/dist/extensions/shared/session-state.js +15 -10
  102. package/dist/extensions/shared/sqlite.js +162 -0
  103. package/dist/extensions/shared/status.js +8 -9
  104. package/dist/extensions/shared/types.js +1 -1
  105. package/dist/extensions/shared/usage-ledger.js +155 -61
  106. package/dist/extensions/shared/usage-store.js +111 -52
  107. package/dist/extensions/shared/utils.js +5 -5
  108. package/dist/extensions/timing/index.js +31 -0
  109. package/dist/extensions/updater/index.js +3 -6
  110. package/dist/tersio.js +8 -3
  111. package/extensions/caveman/index.ts +10 -4
  112. package/extensions/caveman/rule-lite.md +77 -0
  113. package/extensions/caveman/rule-megacave-lite.md +59 -0
  114. package/extensions/caveman/rule-megacave-ultra.md +59 -0
  115. package/extensions/caveman/rule-megacave.md +5 -3
  116. package/extensions/caveman/rule-ultra.md +4 -4
  117. package/extensions/caveman/rule.md +55 -45
  118. package/extensions/combo/index.ts +86 -23
  119. package/extensions/commands/index.ts +1 -1
  120. package/extensions/pstack/index.ts +130 -0
  121. package/extensions/pstack/playbooks/authoring-a-skill.md +12 -0
  122. package/extensions/pstack/playbooks/autonomous-run.md +13 -0
  123. package/extensions/pstack/playbooks/autopilot-full.md +13 -0
  124. package/extensions/pstack/playbooks/autopilot-stack.md +16 -0
  125. package/extensions/pstack/playbooks/babysit.md +23 -0
  126. package/extensions/pstack/playbooks/bug-fix.md +15 -0
  127. package/extensions/pstack/playbooks/eval.md +25 -0
  128. package/extensions/pstack/playbooks/feature.md +21 -0
  129. package/extensions/pstack/playbooks/hillclimb.md +21 -0
  130. package/extensions/pstack/playbooks/investigation.md +14 -0
  131. package/extensions/pstack/playbooks/multi-phase-plan.md +155 -0
  132. package/extensions/pstack/playbooks/opening-a-pr.md +34 -0
  133. package/extensions/pstack/playbooks/orchestrate.md +111 -0
  134. package/extensions/pstack/playbooks/pause-safely.md +10 -0
  135. package/extensions/pstack/playbooks/perf-issue.md +25 -0
  136. package/extensions/pstack/playbooks/prototype.md +14 -0
  137. package/extensions/pstack/playbooks/refactoring.md +16 -0
  138. package/extensions/pstack/playbooks/runtime-forensics.md +11 -0
  139. package/extensions/pstack/playbooks/session-pickup.md +11 -0
  140. package/extensions/pstack/playbooks/shipping.md +17 -0
  141. package/extensions/pstack/playbooks/trace-forensics.md +14 -0
  142. package/extensions/pstack/playbooks/visual-parity.md +11 -0
  143. package/extensions/pstack/playbooks/worktree-cleanup.md +12 -0
  144. package/extensions/pstack/principles/attack-the-premise.md +22 -0
  145. package/extensions/pstack/principles/boundary-discipline.md +33 -0
  146. package/extensions/pstack/principles/build-the-lever.md +22 -0
  147. package/extensions/pstack/principles/encode-lessons-in-structure.md +10 -0
  148. package/extensions/pstack/principles/exhaust-the-design-space.md +8 -0
  149. package/extensions/pstack/principles/experience-first.md +18 -0
  150. package/extensions/pstack/principles/explain-the-number.md +16 -0
  151. package/extensions/pstack/principles/fix-root-causes.md +23 -0
  152. package/extensions/pstack/principles/foundational-thinking.md +20 -0
  153. package/extensions/pstack/principles/guard-the-context-window.md +11 -0
  154. package/extensions/pstack/principles/laziness-protocol.md +9 -0
  155. package/extensions/pstack/principles/make-operations-idempotent.md +23 -0
  156. package/extensions/pstack/principles/migrate-callers-then-delete-legacy-apis.md +21 -0
  157. package/extensions/pstack/principles/minimize-reader-load.md +17 -0
  158. package/extensions/pstack/principles/model-the-domain.md +23 -0
  159. package/extensions/pstack/principles/never-block-on-the-human.md +14 -0
  160. package/extensions/pstack/principles/outcome-oriented-execution.md +20 -0
  161. package/extensions/pstack/principles/prove-it-works.md +21 -0
  162. package/extensions/pstack/principles/redesign-from-first-principles.md +15 -0
  163. package/extensions/pstack/principles/separate-before-serializing-shared-state.md +15 -0
  164. package/extensions/pstack/principles/sequence-verifiable-units.md +16 -0
  165. package/extensions/pstack/principles/subtract-before-you-add.md +17 -0
  166. package/extensions/pstack/principles/test-behavior-not-implementation.md +20 -0
  167. package/extensions/pstack/principles/type-system-discipline.md +17 -0
  168. package/extensions/pstack/references/bugbot-triage.md +142 -0
  169. package/extensions/pstack/rule.md +141 -0
  170. package/extensions/pstack/skills/architect/SKILL.md +78 -0
  171. package/extensions/pstack/skills/architect/references/design-red-flags.md +57 -0
  172. package/extensions/pstack/skills/architect/references/rationale-template.md +35 -0
  173. package/extensions/pstack/skills/architect/references/runner-prompt.md +20 -0
  174. package/extensions/pstack/skills/arena/SKILL.md +47 -0
  175. package/extensions/pstack/skills/benchmark-checklist/SKILL.md +38 -0
  176. package/extensions/pstack/skills/blast-radius/SKILL.md +49 -0
  177. package/extensions/pstack/skills/bro/SKILL.md +6 -0
  178. package/extensions/pstack/skills/correct/SKILL.md +31 -0
  179. package/extensions/pstack/skills/figure-it-out/SKILL.md +51 -0
  180. package/extensions/pstack/skills/how/SKILL.md +35 -0
  181. package/extensions/pstack/skills/how/references/explainer-prompt.md +55 -0
  182. package/extensions/pstack/skills/how/references/explorer-prompt.md +35 -0
  183. package/extensions/pstack/skills/interrogate/SKILL.md +73 -0
  184. package/extensions/pstack/skills/interrogate/references/code-quality-review.md +47 -0
  185. package/extensions/pstack/skills/interrogate/references/lead-judgment.md +58 -0
  186. package/extensions/pstack/skills/interrogate/references/reviewer-prompt.md +62 -0
  187. package/extensions/pstack/skills/interrogate/references/rubric.md +77 -0
  188. package/extensions/pstack/skills/no-comments/SKILL.md +23 -0
  189. package/extensions/pstack/skills/reflect/SKILL.md +66 -0
  190. package/extensions/pstack/skills/reflect/references/divergent-reviewer.md +38 -0
  191. package/extensions/pstack/skills/reflect/references/judgment-reviewer.md +37 -0
  192. package/extensions/pstack/skills/reflect/references/synthesizer.md +56 -0
  193. package/extensions/pstack/skills/reflect/references/tooling-reviewer.md +48 -0
  194. package/extensions/pstack/skills/show-me-your-work/SKILL.md +81 -0
  195. package/extensions/pstack/skills/swarm/SKILL.md +45 -0
  196. package/extensions/pstack/skills/tdd/SKILL.md +41 -0
  197. package/extensions/pstack/skills/technical-writing/SKILL.md +105 -0
  198. package/extensions/pstack/skills/unslop/SKILL.md +61 -0
  199. package/extensions/pstack/skills/why/SKILL.md +97 -0
  200. package/extensions/pstack/skills/why/references/epistemics.md +71 -0
  201. package/extensions/pstack/skills/why/references/investigator-prompt.md +84 -0
  202. package/extensions/pstack/skills/why/references/source-playbook.md +17 -0
  203. package/extensions/pstack/skills/why/references/sources/code-archaeology.md +51 -0
  204. package/extensions/pstack/skills/why/references/sources/databricks.md +70 -0
  205. package/extensions/pstack/skills/why/references/sources/datadog.md +97 -0
  206. package/extensions/pstack/skills/why/references/sources/incident-postmortem.md +5 -0
  207. package/extensions/pstack/skills/why/references/sources/linear.md +48 -0
  208. package/extensions/pstack/skills/why/references/sources/notion.md +55 -0
  209. package/extensions/pstack/skills/why/references/sources/sentry.md +66 -0
  210. package/extensions/pstack/skills/why/references/sources/slack.md +17 -0
  211. package/extensions/pstack/skills/why/references/synthesizer-prompt.md +129 -0
  212. package/extensions/rtk/filter.ts +0 -1
  213. package/extensions/rtk/session.ts +19 -11
  214. package/extensions/server.ts +72 -7
  215. package/extensions/shared/agent-stats.ts +226 -278
  216. package/extensions/shared/brand-marks.ts +34 -34
  217. package/extensions/shared/carbon.ts +1 -1
  218. package/extensions/shared/plugin-settings.ts +12 -6
  219. package/extensions/shared/pricing.ts +10 -26
  220. package/extensions/shared/rtk-eligible.ts +83 -0
  221. package/extensions/shared/rtk-gain.ts +5 -10
  222. package/extensions/shared/session-state.ts +18 -13
  223. package/extensions/shared/sqlite.ts +162 -0
  224. package/extensions/shared/status.ts +9 -9
  225. package/extensions/shared/types.ts +2 -1
  226. package/extensions/shared/usage-ledger.ts +157 -66
  227. package/extensions/shared/usage-store.ts +120 -52
  228. package/extensions/shared/utils.ts +5 -5
  229. package/extensions/timing/index.ts +44 -0
  230. package/extensions/updater/index.ts +3 -8
  231. package/package.json +21 -9
@@ -34,7 +34,6 @@ export function rtkActive(entries) {
34
34
  export function filterableText(parts) {
35
35
  if (!Array.isArray(parts) || parts.length === 0)
36
36
  return null;
37
- // An image or a mixed result is not text to filter.
38
37
  if (parts.some((part) => part?.type !== 'text'))
39
38
  return null;
40
39
  return parts.map((part) => part?.text ?? '').join('\n');
@@ -22,8 +22,11 @@ function setRtkProcessEnabled(enabled) {
22
22
  else
23
23
  process.env.RTK_DISABLED = '1';
24
24
  }
25
- // rtk's awareness contract: state the output contract, never tell the model to type `rtk`.
26
- const RTK_PROMPT = `RTK guidance active. Bash output, and large grep and glob results, are condensed to save tokens: every signal kept, costly noise dropped. Treat a condensed result as complete. Re-run something unfiltered only when its result is empty, garbled, or contradicts its exit code.`;
25
+ // rtk's contract: name the rewritten surface first and the binary second — a hand-typed `rtk` in front of a
26
+ // rewritten command fights the rewrite, and the direct calls cover only what the rewrite cannot express.
27
+ const RTK_PROMPT = `RTK guidance active. What you run is condensed automatically, in the host's own rewritten form: searches (grep, rg, ast-grep), listings (ls, find, tree), reads (cat), package and test runs (bun, npm, pnpm, npx, cargo, go, pytest, vitest, jest, ctest, tsc), version control and forges (git, gh, glab), containers and clusters (docker, kubectl), cloud and platform CLIs (aws, oc, dotnet), PHP tooling (phpunit, pest, phpstan, pint, ecs), lint and format (eslint, ruff, mypy, prettier, sqlfluff, playwright, prisma, next), and data tools (jq, curl, wget, psql, wc, make, tail, docker logs). The runner's own subcommand, such as \`rtk bun run test\`, keeps the failing test name, the assertion, and the file and line. Every signal kept, costly noise dropped. Treat a condensed result as complete. Re-run something unfiltered only when its result is empty, garbled, or contradicts its exit code.
28
+
29
+ Call rtk directly for what the rewrite cannot express: \`rtk read <file> --level aggressive\` for an overview, \`--max-lines N\` for a preview, \`rtk smart <file>\` for a two-line summary, \`rtk summary <cmd>\` for a heuristic one, \`rtk err <cmd>\` for errors only, \`rtk log <file>\` to dedupe logs, \`rtk json <file>\` with \`--keys-only\` for shape, plus \`rtk deps\`, \`rtk diff\`, \`rtk env\`, \`rtk ast-grep\`, and \`rtk test <cmd>\` for a runner without its own form. A command with no condensed form passes through unchanged, so wrap it: \`rtk err node build.js\`, and \`rtk find -name '*.ts'\` in place of fd. Never type \`rtk\` in front of a command listed above.`;
27
30
  export default function rtkSessionExtension(pi) {
28
31
  let enabled = DEFAULT_ENABLED;
29
32
  function fail(text) {
@@ -37,7 +40,7 @@ export default function rtkSessionExtension(pi) {
37
40
  announceStatus(ctx);
38
41
  }
39
42
  setExtensionLabel(pi, 'RTK session toggle');
40
- // Live mirror: a /tersio or /combo switch publishes shared state — adopt it at once so the next turn and the rtk_run gate see it, no reload.
43
+ // Live mirror: a /tersio or /combo switch publishes shared state — adopt it once so the next turn and rtk_run see it.
41
44
  function syncFromShared(state) {
42
45
  enabled = state.rtk === 'on';
43
46
  setRtkProcessEnabled(enabled);
@@ -65,30 +68,35 @@ export default function rtkSessionExtension(pi) {
65
68
  pi.registerTool?.({
66
69
  name: 'rtk_run',
67
70
  label: 'RTK Run',
68
- description: 'Run the installed `rtk` binary for compact command output when RTK mode is enabled.',
71
+ description: 'Run a shell command through the installed `rtk` binary when RTK mode is on, and run it plainly when RTK mode is off.',
69
72
  parameters: stringArrayToolParams(pi, 'args', {
70
73
  minItems: 1,
71
74
  description: "Arguments passed to rtk, e.g. ['git','status'] or ['read','src/index.ts']",
72
75
  }),
73
76
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
74
77
  // A subagent's own flag sits at its start-up default, so honour shared state too.
75
- if (!(enabled || getSharedComboState().rtk === 'on'))
76
- return fail('RTK mode is off. Run /rtk on for this session, or use bash explicitly.');
78
+ const rtkOn = enabled || getSharedComboState().rtk === 'on';
77
79
  if (typeof pi.exec !== 'function')
78
80
  return fail('This host cannot run rtk.');
79
- onUpdate?.({ content: [{ type: 'text', text: `rtk ${params.args.join(' ')}` }], details: { phase: 'start' } });
81
+ // RTK off: run the command as given rather than answering "mode is off". The tool is the only one left under --no-tools, and a refusal there made the model spend turns re-calling it. rtk is the condenser, never the command.
82
+ const cwd = ctx?.cwd || pi.cwd;
83
+ const bin = rtkOn ? resolveRtkBinary() : null;
84
+ if (rtkOn && bin === null)
85
+ return fail('RTK mode is on but the rtk binary is not installed. Run: tersio update');
86
+ const argv = params.args;
87
+ onUpdate?.({ content: [{ type: 'text', text: `${bin === null ? '' : 'rtk '}${argv.join(' ')}` }], details: { phase: 'start' } });
80
88
  try {
81
89
  // A GUI-launched host may lack ~/.bun/bin on PATH, so prefer the resolved path.
82
- const result = await pi.exec(resolveRtkBinary() ?? 'rtk', params.args, { signal, cwd: ctx?.cwd || pi.cwd });
90
+ const result = await pi.exec(bin ?? argv[0], argv, { signal, cwd });
83
91
  const text = [result.stdout, result.stderr].filter(Boolean).join('\n');
84
92
  return {
85
93
  isError: result.code !== 0,
86
- content: [{ type: 'text', text: text || `rtk exited ${result.code}` }],
87
- details: { code: result.code, killed: result.killed, enabled },
94
+ content: [{ type: 'text', text: text || `exited ${result.code}` }],
95
+ details: { code: result.code, killed: result.killed, enabled, condensed: bin !== null },
88
96
  };
89
97
  }
90
98
  catch (e) {
91
- return fail(`rtk could not run: ${e instanceof Error ? e.message : String(e)}`);
99
+ return fail(`${bin === null ? argv[0] : 'rtk'} could not run: ${e instanceof Error ? e.message : String(e)}`);
92
100
  }
93
101
  },
94
102
  });
@@ -97,7 +105,7 @@ export default function rtkSessionExtension(pi) {
97
105
  // A subagent's branch has no Tersio entries; reconciling it would wipe shared state.
98
106
  if (hasModeState(entries))
99
107
  reconcileSharedComboEntries(entries);
100
- // Persisted session state wins; a fresh session falls back to the installer/user-configured default (off unless configured).
108
+ // Persisted session state wins; a fresh session falls back to the installer/user default (off unless configured).
101
109
  const persisted = resolveEnabled(entries);
102
110
  enabled = typeof persisted === 'boolean' ? persisted : enabled || readRtkDefault();
103
111
  setRtkProcessEnabled(enabled);
@@ -1,6 +1,10 @@
1
1
  // OpenCode plugin entry: maps its plugin API onto the shared ExtensionApi the mode extensions consume.
2
2
  import { execFile } from 'node:child_process';
3
+ import { readFileSync, readdirSync } from 'node:fs';
4
+ import { basename, dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
3
6
  import cavemanSessionExtension from './caveman/index.js';
7
+ import pstackSessionExtension from './pstack/index.js';
4
8
  import rtkSessionExtension from './rtk/session.js';
5
9
  import rtkFilterExtension, { FILTER_BY_TOOL, MIN_FILTER_BYTES, filterThroughRtk, filterableText, rtkActive } from './rtk/filter.js';
6
10
  import comboToggleExtension from './combo/index.js';
@@ -20,8 +24,7 @@ const plugin = {
20
24
  const own = (await ctx.storage.get(`entries:${sid}`)) ?? [];
21
25
  if (own.length)
22
26
  return own;
23
- // Pre-scoped installs kept one global list; adopt it once, then diverge.
24
- // Clear it after adoption so it can never shadow saved defaults later.
27
+ // Pre-scoped installs kept one global list; adopt it once and clear it so it cannot shadow saved defaults later.
25
28
  const legacy = (await ctx.storage.get('entries')) ?? [];
26
29
  if (legacy.length)
27
30
  void ctx.storage.set('entries', []).catch(() => { });
@@ -112,7 +115,7 @@ const plugin = {
112
115
  lifecycle.set(event, list);
113
116
  },
114
117
  };
115
- for (const extension of [cavemanSessionExtension, rtkSessionExtension, rtkFilterExtension, comboToggleExtension, tersioCommandsExtension])
118
+ for (const extension of [cavemanSessionExtension, pstackSessionExtension, rtkSessionExtension, rtkFilterExtension, comboToggleExtension, tersioCommandsExtension])
116
119
  extension(host);
117
120
  // The first hook of an unseen session runs its session_start handlers.
118
121
  const sidOf = (value) => typeof value === 'string' && value ? value : undefined;
@@ -127,8 +130,7 @@ const plugin = {
127
130
  await ensureSession(sid);
128
131
  const text = typeof input.prompt?.text === 'string' ? input.prompt.text : '';
129
132
  const args = text.includes(' ') ? text.slice(text.indexOf(' ') + 1).trim() : '';
130
- // The shared handlers report through ui.notify, which has no OpenCode
131
- // surface. Collect it and send it back so a command never answers blank.
133
+ // Shared handlers notify via ui.notify, which OpenCode lacks; echo it back so a command does not answer blank.
132
134
  const notes = [];
133
135
  const cmdCtx = {
134
136
  ...stubCtx(sid),
@@ -163,6 +165,16 @@ const plugin = {
163
165
  });
164
166
  }
165
167
  });
168
+ // The tree ships the ruleset beside the extensions, and OpenCode reads no node_modules from a plugin dir.
169
+ try {
170
+ await ctx.skill.transform((editor) => {
171
+ for (const skill of bundledSkills())
172
+ editor.add(skill);
173
+ });
174
+ }
175
+ catch {
176
+ // Hosts without the skill transform keep the ruleset in the prompt alone.
177
+ }
166
178
  await ctx.session.hook('context', async (event) => {
167
179
  const sid = sidOf(event.sessionID);
168
180
  if (sid)
@@ -198,6 +210,12 @@ const plugin = {
198
210
  const input = event.input;
199
211
  if (typeof input?.command !== 'string' || !input.command)
200
212
  return;
213
+ // /rtk off must stop the rewrite too, or the mode switch only silences the prompt block.
214
+ const sid = typeof event.sessionID === 'string' ? event.sessionID : activeSid;
215
+ if (sid !== undefined)
216
+ await ensureSession(sid);
217
+ if (!rtkActive(sid === undefined ? undefined : sessionEntriesOf(sid)))
218
+ return;
201
219
  const rewritten = await rewriteCommand(input.command);
202
220
  if (rewritten)
203
221
  input.command = rewritten;
@@ -246,6 +264,64 @@ function execRtk(cmd, args, opts) {
246
264
  });
247
265
  });
248
266
  }
267
+ /** `rtk rewrite` exit codes: 0 rewrite found, 1 no equivalent, 3 advisory; the code is the contract. */
268
+ const RTK_REWRITE_FOUND = 0;
269
+ const RTK_REWRITE_ADVISORY = 3;
270
+ /** The bundled ruleset's skill dirs, beside the plugin entry: OpenCode reads no node_modules from a plugin. */
271
+ const PONYTAIL_BUNDLE_SKILLS = join(dirname(fileURLToPath(import.meta.url)), 'ponytail-bundle', 'skills');
272
+ /** Frontmatter a SKILL.md carries: flat keys only, the shape every bundled skill uses. */
273
+ function parseSkillFile(file) {
274
+ let raw;
275
+ try {
276
+ raw = readFileSync(file, 'utf8');
277
+ }
278
+ catch {
279
+ return null;
280
+ }
281
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---[^\S\n]*\r?\n?([\s\S]*)$/);
282
+ if (!match)
283
+ return null;
284
+ const field = (key) => {
285
+ const lines = match[1].split(/\r?\n/);
286
+ const at = lines.findIndex((line) => line.startsWith(`${key}:`));
287
+ if (at === -1)
288
+ return undefined;
289
+ const value = lines[at].slice(key.length + 1).trim();
290
+ if (value[0] !== '>' && value[0] !== '|')
291
+ return value;
292
+ // Folded (`>`) joins the indented lines; the next unindented key ends the block.
293
+ const block = [];
294
+ for (const line of lines.slice(at + 1)) {
295
+ if (line.trim() !== '' && !/^\s/.test(line))
296
+ break;
297
+ block.push(line.trim());
298
+ }
299
+ while (block.length > 0 && block[block.length - 1] === '')
300
+ block.pop();
301
+ return block.join(value[0] === '|' ? '\n' : ' ');
302
+ };
303
+ const id = field('name') ?? basename(dirname(file));
304
+ return { id, name: id, description: field('description') ?? '', path: file, content: match[2] };
305
+ }
306
+ /** Every bundled skill, or an empty list when the installer has not bundled them. */
307
+ function bundledSkills() {
308
+ let entries;
309
+ try {
310
+ entries = readdirSync(PONYTAIL_BUNDLE_SKILLS, { withFileTypes: true });
311
+ }
312
+ catch {
313
+ return [];
314
+ }
315
+ const out = [];
316
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
317
+ if (!entry.isDirectory())
318
+ continue;
319
+ const skill = parseSkillFile(join(PONYTAIL_BUNDLE_SKILLS, entry.name, 'SKILL.md'));
320
+ if (skill)
321
+ out.push(skill);
322
+ }
323
+ return out;
324
+ }
249
325
  function rewriteCommand(command) {
250
326
  const bin = resolveRtkBinary();
251
327
  if (!bin)
@@ -256,8 +332,16 @@ function rewriteCommand(command) {
256
332
  const err = error;
257
333
  if (err && (err.killed || err.signal))
258
334
  return resolve(null);
335
+ const code = typeof err?.code === 'number' ? err.code : RTK_REWRITE_FOUND;
336
+ // Exit 1 is the only "no equivalent" answer, so anything else with output rewrites.
337
+ if (code === 1)
338
+ return resolve(null);
259
339
  const out = String(stdout).trim();
260
- resolve(out && out !== command ? out : null);
340
+ if (!out || out === command)
341
+ return resolve(null);
342
+ if (code !== RTK_REWRITE_FOUND && code !== RTK_REWRITE_ADVISORY)
343
+ return resolve(null);
344
+ resolve(out);
261
345
  });
262
346
  });
263
347
  }