@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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Debounce: fires fn once, ms after the last call, using that last call's arguments. A call during the wait restarts it.
|
|
2
|
+
export function debounce(fn, ms) {
|
|
3
|
+
let timer;
|
|
4
|
+
return (...args) => {
|
|
5
|
+
if (timer !== undefined) {
|
|
6
|
+
clearTimeout(timer);
|
|
7
|
+
}
|
|
8
|
+
timer = setTimeout(() => {
|
|
9
|
+
timer = undefined;
|
|
10
|
+
fn(...args);
|
|
11
|
+
}, ms);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// deepClone: recursive clone of plain objects, arrays, Date, and primitives. Shares no mutable structure with the input; no structuredClone or JSON round-trip.
|
|
2
|
+
export function deepClone(value) {
|
|
3
|
+
if (value === null || typeof value !== 'object') {
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
if (value instanceof Date) {
|
|
7
|
+
return new Date(value.getTime());
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
return value.map((item) => deepClone(item));
|
|
11
|
+
}
|
|
12
|
+
const source = value;
|
|
13
|
+
const clone = {};
|
|
14
|
+
for (const key of Object.keys(source)) {
|
|
15
|
+
clone[key] = deepClone(source[key]);
|
|
16
|
+
}
|
|
17
|
+
return clone;
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// groupBy: buckets items by keyFn. Keys land in first-seen order and each bucket keeps the input order.
|
|
2
|
+
export function groupBy(items, keyFn) {
|
|
3
|
+
const groups = {};
|
|
4
|
+
for (const item of items) {
|
|
5
|
+
const key = keyFn(item);
|
|
6
|
+
const bucket = groups[key];
|
|
7
|
+
if (bucket === undefined) {
|
|
8
|
+
groups[key] = [item];
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
bucket.push(item);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return groups;
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function deepClone(value) {
|
|
2
|
+
if (value === null || typeof value !== 'object') {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
if (value instanceof Date) {
|
|
6
|
+
return new Date(value.getTime());
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
return value.map((item) => deepClone(item));
|
|
10
|
+
}
|
|
11
|
+
const source = value;
|
|
12
|
+
const clone = {};
|
|
13
|
+
for (const key of Object.keys(source)) {
|
|
14
|
+
clone[key] = deepClone(source[key]);
|
|
15
|
+
}
|
|
16
|
+
return clone;
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function groupBy(items, keyFn) {
|
|
2
|
+
const groups = {};
|
|
3
|
+
for (const item of items) {
|
|
4
|
+
const key = keyFn(item);
|
|
5
|
+
const existing = groups[key];
|
|
6
|
+
if (existing === undefined) {
|
|
7
|
+
groups[key] = [item];
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
existing.push(item);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return groups;
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Debounce: the returned wrapper fires fn once, after ms milliseconds of quiet, using the last call's arguments.
|
|
2
|
+
export function debounce(fn, ms) {
|
|
3
|
+
let timer;
|
|
4
|
+
// Each call rebinds args, so the closure the timeout reads is always the latest one.
|
|
5
|
+
return (...args) => {
|
|
6
|
+
if (timer !== undefined)
|
|
7
|
+
clearTimeout(timer);
|
|
8
|
+
timer = setTimeout(() => {
|
|
9
|
+
timer = undefined;
|
|
10
|
+
fn(...args);
|
|
11
|
+
}, ms);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// deepClone: recursive clone for plain objects, arrays, Date, and primitives. No structuredClone or JSON round-trip.
|
|
2
|
+
export function deepClone(value) {
|
|
3
|
+
if (value === null || typeof value !== 'object')
|
|
4
|
+
return value;
|
|
5
|
+
if (value instanceof Date)
|
|
6
|
+
return new Date(value.getTime());
|
|
7
|
+
if (Array.isArray(value))
|
|
8
|
+
return value.map((item) => deepClone(item));
|
|
9
|
+
const source = value;
|
|
10
|
+
const clone = {};
|
|
11
|
+
for (const key of Object.keys(source)) {
|
|
12
|
+
clone[key] = deepClone(source[key]);
|
|
13
|
+
}
|
|
14
|
+
return clone;
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// groupBy: buckets items by keyFn. Keys land in first-seen order and each bucket keeps the input order.
|
|
2
|
+
export function groupBy(items, keyFn) {
|
|
3
|
+
const groups = {};
|
|
4
|
+
for (const item of items) {
|
|
5
|
+
const key = keyFn(item);
|
|
6
|
+
const bucket = groups[key];
|
|
7
|
+
if (bucket === undefined)
|
|
8
|
+
groups[key] = [item];
|
|
9
|
+
else
|
|
10
|
+
bucket.push(item);
|
|
11
|
+
}
|
|
12
|
+
return groups;
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Trailing-edge debounce: `fn` runs only once `ms` passes with no further call, and always with the last call's arguments.
|
|
2
|
+
export function debounce(fn, ms) {
|
|
3
|
+
let timer;
|
|
4
|
+
return (...args) => {
|
|
5
|
+
if (timer !== undefined)
|
|
6
|
+
clearTimeout(timer);
|
|
7
|
+
timer = setTimeout(() => {
|
|
8
|
+
timer = undefined;
|
|
9
|
+
fn(...args);
|
|
10
|
+
}, ms);
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Recursive structural copy: plain objects, arrays, Dates, and primitives. No shared mutable structure with the input.
|
|
2
|
+
export function deepClone(value) {
|
|
3
|
+
if (value === null || typeof value !== 'object')
|
|
4
|
+
return value;
|
|
5
|
+
// SAFETY: a Date owns no enumerable props, so it must be rebuilt from its epoch ms or it clones to `{}`.
|
|
6
|
+
if (value instanceof Date)
|
|
7
|
+
return new Date(value.getTime());
|
|
8
|
+
// SAFETY: Array.isArray above proves an array, so mapping deepClone over the elements preserves its shape.
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
const items = value;
|
|
11
|
+
return items.map((item) => deepClone(item));
|
|
12
|
+
}
|
|
13
|
+
// SAFETY: Object.entries copies every own enumerable key, so the plain-object form of T is reproduced.
|
|
14
|
+
const clone = {};
|
|
15
|
+
for (const [key, item] of Object.entries(value))
|
|
16
|
+
clone[key] = deepClone(item);
|
|
17
|
+
return clone;
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Buckets items by `keyFn`, preserving input order inside each group.
|
|
2
|
+
export function groupBy(items, keyFn) {
|
|
3
|
+
const groups = {};
|
|
4
|
+
for (const item of items) {
|
|
5
|
+
const key = keyFn(item);
|
|
6
|
+
const bucket = groups[key];
|
|
7
|
+
if (bucket === undefined)
|
|
8
|
+
groups[key] = [item];
|
|
9
|
+
else
|
|
10
|
+
bucket.push(item);
|
|
11
|
+
}
|
|
12
|
+
return groups;
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function deepClone(value) {
|
|
2
|
+
if (value === null || typeof value !== 'object') {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
if (value instanceof Date) {
|
|
6
|
+
return new Date(value.getTime());
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
const source = value;
|
|
10
|
+
return source.map((item) => deepClone(item));
|
|
11
|
+
}
|
|
12
|
+
const source = value;
|
|
13
|
+
const clone = {};
|
|
14
|
+
for (const [key, item] of Object.entries(source)) {
|
|
15
|
+
clone[key] = deepClone(item);
|
|
16
|
+
}
|
|
17
|
+
return clone;
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function groupBy(items, keyFn) {
|
|
2
|
+
const groups = {};
|
|
3
|
+
for (const item of items) {
|
|
4
|
+
const key = keyFn(item);
|
|
5
|
+
const existing = groups[key];
|
|
6
|
+
if (existing === undefined) {
|
|
7
|
+
groups[key] = [item];
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
existing.push(item);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return groups;
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function deepClone(value) {
|
|
2
|
+
if (value === null || typeof value !== 'object') {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
if (value instanceof Date) {
|
|
6
|
+
return new Date(value.getTime());
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
return value.map((item) => deepClone(item));
|
|
10
|
+
}
|
|
11
|
+
const clone = {};
|
|
12
|
+
for (const [key, item] of Object.entries(value)) {
|
|
13
|
+
clone[key] = deepClone(item);
|
|
14
|
+
}
|
|
15
|
+
return clone;
|
|
16
|
+
}
|
package/dist/cli/backups.js
CHANGED
|
@@ -1,25 +1,19 @@
|
|
|
1
|
-
// Data snapshots:
|
|
2
|
-
//
|
|
3
|
-
// A snapshot is a directory with a manifest, not a single file, so one copy holds the whole set.
|
|
4
|
-
// Older installs wrote a lone `usage-<stamp>.db`; those still list and restore, over the ledger alone.
|
|
1
|
+
// Data snapshots: a manifest directory, not one file; older lone `usage-<stamp>.db` installs still restore.
|
|
5
2
|
import { promises as fs } from 'node:fs';
|
|
6
3
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
7
|
-
import { execFileSync } from 'node:child_process';
|
|
8
4
|
import path from 'node:path';
|
|
5
|
+
import { sqliteExec } from '../extensions/shared/sqlite.js';
|
|
9
6
|
import { agentDbPath, statsDbPath } from '../extensions/shared/agent-stats.js';
|
|
10
7
|
import { rtkDbPath } from '../extensions/shared/rtk-gain.js';
|
|
11
8
|
import { pricesCachePath } from '../extensions/shared/pricing.js';
|
|
12
9
|
import { usageDbPath } from '../extensions/shared/usage-store.js';
|
|
13
10
|
import { resetMarkerPath } from '../extensions/shared/usage-ledger.js';
|
|
14
11
|
import { tersioHome } from '../extensions/shared/utils.js';
|
|
15
|
-
/** Snapshots kept on disk
|
|
16
|
-
* the pane scrolls instead of showing the whole history at once. */
|
|
12
|
+
/** Snapshots kept on disk, oldest pruned first; six fills past one screen so the pane scrolls. */
|
|
17
13
|
export const BACKUP_KEEP = 6;
|
|
18
|
-
/** Where a snapshot's files live. */
|
|
19
14
|
export function backupDir() {
|
|
20
15
|
return path.join(tersioHome(), 'backups');
|
|
21
16
|
}
|
|
22
|
-
/** A snapshot directory's manifest, read back out with its files. */
|
|
23
17
|
function manifestPath(dir) {
|
|
24
18
|
return path.join(dir, 'manifest.json');
|
|
25
19
|
}
|
|
@@ -90,7 +84,6 @@ export function listDataBackups() {
|
|
|
90
84
|
}
|
|
91
85
|
return [...dirs, ...legacy].toSorted((a, b) => b.mtime - a.mtime);
|
|
92
86
|
}
|
|
93
|
-
/** The files a snapshot directory holds, read from its manifest. */
|
|
94
87
|
function readManifest(dir) {
|
|
95
88
|
try {
|
|
96
89
|
const raw = JSON.parse(readFileSync(manifestPath(dir), 'utf8'));
|
|
@@ -120,13 +113,13 @@ function totalOf(dir) {
|
|
|
120
113
|
/** Put the sqlite ledger's write-ahead log back in the main file, so a copy is one whole database. */
|
|
121
114
|
function checkpointLedger() {
|
|
122
115
|
try {
|
|
123
|
-
|
|
116
|
+
sqliteExec(usageDbPath(), 'PRAGMA wal_checkpoint(TRUNCATE);');
|
|
124
117
|
}
|
|
125
118
|
catch {
|
|
126
|
-
/* no
|
|
119
|
+
/* no SQLite on this host, or no ledger: the plain file copy is the best available */
|
|
127
120
|
}
|
|
128
121
|
}
|
|
129
|
-
/** Take a snapshot
|
|
122
|
+
/** Take a snapshot: one copy of every existing file under a stamp dir, keeping the newest {@link BACKUP_KEEP}. */
|
|
130
123
|
export async function createDataBackup() {
|
|
131
124
|
// The stamp keeps milliseconds: two snapshots in the same second must not land in one directory.
|
|
132
125
|
const stamp = new Date().toISOString().replace(/[-:TZ.]/g, '');
|
|
@@ -159,7 +152,7 @@ function resolveBackup(name) {
|
|
|
159
152
|
const found = listDataBackups().find((entry) => entry.name === name);
|
|
160
153
|
return found ?? null;
|
|
161
154
|
}
|
|
162
|
-
/**
|
|
155
|
+
/** Restore: files the snapshot holds overwrite their live source; files it lacks stay put, so nothing is lost. */
|
|
163
156
|
export async function restoreDataBackup(name) {
|
|
164
157
|
const found = resolveBackup(name);
|
|
165
158
|
if (!found)
|
package/dist/cli/banner.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// cli/banner.ts — mini pixel-scissor welcome. TTY-only
|
|
1
|
+
// cli/banner.ts — mini pixel-scissor welcome. TTY-only, skipped on --dry-run; piped output stays byte-identical.
|
|
2
2
|
import { intro as clackIntro, note as clackNote } from '@clack/prompts';
|
|
3
3
|
import { PACKAGE_VERSION, dryRun } from './common.js';
|
|
4
4
|
function tty() {
|
|
@@ -81,9 +81,9 @@ function printWelcome() {
|
|
|
81
81
|
'tersio update refresh CLI + add-ons',
|
|
82
82
|
'tersio doctor verify the install',
|
|
83
83
|
];
|
|
84
|
-
// Below 26 rows the
|
|
84
|
+
// Below 26 rows the tips scroll off the top and slice the art, so they take one line there.
|
|
85
85
|
if (process.stdout.rows < 26) {
|
|
86
|
-
clackNote(
|
|
86
|
+
clackNote(` ${tips.join(' · ')}`);
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
const art = bannerLines();
|
package/dist/cli/common.js
CHANGED
|
@@ -19,14 +19,15 @@ const PACKAGE_BIN = 'tersio';
|
|
|
19
19
|
const { version: PACKAGE_VERSION } = createRequire(import.meta.url)(path.join(findPackageRoot(path.dirname(fileURLToPath(import.meta.url))), 'package.json'));
|
|
20
20
|
const CAVEMAN_DEFAULTS = new Set(['off', 'lite', 'full', 'ultra', 'megacave-lite', 'megacave-full', 'megacave-ultra']);
|
|
21
21
|
const PONYTAIL_DEFAULTS = new Set(['off', 'lite', 'full', 'ultra']);
|
|
22
|
+
const PSTACK_DEFAULTS = new Set(['off', 'on']);
|
|
22
23
|
const RTK_DEFAULTS = new Set(['on', 'off']);
|
|
23
24
|
const DIAG_SCHEDULES = new Set(['manual', 'daily', 'weekly', 'monthly']);
|
|
24
|
-
// Mirrors COMBO_LEVELS in extensions/shared/session-state.ts
|
|
25
|
+
// Mirrors COMBO_LEVELS in extensions/shared/session-state.ts, so the combo preset implies all four modes.
|
|
25
26
|
const COMBO_PRESET_MODES = {
|
|
26
|
-
off: { caveman: 'off', rtk: false, ponytail: 'off' },
|
|
27
|
-
medium: { caveman: 'lite', rtk: true, ponytail: 'lite' },
|
|
28
|
-
balanced: { caveman: 'full', rtk: true, ponytail: 'full' },
|
|
29
|
-
max: { caveman: 'ultra', rtk: true, ponytail: 'ultra' },
|
|
27
|
+
off: { caveman: 'off', rtk: false, ponytail: 'off', pstack: 'off' },
|
|
28
|
+
medium: { caveman: 'lite', rtk: true, ponytail: 'lite', pstack: 'on' },
|
|
29
|
+
balanced: { caveman: 'full', rtk: true, ponytail: 'full', pstack: 'on' },
|
|
30
|
+
max: { caveman: 'ultra', rtk: true, ponytail: 'ultra', pstack: 'on' },
|
|
30
31
|
};
|
|
31
32
|
const COMBO_DEFAULTS = new Set(Object.keys(COMBO_PRESET_MODES));
|
|
32
33
|
function parseEnum(value, valid, flag) {
|
|
@@ -46,12 +47,11 @@ function flagValue(name) {
|
|
|
46
47
|
const prefix = `${name}=`;
|
|
47
48
|
return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
-
const COMMANDS = { install: true, update: true, doctor: true, uninstall: true, usage: true, dashboard: true, reset: true, settings: true, version: true, help: true };
|
|
50
|
+
const COMMANDS = { install: true, reinstall: true, update: true, doctor: true, uninstall: true, usage: true, dashboard: true, reset: true, settings: true, version: true, help: true };
|
|
51
51
|
const args = process.argv.slice(2);
|
|
52
52
|
const commandArg = args.find((arg) => !arg.startsWith('-'));
|
|
53
53
|
const command = commandArg?.toLowerCase() || null;
|
|
54
|
-
// `tersio settings diagnosis` jumps to one prompt
|
|
54
|
+
// `tersio settings diagnosis` jumps to one prompt. Index-based, so flag values never count as positionals.
|
|
55
55
|
const settingArg = (() => {
|
|
56
56
|
const at = args.indexOf(commandArg ?? '');
|
|
57
57
|
const next = at >= 0 ? args[at + 1] : undefined;
|
|
@@ -61,6 +61,7 @@ const settingArg = (() => {
|
|
|
61
61
|
})();
|
|
62
62
|
const unknownCommand = command !== null && !COMMANDS[command];
|
|
63
63
|
const install = command === 'install';
|
|
64
|
+
const reinstall = command === 'reinstall';
|
|
64
65
|
const update = command === 'update';
|
|
65
66
|
const showVersion = command === 'version' || args.includes('--version') || args.includes('-v');
|
|
66
67
|
const showHelp = command === 'help' || args.includes('--help') || args.includes('-h');
|
|
@@ -96,9 +97,10 @@ if (legacyScope !== null && legacyScope !== 'user') {
|
|
|
96
97
|
const comboDefaultFlag = parseEnum(flagValue('--combo-default'), COMBO_DEFAULTS, '--combo-default');
|
|
97
98
|
const cavemanDefaultFlag = parseEnum(flagValue('--caveman-default'), CAVEMAN_DEFAULTS, '--caveman-default');
|
|
98
99
|
const ponytailDefaultFlag = parseEnum(flagValue('--ponytail-default'), PONYTAIL_DEFAULTS, '--ponytail-default');
|
|
100
|
+
const pstackDefaultFlag = parseEnum(flagValue('--pstack-default'), PSTACK_DEFAULTS, '--pstack-default');
|
|
99
101
|
const rtkDefaultFlag = parseEnum(flagValue('--rtk-default'), RTK_DEFAULTS, '--rtk-default');
|
|
100
102
|
const diagScheduleFlag = parseEnum(flagValue('--diag-schedule'), DIAG_SCHEDULES, '--diag-schedule');
|
|
101
|
-
const profileFlagsGiven = [comboDefaultFlag, cavemanDefaultFlag, rtkDefaultFlag, ponytailDefaultFlag]
|
|
103
|
+
const profileFlagsGiven = [comboDefaultFlag, cavemanDefaultFlag, rtkDefaultFlag, ponytailDefaultFlag, pstackDefaultFlag]
|
|
102
104
|
.some((flag) => flag !== undefined);
|
|
103
105
|
// Repeatable `--subagent-marker`; overrides the built-in host prompt marker.
|
|
104
106
|
const subagentMarkerFlags = args
|
|
@@ -129,7 +131,7 @@ async function writeIfChanged(dest, content, options = {}) {
|
|
|
129
131
|
return true;
|
|
130
132
|
}
|
|
131
133
|
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
132
|
-
if (existing !== null) {
|
|
134
|
+
if (existing !== null && options.backup === true) {
|
|
133
135
|
await fs.copyFile(dest, `${dest}.bak`);
|
|
134
136
|
debug(`${path.basename(dest)} → ${path.basename(dest)}.bak`);
|
|
135
137
|
}
|
|
@@ -138,7 +140,7 @@ async function writeIfChanged(dest, content, options = {}) {
|
|
|
138
140
|
console.log(` [write] ${dest}`);
|
|
139
141
|
return true;
|
|
140
142
|
}
|
|
141
|
-
// omp ships 'extensions: null'
|
|
143
|
+
// omp ships 'extensions: null'; normalize it to a mapping key first, or YAML rejects the list items.
|
|
142
144
|
const EXTENSIONS_KEY_RE = /^\s*extensions\s*:/i;
|
|
143
145
|
const EXTENSIONS_NULL_RE = /^\s*extensions\s*:\s*(?:\[\s*\]|null|~)?\s*$/i;
|
|
144
146
|
function normalizeExtensionsKey(lines) {
|
|
@@ -160,7 +162,7 @@ async function ensureExtensionInConfig(configPath, extensionPath, label, options
|
|
|
160
162
|
const line = ` - ${normalizedPath}`;
|
|
161
163
|
const raw = await readTextIfExists(configPath);
|
|
162
164
|
let lines = (raw || '').split('\n');
|
|
163
|
-
// Drop legacy compiled twins (
|
|
165
|
+
// Drop legacy compiled twins (.js beside the name): OMP must never load both. Matched on the trailing path.
|
|
164
166
|
if (normalizedPath.endsWith('.ts')) {
|
|
165
167
|
const legacyTail = `${normalizedPath.slice(0, -3)}.js`.split('/').slice(-2).join('/');
|
|
166
168
|
lines = lines.filter((l) => !l.trim().replace(/^\.\//, '').endsWith(legacyTail));
|
|
@@ -239,6 +241,12 @@ function readPonytailConfig() {
|
|
|
239
241
|
: path.join(HOME, '.config', 'ponytail');
|
|
240
242
|
return { dir, path: path.join(dir, 'config.json') };
|
|
241
243
|
}
|
|
244
|
+
/** This package's ponytail range, or null; repair pins it, since npm installs the published range. */
|
|
245
|
+
async function bundledPonytailSpec() {
|
|
246
|
+
const root = findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));
|
|
247
|
+
const text = await readTextIfExists(path.join(root, 'package.json'));
|
|
248
|
+
return parseJsonObject(text)?.dependencies?.['@dietrichgebert/ponytail'] ?? null;
|
|
249
|
+
}
|
|
242
250
|
function parseJsonObject(text) {
|
|
243
251
|
if (!text)
|
|
244
252
|
return null;
|
|
@@ -280,6 +288,14 @@ async function ensurePonytailConfigValue(key, value, options = {}) {
|
|
|
280
288
|
return true;
|
|
281
289
|
}, `would set Ponytail ${key}=${value}`, `Set Ponytail ${key}=${value}`, options);
|
|
282
290
|
}
|
|
291
|
+
// npm prefers a nested copy over the pin when the plugin's range excludes it; only `overrides` reaches it.
|
|
292
|
+
function ponytailOverride(pkg) {
|
|
293
|
+
const overrides = pkg.overrides;
|
|
294
|
+
if (typeof overrides !== 'object' || overrides === null)
|
|
295
|
+
return null;
|
|
296
|
+
const value = overrides['@dietrichgebert/ponytail'];
|
|
297
|
+
return typeof value === 'string' ? value : null;
|
|
298
|
+
}
|
|
283
299
|
// Read plugins/package.json tolerantly; a corrupt or missing file starts fresh with installer-managed defaults.
|
|
284
300
|
async function readPluginsPackage(pkgPath) {
|
|
285
301
|
const pkg = parseJsonObject(await readTextIfExists(pkgPath)) ?? {};
|
|
@@ -300,4 +316,4 @@ function relTime(ageMs) {
|
|
|
300
316
|
const weeks = Math.floor(days / 7);
|
|
301
317
|
return `${weeks} week${weeks === 1 ? '' : 's'} ago`;
|
|
302
318
|
}
|
|
303
|
-
export { IS_WINDOWS, RTK_BINARY_NAME, HOME, OMP_AGENT_DIR, OMP_PLUGINS_DIR, BUN_BIN_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_BIN, PACKAGE_VERSION, CAVEMAN_DEFAULTS, PONYTAIL_DEFAULTS, RTK_DEFAULTS, DIAG_SCHEDULES, COMBO_PRESET_MODES, COMBO_DEFAULTS, parseEnum, flagValue, COMMANDS, commandArg, command, settingArg, unknownCommand, install, update, showVersion, showHelp, applyUpdate, args, dryRun, yes, verbose, doctor, fix, uninstall, usage, dashboard, reset, settings, allowUnverified, dashboardPort, dashboardOpen, dashboardExport, currency, currencyGiven, removePonytail, keepPonytail, removeRtk, comboDefaultFlag, cavemanDefaultFlag, ponytailDefaultFlag, rtkDefaultFlag, diagScheduleFlag, profileFlagsGiven, subagentMarkerFlags, subagentMarkersGiven, debug, execFileP, execP, writeIfChanged, normalizeExtensionsKey, writeConfigLines, ensureExtensionInConfig, removeExtensionFromConfig, readPonytailConfig, parseJsonObject, parsePonytailConfig, patchPonytailConfig, ensurePonytailConfigValue, readPluginsPackage, relTime, };
|
|
319
|
+
export { IS_WINDOWS, RTK_BINARY_NAME, HOME, OMP_AGENT_DIR, OMP_PLUGINS_DIR, BUN_BIN_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_BIN, PACKAGE_VERSION, CAVEMAN_DEFAULTS, PONYTAIL_DEFAULTS, PSTACK_DEFAULTS, RTK_DEFAULTS, DIAG_SCHEDULES, COMBO_PRESET_MODES, COMBO_DEFAULTS, parseEnum, flagValue, COMMANDS, commandArg, command, settingArg, unknownCommand, install, reinstall, update, showVersion, showHelp, applyUpdate, args, dryRun, yes, verbose, doctor, fix, uninstall, usage, dashboard, reset, settings, allowUnverified, dashboardPort, dashboardOpen, dashboardExport, currency, currencyGiven, removePonytail, keepPonytail, removeRtk, comboDefaultFlag, cavemanDefaultFlag, ponytailDefaultFlag, pstackDefaultFlag, rtkDefaultFlag, diagScheduleFlag, profileFlagsGiven, subagentMarkerFlags, subagentMarkersGiven, debug, execFileP, execP, writeIfChanged, normalizeExtensionsKey, writeConfigLines, ensureExtensionInConfig, removeExtensionFromConfig, readPonytailConfig, parseJsonObject, parsePonytailConfig, patchPonytailConfig, ensurePonytailConfigValue, readPluginsPackage, relTime, bundledPonytailSpec, ponytailOverride, };
|
package/dist/cli/currency.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// cli/currency.ts — USD-base
|
|
1
|
+
// cli/currency.ts — USD-base helpers on offline snapshot rates. Same 10 currencies as the dashboard's live figures.
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { tersioSettingsFile } from '../extensions/shared/plugin-settings.js';
|
|
4
4
|
const CURRENCY_CODES = [
|