@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.
- package/README.md +42 -38
- package/dashboard/dist/index.html +15 -15
- package/dist/bench/code-out/pfull-0/debounce.js +13 -0
- package/dist/bench/code-out/pfull-0/deepClone.js +18 -0
- package/dist/bench/code-out/pfull-0/groupBy.js +15 -0
- package/dist/bench/code-out/pfull-1/debounce.js +13 -0
- package/dist/bench/code-out/pfull-1/deepClone.js +17 -0
- package/dist/bench/code-out/pfull-1/groupBy.js +14 -0
- package/dist/bench/code-out/pfull-2/debounce.js +13 -0
- package/dist/bench/code-out/pfull-2/deepClone.js +18 -0
- package/dist/bench/code-out/pfull-2/groupBy.js +15 -0
- package/dist/bench/code-out/pfull-3/debounce.js +12 -0
- package/dist/bench/code-out/pfull-3/deepClone.js +17 -0
- package/dist/bench/code-out/pfull-3/groupBy.js +7 -0
- package/dist/bench/code-out/pfull-4/debounce.js +10 -0
- package/dist/bench/code-out/pfull-4/deepClone.js +17 -0
- package/dist/bench/code-out/pfull-4/groupBy.js +14 -0
- package/dist/bench/code-out/plite-0/debounce.js +13 -0
- package/dist/bench/code-out/plite-0/deepClone.js +18 -0
- package/dist/bench/code-out/plite-0/groupBy.js +15 -0
- package/dist/bench/code-out/plite-1/debounce.js +13 -0
- package/dist/bench/code-out/plite-1/deepClone.js +17 -0
- package/dist/bench/code-out/plite-1/groupBy.js +11 -0
- package/dist/bench/code-out/plite-2/debounce.js +12 -0
- package/dist/bench/code-out/plite-2/deepClone.js +17 -0
- package/dist/bench/code-out/plite-2/groupBy.js +14 -0
- package/dist/bench/code-out/plite-3/debounce.js +12 -0
- package/dist/bench/code-out/plite-3/deepClone.js +17 -0
- package/dist/bench/code-out/plite-3/groupBy.js +14 -0
- package/dist/bench/code-out/plite-4/debounce.js +12 -0
- package/dist/bench/code-out/plite-4/deepClone.js +17 -0
- package/dist/bench/code-out/plite-4/groupBy.js +14 -0
- package/dist/bench/code-out/pultra-0/debounce.js +12 -0
- package/dist/bench/code-out/pultra-0/deepClone.js +20 -0
- package/dist/bench/code-out/pultra-0/groupBy.js +14 -0
- package/dist/bench/code-out/pultra-1/debounce.js +10 -0
- package/dist/bench/code-out/pultra-1/deepClone.js +17 -0
- package/dist/bench/code-out/pultra-1/groupBy.js +7 -0
- package/dist/bench/code-out/pultra-2/debounce.js +13 -0
- package/dist/bench/code-out/pultra-2/deepClone.js +18 -0
- package/dist/bench/code-out/pultra-2/groupBy.js +15 -0
- package/dist/bench/code-out/pultra-3/debounce.js +13 -0
- package/dist/bench/code-out/pultra-3/deepClone.js +18 -0
- package/dist/bench/code-out/pultra-3/groupBy.js +15 -0
- package/dist/bench/code-out/pultra-4/debounce.js +13 -0
- package/dist/bench/code-out/pultra-4/deepClone.js +18 -0
- package/dist/bench/code-out/pultra-4/groupBy.js +15 -0
- package/dist/bench/code-out/run-0/debounce.js +12 -0
- package/dist/bench/code-out/run-0/deepClone.js +17 -0
- package/dist/bench/code-out/run-0/groupBy.js +14 -0
- package/dist/bench/code-out/run-1/debounce.js +13 -0
- package/dist/bench/code-out/run-1/deepClone.js +15 -0
- package/dist/bench/code-out/run-1/groupBy.js +13 -0
- package/dist/bench/code-out/run-2/debounce.js +12 -0
- package/dist/bench/code-out/run-2/deepClone.js +18 -0
- package/dist/bench/code-out/run-2/groupBy.js +13 -0
- package/dist/bench/code-out/run-3/debounce.js +12 -0
- package/dist/bench/code-out/run-3/deepClone.js +18 -0
- package/dist/bench/code-out/run-3/groupBy.js +14 -0
- package/dist/bench/code-out/run-4/debounce.js +12 -0
- package/dist/bench/code-out/run-4/deepClone.js +16 -0
- package/dist/bench/code-out/run-4/groupBy.js +13 -0
- package/dist/cli/backups.js +7 -14
- package/dist/cli/banner.js +3 -3
- package/dist/cli/common.js +29 -13
- package/dist/cli/currency.js +1 -1
- package/dist/cli/dashboard.js +66 -35
- package/dist/cli/doctor-fix.js +204 -19
- package/dist/cli/doctor.js +263 -40
- package/dist/cli/hosts.js +25 -6
- package/dist/cli/install.js +150 -49
- package/dist/cli/interactive.js +3 -3
- package/dist/cli/manifest.js +109 -9
- package/dist/cli/profile.js +13 -8
- package/dist/cli/rtk-wiring.js +1 -1
- package/dist/cli/settings.js +20 -5
- package/dist/cli/uninstall.js +4 -4
- package/dist/cli/update.js +6 -7
- package/dist/cli/usage.js +6 -9
- package/dist/extensions/caveman/index.js +10 -4
- package/dist/extensions/caveman/rule-lite.md +77 -0
- package/dist/extensions/caveman/rule-megacave-lite.md +59 -0
- package/dist/extensions/caveman/rule-megacave-ultra.md +59 -0
- package/dist/extensions/caveman/rule-megacave.md +5 -3
- package/dist/extensions/caveman/rule-ultra.md +4 -4
- package/dist/extensions/caveman/rule.md +55 -45
- package/dist/extensions/combo/index.js +85 -25
- package/dist/extensions/commands/index.js +1 -1
- package/dist/extensions/pstack/index.js +121 -0
- package/dist/extensions/pstack/rule.md +141 -0
- package/dist/extensions/rtk/filter.js +0 -1
- package/dist/extensions/rtk/session.js +20 -12
- package/dist/extensions/server.js +90 -6
- package/dist/extensions/shared/agent-stats.js +214 -278
- package/dist/extensions/shared/brand-marks.js +33 -34
- package/dist/extensions/shared/carbon.js +1 -1
- package/dist/extensions/shared/plugin-settings.js +11 -6
- package/dist/extensions/shared/pricing.js +8 -16
- package/dist/extensions/shared/rtk-eligible.js +84 -0
- package/dist/extensions/shared/rtk-gain.js +5 -10
- package/dist/extensions/shared/session-state.js +15 -10
- package/dist/extensions/shared/sqlite.js +162 -0
- package/dist/extensions/shared/status.js +8 -9
- package/dist/extensions/shared/types.js +1 -1
- package/dist/extensions/shared/usage-ledger.js +155 -61
- package/dist/extensions/shared/usage-store.js +111 -52
- package/dist/extensions/shared/utils.js +5 -5
- package/dist/extensions/timing/index.js +31 -0
- package/dist/extensions/updater/index.js +3 -6
- package/dist/tersio.js +8 -3
- package/extensions/caveman/index.ts +10 -4
- package/extensions/caveman/rule-lite.md +77 -0
- package/extensions/caveman/rule-megacave-lite.md +59 -0
- package/extensions/caveman/rule-megacave-ultra.md +59 -0
- package/extensions/caveman/rule-megacave.md +5 -3
- package/extensions/caveman/rule-ultra.md +4 -4
- package/extensions/caveman/rule.md +55 -45
- package/extensions/combo/index.ts +86 -23
- package/extensions/commands/index.ts +1 -1
- package/extensions/pstack/index.ts +130 -0
- package/extensions/pstack/playbooks/authoring-a-skill.md +12 -0
- package/extensions/pstack/playbooks/autonomous-run.md +13 -0
- package/extensions/pstack/playbooks/autopilot-full.md +13 -0
- package/extensions/pstack/playbooks/autopilot-stack.md +16 -0
- package/extensions/pstack/playbooks/babysit.md +23 -0
- package/extensions/pstack/playbooks/bug-fix.md +15 -0
- package/extensions/pstack/playbooks/eval.md +25 -0
- package/extensions/pstack/playbooks/feature.md +21 -0
- package/extensions/pstack/playbooks/hillclimb.md +21 -0
- package/extensions/pstack/playbooks/investigation.md +14 -0
- package/extensions/pstack/playbooks/multi-phase-plan.md +155 -0
- package/extensions/pstack/playbooks/opening-a-pr.md +34 -0
- package/extensions/pstack/playbooks/orchestrate.md +111 -0
- package/extensions/pstack/playbooks/pause-safely.md +10 -0
- package/extensions/pstack/playbooks/perf-issue.md +25 -0
- package/extensions/pstack/playbooks/prototype.md +14 -0
- package/extensions/pstack/playbooks/refactoring.md +16 -0
- package/extensions/pstack/playbooks/runtime-forensics.md +11 -0
- package/extensions/pstack/playbooks/session-pickup.md +11 -0
- package/extensions/pstack/playbooks/shipping.md +17 -0
- package/extensions/pstack/playbooks/trace-forensics.md +14 -0
- package/extensions/pstack/playbooks/visual-parity.md +11 -0
- package/extensions/pstack/playbooks/worktree-cleanup.md +12 -0
- package/extensions/pstack/principles/attack-the-premise.md +22 -0
- package/extensions/pstack/principles/boundary-discipline.md +33 -0
- package/extensions/pstack/principles/build-the-lever.md +22 -0
- package/extensions/pstack/principles/encode-lessons-in-structure.md +10 -0
- package/extensions/pstack/principles/exhaust-the-design-space.md +8 -0
- package/extensions/pstack/principles/experience-first.md +18 -0
- package/extensions/pstack/principles/explain-the-number.md +16 -0
- package/extensions/pstack/principles/fix-root-causes.md +23 -0
- package/extensions/pstack/principles/foundational-thinking.md +20 -0
- package/extensions/pstack/principles/guard-the-context-window.md +11 -0
- package/extensions/pstack/principles/laziness-protocol.md +9 -0
- package/extensions/pstack/principles/make-operations-idempotent.md +23 -0
- package/extensions/pstack/principles/migrate-callers-then-delete-legacy-apis.md +21 -0
- package/extensions/pstack/principles/minimize-reader-load.md +17 -0
- package/extensions/pstack/principles/model-the-domain.md +23 -0
- package/extensions/pstack/principles/never-block-on-the-human.md +14 -0
- package/extensions/pstack/principles/outcome-oriented-execution.md +20 -0
- package/extensions/pstack/principles/prove-it-works.md +21 -0
- package/extensions/pstack/principles/redesign-from-first-principles.md +15 -0
- package/extensions/pstack/principles/separate-before-serializing-shared-state.md +15 -0
- package/extensions/pstack/principles/sequence-verifiable-units.md +16 -0
- package/extensions/pstack/principles/subtract-before-you-add.md +17 -0
- package/extensions/pstack/principles/test-behavior-not-implementation.md +20 -0
- package/extensions/pstack/principles/type-system-discipline.md +17 -0
- package/extensions/pstack/references/bugbot-triage.md +142 -0
- package/extensions/pstack/rule.md +141 -0
- package/extensions/pstack/skills/architect/SKILL.md +78 -0
- package/extensions/pstack/skills/architect/references/design-red-flags.md +57 -0
- package/extensions/pstack/skills/architect/references/rationale-template.md +35 -0
- package/extensions/pstack/skills/architect/references/runner-prompt.md +20 -0
- package/extensions/pstack/skills/arena/SKILL.md +47 -0
- package/extensions/pstack/skills/benchmark-checklist/SKILL.md +38 -0
- package/extensions/pstack/skills/blast-radius/SKILL.md +49 -0
- package/extensions/pstack/skills/bro/SKILL.md +6 -0
- package/extensions/pstack/skills/correct/SKILL.md +31 -0
- package/extensions/pstack/skills/figure-it-out/SKILL.md +51 -0
- package/extensions/pstack/skills/how/SKILL.md +35 -0
- package/extensions/pstack/skills/how/references/explainer-prompt.md +55 -0
- package/extensions/pstack/skills/how/references/explorer-prompt.md +35 -0
- package/extensions/pstack/skills/interrogate/SKILL.md +73 -0
- package/extensions/pstack/skills/interrogate/references/code-quality-review.md +47 -0
- package/extensions/pstack/skills/interrogate/references/lead-judgment.md +58 -0
- package/extensions/pstack/skills/interrogate/references/reviewer-prompt.md +62 -0
- package/extensions/pstack/skills/interrogate/references/rubric.md +77 -0
- package/extensions/pstack/skills/no-comments/SKILL.md +23 -0
- package/extensions/pstack/skills/reflect/SKILL.md +66 -0
- package/extensions/pstack/skills/reflect/references/divergent-reviewer.md +38 -0
- package/extensions/pstack/skills/reflect/references/judgment-reviewer.md +37 -0
- package/extensions/pstack/skills/reflect/references/synthesizer.md +56 -0
- package/extensions/pstack/skills/reflect/references/tooling-reviewer.md +48 -0
- package/extensions/pstack/skills/show-me-your-work/SKILL.md +81 -0
- package/extensions/pstack/skills/swarm/SKILL.md +45 -0
- package/extensions/pstack/skills/tdd/SKILL.md +41 -0
- package/extensions/pstack/skills/technical-writing/SKILL.md +105 -0
- package/extensions/pstack/skills/unslop/SKILL.md +61 -0
- package/extensions/pstack/skills/why/SKILL.md +97 -0
- package/extensions/pstack/skills/why/references/epistemics.md +71 -0
- package/extensions/pstack/skills/why/references/investigator-prompt.md +84 -0
- package/extensions/pstack/skills/why/references/source-playbook.md +17 -0
- package/extensions/pstack/skills/why/references/sources/code-archaeology.md +51 -0
- package/extensions/pstack/skills/why/references/sources/databricks.md +70 -0
- package/extensions/pstack/skills/why/references/sources/datadog.md +97 -0
- package/extensions/pstack/skills/why/references/sources/incident-postmortem.md +5 -0
- package/extensions/pstack/skills/why/references/sources/linear.md +48 -0
- package/extensions/pstack/skills/why/references/sources/notion.md +55 -0
- package/extensions/pstack/skills/why/references/sources/sentry.md +66 -0
- package/extensions/pstack/skills/why/references/sources/slack.md +17 -0
- package/extensions/pstack/skills/why/references/synthesizer-prompt.md +129 -0
- package/extensions/rtk/filter.ts +0 -1
- package/extensions/rtk/session.ts +19 -11
- package/extensions/server.ts +72 -7
- package/extensions/shared/agent-stats.ts +226 -278
- package/extensions/shared/brand-marks.ts +34 -34
- package/extensions/shared/carbon.ts +1 -1
- package/extensions/shared/plugin-settings.ts +12 -6
- package/extensions/shared/pricing.ts +10 -26
- package/extensions/shared/rtk-eligible.ts +83 -0
- package/extensions/shared/rtk-gain.ts +5 -10
- package/extensions/shared/session-state.ts +18 -13
- package/extensions/shared/sqlite.ts +162 -0
- package/extensions/shared/status.ts +9 -9
- package/extensions/shared/types.ts +2 -1
- package/extensions/shared/usage-ledger.ts +157 -66
- package/extensions/shared/usage-store.ts +120 -52
- package/extensions/shared/utils.ts +5 -5
- package/extensions/timing/index.ts +44 -0
- package/extensions/updater/index.ts +3 -8
- 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
|
|
26
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 || `
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
}
|