chill-agent 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/.agents/rules/ponytail.md +30 -0
  2. package/.claude-plugin/contrast-mcp-launcher.mjs +31 -0
  3. package/.claude-plugin/marketplace.json +17 -0
  4. package/.claude-plugin/plugin.json +8 -3
  5. package/.cline-plugin/plugin.js +6 -0
  6. package/.clinerules/ponytail.md +30 -0
  7. package/.codex-plugin/plugin.json +4 -3
  8. package/.cursor/rules/ponytail.mdc +36 -0
  9. package/.cursor-plugin/marketplace.json +16 -0
  10. package/.cursor-plugin/plugin.json +2 -2
  11. package/.devin-plugin/plugin.json +2 -2
  12. package/.grok-plugin/marketplace.json +16 -0
  13. package/.kimi-plugin/plugin.json +11 -29
  14. package/.kiro/steering/ponytail.md +35 -0
  15. package/.omp-plugin/marketplace.json +15 -0
  16. package/.openclaw/skills/ponytail/SKILL.md +47 -0
  17. package/.openclaw/skills/ponytail-audit/SKILL.md +100 -0
  18. package/.openclaw/skills/ponytail-debt/SKILL.md +43 -0
  19. package/.openclaw/skills/ponytail-gain/SKILL.md +51 -0
  20. package/.openclaw/skills/ponytail-help/SKILL.md +76 -0
  21. package/.openclaw/skills/ponytail-review/SKILL.md +98 -0
  22. package/.opencode/command/ponytail-audit.md +5 -0
  23. package/.opencode/command/ponytail-debt.md +5 -0
  24. package/.opencode/command/ponytail-gain.md +5 -0
  25. package/.opencode/command/ponytail-help.md +5 -0
  26. package/.opencode/command/ponytail-review.md +5 -0
  27. package/.opencode/command/ponytail.md +5 -0
  28. package/.opencode/plugins/index.js +4 -0
  29. package/.opencode/plugins/ponytail-frontmatter.cjs +63 -0
  30. package/.opencode/plugins/ponytail.mjs +206 -0
  31. package/.qoder-plugin/plugin.json +12 -26
  32. package/.windsurf/rules/ponytail.md +30 -0
  33. package/AGENTS.md +32 -0
  34. package/__init__.py +228 -0
  35. package/assets/antislop-banner.png +0 -0
  36. package/assets/band/band.png +0 -0
  37. package/assets/band/board.png +0 -0
  38. package/assets/benchmark-3model.svg +21 -0
  39. package/assets/benchmark-agentic.svg +62 -0
  40. package/assets/codex-icon.png +0 -0
  41. package/assets/compare/code/after.webp +0 -0
  42. package/assets/compare/code/before.webp +0 -0
  43. package/assets/compare/text/after.webp +0 -0
  44. package/assets/compare/text/before.webp +0 -0
  45. package/assets/compare/ui/w-all.webp +0 -0
  46. package/assets/compare/ui/w-antislop.webp +0 -0
  47. package/assets/compare/ui/w-design.webp +0 -0
  48. package/assets/compare/ui/without.webp +0 -0
  49. package/assets/logo-dark.png +0 -0
  50. package/assets/logo-dark.svg +115 -0
  51. package/assets/logo-greenpt-dark.svg +27 -0
  52. package/assets/logo-greenpt.svg +27 -0
  53. package/assets/logo.png +0 -0
  54. package/assets/photoreunion-logo-dark.svg +4 -0
  55. package/assets/photoreunion-logo-light.svg +4 -0
  56. package/assets/profile.png +0 -0
  57. package/assets/retriever-logo-dark.svg +4 -0
  58. package/assets/retriever-logo-light.svg +4 -0
  59. package/assets/stories-banner.png +0 -0
  60. package/assets/v5/audit-es.png +0 -0
  61. package/assets/v5/audit-ja.png +0 -0
  62. package/assets/v5/audit-ko.png +0 -0
  63. package/assets/v5/audit-zh-CN.png +0 -0
  64. package/assets/v5/audit.png +0 -0
  65. package/assets/v5/beforeafter-es.png +0 -0
  66. package/assets/v5/beforeafter-ja.png +0 -0
  67. package/assets/v5/beforeafter-ko.png +0 -0
  68. package/assets/v5/beforeafter-zh-CN.png +0 -0
  69. package/assets/v5/beforeafter.png +0 -0
  70. package/assets/v5/chart-es.png +0 -0
  71. package/assets/v5/chart-ja.png +0 -0
  72. package/assets/v5/chart-ko.png +0 -0
  73. package/assets/v5/chart-zh-CN.png +0 -0
  74. package/assets/v5/chart.png +0 -0
  75. package/assets/v5/hero-es.jpg +0 -0
  76. package/assets/v5/hero-ja.jpg +0 -0
  77. package/assets/v5/hero-ko.jpg +0 -0
  78. package/assets/v5/hero-zh-CN.jpg +0 -0
  79. package/assets/v5/hero.jpg +0 -0
  80. package/assets/v5/ladder-es.png +0 -0
  81. package/assets/v5/ladder-ja.png +0 -0
  82. package/assets/v5/ladder-ko.png +0 -0
  83. package/assets/v5/ladder-zh-CN.png +0 -0
  84. package/assets/v5/ladder.png +0 -0
  85. package/assets/v5/review-es.png +0 -0
  86. package/assets/v5/review-ja.png +0 -0
  87. package/assets/v5/review-ko.png +0 -0
  88. package/assets/v5/review-zh-CN.png +0 -0
  89. package/assets/v5/review.png +0 -0
  90. package/assets/v5/tests-es.png +0 -0
  91. package/assets/v5/tests-ja.png +0 -0
  92. package/assets/v5/tests-ko.png +0 -0
  93. package/assets/v5/tests-zh-CN.png +0 -0
  94. package/assets/v5/tests.png +0 -0
  95. package/assets/waitlist-banner-es.png +0 -0
  96. package/assets/waitlist-banner-ja.png +0 -0
  97. package/assets/waitlist-banner-ko.png +0 -0
  98. package/assets/waitlist-banner-zh-CN.png +0 -0
  99. package/assets/waitlist-banner.png +0 -0
  100. package/benchmarks/.env.example +2 -0
  101. package/benchmarks/agentic/README.md +201 -0
  102. package/benchmarks/agentic/answer.py +288 -0
  103. package/benchmarks/agentic/compare.py +93 -0
  104. package/benchmarks/agentic/complete.py +178 -0
  105. package/benchmarks/agentic/judge.py +198 -0
  106. package/benchmarks/agentic/run.py +543 -0
  107. package/benchmarks/agentic/tasks.py +1544 -0
  108. package/benchmarks/arms/baseline.js +2 -0
  109. package/benchmarks/arms/caveman-SKILL.md +67 -0
  110. package/benchmarks/arms/caveman.js +8 -0
  111. package/benchmarks/arms/ponytail.js +8 -0
  112. package/benchmarks/behavior.js +54 -0
  113. package/benchmarks/behavior.yaml +40 -0
  114. package/benchmarks/correctness.js +291 -0
  115. package/benchmarks/loc.js +17 -19
  116. package/benchmarks/promptfooconfig.gemini.yaml +32 -0
  117. package/benchmarks/promptfooconfig.gpt-newest.yaml +33 -0
  118. package/benchmarks/promptfooconfig.yaml +41 -0
  119. package/benchmarks/results/2026-06-12-caveman-vs-ponytail.md +71 -0
  120. package/benchmarks/results/2026-06-12-v4-hardening-vs-caveman.md +125 -0
  121. package/benchmarks/results/2026-06-15-llama3.2-local.md +82 -0
  122. package/benchmarks/results/2026-06-16-correctness-gate-fix.md +113 -0
  123. package/benchmarks/results/2026-06-16-robustness-audit.md +135 -0
  124. package/benchmarks/results/2026-06-17-agentic-safety.md +164 -0
  125. package/benchmarks/results/2026-06-17-cost-verification.md +93 -0
  126. package/benchmarks/results/2026-06-18-agentic.md +219 -0
  127. package/benchmarks/results/2026-06-22-issue-245-217-comprehension.md +98 -0
  128. package/benchmarks/results/2026-10-07-agentic.md +186 -0
  129. package/cli/index.mjs +241 -0
  130. package/cli/lib/banner.mjs +21 -0
  131. package/cli/lib/install.mjs +498 -0
  132. package/cli/lib/settings.mjs +48 -0
  133. package/cli/package-lock.json +87 -0
  134. package/cli/package.json +42 -0
  135. package/cli/scripts/smoke-test.mjs +95 -0
  136. package/cli/scripts/smoke-worker.mjs +411 -0
  137. package/cli/scripts/sync-skills.mjs +81 -0
  138. package/commands/ponytail-audit.toml +2 -0
  139. package/commands/ponytail-debt.toml +2 -0
  140. package/commands/ponytail-gain.toml +2 -0
  141. package/commands/ponytail-help.toml +2 -0
  142. package/commands/ponytail-review.toml +2 -0
  143. package/commands/ponytail.toml +2 -0
  144. package/examples/email-validation.md +156 -0
  145. package/examples/group-by.md +35 -0
  146. package/examples/infinite-scroll.md +58 -0
  147. package/examples/number-formatting.md +37 -0
  148. package/examples/rate-limit.md +272 -0
  149. package/examples/react-countdown.md +390 -0
  150. package/examples/url-params.md +41 -0
  151. package/gemini-extension.json +2 -2
  152. package/hooks/band/board.ts +560 -0
  153. package/hooks/band/logo.ts +3 -0
  154. package/hooks/band/metrics.ts +311 -0
  155. package/hooks/band/register.tsx +495 -0
  156. package/hooks/band/types.d.ts +34 -0
  157. package/hooks/chill-statusline.ps1 +1 -1
  158. package/hooks/claude-band.json +6 -0
  159. package/hooks/claude-codex-hooks.json +34 -4
  160. package/hooks/copilot-hooks.json +12 -2
  161. package/hooks/cursor-hooks.json +2 -2
  162. package/hooks/ponytail-activate.js +201 -0
  163. package/hooks/ponytail-band-import.js +274 -0
  164. package/hooks/ponytail-config.js +175 -0
  165. package/hooks/ponytail-instructions.js +98 -0
  166. package/hooks/ponytail-map.js +111 -0
  167. package/hooks/ponytail-mode-tracker.js +216 -0
  168. package/hooks/ponytail-runtime.js +182 -0
  169. package/hooks/ponytail-statusline.ps1 +24 -0
  170. package/hooks/ponytail-statusline.sh +18 -0
  171. package/hooks/ponytail-subagent.js +94 -0
  172. package/hooks/qoder-hooks.json +19 -4
  173. package/i18n/README.ko.md +230 -0
  174. package/package.json +17 -6
  175. package/pi-extension/index.js +254 -0
  176. package/pi-extension/package.json +8 -0
  177. package/pi-extension/test/extension.test.js +367 -0
  178. package/pi-extension/test/helpers.test.js +179 -0
  179. package/plugin.yaml +21 -12
  180. package/rules/antislop.mdc +18 -0
  181. package/scripts/build-openclaw-skills.js +60 -0
  182. package/scripts/check-repo.mjs +389 -0
  183. package/scripts/check-rule-copies.js +74 -0
  184. package/scripts/check-versions.js +77 -0
  185. package/scripts/cursor-hooks.js +145 -0
  186. package/scripts/generate-chillslop.mjs +42 -0
  187. package/scripts/generate-examples.mjs +149 -0
  188. package/scripts/generate-plugin-doors.mjs +108 -0
  189. package/scripts/publish-openclaw-skills.js +75 -0
  190. package/scripts/skill-index.mjs +81 -0
  191. package/scripts/sync-ponytail.mjs +65 -0
  192. package/scripts/uninstall.js +92 -0
  193. package/skills/antislop/SKILL.md +700 -0
  194. package/skills/antislop/VERSION +1 -0
  195. package/skills/antislop-code/SKILL.md +128 -0
  196. package/skills/antislop-copywriting/SKILL.md +407 -0
  197. package/skills/antislop-human/LICENSE-web-quality-skills +21 -0
  198. package/skills/antislop-human/SKILL.md +204 -0
  199. package/skills/antislop-human/contrast-check.py +168 -0
  200. package/skills/antislop-human/contrast-mcp.py +121 -0
  201. package/skills/antislop-human/references/A11Y-PATTERNS.md +249 -0
  202. package/skills/antislop-human/references/WCAG.md +127 -0
  203. package/skills/antislop-layoutmobile/SKILL.md +170 -0
  204. package/skills/antislop-ui/LICENSE-web-quality-skills +21 -0
  205. package/skills/antislop-ui/SKILL.md +344 -0
  206. package/skills/antislop-ui/references/CORE-WEB-VITALS.md +181 -0
  207. package/skills/antislop-ui/references/WEB-BASELINE.md +28 -0
  208. package/skills/plugin.json +5 -0
  209. package/skills/ponytail/SKILL.md +52 -0
  210. package/skills/ponytail-audit/SKILL.md +103 -0
  211. package/skills/ponytail-debt/SKILL.md +44 -0
  212. package/skills/ponytail-gain/SKILL.md +52 -0
  213. package/skills/ponytail-help/SKILL.md +76 -0
  214. package/skills/ponytail-review/SKILL.md +101 -0
  215. package/src/package.json +3 -0
  216. package/tests/activation-map.test.js +63 -0
  217. package/tests/band-import.test.js +111 -0
  218. package/tests/band.test.tsx +520 -0
  219. package/tests/behavior.test.js +80 -0
  220. package/tests/commands.test.js +94 -0
  221. package/tests/copilot-plugin.test.js +35 -0
  222. package/tests/correctness.test.js +245 -0
  223. package/tests/cursor-hooks.test.js +343 -0
  224. package/tests/gemini-extension.test.js +91 -0
  225. package/tests/grok-plugin.test.js +39 -0
  226. package/tests/hermes-plugin.test.js +403 -0
  227. package/tests/hooks-windows.test.js +205 -0
  228. package/tests/hooks.test.js +1005 -0
  229. package/tests/kimi-plugin.test.js +25 -0
  230. package/tests/loc.test.js +18 -0
  231. package/tests/map.test.js +30 -0
  232. package/tests/openclaw-skills.test.js +26 -0
  233. package/tests/opencode-plugin.test.js +340 -0
  234. package/tests/package-scripts.test.js +35 -0
  235. package/tests/package.test.js +23 -0
  236. package/tests/qoder-plugin.test.js +84 -0
  237. package/tests/uninstall.test.js +202 -0
  238. /package/bin/{chill-agent.js → chill-agent.mjs} +0 -0
  239. /package/tests/{manifests.test.js → manifests.test.mjs} +0 -0
  240. /package/tests/{rules.test.js → rules.test.mjs} +0 -0
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Builds the per-skill catalogue that GUIDE.md carries, from the skills themselves.
4
+ *
5
+ * node scripts/skill-index.mjs
6
+ */
7
+ import fs from 'node:fs'
8
+ import path from 'node:path'
9
+ import { fileURLToPath } from 'node:url'
10
+
11
+ const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..')
12
+
13
+ export const START = '<!-- antislop:skills:start -->'
14
+ export const END = '<!-- antislop:skills:end -->'
15
+
16
+ // One plain-English line per skill, and the order the guide lists them in. The frontmatter
17
+ // description is written for an agent to match on, not for a person to read.
18
+ const NOTES = {
19
+ antislop:
20
+ 'The core, and it is always loaded. All 40 rules and the Delivery Gate live here, so every task gets them whether or not you pick anything else. The five skills below add depth to it.',
21
+ 'antislop-ui':
22
+ 'Load it when the AI builds or restyles anything you can see: color, layout, cards, buttons, motion. Sized media and load priority are built in.',
23
+ 'antislop-copywriting':
24
+ 'Load it when the AI writes or edits text: headlines, buttons, emails, or a page of prose. It removes the excited, empty voice and the made-up numbers.',
25
+ 'antislop-human':
26
+ 'Load it when real people have to use the result. Readable colors, keyboard use, visible focus, button states, the pointer cursor and other interaction affordances, WCAG 2.2 references, and the contrast checker.',
27
+ 'antislop-layoutmobile':
28
+ 'Load it when the same page has to work on a phone and on a desktop. Reflow, breakpoints, overflow, tap targets, mobile navigation.',
29
+ 'antislop-code':
30
+ 'Load it when the AI writes or reviews code comments. It removes the ones an AI adds out of habit and keeps the ones that carry information.',
31
+ }
32
+
33
+ // A pattern entry opens with one of these labels; every other heading under a skill is prose.
34
+ const ENTRY = /^\s*-\s*\*\*(?:Tell|The pattern):\*\*/m
35
+ // A fenced example can hold a '### ' line of its own, so read with the fences taken out.
36
+ const unfenced = (text) => text.replace(/^[ \t]*(?:```|~~~)[\s\S]*?^[ \t]*(?:```|~~~)[ \t]*$/gm, '')
37
+
38
+ const blocksOf = (body, level) => body.split(new RegExp(`^${level} `, 'm')).slice(1)
39
+
40
+ const HEAD = '| What it covers | The patterns it names |\n|----------------|-----------------------|'
41
+
42
+ export function buildIndex() {
43
+ const names = fs
44
+ .readdirSync(path.join(root, 'skills'), { withFileTypes: true })
45
+ .filter((e) => e.isDirectory())
46
+ .map((e) => e.name)
47
+
48
+ const unlisted = names.filter((n) => !NOTES[n])
49
+ if (unlisted.length) throw new Error(`scripts/skill-index.mjs has no intro for: ${unlisted.join(', ')}`)
50
+ const gone = Object.keys(NOTES).filter((n) => !names.includes(n))
51
+ if (gone.length) throw new Error(`scripts/skill-index.mjs describes a skill that is gone: ${gone.join(', ')}`)
52
+
53
+ const out = []
54
+ for (const name of Object.keys(NOTES)) {
55
+ const raw = fs.readFileSync(path.join(root, 'skills', name, 'SKILL.md'), 'utf8').replace(/\r\n/g, '\n')
56
+ const rows = []
57
+ for (const section of blocksOf(unfenced(raw), '##')) {
58
+ const title = section.split('\n')[0].trim()
59
+ const found = blocksOf(section, '###')
60
+ .filter((block) => ENTRY.test(block))
61
+ .map((block) => block.split('\n')[0].trim())
62
+ if (found.length) rows.push(`| ${title} | ${found.join(', ')} |`)
63
+ }
64
+ out.push(`### ${name}`, '', NOTES[name], '')
65
+ if (rows.length) out.push(HEAD, ...rows, '')
66
+ }
67
+ return out.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n'
68
+ }
69
+
70
+ // Run directly to write it into the guide; imported by check-repo to prove the guide is current.
71
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
72
+ const file = path.join(root, 'GUIDE.md')
73
+ const guide = fs.readFileSync(file, 'utf8')
74
+ const from = guide.indexOf(START)
75
+ const to = guide.indexOf(END)
76
+ if (from === -1 || to === -1) throw new Error(`GUIDE.md has no ${START} ... ${END} pair`)
77
+ const eol = guide.includes('\r\n') ? '\r\n' : '\n'
78
+ const block = buildIndex().replace(/\n/g, eol)
79
+ fs.writeFileSync(file, guide.slice(0, from + START.length) + eol + block + guide.slice(to))
80
+ console.log('Regenerated the skill catalogue in GUIDE.md')
81
+ }
@@ -0,0 +1,65 @@
1
+ // scripts/sync-ponytail.mjs
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ const filesToFetch = [
6
+ '__init__.py',
7
+ '.agents/rules/ponytail.md',
8
+ '.claude-plugin/marketplace.json',
9
+ '.clinerules/ponytail.md',
10
+ '.github/FUNDING.yml',
11
+ '.github/copilot-instructions.md',
12
+ '.github/plugin/marketplace.json',
13
+ '.github/plugin/plugin.json',
14
+ '.github/workflows/publish.yml',
15
+ '.github/workflows/test.yml',
16
+ '.grok-plugin/marketplace.json',
17
+ '.kiro/steering/ponytail.md',
18
+ '.qoder/rules/ponytail.md',
19
+ '.windsurf/rules/ponytail.md',
20
+ '.cursor/rules/ponytail.mdc',
21
+ '.opencode/command/ponytail-audit.md',
22
+ '.opencode/command/ponytail-debt.md',
23
+ '.opencode/command/ponytail-gain.md',
24
+ '.opencode/command/ponytail-help.md',
25
+ '.opencode/command/ponytail-review.md',
26
+ '.opencode/command/ponytail.md',
27
+ '.opencode/plugins/index.js',
28
+ '.opencode/plugins/ponytail-frontmatter.cjs',
29
+ '.opencode/plugins/ponytail.mjs',
30
+ 'hooks/ponytail-activate.js',
31
+ 'hooks/ponytail-mode-tracker.js',
32
+ 'hooks/ponytail-instructions.js',
33
+ 'hooks/ponytail-runtime.js',
34
+ 'hooks/ponytail-statusline.ps1',
35
+ 'hooks/ponytail-statusline.sh',
36
+ 'pi-extension/index.js',
37
+ 'pi-extension/package.json',
38
+ 'scripts/check-rule-copies.js',
39
+ 'scripts/check-versions.js',
40
+ 'scripts/cursor-hooks.js',
41
+ 'scripts/uninstall.js'
42
+ ];
43
+
44
+ async function run() {
45
+ console.log('Fetching and syncing full ponytail files...');
46
+ for (const rel of filesToFetch) {
47
+ try {
48
+ const url = `https://raw.githubusercontent.com/DietrichGebert/ponytail/main/${rel}`;
49
+ const res = await fetch(url);
50
+ if (res.ok) {
51
+ const text = await res.text();
52
+ const dir = path.dirname(rel);
53
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
54
+ fs.writeFileSync(rel, text, 'utf8');
55
+ console.log(`✓ Synced: ${rel} (${text.length} B)`);
56
+ } else {
57
+ console.warn(`✗ Not found (${res.status}): ${rel}`);
58
+ }
59
+ } catch (err) {
60
+ console.error(`Error on ${rel}:`, err.message);
61
+ }
62
+ }
63
+ }
64
+
65
+ run();
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ // ponytail — removes state ponytail wrote outside the plugin's own files:
3
+ // the mode flag, the config file, the statusline script copy, the
4
+ // statusLine entry it added to settings.json, and its entries in
5
+ // ~/.cursor/hooks.json. Plugin files themselves are removed by each host's
6
+ // own uninstall command (see README); this only cleans up what those
7
+ // commands can't see.
8
+
9
+ const fs = require('fs');
10
+ const os = require('os');
11
+ const path = require('path');
12
+ const { getConfigPath, getClaudeDir, getQoderDir } = require('../hooks/ponytail-config');
13
+ const cursorHooks = require('./cursor-hooks');
14
+
15
+ const STATUSLINE_SCRIPT = /(?:^|[\s"'\\/])ponytail-statusline\.(?:sh|ps1)(?=$|[\s"'])/;
16
+
17
+ function removeIfExists(filePath, label) {
18
+ try {
19
+ fs.unlinkSync(filePath);
20
+ console.log(`Removed ${label}: ${filePath}`);
21
+ } catch (e) {
22
+ if (e.code !== 'ENOENT') throw e;
23
+ }
24
+ }
25
+
26
+ removeIfExists(path.join(getClaudeDir(), '.ponytail-active'), 'mode flag');
27
+ removeIfExists(path.join(getClaudeDir(), '.ponytail-statusline-nudged'), 'statusline nudge flag');
28
+ removeIfExists(path.join(os.homedir(), '.cursor', '.ponytail-active'), 'Cursor mode flag');
29
+ removeIfExists(path.join(getQoderDir(), '.ponytail-active'), 'Qoder mode flag');
30
+ for (const dir of [path.join(getClaudeDir(), 'ponytail-modes'), path.join(os.homedir(), '.cursor', 'ponytail-modes'), path.join(process.env.CODEBUDDY_CONFIG_DIR || path.join(os.homedir(), '.codebuddy'), 'ponytail-modes')]) {
31
+ if (fs.existsSync(dir)) {
32
+ fs.rmSync(dir, { recursive: true });
33
+ console.log(`Removed per-project mode flags: ${dir}`);
34
+ }
35
+ }
36
+ removeIfExists(path.join(process.env.CODEBUDDY_CONFIG_DIR || path.join(os.homedir(), '.codebuddy'), '.ponytail-active'), 'CodeBuddy mode flag');
37
+ removeIfExists(path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'opencode', '.ponytail-active'), 'OpenCode mode flag');
38
+ removeIfExists(getConfigPath(), 'config file');
39
+ // Statusline script copies the activate hook keeps in the config dir (#1032).
40
+ for (const name of ['ponytail-statusline.sh', 'ponytail-statusline.ps1']) {
41
+ removeIfExists(path.join(getClaudeDir(), name), 'statusline script');
42
+ }
43
+
44
+ // Cursor hooks (#817): drop only ponytail's entries from ~/.cursor/hooks.json,
45
+ // keep every other hook the user configured there.
46
+ try {
47
+ const hooksFile = cursorHooks.uninstall('user');
48
+ if (hooksFile) console.log(`Removed ponytail hooks from ${hooksFile}`);
49
+ } catch (e) {
50
+ if (e instanceof SyntaxError) {
51
+ // ponytail: malformed hooks.json — can't safely edit it; leave intact, warn
52
+ console.warn(`~/.cursor/hooks.json is malformed — could not remove the ponytail hook entries. Remove them manually from: ${cursorHooks.hooksPath('user')} (${e.message})`);
53
+ } else {
54
+ throw e;
55
+ }
56
+ }
57
+
58
+ const settingsPath = path.join(getClaudeDir(), 'settings.json');
59
+ try {
60
+ const raw = fs.readFileSync(settingsPath, 'utf8').replace(/^\uFEFF/, '');
61
+ const settings = JSON.parse(raw);
62
+ const cmd = settings.statusLine && settings.statusLine.command;
63
+ // Only remove the parts ponytail owns. If the user combined statuslines
64
+ // (e.g. caveman && ponytail), keep the other plugin's command intact.
65
+ // ponytail: splits on && / ; to detect other segments — good enough; a user
66
+ // piping statuslines together is on their own.
67
+ if (typeof cmd === 'string' && STATUSLINE_SCRIPT.test(cmd)) {
68
+ const parts = cmd
69
+ .split(/&&|;/)
70
+ .map((s) => s.trim())
71
+ .filter(Boolean);
72
+ const others = parts.filter((s) => !STATUSLINE_SCRIPT.test(s));
73
+ if (others.length === 0) {
74
+ delete settings.statusLine;
75
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
76
+ console.log(`Removed ponytail statusLine entry from ${settingsPath}`);
77
+ } else {
78
+ settings.statusLine.command = others.join(' && ');
79
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
80
+ console.log(`Removed ponytail statusLine segment from ${settingsPath}`);
81
+ }
82
+ }
83
+ } catch (e) {
84
+ if (e.code === 'ENOENT') {
85
+ // no settings.json — nothing to clean
86
+ } else if (e instanceof SyntaxError) {
87
+ // ponytail: malformed settings.json — can't safely edit it; leave intact, warn
88
+ console.warn(`settings.json is malformed — could not remove the ponytail statusLine entry. Remove it manually from: ${settingsPath} (${e.message})`);
89
+ } else {
90
+ throw e;
91
+ }
92
+ }