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.
- package/.agents/rules/ponytail.md +30 -0
- package/.claude-plugin/contrast-mcp-launcher.mjs +31 -0
- package/.claude-plugin/marketplace.json +17 -0
- package/.claude-plugin/plugin.json +8 -3
- package/.cline-plugin/plugin.js +6 -0
- package/.clinerules/ponytail.md +30 -0
- package/.codex-plugin/plugin.json +4 -3
- package/.cursor/rules/ponytail.mdc +36 -0
- package/.cursor-plugin/marketplace.json +16 -0
- package/.cursor-plugin/plugin.json +2 -2
- package/.devin-plugin/plugin.json +2 -2
- package/.grok-plugin/marketplace.json +16 -0
- package/.kimi-plugin/plugin.json +11 -29
- package/.kiro/steering/ponytail.md +35 -0
- package/.omp-plugin/marketplace.json +15 -0
- package/.openclaw/skills/ponytail/SKILL.md +47 -0
- package/.openclaw/skills/ponytail-audit/SKILL.md +100 -0
- package/.openclaw/skills/ponytail-debt/SKILL.md +43 -0
- package/.openclaw/skills/ponytail-gain/SKILL.md +51 -0
- package/.openclaw/skills/ponytail-help/SKILL.md +76 -0
- package/.openclaw/skills/ponytail-review/SKILL.md +98 -0
- package/.opencode/command/ponytail-audit.md +5 -0
- package/.opencode/command/ponytail-debt.md +5 -0
- package/.opencode/command/ponytail-gain.md +5 -0
- package/.opencode/command/ponytail-help.md +5 -0
- package/.opencode/command/ponytail-review.md +5 -0
- package/.opencode/command/ponytail.md +5 -0
- package/.opencode/plugins/index.js +4 -0
- package/.opencode/plugins/ponytail-frontmatter.cjs +63 -0
- package/.opencode/plugins/ponytail.mjs +206 -0
- package/.qoder-plugin/plugin.json +12 -26
- package/.windsurf/rules/ponytail.md +30 -0
- package/AGENTS.md +32 -0
- package/__init__.py +228 -0
- package/assets/antislop-banner.png +0 -0
- package/assets/band/band.png +0 -0
- package/assets/band/board.png +0 -0
- package/assets/benchmark-3model.svg +21 -0
- package/assets/benchmark-agentic.svg +62 -0
- package/assets/codex-icon.png +0 -0
- package/assets/compare/code/after.webp +0 -0
- package/assets/compare/code/before.webp +0 -0
- package/assets/compare/text/after.webp +0 -0
- package/assets/compare/text/before.webp +0 -0
- package/assets/compare/ui/w-all.webp +0 -0
- package/assets/compare/ui/w-antislop.webp +0 -0
- package/assets/compare/ui/w-design.webp +0 -0
- package/assets/compare/ui/without.webp +0 -0
- package/assets/logo-dark.png +0 -0
- package/assets/logo-dark.svg +115 -0
- package/assets/logo-greenpt-dark.svg +27 -0
- package/assets/logo-greenpt.svg +27 -0
- package/assets/logo.png +0 -0
- package/assets/photoreunion-logo-dark.svg +4 -0
- package/assets/photoreunion-logo-light.svg +4 -0
- package/assets/profile.png +0 -0
- package/assets/retriever-logo-dark.svg +4 -0
- package/assets/retriever-logo-light.svg +4 -0
- package/assets/stories-banner.png +0 -0
- package/assets/v5/audit-es.png +0 -0
- package/assets/v5/audit-ja.png +0 -0
- package/assets/v5/audit-ko.png +0 -0
- package/assets/v5/audit-zh-CN.png +0 -0
- package/assets/v5/audit.png +0 -0
- package/assets/v5/beforeafter-es.png +0 -0
- package/assets/v5/beforeafter-ja.png +0 -0
- package/assets/v5/beforeafter-ko.png +0 -0
- package/assets/v5/beforeafter-zh-CN.png +0 -0
- package/assets/v5/beforeafter.png +0 -0
- package/assets/v5/chart-es.png +0 -0
- package/assets/v5/chart-ja.png +0 -0
- package/assets/v5/chart-ko.png +0 -0
- package/assets/v5/chart-zh-CN.png +0 -0
- package/assets/v5/chart.png +0 -0
- package/assets/v5/hero-es.jpg +0 -0
- package/assets/v5/hero-ja.jpg +0 -0
- package/assets/v5/hero-ko.jpg +0 -0
- package/assets/v5/hero-zh-CN.jpg +0 -0
- package/assets/v5/hero.jpg +0 -0
- package/assets/v5/ladder-es.png +0 -0
- package/assets/v5/ladder-ja.png +0 -0
- package/assets/v5/ladder-ko.png +0 -0
- package/assets/v5/ladder-zh-CN.png +0 -0
- package/assets/v5/ladder.png +0 -0
- package/assets/v5/review-es.png +0 -0
- package/assets/v5/review-ja.png +0 -0
- package/assets/v5/review-ko.png +0 -0
- package/assets/v5/review-zh-CN.png +0 -0
- package/assets/v5/review.png +0 -0
- package/assets/v5/tests-es.png +0 -0
- package/assets/v5/tests-ja.png +0 -0
- package/assets/v5/tests-ko.png +0 -0
- package/assets/v5/tests-zh-CN.png +0 -0
- package/assets/v5/tests.png +0 -0
- package/assets/waitlist-banner-es.png +0 -0
- package/assets/waitlist-banner-ja.png +0 -0
- package/assets/waitlist-banner-ko.png +0 -0
- package/assets/waitlist-banner-zh-CN.png +0 -0
- package/assets/waitlist-banner.png +0 -0
- package/benchmarks/.env.example +2 -0
- package/benchmarks/agentic/README.md +201 -0
- package/benchmarks/agentic/answer.py +288 -0
- package/benchmarks/agentic/compare.py +93 -0
- package/benchmarks/agentic/complete.py +178 -0
- package/benchmarks/agentic/judge.py +198 -0
- package/benchmarks/agentic/run.py +543 -0
- package/benchmarks/agentic/tasks.py +1544 -0
- package/benchmarks/arms/baseline.js +2 -0
- package/benchmarks/arms/caveman-SKILL.md +67 -0
- package/benchmarks/arms/caveman.js +8 -0
- package/benchmarks/arms/ponytail.js +8 -0
- package/benchmarks/behavior.js +54 -0
- package/benchmarks/behavior.yaml +40 -0
- package/benchmarks/correctness.js +291 -0
- package/benchmarks/loc.js +17 -19
- package/benchmarks/promptfooconfig.gemini.yaml +32 -0
- package/benchmarks/promptfooconfig.gpt-newest.yaml +33 -0
- package/benchmarks/promptfooconfig.yaml +41 -0
- package/benchmarks/results/2026-06-12-caveman-vs-ponytail.md +71 -0
- package/benchmarks/results/2026-06-12-v4-hardening-vs-caveman.md +125 -0
- package/benchmarks/results/2026-06-15-llama3.2-local.md +82 -0
- package/benchmarks/results/2026-06-16-correctness-gate-fix.md +113 -0
- package/benchmarks/results/2026-06-16-robustness-audit.md +135 -0
- package/benchmarks/results/2026-06-17-agentic-safety.md +164 -0
- package/benchmarks/results/2026-06-17-cost-verification.md +93 -0
- package/benchmarks/results/2026-06-18-agentic.md +219 -0
- package/benchmarks/results/2026-06-22-issue-245-217-comprehension.md +98 -0
- package/benchmarks/results/2026-10-07-agentic.md +186 -0
- package/cli/index.mjs +241 -0
- package/cli/lib/banner.mjs +21 -0
- package/cli/lib/install.mjs +498 -0
- package/cli/lib/settings.mjs +48 -0
- package/cli/package-lock.json +87 -0
- package/cli/package.json +42 -0
- package/cli/scripts/smoke-test.mjs +95 -0
- package/cli/scripts/smoke-worker.mjs +411 -0
- package/cli/scripts/sync-skills.mjs +81 -0
- package/commands/ponytail-audit.toml +2 -0
- package/commands/ponytail-debt.toml +2 -0
- package/commands/ponytail-gain.toml +2 -0
- package/commands/ponytail-help.toml +2 -0
- package/commands/ponytail-review.toml +2 -0
- package/commands/ponytail.toml +2 -0
- package/examples/email-validation.md +156 -0
- package/examples/group-by.md +35 -0
- package/examples/infinite-scroll.md +58 -0
- package/examples/number-formatting.md +37 -0
- package/examples/rate-limit.md +272 -0
- package/examples/react-countdown.md +390 -0
- package/examples/url-params.md +41 -0
- package/gemini-extension.json +2 -2
- package/hooks/band/board.ts +560 -0
- package/hooks/band/logo.ts +3 -0
- package/hooks/band/metrics.ts +311 -0
- package/hooks/band/register.tsx +495 -0
- package/hooks/band/types.d.ts +34 -0
- package/hooks/chill-statusline.ps1 +1 -1
- package/hooks/claude-band.json +6 -0
- package/hooks/claude-codex-hooks.json +34 -4
- package/hooks/copilot-hooks.json +12 -2
- package/hooks/cursor-hooks.json +2 -2
- package/hooks/ponytail-activate.js +201 -0
- package/hooks/ponytail-band-import.js +274 -0
- package/hooks/ponytail-config.js +175 -0
- package/hooks/ponytail-instructions.js +98 -0
- package/hooks/ponytail-map.js +111 -0
- package/hooks/ponytail-mode-tracker.js +216 -0
- package/hooks/ponytail-runtime.js +182 -0
- package/hooks/ponytail-statusline.ps1 +24 -0
- package/hooks/ponytail-statusline.sh +18 -0
- package/hooks/ponytail-subagent.js +94 -0
- package/hooks/qoder-hooks.json +19 -4
- package/i18n/README.ko.md +230 -0
- package/package.json +17 -6
- package/pi-extension/index.js +254 -0
- package/pi-extension/package.json +8 -0
- package/pi-extension/test/extension.test.js +367 -0
- package/pi-extension/test/helpers.test.js +179 -0
- package/plugin.yaml +21 -12
- package/rules/antislop.mdc +18 -0
- package/scripts/build-openclaw-skills.js +60 -0
- package/scripts/check-repo.mjs +389 -0
- package/scripts/check-rule-copies.js +74 -0
- package/scripts/check-versions.js +77 -0
- package/scripts/cursor-hooks.js +145 -0
- package/scripts/generate-chillslop.mjs +42 -0
- package/scripts/generate-examples.mjs +149 -0
- package/scripts/generate-plugin-doors.mjs +108 -0
- package/scripts/publish-openclaw-skills.js +75 -0
- package/scripts/skill-index.mjs +81 -0
- package/scripts/sync-ponytail.mjs +65 -0
- package/scripts/uninstall.js +92 -0
- package/skills/antislop/SKILL.md +700 -0
- package/skills/antislop/VERSION +1 -0
- package/skills/antislop-code/SKILL.md +128 -0
- package/skills/antislop-copywriting/SKILL.md +407 -0
- package/skills/antislop-human/LICENSE-web-quality-skills +21 -0
- package/skills/antislop-human/SKILL.md +204 -0
- package/skills/antislop-human/contrast-check.py +168 -0
- package/skills/antislop-human/contrast-mcp.py +121 -0
- package/skills/antislop-human/references/A11Y-PATTERNS.md +249 -0
- package/skills/antislop-human/references/WCAG.md +127 -0
- package/skills/antislop-layoutmobile/SKILL.md +170 -0
- package/skills/antislop-ui/LICENSE-web-quality-skills +21 -0
- package/skills/antislop-ui/SKILL.md +344 -0
- package/skills/antislop-ui/references/CORE-WEB-VITALS.md +181 -0
- package/skills/antislop-ui/references/WEB-BASELINE.md +28 -0
- package/skills/plugin.json +5 -0
- package/skills/ponytail/SKILL.md +52 -0
- package/skills/ponytail-audit/SKILL.md +103 -0
- package/skills/ponytail-debt/SKILL.md +44 -0
- package/skills/ponytail-gain/SKILL.md +52 -0
- package/skills/ponytail-help/SKILL.md +76 -0
- package/skills/ponytail-review/SKILL.md +101 -0
- package/src/package.json +3 -0
- package/tests/activation-map.test.js +63 -0
- package/tests/band-import.test.js +111 -0
- package/tests/band.test.tsx +520 -0
- package/tests/behavior.test.js +80 -0
- package/tests/commands.test.js +94 -0
- package/tests/copilot-plugin.test.js +35 -0
- package/tests/correctness.test.js +245 -0
- package/tests/cursor-hooks.test.js +343 -0
- package/tests/gemini-extension.test.js +91 -0
- package/tests/grok-plugin.test.js +39 -0
- package/tests/hermes-plugin.test.js +403 -0
- package/tests/hooks-windows.test.js +205 -0
- package/tests/hooks.test.js +1005 -0
- package/tests/kimi-plugin.test.js +25 -0
- package/tests/loc.test.js +18 -0
- package/tests/map.test.js +30 -0
- package/tests/openclaw-skills.test.js +26 -0
- package/tests/opencode-plugin.test.js +340 -0
- package/tests/package-scripts.test.js +35 -0
- package/tests/package.test.js +23 -0
- package/tests/qoder-plugin.test.js +84 -0
- package/tests/uninstall.test.js +202 -0
- /package/bin/{chill-agent.js → chill-agent.mjs} +0 -0
- /package/tests/{manifests.test.js → manifests.test.mjs} +0 -0
- /package/tests/{rules.test.js → rules.test.mjs} +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const root = path.join(__dirname, '..');
|
|
6
|
+
|
|
7
|
+
function read(relPath) {
|
|
8
|
+
return fs.readFileSync(path.join(root, relPath), 'utf8').replace(/\r\n/g, '\n').trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function stripFrontmatter(text) {
|
|
12
|
+
return text.replace(/^---\n[\s\S]*?\n---\n*/, '').trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const agents = read('AGENTS.md');
|
|
16
|
+
const canonical = agents.replace(/\n\n\(Yes, this file also applies[\s\S]*?\)$/, '').trim();
|
|
17
|
+
|
|
18
|
+
// Compact copies: same body as AGENTS.md, host-specific frontmatter stripped.
|
|
19
|
+
const copies = [
|
|
20
|
+
['.cursor/rules/ponytail.mdc', stripFrontmatter],
|
|
21
|
+
['.windsurf/rules/ponytail.md', text => text.trim()],
|
|
22
|
+
['.clinerules/ponytail.md', text => text.trim()],
|
|
23
|
+
['.agents/rules/ponytail.md', text => text.trim()],
|
|
24
|
+
['.qoder/rules/ponytail.md', text => text.trim()],
|
|
25
|
+
['.github/copilot-instructions.md', text => text.trim()],
|
|
26
|
+
['.kiro/steering/ponytail.md', stripFrontmatter],
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
let failed = false;
|
|
30
|
+
|
|
31
|
+
for (const [relPath, normalize] of copies) {
|
|
32
|
+
const actual = normalize(read(relPath));
|
|
33
|
+
if (actual !== canonical) {
|
|
34
|
+
console.error(`${relPath} drifted from AGENTS.md`);
|
|
35
|
+
failed = true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// SKILL.md is the runtime source of truth and is longer than the compact body,
|
|
40
|
+
// so it cannot be byte-compared. ponytail: canary, not full equality. Assert the
|
|
41
|
+
// load-bearing rules survive verbatim in both the source and AGENTS.md. Changing
|
|
42
|
+
// a rule's wording trips this, which is the reminder to propagate it everywhere.
|
|
43
|
+
// Upgrade path: generate the copies from SKILL.md if this ever misses a real drift.
|
|
44
|
+
const INVARIANTS = [
|
|
45
|
+
'in this codebase', // ladder rung: reuse what already exists (#217)
|
|
46
|
+
'grep every caller', // root-cause bug fix (#245)
|
|
47
|
+
'never about the change itself', // scope: callers, tests and fixtures the change breaks
|
|
48
|
+
'shortcut: <the limit>, <when to upgrade>', // shortcut marker comment
|
|
49
|
+
'correct on edge cases', // robust-variant rule
|
|
50
|
+
'Lazy code without its check is unfinished', // test reflex
|
|
51
|
+
// the "never cut" carve-outs: pin each so a reword in either file can't silently drop one.
|
|
52
|
+
'validation at trust boundaries',
|
|
53
|
+
'prevents data loss',
|
|
54
|
+
'security',
|
|
55
|
+
'accessibility',
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const skill = read('skills/ponytail/SKILL.md');
|
|
59
|
+
const sources = [['skills/ponytail/SKILL.md', skill], ['AGENTS.md', agents]];
|
|
60
|
+
for (const phrase of INVARIANTS) {
|
|
61
|
+
for (const [label, text] of sources) {
|
|
62
|
+
if (!text.includes(phrase)) {
|
|
63
|
+
console.error(`${label} is missing rule invariant: "${phrase}"`);
|
|
64
|
+
failed = true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (failed) {
|
|
70
|
+
console.error('Update the copied rule text, AGENTS.md, or SKILL.md so the shared rules match.');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(`Rule copies match AGENTS.md; ${INVARIANTS.length} rule invariants present in SKILL.md and AGENTS.md.`);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Version-consistency guard. Ponytail declares its version in seven files across
|
|
3
|
+
// five host ecosystems, and every release bumps all of them by hand.
|
|
4
|
+
//
|
|
5
|
+
// tests/gemini-extension.test.js already checks the four plugin manifests agree
|
|
6
|
+
// with each other, but that can't catch the failure mode that shipped in v4.8.0:
|
|
7
|
+
// every manifest stayed stale at 4.7.0 *together* while the release moved on, so
|
|
8
|
+
// they "agreed" and the test passed (#260, #262). It also ignores the two
|
|
9
|
+
// package.json files. This check closes both gaps:
|
|
10
|
+
// 1. every version-bearing file must share one pinned X.Y.Z version, and
|
|
11
|
+
// 2. on a release-tag CI run, that shared version must equal the tag.
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const root = path.join(__dirname, '..');
|
|
17
|
+
const PINNED_SEMVER = /^\d+\.\d+\.\d+$/;
|
|
18
|
+
|
|
19
|
+
// Every file that declares the project version, and who reads it. Add new host
|
|
20
|
+
// manifests here so a future ecosystem can't drift unnoticed.
|
|
21
|
+
const VERSION_FILES = [
|
|
22
|
+
'.claude-plugin/plugin.json', // Claude Code plugin — what users install
|
|
23
|
+
'.codex-plugin/plugin.json', // Codex plugin
|
|
24
|
+
'.devin-plugin/plugin.json', // Devin CLI plugin
|
|
25
|
+
'.github/plugin/plugin.json', // Copilot plugin
|
|
26
|
+
'.qoder-plugin/plugin.json', // Qoder plugin
|
|
27
|
+
'gemini-extension.json', // Gemini CLI extension
|
|
28
|
+
'package.json', // pi-package / repo root
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
function readVersion(relPath) {
|
|
32
|
+
try {
|
|
33
|
+
// Strip a UTF-8 BOM some Windows editors prepend (breaks JSON.parse).
|
|
34
|
+
const raw = fs.readFileSync(path.join(root, relPath), 'utf8').replace(/^\uFEFF/, '');
|
|
35
|
+
return JSON.parse(raw).version;
|
|
36
|
+
} catch (e) {
|
|
37
|
+
throw new Error(`${relPath}: ${e.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let failed = false;
|
|
42
|
+
const versions = VERSION_FILES.map((relPath) => {
|
|
43
|
+
const version = readVersion(relPath);
|
|
44
|
+
if (typeof version !== 'string' || !PINNED_SEMVER.test(version)) {
|
|
45
|
+
console.error(`${relPath}: version must be a pinned X.Y.Z semver, got ${JSON.stringify(version)}`);
|
|
46
|
+
failed = true;
|
|
47
|
+
}
|
|
48
|
+
return [relPath, version];
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Every file must declare the same version.
|
|
52
|
+
const distinct = [...new Set(versions.map(([, v]) => v))];
|
|
53
|
+
if (distinct.length > 1) {
|
|
54
|
+
console.error('Version mismatch — every manifest must share one version:');
|
|
55
|
+
for (const [relPath, version] of versions) console.error(` ${version}\t${relPath}`);
|
|
56
|
+
failed = true;
|
|
57
|
+
}
|
|
58
|
+
const shared = distinct.length === 1 ? distinct[0] : null;
|
|
59
|
+
|
|
60
|
+
// On a release-tag push CI sets GITHUB_REF_TYPE=tag and GITHUB_REF_NAME=vX.Y.Z.
|
|
61
|
+
// The shared version must equal the tag — this catches tagging a release whose
|
|
62
|
+
// version files were never bumped, which mutual agreement alone cannot.
|
|
63
|
+
if (shared && process.env.GITHUB_REF_TYPE === 'tag') {
|
|
64
|
+
const tag = process.env.GITHUB_REF_NAME || '';
|
|
65
|
+
const tagVersion = tag.replace(/^v/, '');
|
|
66
|
+
if (PINNED_SEMVER.test(tagVersion) && tagVersion !== shared) {
|
|
67
|
+
console.error(`release tag ${tag} does not match version ${shared}; bump the version files before tagging`);
|
|
68
|
+
failed = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (failed) {
|
|
73
|
+
console.error('Align the version fields (see issue #260) so every manifest shares one version.');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(`All ${VERSION_FILES.length} version files pinned at ${shared}.`);
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ponytail — install or remove the Cursor hooks (hooks/cursor-hooks.json) in
|
|
3
|
+
// ~/.cursor/hooks.json (default) or <cwd>/.cursor/hooks.json (--project),
|
|
4
|
+
// merging with whatever hooks are already there. Only entries that run one of
|
|
5
|
+
// ponytail's own hooks/ponytail-*.js scripts are added or removed; every other
|
|
6
|
+
// hook stays as it was.
|
|
7
|
+
//
|
|
8
|
+
// node scripts/cursor-hooks.js install [--project]
|
|
9
|
+
// node scripts/cursor-hooks.js uninstall [--project]
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { isShellSafe } = require('../hooks/ponytail-config');
|
|
15
|
+
|
|
16
|
+
const ROOT = path.join(__dirname, '..');
|
|
17
|
+
const TEMPLATE = path.join(ROOT, 'hooks', 'cursor-hooks.json');
|
|
18
|
+
const PONYTAIL_HOOK = /[\\/]hooks[\\/]ponytail-[\w-]+\.js(?=["'\s]|$)/;
|
|
19
|
+
|
|
20
|
+
function isPonytailHook(entry) {
|
|
21
|
+
return Boolean(entry && typeof entry.command === 'string' && PONYTAIL_HOOK.test(entry.command));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hooksPath(scope) {
|
|
25
|
+
return scope === 'project'
|
|
26
|
+
? path.join(process.cwd(), '.cursor', 'hooks.json')
|
|
27
|
+
: path.join(os.homedir(), '.cursor', 'hooks.json');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Missing file → empty config. Malformed JSON throws a SyntaxError so the
|
|
31
|
+
// caller can refuse to touch the file rather than overwrite it.
|
|
32
|
+
function readConfig(file) {
|
|
33
|
+
let config = {};
|
|
34
|
+
try {
|
|
35
|
+
config = JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''));
|
|
36
|
+
} catch (e) {
|
|
37
|
+
if (e.code !== 'ENOENT') throw e;
|
|
38
|
+
}
|
|
39
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
40
|
+
throw new Error(file + ' must contain a JSON object; nothing was changed');
|
|
41
|
+
}
|
|
42
|
+
if (config.hooks === undefined) config.hooks = {};
|
|
43
|
+
if (!config.hooks || typeof config.hooks !== 'object' || Array.isArray(config.hooks)) {
|
|
44
|
+
throw new Error(file + ': hooks must be an object; nothing was changed');
|
|
45
|
+
}
|
|
46
|
+
for (const [event, entries] of Object.entries(config.hooks)) {
|
|
47
|
+
if (!Array.isArray(entries)) {
|
|
48
|
+
throw new Error(file + ': hooks.' + event + ' must be an array; nothing was changed');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function writeConfig(file, config) {
|
|
55
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
56
|
+
fs.writeFileSync(file, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The template's PONYTAIL_DIR placeholder becomes this checkout's absolute
|
|
60
|
+
// path. Forward slashes run unchanged under cmd, PowerShell and bash, and the
|
|
61
|
+
// same allowlist that guards the statusline snippet keeps shell metacharacters
|
|
62
|
+
// out of the command string.
|
|
63
|
+
function ponytailEntries() {
|
|
64
|
+
const root = ROOT.replace(/\\/g, '/');
|
|
65
|
+
if (!isShellSafe(root)) {
|
|
66
|
+
throw new Error('ponytail is checked out at a path with shell metacharacters (' + ROOT +
|
|
67
|
+
'); move it, or copy hooks/cursor-hooks.json by hand and quote the path for your shell');
|
|
68
|
+
}
|
|
69
|
+
const template = JSON.parse(fs.readFileSync(TEMPLATE, 'utf8'));
|
|
70
|
+
const hooks = {};
|
|
71
|
+
for (const [event, entries] of Object.entries(template.hooks)) {
|
|
72
|
+
hooks[event] = entries.map((entry) => ({ ...entry, command: entry.command.replace(/PONYTAIL_DIR/g, root) }));
|
|
73
|
+
}
|
|
74
|
+
return hooks;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function stripPonytail(config) {
|
|
78
|
+
for (const [event, entries] of Object.entries(config.hooks)) {
|
|
79
|
+
if (!Array.isArray(entries)) continue;
|
|
80
|
+
const kept = entries.filter((entry) => !isPonytailHook(entry));
|
|
81
|
+
if (kept.length) config.hooks[event] = kept;
|
|
82
|
+
else delete config.hooks[event];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function install(scope) {
|
|
87
|
+
const file = hooksPath(scope);
|
|
88
|
+
const config = readConfig(file);
|
|
89
|
+
if (config.version === undefined) config.version = 1;
|
|
90
|
+
// Re-running replaces stale ponytail entries instead of duplicating them.
|
|
91
|
+
stripPonytail(config);
|
|
92
|
+
for (const [event, entries] of Object.entries(ponytailEntries())) {
|
|
93
|
+
config.hooks[event] = [...(config.hooks[event] || []), ...entries];
|
|
94
|
+
}
|
|
95
|
+
writeConfig(file, config);
|
|
96
|
+
return file;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Returns the file it changed, or null when there was nothing of ponytail's in it.
|
|
100
|
+
function uninstall(scope) {
|
|
101
|
+
const file = hooksPath(scope);
|
|
102
|
+
if (!fs.existsSync(file)) return null;
|
|
103
|
+
const config = readConfig(file);
|
|
104
|
+
const before = JSON.stringify(config);
|
|
105
|
+
stripPonytail(config);
|
|
106
|
+
if (JSON.stringify(config) === before) return null;
|
|
107
|
+
const otherKeys = Object.keys(config).filter((k) => k !== 'version' && k !== 'hooks');
|
|
108
|
+
if (Object.keys(config.hooks).length === 0 && otherKeys.length === 0) {
|
|
109
|
+
// Only ponytail lived here: drop the file rather than leave an empty husk.
|
|
110
|
+
fs.unlinkSync(file);
|
|
111
|
+
} else {
|
|
112
|
+
writeConfig(file, config);
|
|
113
|
+
}
|
|
114
|
+
return file;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (require.main === module) {
|
|
118
|
+
const args = process.argv.slice(2);
|
|
119
|
+
const action = args[0];
|
|
120
|
+
const scope = args.includes('--project') ? 'project' : 'user';
|
|
121
|
+
try {
|
|
122
|
+
if (action === 'install') {
|
|
123
|
+
const file = install(scope);
|
|
124
|
+
console.log('Installed ponytail hooks in ' + file);
|
|
125
|
+
console.log('Cursor reloads hooks.json on save; start a new chat to activate.');
|
|
126
|
+
} else if (action === 'uninstall') {
|
|
127
|
+
const file = uninstall(scope);
|
|
128
|
+
console.log(file
|
|
129
|
+
? 'Removed ponytail hooks from ' + file
|
|
130
|
+
: 'No ponytail hooks in ' + hooksPath(scope));
|
|
131
|
+
} else {
|
|
132
|
+
console.error('usage: node scripts/cursor-hooks.js install|uninstall [--project]');
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
} catch (e) {
|
|
136
|
+
if (e instanceof SyntaxError) {
|
|
137
|
+
console.error(hooksPath(scope) + ' is not valid JSON; nothing was changed, fix it by hand (' + e.message + ')');
|
|
138
|
+
} else {
|
|
139
|
+
console.error(e.message);
|
|
140
|
+
}
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = { hooksPath, install, isPonytailHook, uninstall };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// scripts/generate-chillslop.mjs
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
let text = fs.readFileSync('antislop.raw.md', 'utf8');
|
|
5
|
+
|
|
6
|
+
text = text.replace(
|
|
7
|
+
/# antislop\n\n> Anti Slop: Rules for AI Coding Agents/g,
|
|
8
|
+
'# ChillSlop 2.0: Anti-Slop & Pragmatic Senior Dev Standard\n\n> Dual Core: Anti-Slop (miqdadbadjuber) + Ponytail (dietrichgebert) + ChillRewind Time-Travel'
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
text = text.replace(/antislop/g, 'chill-agent');
|
|
12
|
+
text = text.replace(/Anti Slop/g, 'ChillAgent Anti-Slop & Ponytail Core');
|
|
13
|
+
|
|
14
|
+
const ponytailHeader = `
|
|
15
|
+
## The Pragmatic Senior Dev Creed (Ponytail Synthesis)
|
|
16
|
+
|
|
17
|
+
> **"The best code is the code you never wrote. The best design is the interface that gets out of the user's way."**
|
|
18
|
+
|
|
19
|
+
Active for the whole session until the user says "stop chill" or "normal mode".
|
|
20
|
+
Switch intensity mode: \`/chill [lite|full|ultra]\`.
|
|
21
|
+
|
|
22
|
+
### The 6-Step Minimalist Execution Ladder
|
|
23
|
+
Take the first option that completely solves the problem:
|
|
24
|
+
1. **Does it need to exist?** Skip features, options, and flexibility nobody asked for.
|
|
25
|
+
2. **Already in this codebase?** Use existing helpers, components, and services.
|
|
26
|
+
3. **Standard library or native platform?** Reach for \`node:*\`, native \`fetch\`, CSS Grid, native \`<dialog>\`.
|
|
27
|
+
4. **Already-installed dependency?** Use what exists in \`package.json\`. Never add an npm package for 10 lines.
|
|
28
|
+
5. **Can it be a single readable line?** Write the single line.
|
|
29
|
+
6. **Otherwise:** The minimum code that works reliably.
|
|
30
|
+
|
|
31
|
+
Be lazy about the solution, never about the change itself: finish every caller, test, fixture, and edge case your change touches. Deletion beats addition.
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
text = text.replace('## Core Principle', ponytailHeader + '\n## Core Principle');
|
|
35
|
+
|
|
36
|
+
fs.writeFileSync('chillslop.md', text, 'utf8');
|
|
37
|
+
fs.writeFileSync('antislop.md', text, 'utf8');
|
|
38
|
+
fs.writeFileSync('rules/chillslop.md', text, 'utf8');
|
|
39
|
+
fs.writeFileSync('rules/antislop.md', text, 'utf8');
|
|
40
|
+
fs.writeFileSync('rules/chillslop.mdc', text, 'utf8');
|
|
41
|
+
|
|
42
|
+
console.log('Successfully generated full-length master rules (chars: ' + text.length + ')');
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// scripts/generate-examples.mjs
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const examples = [
|
|
5
|
+
{
|
|
6
|
+
file: 'examples/README.md',
|
|
7
|
+
content: `# ChillAgent Examples & Empirical Diff Comparisons
|
|
8
|
+
|
|
9
|
+
This folder documents verbatim model outputs across benchmark runs comparing generic AI generation (bloat, slop, unnecessary dependencies) with **ChillAgent (Anti-Slop 2.0 + Ponytail)**.
|
|
10
|
+
|
|
11
|
+
## Categories
|
|
12
|
+
- **Logic & Minimalism:** CSV parsing, debounce, deep cloning, group by, URL parsing, rate limiting.
|
|
13
|
+
- **UI & Accessibility:** Modal dialogs, countdown timers, infinite scroll, typography contrast.
|
|
14
|
+
- **Visual & Copywriting:** Eliminating generic purple gradients and robotic marketing buzzwords.
|
|
15
|
+
`
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
file: 'examples/csv-sum.md',
|
|
19
|
+
content: `# CSV Sum
|
|
20
|
+
|
|
21
|
+
**Task:** "Write Python code that reads sales.csv and sums the 'amount' column."
|
|
22
|
+
|
|
23
|
+
## Without ChillAgent (20 lines, 1 heavy dependency)
|
|
24
|
+
\`\`\`python
|
|
25
|
+
import pandas as pd
|
|
26
|
+
|
|
27
|
+
# Read the CSV file into a DataFrame
|
|
28
|
+
df = pd.read_csv('sales.csv')
|
|
29
|
+
|
|
30
|
+
# Sum the amount column
|
|
31
|
+
total_amount = df['amount'].sum()
|
|
32
|
+
|
|
33
|
+
# Print the result
|
|
34
|
+
print(f"Total sales amount: {total_amount}")
|
|
35
|
+
\`\`\`
|
|
36
|
+
*Bloat:* Forces 35MB \`pandas\` dependency for a simple column sum.
|
|
37
|
+
|
|
38
|
+
## With ChillAgent (4 lines, 0 dependencies)
|
|
39
|
+
\`\`\`python
|
|
40
|
+
import csv
|
|
41
|
+
|
|
42
|
+
with open('sales.csv') as f:
|
|
43
|
+
print(sum(float(row['amount']) for row in csv.DictReader(f)))
|
|
44
|
+
\`\`\`
|
|
45
|
+
*Result:* 0 dependencies, instant execution, standard library only.
|
|
46
|
+
`
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
file: 'examples/debounce.md',
|
|
50
|
+
content: `# Debounce Utility
|
|
51
|
+
|
|
52
|
+
**Task:** "Write a debounce function in TypeScript."
|
|
53
|
+
|
|
54
|
+
## Without ChillAgent (25 lines + lodash dependency)
|
|
55
|
+
\`\`\`typescript
|
|
56
|
+
import debounce from 'lodash/debounce';
|
|
57
|
+
export const debouncedSearch = debounce((query: string) => {
|
|
58
|
+
api.search(query);
|
|
59
|
+
}, 300);
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
## With ChillAgent (6 lines, standard native timer)
|
|
63
|
+
\`\`\`typescript
|
|
64
|
+
export function debounce<T extends (...args: any[]) => void>(fn: T, ms = 300) {
|
|
65
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
66
|
+
return (...args: Parameters<T>) => {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
timer = setTimeout(() => fn(...args), ms);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
\`\`\`
|
|
72
|
+
`
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
file: 'examples/deep-clone.md',
|
|
76
|
+
content: `# Deep Clone
|
|
77
|
+
|
|
78
|
+
**Task:** "Deep clone a JavaScript object in Node 18+."
|
|
79
|
+
|
|
80
|
+
## Without ChillAgent
|
|
81
|
+
\`\`\`javascript
|
|
82
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
83
|
+
const copy = cloneDeep(source);
|
|
84
|
+
\`\`\`
|
|
85
|
+
|
|
86
|
+
## With ChillAgent (1 line native standard library)
|
|
87
|
+
\`\`\`javascript
|
|
88
|
+
const copy = structuredClone(source);
|
|
89
|
+
\`\`\`
|
|
90
|
+
`
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
file: 'examples/modal-dialog.md',
|
|
94
|
+
content: `# Modal Dialog Component
|
|
95
|
+
|
|
96
|
+
**Task:** "Create an accessible modal dialog in vanilla HTML/CSS/JS."
|
|
97
|
+
|
|
98
|
+
## Without ChillAgent (120 lines, custom backdrop, custom focus trap, custom scroll locks)
|
|
99
|
+
Manual z-index soup, forgotten Escape key handlers, broken keyboard traps.
|
|
100
|
+
|
|
101
|
+
## With ChillAgent (12 lines using native HTML <dialog>)
|
|
102
|
+
\`\`\`html
|
|
103
|
+
<dialog id="confirmModal" style="border: 1px solid #26262E; border-radius: 8px; background: #111114; color: #F4F4F6; padding: 20px;">
|
|
104
|
+
<form method="dialog">
|
|
105
|
+
<p>Confirm irreversible session fork?</p>
|
|
106
|
+
<button value="cancel">Cancel</button>
|
|
107
|
+
<button value="confirm" style="background: #818CF8; color: #09090B;">Fork</button>
|
|
108
|
+
</form>
|
|
109
|
+
</dialog>
|
|
110
|
+
<button onclick="document.getElementById('confirmModal').showModal()">Open</button>
|
|
111
|
+
\`\`\`
|
|
112
|
+
*Features:* Built-in backdrop, native Escape key closing, native focus trapping, zero external libraries.
|
|
113
|
+
`
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
file: 'examples/anti-slop-ui.md',
|
|
117
|
+
content: `# Anti-Slop UI Transformation
|
|
118
|
+
|
|
119
|
+
## The Generic AI Default (Rejected as Slop)
|
|
120
|
+
- Background: \`bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500\`
|
|
121
|
+
- Card: \`rounded-3xl shadow-2xl backdrop-blur-xl p-8 bg-white/10\`
|
|
122
|
+
- Button: Pill-shaped glowing cyan bubble with rocket emoji \`🚀\`
|
|
123
|
+
- Heading: *"Unleash the Power of Next-Gen AI"*
|
|
124
|
+
|
|
125
|
+
## The ChillAgent Standard (Accepted High-Craft)
|
|
126
|
+
- Background: Deep obsidian carbon \`#09090B\`
|
|
127
|
+
- Card: Monochromatic surface \`#111114\` with 1px hairline border \`#26262E\` and 8px radius
|
|
128
|
+
- Numbers: Tabular monospace font \`JetBrains Mono\`
|
|
129
|
+
- Accent: Single semantic indicator (Emerald \`#10B981\` for active status)
|
|
130
|
+
- Heading: Direct domain statement: *"Deterministic time-travel debugger for AI agents."*
|
|
131
|
+
`
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
file: 'examples/anti-slop-copy.md',
|
|
135
|
+
content: `# Anti-Slop Copywriting Transformation
|
|
136
|
+
|
|
137
|
+
## AI Slop Copy (Banned)
|
|
138
|
+
> *"Unleash the revolutionary power of our cutting-edge AI debugger! Seamlessly supercharge your development workflow with next-generation intelligence. Trusted by 10,000+ happy developers across the globe! ✨"*
|
|
139
|
+
|
|
140
|
+
## ChillAgent Clean Copy (Approved)
|
|
141
|
+
> *"ChillRewind is a zero-dependency local proxy running on \`node:sqlite\` in WAL mode. It prunes stale tool outputs over 1.5KB and halts agent runs when the same tool signature repeats 3 consecutive times."*
|
|
142
|
+
`
|
|
143
|
+
}
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
for (const ex of examples) {
|
|
147
|
+
fs.writeFileSync(ex.file, ex.content.trim() + '\n', 'utf8');
|
|
148
|
+
console.log('Saved', ex.file);
|
|
149
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// scripts/generate-plugin-doors.mjs
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const manifest = {
|
|
5
|
+
name: "chill-agent",
|
|
6
|
+
displayName: "ChillAgent (Anti-Slop 2.0 + Ponytail Senior Dev)",
|
|
7
|
+
version: "1.2.0",
|
|
8
|
+
description: "Master Negative Filter, Anti-Slop, and Pragmatic Senior Dev Standard. Combines ruthless YAGNI minimalism with Linear-grade visual engineering.",
|
|
9
|
+
author: {
|
|
10
|
+
name: "sann"
|
|
11
|
+
},
|
|
12
|
+
homepage: "https://github.com/sann/chill-agent",
|
|
13
|
+
repository: "https://github.com/sann/chill-agent",
|
|
14
|
+
license: "MIT",
|
|
15
|
+
keywords: [
|
|
16
|
+
"anti-slop",
|
|
17
|
+
"ponytail",
|
|
18
|
+
"design-rules",
|
|
19
|
+
"minimalism",
|
|
20
|
+
"agent-skills",
|
|
21
|
+
"yagni",
|
|
22
|
+
"ui",
|
|
23
|
+
"accessibility"
|
|
24
|
+
],
|
|
25
|
+
skills: [
|
|
26
|
+
"./skills/chill-slop/",
|
|
27
|
+
"./skills/chill-slop-ui/",
|
|
28
|
+
"./skills/chill-slop-code/",
|
|
29
|
+
"./skills/chill-slop-copywriting/",
|
|
30
|
+
"./skills/chill-slop-human/",
|
|
31
|
+
"./skills/chill-slop-mobile/",
|
|
32
|
+
"./skills/chill-audit/",
|
|
33
|
+
"./skills/chill-review/",
|
|
34
|
+
"./skills/chill-debt/",
|
|
35
|
+
"./skills/chill-gain/",
|
|
36
|
+
"./skills/chill-help/"
|
|
37
|
+
]
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// 1. Antigravity plugin.json
|
|
41
|
+
fs.writeFileSync('plugin.json', JSON.stringify({ name: "chill-agent" }, null, 2), 'utf8');
|
|
42
|
+
|
|
43
|
+
// 2. Universal plugin.yaml
|
|
44
|
+
fs.writeFileSync('plugin.yaml', `name: chill-agent
|
|
45
|
+
version: 1.2.0
|
|
46
|
+
description: ChillAgent Anti-Slop 2.0 & Pragmatic Senior Dev Standard
|
|
47
|
+
skills:
|
|
48
|
+
- ./skills/chill-slop/
|
|
49
|
+
- ./skills/chill-slop-ui/
|
|
50
|
+
- ./skills/chill-slop-code/
|
|
51
|
+
- ./skills/chill-slop-copywriting/
|
|
52
|
+
- ./skills/chill-slop-human/
|
|
53
|
+
- ./skills/chill-slop-mobile/
|
|
54
|
+
- ./skills/chill-audit/
|
|
55
|
+
- ./skills/chill-review/
|
|
56
|
+
`, 'utf8');
|
|
57
|
+
|
|
58
|
+
// 3. gemini-extension.json
|
|
59
|
+
fs.writeFileSync('gemini-extension.json', JSON.stringify({
|
|
60
|
+
name: "chill-agent",
|
|
61
|
+
version: "1.2.0",
|
|
62
|
+
description: "ChillAgent Anti-Slop 2.0 & Pragmatic Senior Dev Mode. Forces smallest working diff, YAGNI, standard library first, zero AI slop.",
|
|
63
|
+
contextFileName: "AGENTS.md"
|
|
64
|
+
}, null, 2), 'utf8');
|
|
65
|
+
|
|
66
|
+
// 4. opencode.json
|
|
67
|
+
fs.writeFileSync('opencode.json', JSON.stringify({
|
|
68
|
+
name: "chill-agent",
|
|
69
|
+
version: "1.2.0",
|
|
70
|
+
description: "ChillAgent Anti-Slop & Ponytail Engine for OpenCode",
|
|
71
|
+
skills: manifest.skills
|
|
72
|
+
}, null, 2), 'utf8');
|
|
73
|
+
|
|
74
|
+
// 5. .claude-plugin/plugin.json
|
|
75
|
+
fs.writeFileSync('.claude-plugin/plugin.json', JSON.stringify(manifest, null, 2), 'utf8');
|
|
76
|
+
|
|
77
|
+
// 6. .cursor-plugin/plugin.json
|
|
78
|
+
fs.writeFileSync('.cursor-plugin/plugin.json', JSON.stringify(manifest, null, 2), 'utf8');
|
|
79
|
+
|
|
80
|
+
// 7. .cursor/rules/chill-agent.mdc
|
|
81
|
+
fs.writeFileSync('.cursor/rules/chill-agent.mdc', `---
|
|
82
|
+
description: ChillAgent Anti-Slop 2.0 & Pragmatic Senior Dev Standard
|
|
83
|
+
globs: *
|
|
84
|
+
---
|
|
85
|
+
` + fs.readFileSync('skills/chill-slop/SKILL.md', 'utf8'), 'utf8');
|
|
86
|
+
|
|
87
|
+
// 8. .windsurf/rules/chill-agent.md
|
|
88
|
+
fs.writeFileSync('.windsurf/rules/chill-agent.md', fs.readFileSync('skills/chill-slop/SKILL.md', 'utf8'), 'utf8');
|
|
89
|
+
|
|
90
|
+
// 9. .cline-plugin/plugin.json & .clinerules
|
|
91
|
+
fs.writeFileSync('.cline-plugin/plugin.json', JSON.stringify(manifest, null, 2), 'utf8');
|
|
92
|
+
fs.writeFileSync('.clinerules/chill-agent', fs.readFileSync('skills/chill-slop/SKILL.md', 'utf8'), 'utf8');
|
|
93
|
+
|
|
94
|
+
// 10. .codex-plugin/plugin.json & .agents/plugins/marketplace.json
|
|
95
|
+
fs.writeFileSync('.codex-plugin/plugin.json', JSON.stringify(manifest, null, 2), 'utf8');
|
|
96
|
+
fs.writeFileSync('.agents/plugins/marketplace.json', JSON.stringify({
|
|
97
|
+
plugins: [manifest]
|
|
98
|
+
}, null, 2), 'utf8');
|
|
99
|
+
|
|
100
|
+
// 11. .kimi-plugin, .omp-plugin, .devin-plugin, .grok-plugin, .qoder-plugin
|
|
101
|
+
fs.writeFileSync('.kimi-plugin/plugin.json', JSON.stringify(manifest, null, 2), 'utf8');
|
|
102
|
+
fs.writeFileSync('.omp-plugin/plugin.json', JSON.stringify(manifest, null, 2), 'utf8');
|
|
103
|
+
fs.writeFileSync('.devin-plugin/plugin.json', JSON.stringify(manifest, null, 2), 'utf8');
|
|
104
|
+
fs.writeFileSync('.grok-plugin/plugin.json', JSON.stringify(manifest, null, 2), 'utf8');
|
|
105
|
+
fs.writeFileSync('.qoder-plugin/plugin.json', JSON.stringify(manifest, null, 2), 'utf8');
|
|
106
|
+
fs.writeFileSync('.qoder/rules/chill-agent.md', fs.readFileSync('skills/chill-slop/SKILL.md', 'utf8'), 'utf8');
|
|
107
|
+
|
|
108
|
+
console.log('Successfully generated all multi-agent plugin doors and manifests!');
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Publish the generated OpenClaw skills (.openclaw/skills/) to ClawHub.
|
|
3
|
+
//
|
|
4
|
+
// ClawHub does not sync from GitHub: each skill is pushed explicitly with the
|
|
5
|
+
// clawhub CLI and carries its own version. This publishes every generated skill
|
|
6
|
+
// in one pass, versioned from the repo's package.json so ClawHub tracks the repo
|
|
7
|
+
// instead of drifting (the same drift that hit the plugin manifests in #260).
|
|
8
|
+
//
|
|
9
|
+
// Prereqs:
|
|
10
|
+
// - `clawhub login` once (registry auth persists)
|
|
11
|
+
// - skills must be current: run `node scripts/build-openclaw-skills.js` first
|
|
12
|
+
// if you changed a skill (CI fails if the committed copies are stale)
|
|
13
|
+
//
|
|
14
|
+
// Usage:
|
|
15
|
+
// node scripts/publish-openclaw-skills.js # publish all as latest
|
|
16
|
+
// node scripts/publish-openclaw-skills.js --dry-run # preview, upload nothing
|
|
17
|
+
// (any extra args are passed through to `clawhub skill publish`)
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const { spawnSync } = require('child_process');
|
|
22
|
+
|
|
23
|
+
const root = path.join(__dirname, '..');
|
|
24
|
+
const skillsDir = path.join(root, '.openclaw', 'skills');
|
|
25
|
+
|
|
26
|
+
const version = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version;
|
|
27
|
+
|
|
28
|
+
// Every generated skill dir with a SKILL.md is publishable. Reading the dir
|
|
29
|
+
// (instead of a hardcoded list) covers whatever build-openclaw-skills emits,
|
|
30
|
+
// with nothing to keep in sync.
|
|
31
|
+
const slugs = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
32
|
+
.filter((e) => e.isDirectory() && fs.existsSync(path.join(skillsDir, e.name, 'SKILL.md')))
|
|
33
|
+
.map((e) => e.name)
|
|
34
|
+
.sort();
|
|
35
|
+
|
|
36
|
+
if (slugs.length === 0) {
|
|
37
|
+
console.error(`No skills under ${path.relative(root, skillsDir)}; run build-openclaw-skills.js first.`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// "ponytail-review" -> "Ponytail Review"
|
|
42
|
+
const displayName = (slug) =>
|
|
43
|
+
slug.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
44
|
+
|
|
45
|
+
// Minimal quoting that satisfies both POSIX sh and cmd.exe: only display names
|
|
46
|
+
// (which contain a space) need wrapping; slugs, versions, paths, and flags don't.
|
|
47
|
+
const quote = (a) => (/[^\w./-]/.test(a) ? `"${a}"` : a);
|
|
48
|
+
|
|
49
|
+
const passthrough = process.argv.slice(2);
|
|
50
|
+
const extra = passthrough.length ? ` (${passthrough.join(' ')})` : '';
|
|
51
|
+
console.log(`Publishing ${slugs.length} skills to ClawHub at version ${version}${extra}:`);
|
|
52
|
+
|
|
53
|
+
for (const slug of slugs) {
|
|
54
|
+
const args = [
|
|
55
|
+
'clawhub', 'skill', 'publish', `.openclaw/skills/${slug}`,
|
|
56
|
+
'--slug', slug,
|
|
57
|
+
'--name', displayName(slug),
|
|
58
|
+
'--version', version,
|
|
59
|
+
'--tags', 'latest',
|
|
60
|
+
...passthrough,
|
|
61
|
+
];
|
|
62
|
+
const cmdline = args.map(quote).join(' ');
|
|
63
|
+
console.log(`\n$ ${cmdline}`);
|
|
64
|
+
const res = spawnSync(cmdline, { stdio: 'inherit', cwd: root, shell: true });
|
|
65
|
+
if (res.status !== 0) {
|
|
66
|
+
console.error(
|
|
67
|
+
`\nPublish failed for "${slug}" (exit ${res.status}). ` +
|
|
68
|
+
`Check that the clawhub CLI is installed and you have run \`clawhub login\`, then re-run. ` +
|
|
69
|
+
`Skills already published in this run are unaffected.`,
|
|
70
|
+
);
|
|
71
|
+
process.exit(res.status || 1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`\nDone. Published ${slugs.length} skills at ${version}.`);
|