cssprobe-cli 0.1.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.md +77 -0
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/bin/cssprobe-cli.js +6 -0
- package/dist/browser/launcher.d.ts +25 -0
- package/dist/browser/launcher.js +213 -0
- package/dist/cli/command.d.ts +27 -0
- package/dist/cli/command.js +84 -0
- package/dist/cli/commands.d.ts +42 -0
- package/dist/cli/commands.js +166 -0
- package/dist/cli/minimist.d.ts +9 -0
- package/dist/cli/minimist.js +120 -0
- package/dist/cli/output.d.ts +26 -0
- package/dist/cli/output.js +80 -0
- package/dist/cli/program.d.ts +1 -0
- package/dist/cli/program.js +381 -0
- package/dist/collector-bundle.js +342 -0
- package/dist/config/config.d.ts +23 -0
- package/dist/config/config.js +128 -0
- package/dist/config/helpGenerator.d.ts +10 -0
- package/dist/config/helpGenerator.js +156 -0
- package/dist/engine/analyzer.d.ts +2 -0
- package/dist/engine/analyzer.js +385 -0
- package/dist/engine/collector.d.ts +3 -0
- package/dist/engine/collector.js +291 -0
- package/dist/engine/renderer.d.ts +3 -0
- package/dist/engine/renderer.js +159 -0
- package/dist/engine/types.d.ts +130 -0
- package/dist/engine/types.js +4 -0
- package/dist/help.json +107 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/installer/index.d.ts +3 -0
- package/dist/installer/index.js +12 -0
- package/dist/installer/skill-template.d.ts +9 -0
- package/dist/installer/skill-template.js +274 -0
- package/dist/installer/targets/registry.d.ts +9 -0
- package/dist/installer/targets/registry.js +121 -0
- package/dist/installer/targets/shared.d.ts +9 -0
- package/dist/installer/targets/shared.js +112 -0
- package/dist/installer/targets/types.d.ts +22 -0
- package/dist/installer/targets/types.js +3 -0
- package/docs/guide/installation.md +230 -0
- package/package.json +51 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* cssprobe-cli-cli Agent Skill templates.
|
|
4
|
+
*
|
|
5
|
+
* Installed into agent-specific skill directories via `cssprobe-cli-cli skill-install`.
|
|
6
|
+
* Teaches agents how to use cssprobe-cli-cli for runtime CSS inspection — layout,
|
|
7
|
+
* scroll, overflow, colors, backgrounds, fonts — with confidence levels.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.buildSkillMd = buildSkillMd;
|
|
11
|
+
exports.buildPitfallsMd = buildPitfallsMd;
|
|
12
|
+
const FRONTMATTER_DELIM = '---';
|
|
13
|
+
const BT = '`'; // backtick
|
|
14
|
+
const BT3 = '```'; // triple backtick
|
|
15
|
+
function buildSkillMd() {
|
|
16
|
+
return [
|
|
17
|
+
FRONTMATTER_DELIM,
|
|
18
|
+
'name: cssprobe-cli-cli',
|
|
19
|
+
'description: >',
|
|
20
|
+
' Runtime CSS probe for layout/scroll/overflow inspection. Use cssprobe-cli-cli to',
|
|
21
|
+
' diagnose CSS issues in live browsers — height chains, scroll containers,',
|
|
22
|
+
' overflow, flex constraints, containing block hijacks. Produces structured',
|
|
23
|
+
' reports with DEFINITE/INDEFINITE/UNVERIFIABLE confidence levels.',
|
|
24
|
+
'metadata:',
|
|
25
|
+
' requires:',
|
|
26
|
+
' bins: ["cssprobe-cli-cli", "playwright"]',
|
|
27
|
+
FRONTMATTER_DELIM,
|
|
28
|
+
'',
|
|
29
|
+
'# cssprobe-cli-cli -- Runtime CSS Probe Skill',
|
|
30
|
+
'',
|
|
31
|
+
'Use cssprobe-cli-cli to inspect the runtime CSS of any web page. It launches a real',
|
|
32
|
+
'browser, captures computed styles and DOM structure, and produces a structured',
|
|
33
|
+
'report with confidence levels.',
|
|
34
|
+
'',
|
|
35
|
+
'## Prerequisite: Browser Installation',
|
|
36
|
+
'',
|
|
37
|
+
'Before first use, install Chromium:',
|
|
38
|
+
'',
|
|
39
|
+
BT3 + 'bash',
|
|
40
|
+
'npx playwright install chromium',
|
|
41
|
+
BT3,
|
|
42
|
+
'',
|
|
43
|
+
'Verify: ' + BT + 'cssprobe-cli-cli inspect https://example.com "body" --json | head -5' + BT,
|
|
44
|
+
'',
|
|
45
|
+
'## Command Discovery',
|
|
46
|
+
'',
|
|
47
|
+
'Do NOT memorize all commands. Use:',
|
|
48
|
+
'',
|
|
49
|
+
BT3 + 'bash',
|
|
50
|
+
'cssprobe-cli-cli --help # all commands grouped by category',
|
|
51
|
+
'cssprobe-cli-cli inspect --help # specific command: args, options, description',
|
|
52
|
+
'cssprobe-cli-cli state-import --help # cookie import command',
|
|
53
|
+
BT3,
|
|
54
|
+
'',
|
|
55
|
+
'## Core Workflow: Inspect a Page',
|
|
56
|
+
'',
|
|
57
|
+
BT3 + 'bash',
|
|
58
|
+
'# With explicit selector',
|
|
59
|
+
'cssprobe-cli-cli inspect https://example.com ".main-content"',
|
|
60
|
+
'',
|
|
61
|
+
'# Auto-detect root element (picks modal/dialog/scroll-container)',
|
|
62
|
+
'cssprobe-cli inspect https://example.com',
|
|
63
|
+
'',
|
|
64
|
+
'# Local HTML file',
|
|
65
|
+
'cssprobe-cli inspect ./test.html ".container"',
|
|
66
|
+
BT3,
|
|
67
|
+
'',
|
|
68
|
+
'## Login-Protected Pages',
|
|
69
|
+
'',
|
|
70
|
+
'Many pages require authentication. Three approaches:',
|
|
71
|
+
'',
|
|
72
|
+
'### Option A: Import cookies from browser export',
|
|
73
|
+
'',
|
|
74
|
+
BT3 + 'bash',
|
|
75
|
+
'# 1. Export cookies from browser (Netscape format)',
|
|
76
|
+
'# 2. Import into cssprobe-cli',
|
|
77
|
+
'cssprobe-cli state-import cookies.txt --out ~/.cssprobe-cli/states/mysite.json',
|
|
78
|
+
'',
|
|
79
|
+
'# 3. Use the state file',
|
|
80
|
+
'cssprobe-cli inspect https://mysite.com/page ".target" --state ~/.cssprobe-cli/states/mysite.json',
|
|
81
|
+
BT3,
|
|
82
|
+
'',
|
|
83
|
+
'### Option B: Interactive login',
|
|
84
|
+
'',
|
|
85
|
+
BT3 + 'bash',
|
|
86
|
+
'cssprobe-cli login https://mysite.com',
|
|
87
|
+
'# Browser opens -> complete login manually -> press Enter',
|
|
88
|
+
'# State saved to ~/.cssprobe-cli/states/mysite.com.json',
|
|
89
|
+
'',
|
|
90
|
+
'cssprobe-cli inspect https://mysite.com/page ".target" --state ~/.cssprobe-cli/states/mysite.com.json',
|
|
91
|
+
BT3,
|
|
92
|
+
'',
|
|
93
|
+
'### Option C: Merge cookies into existing state',
|
|
94
|
+
'',
|
|
95
|
+
BT3 + 'bash',
|
|
96
|
+
'cssprobe-cli state-import new-cookies.txt --merge existing.json --out merged.json',
|
|
97
|
+
BT3,
|
|
98
|
+
'',
|
|
99
|
+
'## Output Modes',
|
|
100
|
+
'',
|
|
101
|
+
'Always pass ' + BT + '--json' + BT + ' when consuming output programmatically:',
|
|
102
|
+
'',
|
|
103
|
+
BT3 + 'bash',
|
|
104
|
+
'cssprobe-cli inspect https://example.com ".content" --json',
|
|
105
|
+
'cssprobe-cli --json inspect https://example.com ".content" | jq ' + BT + "'.findings[] | {id, confidence, message}'" + BT,
|
|
106
|
+
BT3,
|
|
107
|
+
'',
|
|
108
|
+
'Without ' + BT + '--json' + BT + ', output is a Markdown report with ancestor chain,',
|
|
109
|
+
'DOM tree, and findings with confidence labels.',
|
|
110
|
+
'',
|
|
111
|
+
'## Confidence Model',
|
|
112
|
+
'',
|
|
113
|
+
'Every finding carries a confidence level:',
|
|
114
|
+
'',
|
|
115
|
+
'| Level | Meaning |',
|
|
116
|
+
'|-------|---------|',
|
|
117
|
+
'| **DEFINITE** | Based on computed values (facts from getComputedStyle) or accessible declared values |',
|
|
118
|
+
'| **INDEFINITE** | Declared value uses ' + BT + '%' + BT + ' -- resolves at runtime |',
|
|
119
|
+
'| **UNVERIFIABLE** | Declared value missing or from blocked cross-origin stylesheet |',
|
|
120
|
+
'',
|
|
121
|
+
'The report header shows: ' + BT + 'confidence: DEFINITE 8 | INDEFINITE 0 | UNVERIFIABLE 1' + BT,
|
|
122
|
+
'',
|
|
123
|
+
'## inspect Options',
|
|
124
|
+
'',
|
|
125
|
+
BT3 + 'bash',
|
|
126
|
+
'cssprobe-cli inspect <url> [selector] [options]',
|
|
127
|
+
'',
|
|
128
|
+
'Arguments:',
|
|
129
|
+
' <url> URL, file:// path, or local HTML file',
|
|
130
|
+
' [selector] CSS selector for root (auto-detected if omitted)',
|
|
131
|
+
'',
|
|
132
|
+
'Options:',
|
|
133
|
+
' --json Output structured JSON',
|
|
134
|
+
' --headed Show browser window',
|
|
135
|
+
' --browser <engine> chromium|firefox|webkit (default chromium)',
|
|
136
|
+
' --zoom Run 1x/0.5x viewport diagnosis',
|
|
137
|
+
' --depth <n> DOM tree depth (default 6)',
|
|
138
|
+
' --max-nodes <n> Node count cap (default 60)',
|
|
139
|
+
' --up-to <tag> Ancestor stop tag (default html)',
|
|
140
|
+
' --state <file> Load saved state (cookies + localStorage)',
|
|
141
|
+
BT3,
|
|
142
|
+
'',
|
|
143
|
+
'## state-import: Netscape Cookie Format',
|
|
144
|
+
'',
|
|
145
|
+
BT3 + 'bash',
|
|
146
|
+
'# From file',
|
|
147
|
+
'cssprobe-cli state-import cookies.txt --out mystate.json',
|
|
148
|
+
'',
|
|
149
|
+
'# From stdin',
|
|
150
|
+
'cat cookies.txt | cssprobe-cli state-import --out mystate.json',
|
|
151
|
+
'',
|
|
152
|
+
'# Merge into existing state',
|
|
153
|
+
'cssprobe-cli state-import new-cookies.txt --merge existing.json --out merged.json',
|
|
154
|
+
BT3,
|
|
155
|
+
'',
|
|
156
|
+
'Netscape format (tab-separated):',
|
|
157
|
+
'',
|
|
158
|
+
BT3,
|
|
159
|
+
'.example.com\tTRUE\t/\tTRUE\t1813025057\tsession_id\tabc123',
|
|
160
|
+
'.example.com\tTRUE\t/\tFALSE\t-1\tlang\tzh-CN',
|
|
161
|
+
BT3,
|
|
162
|
+
'',
|
|
163
|
+
'## High-Frequency Workflows',
|
|
164
|
+
'',
|
|
165
|
+
'### 1. Diagnose layout/scroll issues',
|
|
166
|
+
'',
|
|
167
|
+
BT3 + 'bash',
|
|
168
|
+
'# Inspect a specific component',
|
|
169
|
+
'cssprobe-cli inspect https://app.example.com/page ".modal-body"',
|
|
170
|
+
'',
|
|
171
|
+
'# Get structured data for programmatic analysis',
|
|
172
|
+
'cssprobe-cli --json inspect https://app.example.com/page ".modal-body" | jq ' + BT + "'.findings[]'" + BT,
|
|
173
|
+
BT3,
|
|
174
|
+
'',
|
|
175
|
+
'### 2. Check if a page has overflow issues',
|
|
176
|
+
'',
|
|
177
|
+
BT3 + 'bash',
|
|
178
|
+
'# Auto-detect and analyze',
|
|
179
|
+
'cssprobe-cli inspect https://example.com',
|
|
180
|
+
'',
|
|
181
|
+
'# Look for findings with level "warning" or "error"',
|
|
182
|
+
'cssprobe-cli --json inspect https://example.com | jq ' + BT + "'.findings[] | select(.level != \"info\")'" + BT,
|
|
183
|
+
BT3,
|
|
184
|
+
'',
|
|
185
|
+
'### 3. Inspect with authentication',
|
|
186
|
+
'',
|
|
187
|
+
BT3 + 'bash',
|
|
188
|
+
'# Import cookies first',
|
|
189
|
+
'cssprobe-cli state-import exported-cookies.txt --out ~/.cssprobe-cli/states/app.json',
|
|
190
|
+
'',
|
|
191
|
+
'# Then inspect with state',
|
|
192
|
+
'cssprobe-cli inspect https://app.example.com/dashboard ".main-panel" --state ~/.cssprobe-cli/states/app.json',
|
|
193
|
+
BT3,
|
|
194
|
+
'',
|
|
195
|
+
'### 4. Compare layouts at different viewports',
|
|
196
|
+
'',
|
|
197
|
+
BT3 + 'bash',
|
|
198
|
+
'# Use --zoom to compare 1x vs 0.5x viewport',
|
|
199
|
+
'cssprobe-cli inspect https://example.com ".content" --zoom',
|
|
200
|
+
BT3,
|
|
201
|
+
'',
|
|
202
|
+
'## Anti-patterns',
|
|
203
|
+
'',
|
|
204
|
+
'- Always use ' + BT + '--json' + BT + ' when consuming output programmatically.',
|
|
205
|
+
'- ' + BT + 'viewport-overflow' + BT + ' flags on elements below the fold are normal for scrolling pages -- not bugs.',
|
|
206
|
+
'- Cross-origin stylesheet blocking is expected. Declared values from blocked sheets are',
|
|
207
|
+
' marked UNVERIFIABLE. Computed values from getComputedStyle() are still accurate.',
|
|
208
|
+
'- ' + BT + '--auto-root' + BT + ' picks the first modal/dialog/scroll-container -- for specific elements,',
|
|
209
|
+
' provide an explicit selector.',
|
|
210
|
+
'- Login state files contain cookies -- treat them as secrets.',
|
|
211
|
+
'',
|
|
212
|
+
].join('\n');
|
|
213
|
+
}
|
|
214
|
+
function buildPitfallsMd() {
|
|
215
|
+
return [
|
|
216
|
+
'# cssprobe-cli Pitfalls & Boundaries',
|
|
217
|
+
'',
|
|
218
|
+
'## Browser Installation',
|
|
219
|
+
'',
|
|
220
|
+
'- cssprobe-cli uses ' + BT + 'playwright-core' + BT + ' (not ' + BT + 'playwright' + BT + '). Browsers must be installed separately:',
|
|
221
|
+
' ' + BT + 'npx playwright install chromium' + BT,
|
|
222
|
+
'- On macOS, Chromium is cached at ' + BT + '~/Library/Caches/ms-playwright/chromium-*' + BT + '.',
|
|
223
|
+
'- cssprobe-cli auto-discovers existing Chromium installations in the Playwright cache.',
|
|
224
|
+
'',
|
|
225
|
+
'## Cross-origin Stylesheets',
|
|
226
|
+
'',
|
|
227
|
+
'- Browser security blocks ' + BT + 'cssRules' + BT + ' access on cross-origin stylesheets (CDN, third-party CSS).',
|
|
228
|
+
'- Declared values from blocked sheets are marked ' + BT + 'UNVERIFIABLE' + BT + '.',
|
|
229
|
+
'- Computed values from ' + BT + 'getComputedStyle()' + BT + ' are always accurate (not affected by CORS).',
|
|
230
|
+
'- The report header shows: ' + BT + '⚠ N stylesheet(s) blocked by browser security' + BT + '.',
|
|
231
|
+
'',
|
|
232
|
+
'## Selector Not Found',
|
|
233
|
+
'',
|
|
234
|
+
'- If ' + BT + 'cssprobe-cli inspect <url> <selector>' + BT + ' returns ' + BT + '未找到 <selector>' + BT + ',',
|
|
235
|
+
' the element doesn\'t exist in the DOM at page load.',
|
|
236
|
+
'- Use ' + BT + '--auto-root' + BT + ' (omit selector) to let cssprobe-cli pick a candidate.',
|
|
237
|
+
'- For SPAs that load content dynamically, use ' + BT + '--headed' + BT + ' to see what\'s on the page.',
|
|
238
|
+
'',
|
|
239
|
+
'## Login State',
|
|
240
|
+
'',
|
|
241
|
+
'- State files contain cookies and localStorage -- treat them as secrets.',
|
|
242
|
+
'- Cookies may expire -- re-import or re-login if inspection returns unexpected results.',
|
|
243
|
+
'- ' + BT + 'state-import' + BT + ' expects Netscape cookie format (tab-separated).',
|
|
244
|
+
' Browser extensions like "EditThisCookie" or "Cookie Editor" can export in this format.',
|
|
245
|
+
'',
|
|
246
|
+
'## Viewport Overflow Flags',
|
|
247
|
+
'',
|
|
248
|
+
'- ' + BT + '⚠viewport-overflow' + BT + ' means the element\'s rect extends beyond the viewport.',
|
|
249
|
+
'- For elements below the fold on scrolling pages, this is expected -- not a bug.',
|
|
250
|
+
'- Focus on ' + BT + '⚠parent-overflow' + BT + ' and ' + BT + '⚠overflow(N>M)but-overflow=visible' + BT + ' for real issues.',
|
|
251
|
+
'',
|
|
252
|
+
'## Confidence Levels',
|
|
253
|
+
'',
|
|
254
|
+
'- ' + BT + 'DEFINITE' + BT + ': based on computed values (facts) or accessible declared values.',
|
|
255
|
+
'- ' + BT + 'INDEFINITE' + BT + ': declared value uses ' + BT + '%' + BT + ' -- resolves at runtime.',
|
|
256
|
+
'- ' + BT + 'UNVERIFIABLE' + BT + ': declared value missing or from blocked stylesheet.',
|
|
257
|
+
'- When all ancestors have ' + BT + 'UNVERIFIABLE' + BT + ' height declarations, the anchor check',
|
|
258
|
+
' reports ' + BT + 'UNVERIFIABLE' + BT + ' -- this means we can\'t prove whether an anchor exists.',
|
|
259
|
+
'',
|
|
260
|
+
'## JSON Output',
|
|
261
|
+
'',
|
|
262
|
+
'- Pass ' + BT + '--json' + BT + ' BEFORE the command name: ' + BT + 'cssprobe-cli --json inspect <url>' + BT + '.',
|
|
263
|
+
'- In JSON mode, errors are ' + BT + '{ "isError": true, "error": "message" }' + BT + ' and the',
|
|
264
|
+
' process exits with code 1. Check for ' + BT + 'isError' + BT + ' before parsing results.',
|
|
265
|
+
'',
|
|
266
|
+
'## Config',
|
|
267
|
+
'',
|
|
268
|
+
'- Config file: ' + BT + '~/.cssprobe-clirc' + BT + ' (JSON). Use ' + BT + 'config-path' + BT + ' to confirm.',
|
|
269
|
+
'- Priority: CLI flags > env vars (' + BT + 'CSSPROBE_BROWSER' + BT + ', ' + BT + 'CSSPROBE_DEPTH' + BT + ',',
|
|
270
|
+
' ' + BT + 'CSSPROBE_HEADED' + BT + ') > config file.',
|
|
271
|
+
'',
|
|
272
|
+
].join('\n');
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=skill-template.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AgentTarget, Location, TargetId } from './types';
|
|
2
|
+
export declare const ALL_TARGETS: readonly AgentTarget[];
|
|
3
|
+
export declare function getTarget(id: string): AgentTarget | undefined;
|
|
4
|
+
export declare function listTargetIds(): TargetId[];
|
|
5
|
+
/**
|
|
6
|
+
* Resolve `--target` flag to a list of AgentTarget.
|
|
7
|
+
* Accepts: 'auto' (detect installed), 'all', 'none', or comma-separated ids.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveTargetFlag(value: string, loc: Location): AgentTarget[];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ALL_TARGETS = void 0;
|
|
37
|
+
exports.getTarget = getTarget;
|
|
38
|
+
exports.listTargetIds = listTargetIds;
|
|
39
|
+
exports.resolveTargetFlag = resolveTargetFlag;
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const shared_1 = require("./shared");
|
|
44
|
+
function makeTarget(id, displayName, globalSkillSubdir, localSkillSubdir) {
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
displayName,
|
|
48
|
+
skillDir(loc) {
|
|
49
|
+
if (loc === 'global')
|
|
50
|
+
return path.join(os.homedir(), ...globalSkillSubdir.split('/'));
|
|
51
|
+
return path.join(process.cwd(), ...(localSkillSubdir || '.agents/skills').split('/'));
|
|
52
|
+
},
|
|
53
|
+
detect(loc) {
|
|
54
|
+
const dir = this.skillDir(loc);
|
|
55
|
+
if (!dir)
|
|
56
|
+
return { installed: false, alreadyConfigured: false };
|
|
57
|
+
const skillMd = path.join(dir, 'cssprobe-cli', 'SKILL.md');
|
|
58
|
+
return { installed: fs.existsSync(dir), alreadyConfigured: fs.existsSync(skillMd) };
|
|
59
|
+
},
|
|
60
|
+
install(loc) {
|
|
61
|
+
const dir = this.skillDir(loc);
|
|
62
|
+
if (!dir)
|
|
63
|
+
return { files: [] };
|
|
64
|
+
return { files: [(0, shared_1.writeSkill)(dir)] };
|
|
65
|
+
},
|
|
66
|
+
uninstall(loc) {
|
|
67
|
+
const dir = this.skillDir(loc);
|
|
68
|
+
if (!dir)
|
|
69
|
+
return { files: [] };
|
|
70
|
+
return { files: [(0, shared_1.removeSkill)(dir)] };
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const claudeTarget = makeTarget('claude', 'Claude Code', '.claude/skills', '.claude/skills');
|
|
75
|
+
const opencodeTarget = makeTarget('opencode', 'opencode', '.agents/skills');
|
|
76
|
+
const cursorTarget = makeTarget('cursor', 'Cursor', '.agents/skills');
|
|
77
|
+
const codexTarget = makeTarget('codex', 'Codex', '.codex/skills');
|
|
78
|
+
const hermesTarget = makeTarget('hermes', 'Hermes', '.hermes/skills');
|
|
79
|
+
const geminiTarget = makeTarget('gemini', 'Gemini', '.gemini/skills');
|
|
80
|
+
exports.ALL_TARGETS = Object.freeze([
|
|
81
|
+
claudeTarget, opencodeTarget, cursorTarget, codexTarget, hermesTarget, geminiTarget,
|
|
82
|
+
]);
|
|
83
|
+
function getTarget(id) {
|
|
84
|
+
return exports.ALL_TARGETS.find((t) => t.id === id);
|
|
85
|
+
}
|
|
86
|
+
function listTargetIds() {
|
|
87
|
+
return exports.ALL_TARGETS.map((t) => t.id);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve `--target` flag to a list of AgentTarget.
|
|
91
|
+
* Accepts: 'auto' (detect installed), 'all', 'none', or comma-separated ids.
|
|
92
|
+
*/
|
|
93
|
+
function resolveTargetFlag(value, loc) {
|
|
94
|
+
if (value === 'none')
|
|
95
|
+
return [];
|
|
96
|
+
if (value === 'all')
|
|
97
|
+
return [...exports.ALL_TARGETS];
|
|
98
|
+
if (value === 'auto') {
|
|
99
|
+
const detected = exports.ALL_TARGETS.filter((t) => t.detect(loc).installed);
|
|
100
|
+
if (detected.length > 0)
|
|
101
|
+
return detected;
|
|
102
|
+
const fallback = getTarget('claude');
|
|
103
|
+
return fallback ? [fallback] : [];
|
|
104
|
+
}
|
|
105
|
+
const ids = value.split(',').map((s) => s.trim()).filter(Boolean);
|
|
106
|
+
const resolved = [];
|
|
107
|
+
const unknown = [];
|
|
108
|
+
for (const id of ids) {
|
|
109
|
+
const t = getTarget(id);
|
|
110
|
+
if (t)
|
|
111
|
+
resolved.push(t);
|
|
112
|
+
else
|
|
113
|
+
unknown.push(id);
|
|
114
|
+
}
|
|
115
|
+
if (unknown.length > 0) {
|
|
116
|
+
const known = listTargetIds().join(', ');
|
|
117
|
+
throw new Error(`Unknown --target id(s): ${unknown.join(', ')}. Known: ${known}, plus 'auto' / 'all' / 'none'.`);
|
|
118
|
+
}
|
|
119
|
+
return resolved;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function atomicWriteFileSync(filePath: string, content: string): void;
|
|
2
|
+
export declare function writeSkill(skillDir: string): {
|
|
3
|
+
path: string;
|
|
4
|
+
action: 'created' | 'updated' | 'unchanged';
|
|
5
|
+
};
|
|
6
|
+
export declare function removeSkill(skillDir: string): {
|
|
7
|
+
path: string;
|
|
8
|
+
action: 'removed' | 'not-found' | 'kept';
|
|
9
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.atomicWriteFileSync = atomicWriteFileSync;
|
|
37
|
+
exports.writeSkill = writeSkill;
|
|
38
|
+
exports.removeSkill = removeSkill;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const skill_template_1 = require("../skill-template");
|
|
42
|
+
const SKILL_NAME = 'cssprobe-cli';
|
|
43
|
+
function atomicWriteFileSync(filePath, content) {
|
|
44
|
+
const dir = path.dirname(filePath);
|
|
45
|
+
if (!fs.existsSync(dir))
|
|
46
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
47
|
+
const tmpPath = filePath + '.tmp.' + process.pid;
|
|
48
|
+
try {
|
|
49
|
+
fs.writeFileSync(tmpPath, content);
|
|
50
|
+
fs.renameSync(tmpPath, filePath);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
try {
|
|
54
|
+
fs.unlinkSync(tmpPath);
|
|
55
|
+
}
|
|
56
|
+
catch { /* ignore */ }
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function writeSkill(skillDir) {
|
|
61
|
+
const skillRoot = path.join(skillDir, SKILL_NAME);
|
|
62
|
+
const skillMdPath = path.join(skillRoot, 'SKILL.md');
|
|
63
|
+
const refsDir = path.join(skillRoot, 'references');
|
|
64
|
+
const pitfallsPath = path.join(refsDir, 'pitfalls.md');
|
|
65
|
+
const skillMdContent = (0, skill_template_1.buildSkillMd)();
|
|
66
|
+
const pitfallsContent = (0, skill_template_1.buildPitfallsMd)();
|
|
67
|
+
const existingSkill = safeRead(skillMdPath);
|
|
68
|
+
const existingPitfalls = safeRead(pitfallsPath);
|
|
69
|
+
if (existingSkill === skillMdContent && existingPitfalls === pitfallsContent)
|
|
70
|
+
return { path: skillRoot, action: 'unchanged' };
|
|
71
|
+
const existed = fs.existsSync(skillMdPath);
|
|
72
|
+
if (!fs.existsSync(refsDir))
|
|
73
|
+
fs.mkdirSync(refsDir, { recursive: true });
|
|
74
|
+
atomicWriteFileSync(skillMdPath, skillMdContent);
|
|
75
|
+
atomicWriteFileSync(pitfallsPath, pitfallsContent);
|
|
76
|
+
return { path: skillRoot, action: existed ? 'updated' : 'created' };
|
|
77
|
+
}
|
|
78
|
+
function removeSkill(skillDir) {
|
|
79
|
+
const skillRoot = path.join(skillDir, SKILL_NAME);
|
|
80
|
+
const skillMdPath = path.join(skillRoot, 'SKILL.md');
|
|
81
|
+
if (!fs.existsSync(skillMdPath))
|
|
82
|
+
return { path: skillRoot, action: 'kept' };
|
|
83
|
+
rmRf(skillRoot);
|
|
84
|
+
return { path: skillRoot, action: fs.existsSync(skillMdPath) ? 'not-found' : 'removed' };
|
|
85
|
+
}
|
|
86
|
+
function rmRf(dir) {
|
|
87
|
+
if (!fs.existsSync(dir))
|
|
88
|
+
return;
|
|
89
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
90
|
+
const full = path.join(dir, entry.name);
|
|
91
|
+
if (entry.isDirectory())
|
|
92
|
+
rmRf(full);
|
|
93
|
+
else
|
|
94
|
+
try {
|
|
95
|
+
fs.unlinkSync(full);
|
|
96
|
+
}
|
|
97
|
+
catch { /* ignore */ }
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
fs.rmdirSync(dir);
|
|
101
|
+
}
|
|
102
|
+
catch { /* ignore */ }
|
|
103
|
+
}
|
|
104
|
+
function safeRead(filePath) {
|
|
105
|
+
try {
|
|
106
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type Location = 'global' | 'local';
|
|
2
|
+
export type TargetId = 'claude' | 'opencode' | 'cursor' | 'codex' | 'hermes' | 'gemini';
|
|
3
|
+
export interface WriteResult {
|
|
4
|
+
files: Array<{
|
|
5
|
+
path: string;
|
|
6
|
+
action: 'created' | 'updated' | 'unchanged' | 'removed' | 'not-found' | 'kept';
|
|
7
|
+
}>;
|
|
8
|
+
notes?: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface AgentTarget {
|
|
11
|
+
readonly id: TargetId;
|
|
12
|
+
readonly displayName: string;
|
|
13
|
+
/** Per-agent skill directory path (null if no skill concept). */
|
|
14
|
+
skillDir(loc: Location): string | null;
|
|
15
|
+
/** Check if this agent's skill directory already has our skill installed. */
|
|
16
|
+
detect(loc: Location): {
|
|
17
|
+
installed: boolean;
|
|
18
|
+
alreadyConfigured: boolean;
|
|
19
|
+
};
|
|
20
|
+
install(loc: Location): WriteResult;
|
|
21
|
+
uninstall(loc: Location): WriteResult;
|
|
22
|
+
}
|