@mengyuly/dsh-ponytail 0.3.1 → 0.3.3
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/CHANGELOG.md +409 -355
- package/LICENSE +21 -21
- package/README.md +183 -168
- package/cordis.patch.yml +3 -3
- package/dist-provenance.json +11 -11
- package/lib/index.js +1912 -1907
- package/lib/invariant.js +23 -23
- package/lib/types/content.d.ts +26 -26
- package/lib/types/index.d.ts +50 -50
- package/lib/types/instructions.d.ts +17 -17
- package/lib/types/invariant.d.ts +15 -15
- package/lib/types/modes.d.ts +114 -112
- package/package.json +50 -60
package/lib/invariant.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
//#region lib/types/invariant.js
|
|
2
|
-
/**
|
|
3
|
-
* Package-owned invariant companion for `@
|
|
4
|
-
* @module @
|
|
5
|
-
*/
|
|
6
|
-
const PACKAGE_NAME = "@
|
|
7
|
-
/** Cordis companion plugin name. */
|
|
8
|
-
const name = "ponytail-invariant";
|
|
9
|
-
/** Service required before the companion can reserve package ownership. */
|
|
10
|
-
const inject = ["invariants"];
|
|
11
|
-
/**
|
|
12
|
-
* No runtime invariant: mode overrides live in a session-scoped in-memory
|
|
13
|
-
* store with no cross-event or durable-data relationship to enforce.
|
|
14
|
-
*/
|
|
15
|
-
const install = () => {};
|
|
16
|
-
/**
|
|
17
|
-
* Register this package's invariant companion.
|
|
18
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
19
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
20
|
-
*/
|
|
21
|
-
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
22
|
-
//#endregion
|
|
23
|
-
export { apply, inject, name };
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/**
|
|
3
|
+
* Package-owned invariant companion for `@mengyuly/dsh-ponytail`.
|
|
4
|
+
* @module @mengyuly/dsh-ponytail/invariant
|
|
5
|
+
*/
|
|
6
|
+
const PACKAGE_NAME = "@mengyuly/dsh-ponytail";
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
const name = "ponytail-invariant";
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
const inject = ["invariants"];
|
|
11
|
+
/**
|
|
12
|
+
* No runtime invariant: mode overrides live in a session-scoped in-memory
|
|
13
|
+
* store with no cross-event or durable-data relationship to enforce.
|
|
14
|
+
*/
|
|
15
|
+
const install = () => {};
|
|
16
|
+
/**
|
|
17
|
+
* Register this package's invariant companion.
|
|
18
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
19
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
20
|
+
*/
|
|
21
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
22
|
+
//#endregion
|
|
23
|
+
export { apply, inject, name };
|
package/lib/types/content.d.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ponytail skill bodies, ported from github.com/DietrichGebert/ponytail and
|
|
3
|
-
* lightly adapted to the DeepSeek Harness surface (slash commands and the
|
|
4
|
-
* `skill` tool). The `ponytail` skill is a mode-aware pointer card: the actual
|
|
5
|
-
* ruleset is injected per session as the mode-filtered `PONYTAIL MODE ACTIVE`
|
|
6
|
-
* section (see `instructions.ts`) and must not be duplicated here. The other
|
|
7
|
-
* five skills ship verbatim as runtime skills.
|
|
8
|
-
*
|
|
9
|
-
* @module @
|
|
10
|
-
*/
|
|
11
|
-
import type { SkillRegistration } from '@deepseek-ai/dsh-skill';
|
|
12
|
-
/** The always-on lazy-senior-dev ruleset: also registered as a loadable skill. */
|
|
13
|
-
export declare const PONYTAIL_SKILL_BODY = "\nYou are the ponytail persona \u2014 the lazy senior developer. Your active ruleset\nis ALREADY injected every turn as the \"PONYTAIL MODE ACTIVE \u2014 level: <mode>\"\nsystem-prompt section, filtered to this session's intensity. Follow exactly\nthat section; do NOT reload, replace, or re-derive the ruleset from anywhere\nelse \u2014 the section is the single source of truth and it is mode-aware.\n\n- Switch level: `/ponytail lite|full|ultra|off` (session-scoped)\n- Query: `/ponytail status`\n- Deactivate: \"stop ponytail\" / \"normal mode\"\n- One-shot skills: `/ponytail-review`, `/ponytail-audit`, `/ponytail-debt`,\n `/ponytail-gain`, `/ponytail-help`\n- Reference: https://github.com/DietrichGebert/ponytail\n";
|
|
14
|
-
export declare const PONYTAIL_DESCRIPTION: string;
|
|
15
|
-
export declare const REVIEW_SKILL_BODY = "\nReview diffs for unnecessary complexity. One line per finding: location, what\nto cut, what replaces it. The diff's best outcome is getting shorter.\n\n## Format\n\n`L<line>: <tag> <what>. <replacement>.`, or `<file>:L<line>: ...` for\nmulti-file diffs.\n\nTags:\n\n- `delete:` dead code, unused flexibility, speculative feature. Replacement: nothing.\n- `stdlib:` hand-rolled thing the standard library ships. Name the function.\n- `native:` dependency or code doing what the platform already does. Name the feature.\n- `yagni:` abstraction with one implementation, config nobody sets, layer with one caller.\n- `shrink:` same logic, fewer lines. Show the shorter form.\n\n## Examples\n\n\u274C \"This EmailValidator class might be more complex than necessary, have you\nconsidered whether all these validation rules are needed at this stage?\"\n\n\u2705 `L12-38: stdlib: 27-line validator class. \"@\" in email, 1 line, real validation is the confirmation mail.`\n\n\u2705 `L4: native: moment.js imported for one format call. Intl.DateTimeFormat, 0 deps.`\n\n\u2705 `repo.py:L88: yagni: AbstractRepository with one implementation. Inline it until a second one exists.`\n\n\u2705 `L52-71: delete: retry wrapper around an idempotent local call. Nothing replaces it.`\n\n\u2705 `L30-44: shrink: manual loop builds dict. dict(zip(keys, values)), 1 line.`\n\n## Scoring\n\nEnd with the only metric that matters: `net: -<N> lines possible.`\n\nIf there is nothing to cut, say `Lean already. Ship.` and stop.\n\n## Boundaries\n\nScope: over-engineering and complexity only. Correctness bugs, security holes,\nand performance are explicitly out of scope. Route them to a normal review\npass, not this one. A single smoke test or `assert`-based\nself-check is the ponytail minimum, not bloat, never flag it for deletion.\nDoes not apply the fixes, only lists them.\n\"stop ponytail-review\" or \"normal mode\": revert to verbose review style.\n";
|
|
16
|
-
export declare const REVIEW_DESCRIPTION: string;
|
|
17
|
-
export declare const AUDIT_SKILL_BODY = "\nponytail-review, repo-wide. Scan the whole tree instead of a diff. Rank\nfindings biggest cut first.\n\n## Tags\n\nSame as ponytail-review:\n\n- `delete:` dead code, unused flexibility, speculative feature. Replacement: nothing.\n- `stdlib:` hand-rolled thing the standard library ships. Name the function.\n- `native:` dependency or code doing what the platform already does. Name the feature.\n- `yagni:` abstraction with one implementation, config nobody sets, layer with one caller.\n- `shrink:` same logic, fewer lines. Show the shorter form.\n\n## Hunt\n\nDeps the stdlib or platform already ships, single-implementation interfaces,\nfactories with one product, wrappers that only delegate, files exporting one\nthing, dead flags and config, hand-rolled stdlib.\n\n## Output\n\nOne line per finding, ranked: `<tag> <what to cut>. <replacement>. [path]`.\nEnd with `net: -<N> lines, -<M> deps possible.` Nothing to cut: `Lean already. Ship.`\n\n## Boundaries\n\nScope: over-engineering and complexity only. Correctness bugs, security holes,\nand performance are explicitly out of scope. Route them to a normal review\npass. Lists findings, applies nothing. One-shot.\n\"stop ponytail-audit\" or \"normal mode\" to revert.\n";
|
|
18
|
-
export declare const AUDIT_DESCRIPTION: string;
|
|
19
|
-
export declare const DEBT_SKILL_BODY = "\nEvery deliberate ponytail shortcut is marked with a `ponytail:` comment naming\nits ceiling and upgrade path. This collects them into one ledger so a deferral\ncan't quietly become permanent.\n\n## Scan\n\nGrep the repo for comment markers, skipping `node_modules`, `.git`, and build\noutput:\n\n`grep -rnE '(#|//) ?ponytail:' .` (add other comment prefixes if your stack uses them)\n\nEach hit is one ledger row. The comment prefix keeps prose that merely mentions\nthe convention out of the ledger.\n\n## Output\n\nOne row per marker, grouped by file:\n\n`<file>:<line>, <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`\n\nThe convention is `ponytail: <ceiling>, <upgrade path>`, so pull the ceiling\nand the trigger straight from the comment. Want an owner per row too? add\n`git blame -L<line>,<line>`.\n\nFlag the rot risk: any `ponytail:` comment that names no upgrade path or\ntrigger gets a `no-trigger` tag, those are the ones that silently rot.\n\nEnd with `<N> markers, <M> with no trigger.` Nothing found: `No ponytail: debt. Clean ledger.`\n\n## Boundaries\n\nReads and reports only, changes nothing. To persist it, ask and it writes the\nledger to a file (e.g. `PONYTAIL-DEBT.md`). One-shot. \"stop ponytail-debt\" or\n\"normal mode\" to revert.\n";
|
|
20
|
-
export declare const DEBT_DESCRIPTION: string;
|
|
21
|
-
export declare const GAIN_SKILL_BODY = "\nDisplay this scoreboard when invoked. One-shot: do NOT change mode, write flag\nfiles, or persist anything.\n\nThese are upstream Ponytail results, not measured guarantees for this DSH\nadapter.\n\nSavings depend on model, workload, prompt caching, tool usage, and execution\npath. Already-minimal tasks may show little or no savings. Some reasoning\nmodels may become more expensive because prompt and reasoning overhead can\nexceed the saved output.\n\n## 1. Upstream agentic reference\n\nReal Claude Code sessions on real repositories; 12 feature tasks:\n\n- Source LOC: ~\u221254%\n- Tokens: ~\u221222%\n- Cost: ~\u221220%\n- Time: ~\u221227%\n- Over-build tasks: \u221260\u201394%\n- Safety tests: 100%\n\n## 2. Upstream single-shot reference\n\n5 everyday tasks (email validator, debounce, CSV sum, countdown timer, rate\nlimiter); 3 Claude models; single generation per task:\n\n- Lines of code: \u221280\u201394%\n- Cost (Claude): \u221242\u201375%\n- Latency: ~3.1\u20135.8\u00D7 faster\n\n## 3. DSH adapter status\n\nCurrent DSH smoke tests provide directional evidence only. Stable token,\ncost, and latency savings have not been established.\n\nSee the repository's DSH smoke reports for limited, non-statistical\ndirectional evidence (docs/dsh-smoke-summary.md).\n\n## 4. Honesty boundary\n\nThese are upstream benchmark medians, not this repo and not this DSH\nadapter. NEVER print a per-repo savings number (\"you saved X lines/tokens\nhere\"): the unbuilt version was never written, so there is no real baseline\nto subtract from in a live repo. The only real per-repo figures come from\n`/ponytail-debt` (a counted ledger), and this card points there instead of\ninventing one. Never claim \"Ponytail always saves tokens/cost\" or that this\nadapter reproduces the upstream percentages. A missing cost figure (null) is\nnot a zero cost.\n\n## Boundaries\n\nOne-shot display. Edits nothing, changes no mode.\n\"stop ponytail\" or \"normal mode\": revert.\n";
|
|
22
|
-
export declare const GAIN_DESCRIPTION: string;
|
|
23
|
-
export declare const HELP_SKILL_BODY = "\nDisplay this reference card when invoked. One-shot, do NOT change mode,\nwrite flag files, or persist anything.\n\n## Levels\n\n| Level | Trigger | What change |\n|-------|---------|-------------|\n| **Lite** | `/ponytail lite` | Build what's asked, name the lazier alternative in one line. |\n| **Full** | `/ponytail` | The ladder enforced: YAGNI \u2192 stdlib \u2192 native \u2192 one line \u2192 minimum. Default. |\n| **Ultra** | `/ponytail ultra` | YAGNI extremist: deletion first, questions speculation \u2014 never cuts explicit requirements. |\n| **Off** | `/ponytail off` | Ponytail stops injecting its ruleset for this session. |\n\nLevel is session-scoped until changed.\n\n## Choosing a level\n\n- **Lite**: Use for small, explicit changes or when the implementation is\n already clear. Completes explicit requirements without actively\n challenging them.\n Lite\uFF1A\u5C0F\u6539\u52A8\u3001\u9700\u6C42\u660E\u786E\u65F6\u4F7F\u7528\u3002\n- **Full**: Use for new features, refactors, root-cause bug fixes, or tasks\n likely to invite unnecessary abstractions, dependencies, or custom\n components.\n Full\uFF1A\u65B0\u529F\u80FD\u3001\u91CD\u6784\u3001\u6839\u56E0\u4FEE\u590D\u3001\u5BB9\u6613\u8FC7\u5EA6\u8BBE\u8BA1\u65F6\u4F7F\u7528\u3002\n- **Ultra**: Use for deliberate code cleanup and over-engineering removal.\n It questions speculative scope, but never removes explicit requirements,\n security, validation, accessibility, or data-loss protection.\n Ultra\uFF1A\u4E13\u95E8\u6E05\u7406\u5197\u4F59\u548C\u8FC7\u5EA6\u62BD\u8C61\u65F6\u4F7F\u7528\u3002\n- **Off**: Use when the task is non-coding, already fully specified, or when\n the fixed prompt overhead is not worthwhile.\n Off\uFF1A\u975E\u7F16\u7801\u4EFB\u52A1\u6216\u5DF2\u7ECF\u660E\u786E\u5230\u65E0\u9700\u989D\u5916\u7F16\u7801\u5224\u65AD\u7684\u4EFB\u52A1\u3002\n\nPonytail is not a guaranteed token-saving switch. It trades a small fixed\nprompt cost for a chance to reduce unnecessary work. Do not default every\ntask to Ultra.\n\n## Skills\n\n| Skill | Trigger | What it does |\n|-------|---------|--------------|\n| **ponytail** | `/ponytail` | Lazy mode itself. Simplest solution that works. |\n| **ponytail-review** | `/ponytail-review` | Over-engineering review: `L42: yagni: factory, one product. Inline.` |\n| **ponytail-audit** | `/ponytail-audit` | Whole-repo over-engineering audit: ranked list of what to delete. |\n| **ponytail-debt** | `/ponytail-debt` | Harvest `ponytail:` shortcut comments into a tracked ledger. |\n| **ponytail-gain** | `/ponytail-gain` | Upstream benchmark reference: less unnecessary work; token/cost/latency effects depend on model and workload. |\n| **ponytail-help** | `/ponytail-help` | This card. |\n\nYou can also load any of these with the `skill` tool.\n\n## Deactivate\n\nSay \"stop ponytail\" or \"normal mode\". Resume anytime with `/ponytail` \u2014\nit re-enables at the effective default (or `full` when that is off too).\n`/ponytail status` only shows the current level, never changes it.\n`/ponytail off` also works. Level is session-scoped; a new session starts\nfrom the configured default.\n\n## Configure Default Mode\n\nDefault mode = `full`, auto-active every session. Change it:\n\n**Environment variable** (highest priority):\n```bash\nexport PONYTAIL_DEFAULT_MODE=ultra\n```\n\n**Config file** (`~/.config/ponytail/config.json`, Windows: `%APPDATA%\\ponytail\\config.json`):\n```json\n{ \"defaultMode\": \"lite\" }\n```\n\n**Profile config** (per DSH profile, via the bundle row's `config` \u2014 e.g.\n`tui` \u2192 lite):\n\n```yaml\n- insert:\n - id: ponytail\n name: '@mengyuly/dsh-ponytail'\n config:\n defaultMode: lite\n```\n\nSet `\"off\"` to disable auto-activation on session start, activate manually\nwith `/ponytail` when wanted. `/ponytail default <mode>` persists a new\ndefault to the user config file; an exported `PONYTAIL_DEFAULT_MODE` or a\nprofile `defaultMode` still outranks the saved value for new sessions.\n\nResolution: session override > env var > profile config > config file > `full`.\n\n## More\n\nFull docs + examples: https://github.com/DietrichGebert/ponytail\n";
|
|
24
|
-
export declare const HELP_DESCRIPTION: string;
|
|
25
|
-
/** Ordered set of runtime skills surfaced to the model catalog and `/` menu. */
|
|
26
|
-
export declare function ponytailSkills(): readonly SkillRegistration[];
|
|
1
|
+
/**
|
|
2
|
+
* Ponytail skill bodies, ported from github.com/DietrichGebert/ponytail and
|
|
3
|
+
* lightly adapted to the DeepSeek Harness surface (slash commands and the
|
|
4
|
+
* `skill` tool). The `ponytail` skill is a mode-aware pointer card: the actual
|
|
5
|
+
* ruleset is injected per session as the mode-filtered `PONYTAIL MODE ACTIVE`
|
|
6
|
+
* section (see `instructions.ts`) and must not be duplicated here. The other
|
|
7
|
+
* five skills ship verbatim as runtime skills.
|
|
8
|
+
*
|
|
9
|
+
* @module @mengyuly/dsh-ponytail
|
|
10
|
+
*/
|
|
11
|
+
import type { SkillRegistration } from '@deepseek-ai/dsh-skill';
|
|
12
|
+
/** The always-on lazy-senior-dev ruleset: also registered as a loadable skill. */
|
|
13
|
+
export declare const PONYTAIL_SKILL_BODY = "\nYou are the ponytail persona \u2014 the lazy senior developer. Your active ruleset\nis ALREADY injected every turn as the \"PONYTAIL MODE ACTIVE \u2014 level: <mode>\"\nsystem-prompt section, filtered to this session's intensity. Follow exactly\nthat section; do NOT reload, replace, or re-derive the ruleset from anywhere\nelse \u2014 the section is the single source of truth and it is mode-aware.\n\n- Switch level: `/ponytail lite|full|ultra|off` (session-scoped)\n- Query: `/ponytail status`\n- Deactivate: \"stop ponytail\" / \"normal mode\"\n- One-shot skills: `/ponytail-review`, `/ponytail-audit`, `/ponytail-debt`,\n `/ponytail-gain`, `/ponytail-help`\n- Reference: https://github.com/DietrichGebert/ponytail\n";
|
|
14
|
+
export declare const PONYTAIL_DESCRIPTION: string;
|
|
15
|
+
export declare const REVIEW_SKILL_BODY = "\nReview diffs for unnecessary complexity. One line per finding: location, what\nto cut, what replaces it. The diff's best outcome is getting shorter.\n\n## Format\n\n`L<line>: <tag> <what>. <replacement>.`, or `<file>:L<line>: ...` for\nmulti-file diffs.\n\nTags:\n\n- `delete:` dead code, unused flexibility, speculative feature. Replacement: nothing.\n- `stdlib:` hand-rolled thing the standard library ships. Name the function.\n- `native:` dependency or code doing what the platform already does. Name the feature.\n- `yagni:` abstraction with one implementation, config nobody sets, layer with one caller.\n- `shrink:` same logic, fewer lines. Show the shorter form.\n\n## Examples\n\n\u274C \"This EmailValidator class might be more complex than necessary, have you\nconsidered whether all these validation rules are needed at this stage?\"\n\n\u2705 `L12-38: stdlib: 27-line validator class. \"@\" in email, 1 line, real validation is the confirmation mail.`\n\n\u2705 `L4: native: moment.js imported for one format call. Intl.DateTimeFormat, 0 deps.`\n\n\u2705 `repo.py:L88: yagni: AbstractRepository with one implementation. Inline it until a second one exists.`\n\n\u2705 `L52-71: delete: retry wrapper around an idempotent local call. Nothing replaces it.`\n\n\u2705 `L30-44: shrink: manual loop builds dict. dict(zip(keys, values)), 1 line.`\n\n## Scoring\n\nEnd with the only metric that matters: `net: -<N> lines possible.`\n\nIf there is nothing to cut, say `Lean already. Ship.` and stop.\n\n## Boundaries\n\nScope: over-engineering and complexity only. Correctness bugs, security holes,\nand performance are explicitly out of scope. Route them to a normal review\npass, not this one. A single smoke test or `assert`-based\nself-check is the ponytail minimum, not bloat, never flag it for deletion.\nDoes not apply the fixes, only lists them.\n\"stop ponytail-review\" or \"normal mode\": revert to verbose review style.\n";
|
|
16
|
+
export declare const REVIEW_DESCRIPTION: string;
|
|
17
|
+
export declare const AUDIT_SKILL_BODY = "\nponytail-review, repo-wide. Scan the whole tree instead of a diff. Rank\nfindings biggest cut first.\n\n## Tags\n\nSame as ponytail-review:\n\n- `delete:` dead code, unused flexibility, speculative feature. Replacement: nothing.\n- `stdlib:` hand-rolled thing the standard library ships. Name the function.\n- `native:` dependency or code doing what the platform already does. Name the feature.\n- `yagni:` abstraction with one implementation, config nobody sets, layer with one caller.\n- `shrink:` same logic, fewer lines. Show the shorter form.\n\n## Hunt\n\nDeps the stdlib or platform already ships, single-implementation interfaces,\nfactories with one product, wrappers that only delegate, files exporting one\nthing, dead flags and config, hand-rolled stdlib.\n\n## Output\n\nOne line per finding, ranked: `<tag> <what to cut>. <replacement>. [path]`.\nEnd with `net: -<N> lines, -<M> deps possible.` Nothing to cut: `Lean already. Ship.`\n\n## Boundaries\n\nScope: over-engineering and complexity only. Correctness bugs, security holes,\nand performance are explicitly out of scope. Route them to a normal review\npass. Lists findings, applies nothing. One-shot.\n\"stop ponytail-audit\" or \"normal mode\" to revert.\n";
|
|
18
|
+
export declare const AUDIT_DESCRIPTION: string;
|
|
19
|
+
export declare const DEBT_SKILL_BODY = "\nEvery deliberate ponytail shortcut is marked with a `ponytail:` comment naming\nits ceiling and upgrade path. This collects them into one ledger so a deferral\ncan't quietly become permanent.\n\n## Scan\n\nGrep the repo for comment markers, skipping `node_modules`, `.git`, and build\noutput:\n\n`grep -rnE '(#|//) ?ponytail:' .` (add other comment prefixes if your stack uses them)\n\nEach hit is one ledger row. The comment prefix keeps prose that merely mentions\nthe convention out of the ledger.\n\n## Output\n\nOne row per marker, grouped by file:\n\n`<file>:<line>, <what was simplified>. ceiling: <the limit named>. upgrade: <the trigger to revisit>.`\n\nThe convention is `ponytail: <ceiling>, <upgrade path>`, so pull the ceiling\nand the trigger straight from the comment. Want an owner per row too? add\n`git blame -L<line>,<line>`.\n\nFlag the rot risk: any `ponytail:` comment that names no upgrade path or\ntrigger gets a `no-trigger` tag, those are the ones that silently rot.\n\nEnd with `<N> markers, <M> with no trigger.` Nothing found: `No ponytail: debt. Clean ledger.`\n\n## Boundaries\n\nReads and reports only, changes nothing. To persist it, ask and it writes the\nledger to a file (e.g. `PONYTAIL-DEBT.md`). One-shot. \"stop ponytail-debt\" or\n\"normal mode\" to revert.\n";
|
|
20
|
+
export declare const DEBT_DESCRIPTION: string;
|
|
21
|
+
export declare const GAIN_SKILL_BODY = "\nDisplay this scoreboard when invoked. One-shot: do NOT change mode, write flag\nfiles, or persist anything.\n\nThese are upstream Ponytail results, not measured guarantees for this DSH\nadapter.\n\nSavings depend on model, workload, prompt caching, tool usage, and execution\npath. Already-minimal tasks may show little or no savings. Some reasoning\nmodels may become more expensive because prompt and reasoning overhead can\nexceed the saved output.\n\n## 1. Upstream agentic reference\n\nReal Claude Code sessions on real repositories; 12 feature tasks:\n\n- Source LOC: ~\u221254%\n- Tokens: ~\u221222%\n- Cost: ~\u221220%\n- Time: ~\u221227%\n- Over-build tasks: \u221260\u201394%\n- Safety tests: 100%\n\n## 2. Upstream single-shot reference\n\n5 everyday tasks (email validator, debounce, CSV sum, countdown timer, rate\nlimiter); 3 Claude models; single generation per task:\n\n- Lines of code: \u221280\u201394%\n- Cost (Claude): \u221242\u201375%\n- Latency: ~3.1\u20135.8\u00D7 faster\n\n## 3. DSH adapter status\n\nCurrent DSH smoke tests provide directional evidence only. Stable token,\ncost, and latency savings have not been established.\n\nSee the repository's DSH smoke reports for limited, non-statistical\ndirectional evidence (docs/dsh-smoke-summary.md).\n\n## 4. Honesty boundary\n\nThese are upstream benchmark medians, not this repo and not this DSH\nadapter. NEVER print a per-repo savings number (\"you saved X lines/tokens\nhere\"): the unbuilt version was never written, so there is no real baseline\nto subtract from in a live repo. The only real per-repo figures come from\n`/ponytail-debt` (a counted ledger), and this card points there instead of\ninventing one. Never claim \"Ponytail always saves tokens/cost\" or that this\nadapter reproduces the upstream percentages. A missing cost figure (null) is\nnot a zero cost.\n\n## Boundaries\n\nOne-shot display. Edits nothing, changes no mode.\n\"stop ponytail\" or \"normal mode\": revert.\n";
|
|
22
|
+
export declare const GAIN_DESCRIPTION: string;
|
|
23
|
+
export declare const HELP_SKILL_BODY = "\nDisplay this reference card when invoked. One-shot, do NOT change mode,\nwrite flag files, or persist anything.\n\n## Levels\n\n| Level | Trigger | What change |\n|-------|---------|-------------|\n| **Lite** | `/ponytail lite` | Build what's asked, name the lazier alternative in one line. |\n| **Full** | `/ponytail` | The ladder enforced: YAGNI \u2192 stdlib \u2192 native \u2192 one line \u2192 minimum. Default. |\n| **Ultra** | `/ponytail ultra` | YAGNI extremist: deletion first, questions speculation \u2014 never cuts explicit requirements. |\n| **Off** | `/ponytail off` | Ponytail stops injecting its ruleset for this session. |\n\nLevel is session-scoped until changed.\n\n## Choosing a level\n\n- **Lite**: Use for small, explicit changes or when the implementation is\n already clear. Completes explicit requirements without actively\n challenging them.\n Lite\uFF1A\u5C0F\u6539\u52A8\u3001\u9700\u6C42\u660E\u786E\u65F6\u4F7F\u7528\u3002\n- **Full**: Use for new features, refactors, root-cause bug fixes, or tasks\n likely to invite unnecessary abstractions, dependencies, or custom\n components.\n Full\uFF1A\u65B0\u529F\u80FD\u3001\u91CD\u6784\u3001\u6839\u56E0\u4FEE\u590D\u3001\u5BB9\u6613\u8FC7\u5EA6\u8BBE\u8BA1\u65F6\u4F7F\u7528\u3002\n- **Ultra**: Use for deliberate code cleanup and over-engineering removal.\n It questions speculative scope, but never removes explicit requirements,\n security, validation, accessibility, or data-loss protection.\n Ultra\uFF1A\u4E13\u95E8\u6E05\u7406\u5197\u4F59\u548C\u8FC7\u5EA6\u62BD\u8C61\u65F6\u4F7F\u7528\u3002\n- **Off**: Use when the task is non-coding, already fully specified, or when\n the fixed prompt overhead is not worthwhile.\n Off\uFF1A\u975E\u7F16\u7801\u4EFB\u52A1\u6216\u5DF2\u7ECF\u660E\u786E\u5230\u65E0\u9700\u989D\u5916\u7F16\u7801\u5224\u65AD\u7684\u4EFB\u52A1\u3002\n\nPonytail is not a guaranteed token-saving switch. It trades a small fixed\nprompt cost for a chance to reduce unnecessary work. Do not default every\ntask to Ultra.\n\n## Skills\n\n| Skill | Trigger | What it does |\n|-------|---------|--------------|\n| **ponytail** | `/ponytail` | Lazy mode itself. Simplest solution that works. |\n| **ponytail-review** | `/ponytail-review` | Over-engineering review: `L42: yagni: factory, one product. Inline.` |\n| **ponytail-audit** | `/ponytail-audit` | Whole-repo over-engineering audit: ranked list of what to delete. |\n| **ponytail-debt** | `/ponytail-debt` | Harvest `ponytail:` shortcut comments into a tracked ledger. |\n| **ponytail-gain** | `/ponytail-gain` | Upstream benchmark reference: less unnecessary work; token/cost/latency effects depend on model and workload. |\n| **ponytail-help** | `/ponytail-help` | This card. |\n\nYou can also load any of these with the `skill` tool.\n\n## Deactivate\n\nSay \"stop ponytail\" or \"normal mode\". Resume anytime with `/ponytail` \u2014\nit re-enables at the effective default (or `full` when that is off too).\n`/ponytail status` only shows the current level, never changes it.\n`/ponytail off` also works. Level is session-scoped; a new session starts\nfrom the configured default.\n\n## Configure Default Mode\n\nDefault mode = `full`, auto-active every session. Change it:\n\n**Environment variable** (highest priority):\n```bash\nexport PONYTAIL_DEFAULT_MODE=ultra\n```\n\n**Config file** (`~/.config/ponytail/config.json`, Windows: `%APPDATA%\\ponytail\\config.json`):\n```json\n{ \"defaultMode\": \"lite\" }\n```\n\n**Profile config** (per DSH profile, via the bundle row's `config` \u2014 e.g.\n`tui` \u2192 lite):\n\n```yaml\n- insert:\n - id: ponytail\n name: '@mengyuly/dsh-ponytail'\n config:\n defaultMode: lite\n```\n\nSet `\"off\"` to disable auto-activation on session start, activate manually\nwith `/ponytail` when wanted. `/ponytail default <mode>` persists a new\ndefault to the user config file; an exported `PONYTAIL_DEFAULT_MODE` or a\nprofile `defaultMode` still outranks the saved value for new sessions.\n\nResolution: session override > env var > profile config > config file > `full`.\n\n## More\n\nFull docs + examples: https://github.com/DietrichGebert/ponytail\n";
|
|
24
|
+
export declare const HELP_DESCRIPTION: string;
|
|
25
|
+
/** Ordered set of runtime skills surfaced to the model catalog and `/` menu. */
|
|
26
|
+
export declare function ponytailSkills(): readonly SkillRegistration[];
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ponytail: the "lazy senior developer" persona as a DeepSeek Harness plugin.
|
|
3
|
-
*
|
|
4
|
-
* One system-prompt section injects the mode-filtered ruleset every turn (the
|
|
5
|
-
* always-on adapter), six runtime skills surface the review/audit/debt/gain/
|
|
6
|
-
* help one-shots, six slash commands drive them from the command plane, and an
|
|
7
|
-
* `agent/pre-step` listener honors the plain-text deactivation phrases.
|
|
8
|
-
*
|
|
9
|
-
* Mode is session-scoped and held in memory; the configured default resolves
|
|
10
|
-
* from `PONYTAIL_DEFAULT_MODE`, then the Cordis profile `defaultMode`, then
|
|
11
|
-
* `~/.config/ponytail/config.json` (see {@link readDefaultMode}), then
|
|
12
|
-
* `full`. A session override via `/ponytail` outranks all of them.
|
|
13
|
-
*
|
|
14
|
-
* @module @
|
|
15
|
-
*/
|
|
16
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
17
|
-
export declare const name = "ponytail";
|
|
18
|
-
export declare const inject: string[];
|
|
19
|
-
/**
|
|
20
|
-
* Cordis profile-level configuration (set per profile via the bundle row's
|
|
21
|
-
* `config` in a profile patch). `defaultMode` sits between the environment
|
|
22
|
-
* variable and the user config file in the default-resolution chain.
|
|
23
|
-
*
|
|
24
|
-
* No `Config` schema is exported on purpose: cordis passes the raw config
|
|
25
|
-
* through unvalidated then, so an invalid `defaultMode` falls back with a
|
|
26
|
-
* one-time warning instead of failing the plugin mount.
|
|
27
|
-
*/
|
|
28
|
-
export interface PonytailConfig {
|
|
29
|
-
/** Profile-scoped default intensity, e.g. `web → full`, `tui → lite`. */
|
|
30
|
-
defaultMode?: unknown;
|
|
31
|
-
}
|
|
32
|
-
/** Extract the plain text of one user message (only its text blocks). */
|
|
33
|
-
export declare function messageText(message: {
|
|
34
|
-
content: readonly {
|
|
35
|
-
type: string;
|
|
36
|
-
text?: string;
|
|
37
|
-
}[];
|
|
38
|
-
}): string;
|
|
39
|
-
/** Whether any message in a claimed batch is exactly a deactivation command. */
|
|
40
|
-
export declare function containsDeactivation(messages: readonly {
|
|
41
|
-
content: readonly {
|
|
42
|
-
type: string;
|
|
43
|
-
text?: string;
|
|
44
|
-
}[];
|
|
45
|
-
}[]): boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Register the always-on ruleset section, the runtime skills, the slash
|
|
48
|
-
* commands, and the plain-text deactivation listener.
|
|
49
|
-
*/
|
|
50
|
-
export declare function apply(ctx: Context, config?: PonytailConfig): void;
|
|
1
|
+
/**
|
|
2
|
+
* Ponytail: the "lazy senior developer" persona as a DeepSeek Harness plugin.
|
|
3
|
+
*
|
|
4
|
+
* One system-prompt section injects the mode-filtered ruleset every turn (the
|
|
5
|
+
* always-on adapter), six runtime skills surface the review/audit/debt/gain/
|
|
6
|
+
* help one-shots, six slash commands drive them from the command plane, and an
|
|
7
|
+
* `agent/pre-step` listener honors the plain-text deactivation phrases.
|
|
8
|
+
*
|
|
9
|
+
* Mode is session-scoped and held in memory; the configured default resolves
|
|
10
|
+
* from `PONYTAIL_DEFAULT_MODE`, then the Cordis profile `defaultMode`, then
|
|
11
|
+
* `~/.config/ponytail/config.json` (see {@link readDefaultMode}), then
|
|
12
|
+
* `full`. A session override via `/ponytail` outranks all of them.
|
|
13
|
+
*
|
|
14
|
+
* @module @mengyuly/dsh-ponytail
|
|
15
|
+
*/
|
|
16
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
17
|
+
export declare const name = "ponytail";
|
|
18
|
+
export declare const inject: string[];
|
|
19
|
+
/**
|
|
20
|
+
* Cordis profile-level configuration (set per profile via the bundle row's
|
|
21
|
+
* `config` in a profile patch). `defaultMode` sits between the environment
|
|
22
|
+
* variable and the user config file in the default-resolution chain.
|
|
23
|
+
*
|
|
24
|
+
* No `Config` schema is exported on purpose: cordis passes the raw config
|
|
25
|
+
* through unvalidated then, so an invalid `defaultMode` falls back with a
|
|
26
|
+
* one-time warning instead of failing the plugin mount.
|
|
27
|
+
*/
|
|
28
|
+
export interface PonytailConfig {
|
|
29
|
+
/** Profile-scoped default intensity, e.g. `web → full`, `tui → lite`. */
|
|
30
|
+
defaultMode?: unknown;
|
|
31
|
+
}
|
|
32
|
+
/** Extract the plain text of one user message (only its text blocks). */
|
|
33
|
+
export declare function messageText(message: {
|
|
34
|
+
content: readonly {
|
|
35
|
+
type: string;
|
|
36
|
+
text?: string;
|
|
37
|
+
}[];
|
|
38
|
+
}): string;
|
|
39
|
+
/** Whether any message in a claimed batch is exactly a deactivation command. */
|
|
40
|
+
export declare function containsDeactivation(messages: readonly {
|
|
41
|
+
content: readonly {
|
|
42
|
+
type: string;
|
|
43
|
+
text?: string;
|
|
44
|
+
}[];
|
|
45
|
+
}[]): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Register the always-on ruleset section, the runtime skills, the slash
|
|
48
|
+
* commands, and the plain-text deactivation listener.
|
|
49
|
+
*/
|
|
50
|
+
export declare function apply(ctx: Context, config?: PonytailConfig): void;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Structured ponytail ruleset composition. Each intensity is built from
|
|
3
|
-
* explicit fragments — common rules, a never-cut safety boundary list, and
|
|
4
|
-
* the mode's own rules — instead of filtering one Markdown body with regexes.
|
|
5
|
-
* The three intensities therefore differ in their actual instructions, not
|
|
6
|
-
* just in a table row.
|
|
7
|
-
*
|
|
8
|
-
* @module @
|
|
9
|
-
*/
|
|
10
|
-
import { type PonytailRuntimeMode } from './modes.ts';
|
|
11
|
-
/**
|
|
12
|
-
* The injected ruleset for one intensity, composed from the structured
|
|
13
|
-
* fragments above. Returns an empty string for `off` (ponytail contributes
|
|
14
|
-
* nothing). Renders are pure per mode and cached so every turn's bytes stay
|
|
15
|
-
* identical.
|
|
16
|
-
*/
|
|
17
|
-
export declare function getPonytailInstructions(mode: PonytailRuntimeMode | null | undefined): string;
|
|
1
|
+
/**
|
|
2
|
+
* Structured ponytail ruleset composition. Each intensity is built from
|
|
3
|
+
* explicit fragments — common rules, a never-cut safety boundary list, and
|
|
4
|
+
* the mode's own rules — instead of filtering one Markdown body with regexes.
|
|
5
|
+
* The three intensities therefore differ in their actual instructions, not
|
|
6
|
+
* just in a table row.
|
|
7
|
+
*
|
|
8
|
+
* @module @mengyuly/dsh-ponytail
|
|
9
|
+
*/
|
|
10
|
+
import { type PonytailRuntimeMode } from './modes.ts';
|
|
11
|
+
/**
|
|
12
|
+
* The injected ruleset for one intensity, composed from the structured
|
|
13
|
+
* fragments above. Returns an empty string for `off` (ponytail contributes
|
|
14
|
+
* nothing). Renders are pure per mode and cached so every turn's bytes stay
|
|
15
|
+
* identical.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getPonytailInstructions(mode: PonytailRuntimeMode | null | undefined): string;
|
package/lib/types/invariant.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package-owned invariant companion for `@
|
|
3
|
-
* @module @
|
|
4
|
-
*/
|
|
5
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
-
/** Cordis companion plugin name. */
|
|
7
|
-
export declare const name = "ponytail-invariant";
|
|
8
|
-
/** Service required before the companion can reserve package ownership. */
|
|
9
|
-
export declare const inject: string[];
|
|
10
|
-
/**
|
|
11
|
-
* Register this package's invariant companion.
|
|
12
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
-
*/
|
|
15
|
-
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@mengyuly/dsh-ponytail`.
|
|
3
|
+
* @module @mengyuly/dsh-ponytail/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "ponytail-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
package/lib/types/modes.d.ts
CHANGED
|
@@ -1,115 +1,117 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ponytail mode resolution: the effective default comes from, in order, the
|
|
3
|
-
* `PONYTAIL_DEFAULT_MODE` environment variable, the Cordis profile
|
|
4
|
-
* `defaultMode`, the optional user config file
|
|
5
|
-
* `~/.config/ponytail/config.json` (`defaultMode`), then `full`. Setting a
|
|
6
|
-
* level via the `/ponytail` command is session-scoped and lives in an
|
|
7
|
-
* in-memory, per-agent {@link ModeStore}.
|
|
8
|
-
*
|
|
9
|
-
* @module @
|
|
10
|
-
*/
|
|
11
|
-
/** One settable runtime intensity. `review` is command-only and never a default. */
|
|
12
|
-
export type PonytailRuntimeMode = 'off' | 'lite' | 'full' | 'ultra';
|
|
13
|
-
export declare const DEFAULT_MODE: PonytailRuntimeMode;
|
|
14
|
-
/**
|
|
15
|
-
* Normalize free-form input to a runtime intensity. `null` for anything that
|
|
16
|
-
* is not exactly `off`, `lite`, `full`, or `ultra`.
|
|
17
|
-
*/
|
|
18
|
-
export declare function normalizeRuntimeMode(mode: unknown): PonytailRuntimeMode | null;
|
|
19
|
-
/**
|
|
20
|
-
* Deactivation commands only match when the whole message is the command,
|
|
21
|
-
* ignoring case and trailing punctuation. Matching the phrase anywhere would
|
|
22
|
-
* turn ponytail off mid-task for ordinary requests like "add a normal mode
|
|
23
|
-
* toggle".
|
|
24
|
-
*/
|
|
25
|
-
export declare function isDeactivationCommand(text: unknown): boolean;
|
|
26
|
-
/** Config directory: `$XDG_CONFIG_HOME/ponytail`, `%APPDATA%\ponytail`, else `~/.config/ponytail`. */
|
|
27
|
-
export declare function configDir(env?: NodeJS.ProcessEnv): string;
|
|
28
|
-
/** Absolute path of the optional `config.json`. */
|
|
29
|
-
export declare function configPath(env?: NodeJS.ProcessEnv): string;
|
|
30
|
-
/**
|
|
31
|
-
* Resolve a default from the environment value, then a parsed config document,
|
|
32
|
-
* then {@link DEFAULT_MODE}. Pure so callers can supply fixtures.
|
|
33
|
-
*/
|
|
34
|
-
export declare function resolveDefaultMode(envMode: unknown, configText: string | undefined): PonytailRuntimeMode;
|
|
35
|
-
/**
|
|
36
|
-
* One configuration problem worth surfacing exactly once. `read` covers
|
|
37
|
-
* permission/IO failures, `json` malformed documents, `shape` a root that is
|
|
38
|
-
* not an object, and `value` a `defaultMode` that is not a runtime level.
|
|
39
|
-
*/
|
|
40
|
-
export type DefaultModeIssueKind = 'read' | 'json' | 'shape' | 'value';
|
|
41
|
-
export interface DefaultModeIssue {
|
|
42
|
-
readonly kind: DefaultModeIssueKind;
|
|
43
|
-
readonly detail: string;
|
|
44
|
-
}
|
|
45
|
-
/** The resolved default plus the first config problem found, if any. */
|
|
46
|
-
export interface DefaultModeResolution {
|
|
47
|
-
readonly mode: PonytailRuntimeMode;
|
|
48
|
-
readonly issue?: DefaultModeIssue;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Read the configured default with diagnostics. Priority:
|
|
52
|
-
* `PONYTAIL_DEFAULT_MODE` → Cordis profile `defaultMode` → user config file →
|
|
53
|
-
* `full`. A missing config file is normal and yields no issue; a broken one
|
|
54
|
-
* yields the fallback mode plus one issue for the caller to warn about once.
|
|
55
|
-
* @param env - the process environment to read.
|
|
56
|
-
* @param profileMode - the validated Cordis profile `defaultMode`, or `null`
|
|
57
|
-
* when the profile config is absent or invalid (invalid values are reported
|
|
58
|
-
* by the caller; this function only consumes valid ones).
|
|
59
|
-
*/
|
|
60
|
-
export declare function readDefaultModeInfo(env?: NodeJS.ProcessEnv, profileMode?: PonytailRuntimeMode | null): DefaultModeResolution;
|
|
61
|
-
/**
|
|
62
|
-
* Read the configured default for this host: environment variable first, then
|
|
63
|
-
* the Cordis profile `defaultMode`, then the user config file, then `full`.
|
|
64
|
-
*/
|
|
65
|
-
export declare function readDefaultMode(env?: NodeJS.ProcessEnv, profileMode?: PonytailRuntimeMode | null): PonytailRuntimeMode;
|
|
66
|
-
/**
|
|
67
|
-
* Why a `saved` default is not the effective one — for the `/ponytail default`
|
|
68
|
-
* result message. `null` means the saved value is effective.
|
|
69
|
-
*/
|
|
70
|
-
export declare function defaultOverrideReason(env: NodeJS.ProcessEnv, profileMode: PonytailRuntimeMode | null): 'PONYTAIL_DEFAULT_MODE' | 'profile configuration' | null;
|
|
71
|
-
/**
|
|
72
|
-
* Persist a new default level to the config file, preserving other fields.
|
|
73
|
-
* Returns the normalized mode, or `null` when the value is not a runtime mode.
|
|
74
|
-
* Throws when the write itself fails, so callers never report success for a
|
|
75
|
-
* file that was not written.
|
|
76
|
-
*/
|
|
77
|
-
export declare function writeDefaultMode(mode: unknown, env?: NodeJS.ProcessEnv): PonytailRuntimeMode | null;
|
|
78
|
-
/**
|
|
79
|
-
* Session-scoped live mode. The absence of an entry means "use the configured
|
|
80
|
-
* default", which matches the upstream behavior where each session starts from
|
|
81
|
-
* the default until the user switches it.
|
|
82
|
-
*/
|
|
83
|
-
export declare class ModeStore {
|
|
84
|
-
private readonly modes;
|
|
1
|
+
/**
|
|
2
|
+
* Ponytail mode resolution: the effective default comes from, in order, the
|
|
3
|
+
* `PONYTAIL_DEFAULT_MODE` environment variable, the Cordis profile
|
|
4
|
+
* `defaultMode`, the optional user config file
|
|
5
|
+
* `~/.config/ponytail/config.json` (`defaultMode`), then `full`. Setting a
|
|
6
|
+
* level via the `/ponytail` command is session-scoped and lives in an
|
|
7
|
+
* in-memory, per-agent {@link ModeStore}.
|
|
8
|
+
*
|
|
9
|
+
* @module @mengyuly/dsh-ponytail
|
|
10
|
+
*/
|
|
11
|
+
/** One settable runtime intensity. `review` is command-only and never a default. */
|
|
12
|
+
export type PonytailRuntimeMode = 'off' | 'lite' | 'full' | 'ultra';
|
|
13
|
+
export declare const DEFAULT_MODE: PonytailRuntimeMode;
|
|
14
|
+
/**
|
|
15
|
+
* Normalize free-form input to a runtime intensity. `null` for anything that
|
|
16
|
+
* is not exactly `off`, `lite`, `full`, or `ultra`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeRuntimeMode(mode: unknown): PonytailRuntimeMode | null;
|
|
19
|
+
/**
|
|
20
|
+
* Deactivation commands only match when the whole message is the command,
|
|
21
|
+
* ignoring case and trailing punctuation. Matching the phrase anywhere would
|
|
22
|
+
* turn ponytail off mid-task for ordinary requests like "add a normal mode
|
|
23
|
+
* toggle".
|
|
24
|
+
*/
|
|
25
|
+
export declare function isDeactivationCommand(text: unknown): boolean;
|
|
26
|
+
/** Config directory: `$XDG_CONFIG_HOME/ponytail`, `%APPDATA%\ponytail`, else `~/.config/ponytail`. */
|
|
27
|
+
export declare function configDir(env?: NodeJS.ProcessEnv): string;
|
|
28
|
+
/** Absolute path of the optional `config.json`. */
|
|
29
|
+
export declare function configPath(env?: NodeJS.ProcessEnv): string;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a default from the environment value, then a parsed config document,
|
|
32
|
+
* then {@link DEFAULT_MODE}. Pure so callers can supply fixtures.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveDefaultMode(envMode: unknown, configText: string | undefined): PonytailRuntimeMode;
|
|
35
|
+
/**
|
|
36
|
+
* One configuration problem worth surfacing exactly once. `read` covers
|
|
37
|
+
* permission/IO failures, `json` malformed documents, `shape` a root that is
|
|
38
|
+
* not an object, and `value` a `defaultMode` that is not a runtime level.
|
|
39
|
+
*/
|
|
40
|
+
export type DefaultModeIssueKind = 'read' | 'json' | 'shape' | 'value';
|
|
41
|
+
export interface DefaultModeIssue {
|
|
42
|
+
readonly kind: DefaultModeIssueKind;
|
|
43
|
+
readonly detail: string;
|
|
44
|
+
}
|
|
45
|
+
/** The resolved default plus the first config problem found, if any. */
|
|
46
|
+
export interface DefaultModeResolution {
|
|
47
|
+
readonly mode: PonytailRuntimeMode;
|
|
48
|
+
readonly issue?: DefaultModeIssue;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Read the configured default with diagnostics. Priority:
|
|
52
|
+
* `PONYTAIL_DEFAULT_MODE` → Cordis profile `defaultMode` → user config file →
|
|
53
|
+
* `full`. A missing config file is normal and yields no issue; a broken one
|
|
54
|
+
* yields the fallback mode plus one issue for the caller to warn about once.
|
|
55
|
+
* @param env - the process environment to read.
|
|
56
|
+
* @param profileMode - the validated Cordis profile `defaultMode`, or `null`
|
|
57
|
+
* when the profile config is absent or invalid (invalid values are reported
|
|
58
|
+
* by the caller; this function only consumes valid ones).
|
|
59
|
+
*/
|
|
60
|
+
export declare function readDefaultModeInfo(env?: NodeJS.ProcessEnv, profileMode?: PonytailRuntimeMode | null): DefaultModeResolution;
|
|
61
|
+
/**
|
|
62
|
+
* Read the configured default for this host: environment variable first, then
|
|
63
|
+
* the Cordis profile `defaultMode`, then the user config file, then `full`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function readDefaultMode(env?: NodeJS.ProcessEnv, profileMode?: PonytailRuntimeMode | null): PonytailRuntimeMode;
|
|
66
|
+
/**
|
|
67
|
+
* Why a `saved` default is not the effective one — for the `/ponytail default`
|
|
68
|
+
* result message. `null` means the saved value is effective.
|
|
69
|
+
*/
|
|
70
|
+
export declare function defaultOverrideReason(env: NodeJS.ProcessEnv, profileMode: PonytailRuntimeMode | null): 'PONYTAIL_DEFAULT_MODE' | 'profile configuration' | null;
|
|
71
|
+
/**
|
|
72
|
+
* Persist a new default level to the config file, preserving other fields.
|
|
73
|
+
* Returns the normalized mode, or `null` when the value is not a runtime mode.
|
|
74
|
+
* Throws when the write itself fails, so callers never report success for a
|
|
75
|
+
* file that was not written.
|
|
76
|
+
*/
|
|
77
|
+
export declare function writeDefaultMode(mode: unknown, env?: NodeJS.ProcessEnv): PonytailRuntimeMode | null;
|
|
78
|
+
/**
|
|
79
|
+
* Session-scoped live mode. The absence of an entry means "use the configured
|
|
80
|
+
* default", which matches the upstream behavior where each session starts from
|
|
81
|
+
* the default until the user switches it.
|
|
82
|
+
*/
|
|
83
|
+
export declare class ModeStore {
|
|
84
|
+
private readonly modes;
|
|
85
85
|
/** The mode in force for one agent, or the configured default. */
|
|
86
86
|
modeFor(agentId: string, fallback: PonytailRuntimeMode): PonytailRuntimeMode;
|
|
87
|
+
/** Whether this session currently overrides the configured default. */
|
|
88
|
+
has(agentId: string): boolean;
|
|
87
89
|
/** Set the mode for one agent's session (session-scoped, survives until changed or disposal). */
|
|
88
|
-
set(agentId: string, mode: PonytailRuntimeMode): void;
|
|
89
|
-
/** Forget a session-scoped override so the next lookup returns the default. */
|
|
90
|
-
clear(agentId: string): void;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Compile `PONYTAIL_SUBAGENT_MATCHER` into a case-insensitive regex. An unset
|
|
94
|
-
* matcher yields `null`; an invalid pattern stays fail-open (every agent gets
|
|
95
|
-
* the ruleset) but is reported so the caller can warn exactly once.
|
|
96
|
-
*/
|
|
97
|
-
export declare function compileSubagentMatcher(raw: string | undefined): {
|
|
98
|
-
matcher: RegExp | null;
|
|
99
|
-
invalid: boolean;
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* The stable per-session identity backing every mode override. DSH's `Agent`
|
|
103
|
-
* type documents `id` as "the single identity shared with session", so the
|
|
104
|
-
* agent id IS the SessionId: one entry per live session, never shared between
|
|
105
|
-
* two sessions, and stable across the session's lifetime. Centralized so the
|
|
106
|
-
* key choice lives in exactly one place.
|
|
107
|
-
*/
|
|
108
|
-
export declare function sessionKey(agent: {
|
|
109
|
-
readonly id: string;
|
|
110
|
-
}): string;
|
|
111
|
-
/** Whether a session is a subagent child (origin, or any delegation depth with no origin). */
|
|
112
|
-
export declare function isSubagentSession(header: {
|
|
113
|
-
origin?: 'subagent';
|
|
114
|
-
delegationDepth?: number;
|
|
115
|
-
}): boolean;
|
|
90
|
+
set(agentId: string, mode: PonytailRuntimeMode): void;
|
|
91
|
+
/** Forget a session-scoped override so the next lookup returns the default. */
|
|
92
|
+
clear(agentId: string): void;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Compile `PONYTAIL_SUBAGENT_MATCHER` into a case-insensitive regex. An unset
|
|
96
|
+
* matcher yields `null`; an invalid pattern stays fail-open (every agent gets
|
|
97
|
+
* the ruleset) but is reported so the caller can warn exactly once.
|
|
98
|
+
*/
|
|
99
|
+
export declare function compileSubagentMatcher(raw: string | undefined): {
|
|
100
|
+
matcher: RegExp | null;
|
|
101
|
+
invalid: boolean;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* The stable per-session identity backing every mode override. DSH's `Agent`
|
|
105
|
+
* type documents `id` as "the single identity shared with session", so the
|
|
106
|
+
* agent id IS the SessionId: one entry per live session, never shared between
|
|
107
|
+
* two sessions, and stable across the session's lifetime. Centralized so the
|
|
108
|
+
* key choice lives in exactly one place.
|
|
109
|
+
*/
|
|
110
|
+
export declare function sessionKey(agent: {
|
|
111
|
+
readonly id: string;
|
|
112
|
+
}): string;
|
|
113
|
+
/** Whether a session is a subagent child (origin, or any delegation depth with no origin). */
|
|
114
|
+
export declare function isSubagentSession(header: {
|
|
115
|
+
origin?: 'subagent';
|
|
116
|
+
delegationDepth?: number;
|
|
117
|
+
}): boolean;
|