servicemap 0.1.5 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/bin/servicemap.mjs +85 -114
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npx servicemap
|
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
That opens the interactive install survey.
|
|
16
|
-
It lets users choose
|
|
16
|
+
It installs to the universal `.agents/skills` folder and lets users choose additional agent-specific skill folders.
|
|
17
17
|
|
|
18
18
|
To install the bundled AI skill so `/servicemap` appears in compatible coding agents:
|
|
19
19
|
|
|
@@ -41,7 +41,6 @@ npx servicemap skills install --agent claude
|
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
Agents with known local skill folders get the real `servicemap` skill installed.
|
|
44
|
-
Other selected AI tools get setup notes under `~/.servicemap/model-guides`.
|
|
45
44
|
|
|
46
45
|
After a global install, the command is:
|
|
47
46
|
|
|
@@ -72,7 +71,7 @@ servicemap install-skill --agent codex
|
|
|
72
71
|
servicemap install-skill --agent claude
|
|
73
72
|
servicemap install-skill --agent grok
|
|
74
73
|
servicemap install-skill --agent all
|
|
75
|
-
servicemap install-skill --targets
|
|
74
|
+
servicemap install-skill --targets universal,claude,grok
|
|
76
75
|
servicemap skills install --agent codex
|
|
77
76
|
```
|
|
78
77
|
|
|
@@ -92,7 +91,7 @@ node bin/servicemap.mjs generate --root demo-project --allow-emails founder@exam
|
|
|
92
91
|
node bin/servicemap.mjs --root no-website-demo --dry-run
|
|
93
92
|
node bin/servicemap.mjs --dry-run
|
|
94
93
|
node bin/servicemap.mjs install-skill --agent all --dry-run
|
|
95
|
-
node bin/servicemap.mjs install-skill --targets
|
|
94
|
+
node bin/servicemap.mjs install-skill --targets universal,claude,grok --dry-run
|
|
96
95
|
node bin/servicemap.mjs skills install --agent claude --dry-run
|
|
97
96
|
python3 /Users/dougie/.codex/skills/.system/skill-creator/scripts/quick_validate.py servicemap
|
|
98
97
|
```
|
package/bin/servicemap.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
|
-
import { cp, mkdir, readFile, rm
|
|
4
|
+
import { cp, mkdir, readFile, rm } from 'node:fs/promises';
|
|
5
5
|
import { dirname, join, resolve } from 'node:path';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import readline from 'node:readline';
|
|
@@ -12,67 +12,63 @@ const scanScript = resolve(packageRoot, 'servicemap/scripts/scan-project.mjs');
|
|
|
12
12
|
const pageScript = resolve(packageRoot, 'servicemap/scripts/generate-page.mjs');
|
|
13
13
|
const desktopScript = resolve(packageRoot, 'servicemap/scripts/generate-desktop-app.mjs');
|
|
14
14
|
|
|
15
|
+
const universalRoot = () => resolve(homedir(), '.agents/skills');
|
|
16
|
+
|
|
17
|
+
const universalAgents = [
|
|
18
|
+
'Amp',
|
|
19
|
+
'Cline',
|
|
20
|
+
'Codex',
|
|
21
|
+
'Cursor',
|
|
22
|
+
'Gemini CLI',
|
|
23
|
+
'GitHub Copilot',
|
|
24
|
+
'OpenCode',
|
|
25
|
+
'Warp',
|
|
26
|
+
'Zed',
|
|
27
|
+
];
|
|
28
|
+
|
|
15
29
|
const targetCatalog = [
|
|
16
|
-
{ id: '
|
|
17
|
-
{ id: '
|
|
18
|
-
{ id: '
|
|
19
|
-
{ id: '
|
|
20
|
-
{ id: '
|
|
21
|
-
{ id: '
|
|
22
|
-
{ id: '
|
|
23
|
-
{ id: '
|
|
24
|
-
{ id: '
|
|
25
|
-
{ id: '
|
|
26
|
-
{ id: '
|
|
27
|
-
{ id: '
|
|
28
|
-
{ id: '
|
|
29
|
-
{ id: '
|
|
30
|
-
{ id: 'lovable', label: 'Lovable', kind: 'guide' },
|
|
31
|
-
{ id: 'bolt', label: 'Bolt', kind: 'guide' },
|
|
32
|
-
{ id: 'v0', label: 'v0', kind: 'guide' },
|
|
33
|
-
{ id: 'perplexity', label: 'Perplexity', kind: 'guide' },
|
|
34
|
-
{ id: 'mistral', label: 'Mistral', kind: 'guide' },
|
|
35
|
-
{ id: 'deepseek', label: 'DeepSeek', kind: 'guide' },
|
|
36
|
-
{ id: 'qwen', label: 'Qwen', kind: 'guide' },
|
|
37
|
-
{ id: 'meta', label: 'Meta (Llama)', kind: 'guide' },
|
|
38
|
-
{ id: 'local-models', label: 'Local models (Ollama, LM Studio)', kind: 'guide' },
|
|
39
|
-
{ id: 'openrouter', label: 'OpenRouter', kind: 'guide' },
|
|
40
|
-
{ id: 'bedrock', label: 'Amazon Bedrock', kind: 'guide' },
|
|
41
|
-
{ id: 'azure-openai', label: 'Azure OpenAI', kind: 'guide' },
|
|
42
|
-
{ id: 'poe', label: 'Poe', kind: 'guide' },
|
|
43
|
-
{ id: 'you-com', label: 'You.com', kind: 'guide' },
|
|
44
|
-
{ id: 'phind', label: 'Phind', kind: 'guide' },
|
|
45
|
-
{ id: 'sourcegraph-cody', label: 'Sourcegraph Cody', kind: 'guide' },
|
|
46
|
-
{ id: 'tabnine', label: 'Tabnine', kind: 'guide' },
|
|
47
|
-
{ id: 'codeium', label: 'Codeium', kind: 'guide' },
|
|
48
|
-
{ id: 'jetbrains-ai', label: 'JetBrains AI', kind: 'guide' },
|
|
49
|
-
{ id: 'custom', label: 'Other custom model or agent', kind: 'guide' },
|
|
30
|
+
{ id: 'claude', label: 'Claude Code', root: () => resolve(homedir(), '.claude/skills') },
|
|
31
|
+
{ id: 'grok', label: 'Grok', root: () => resolve(homedir(), '.grok/skills') },
|
|
32
|
+
{ id: 'aiderdesk', label: 'AiderDesk', root: () => resolve(homedir(), '.aider-desk/skills') },
|
|
33
|
+
{ id: 'astrbot', label: 'AstrBot', root: () => resolve(homedir(), '.astrbot/data/skills') },
|
|
34
|
+
{ id: 'autohand', label: 'Autohand Code CLI', root: () => resolve(homedir(), '.autohand/skills') },
|
|
35
|
+
{ id: 'augment', label: 'Augment', root: () => resolve(homedir(), '.augment/skills') },
|
|
36
|
+
{ id: 'ibm-bob', label: 'IBM Bob', root: () => resolve(homedir(), '.bob/skills') },
|
|
37
|
+
{ id: 'openclaw', label: 'OpenClaw', root: () => resolve(homedir(), '.openclaw/skills') },
|
|
38
|
+
{ id: 'codearts', label: 'CodeArts Agent', root: () => resolve(homedir(), '.codeartsdoer/skills') },
|
|
39
|
+
{ id: 'roo-code', label: 'Roo Code', root: () => resolve(homedir(), '.roo/skills') },
|
|
40
|
+
{ id: 'continue', label: 'Continue', root: () => resolve(homedir(), '.continue/skills') },
|
|
41
|
+
{ id: 'windsurf', label: 'Windsurf', root: () => resolve(homedir(), '.windsurf/skills') },
|
|
42
|
+
{ id: 'replit-agent', label: 'Replit Agent', root: () => resolve(homedir(), '.replit/skills') },
|
|
43
|
+
{ id: 'jetbrains-ai', label: 'JetBrains AI', root: () => resolve(homedir(), '.jetbrains-ai/skills') },
|
|
50
44
|
];
|
|
51
45
|
|
|
52
46
|
const targetAliases = new Map([
|
|
53
|
-
['
|
|
54
|
-
['
|
|
55
|
-
['
|
|
56
|
-
['
|
|
57
|
-
['
|
|
58
|
-
['
|
|
59
|
-
['
|
|
60
|
-
['
|
|
61
|
-
['
|
|
62
|
-
['
|
|
47
|
+
['codex', 'universal'],
|
|
48
|
+
['agents', 'universal'],
|
|
49
|
+
['universal', 'universal'],
|
|
50
|
+
['cursor', 'universal'],
|
|
51
|
+
['cline', 'universal'],
|
|
52
|
+
['opencode', 'universal'],
|
|
53
|
+
['zed', 'universal'],
|
|
54
|
+
['github-copilot', 'universal'],
|
|
55
|
+
['copilot', 'universal'],
|
|
56
|
+
['gemini-cli', 'universal'],
|
|
57
|
+
['chatgpt', 'universal'],
|
|
58
|
+
['openai', 'universal'],
|
|
59
|
+
['openai-api', 'universal'],
|
|
60
|
+
['gpt-6-astra', 'universal'],
|
|
61
|
+
['gpt-6-sol', 'universal'],
|
|
62
|
+
['gpt-6-luna', 'universal'],
|
|
63
|
+
['gpt-5.6-sol', 'universal'],
|
|
64
|
+
['gpt-5.6-terra', 'universal'],
|
|
65
|
+
['gpt-5.6-luna', 'universal'],
|
|
63
66
|
['anthropic', 'claude'],
|
|
64
67
|
['anthropic-api', 'claude'],
|
|
65
68
|
['claude-opus', 'claude'],
|
|
66
69
|
['claude-sonnet', 'claude'],
|
|
67
70
|
['claude-haiku', 'claude'],
|
|
68
|
-
['
|
|
69
|
-
['gemini-pro', 'gemini'],
|
|
70
|
-
['gemini-flash', 'gemini'],
|
|
71
|
-
['gemini-api', 'gemini'],
|
|
72
|
-
['vertex-ai', 'gemini'],
|
|
73
|
-
['llama', 'meta'],
|
|
74
|
-
['ollama', 'local-models'],
|
|
75
|
-
['lm-studio', 'local-models'],
|
|
71
|
+
['xai', 'grok'],
|
|
76
72
|
]);
|
|
77
73
|
|
|
78
74
|
function parseArgs() {
|
|
@@ -178,50 +174,63 @@ async function readScan(root) {
|
|
|
178
174
|
|
|
179
175
|
function targetsByIds(ids) {
|
|
180
176
|
const byId = new Map(targetCatalog.map((target) => [target.id, target]));
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
const targets = [];
|
|
178
|
+
let includeUniversal = false;
|
|
179
|
+
|
|
180
|
+
for (const id of ids) {
|
|
181
|
+
const resolvedId = targetAliases.get(id) ?? id;
|
|
182
|
+
if (resolvedId === 'universal') {
|
|
183
|
+
includeUniversal = true;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const target = byId.get(resolvedId);
|
|
183
187
|
if (!target) {
|
|
184
188
|
throw new Error(`Unknown target "${id}". Use --survey to choose from the list.`);
|
|
185
189
|
}
|
|
186
|
-
|
|
187
|
-
}
|
|
190
|
+
targets.push(target);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (includeUniversal) {
|
|
194
|
+
targets.unshift({ id: 'universal', label: 'Universal', root: universalRoot });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return targets;
|
|
188
198
|
}
|
|
189
199
|
|
|
190
200
|
function targetsForOptions(options) {
|
|
191
201
|
if (options.targets?.length) return targetsByIds(options.targets);
|
|
192
|
-
if (options.agent === 'all') return
|
|
202
|
+
if (options.agent === 'all') return [{ id: 'universal', label: 'Universal', root: universalRoot }, ...targetCatalog];
|
|
193
203
|
return targetsByIds([options.agent]);
|
|
194
204
|
}
|
|
195
205
|
|
|
196
206
|
function skillRootsForTargets(options, targets) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
return [...new Set(skillTargets.map((target) => target.root()))];
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function guideTargets(targets) {
|
|
203
|
-
return targets.filter((target) => target.kind === 'guide');
|
|
207
|
+
if (options.skillsDir && targets.length) return [options.skillsDir];
|
|
208
|
+
return [...new Set(targets.map((target) => target.root()))];
|
|
204
209
|
}
|
|
205
210
|
|
|
206
211
|
function renderSurvey(selected, cursor) {
|
|
207
212
|
console.clear();
|
|
208
|
-
console.log('
|
|
213
|
+
console.log('Which agents do you want to install Servicemap to?\n');
|
|
214
|
+
console.log('— Universal (.agents/skills) — always included —');
|
|
215
|
+
for (const agent of universalAgents) {
|
|
216
|
+
console.log(` • ${agent}`);
|
|
217
|
+
}
|
|
218
|
+
console.log('\n— Additional agents —');
|
|
209
219
|
console.log('Use ↑/↓, Space to select, Enter to install.\n');
|
|
210
220
|
|
|
211
221
|
targetCatalog.forEach((target, index) => {
|
|
212
222
|
const pointer = index === cursor ? '>' : ' ';
|
|
213
223
|
const checked = selected.has(target.id) ? 'x' : ' ';
|
|
214
|
-
|
|
215
|
-
console.log(`${pointer} [${checked}] ${target.label} (${detail})`);
|
|
224
|
+
console.log(`${pointer} [${checked}] ${target.label}`);
|
|
216
225
|
});
|
|
217
226
|
}
|
|
218
227
|
|
|
219
228
|
function runTargetSurvey() {
|
|
220
229
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
221
|
-
return Promise.resolve(
|
|
230
|
+
return Promise.resolve([]);
|
|
222
231
|
}
|
|
223
232
|
|
|
224
|
-
const selected = new Set(
|
|
233
|
+
const selected = new Set();
|
|
225
234
|
let cursor = 0;
|
|
226
235
|
|
|
227
236
|
return new Promise((resolvePromise) => {
|
|
@@ -257,50 +266,10 @@ function runTargetSurvey() {
|
|
|
257
266
|
});
|
|
258
267
|
}
|
|
259
268
|
|
|
260
|
-
function guideText(target) {
|
|
261
|
-
return `# Servicemap for ${target.label}
|
|
262
|
-
|
|
263
|
-
These setup notes help ${target.label} use Servicemap when you want it to create a service map for a software project.
|
|
264
|
-
|
|
265
|
-
Invoke: /servicemap
|
|
266
|
-
|
|
267
|
-
Goal:
|
|
268
|
-
Scan the current project for third-party services, resolve service logos from the bundled Servicemap database, and create an /internalservicemap page when a website is present.
|
|
269
|
-
If no website can be safely found, create a desktop app fallback named servicemap-desktop.
|
|
270
|
-
|
|
271
|
-
Recommended local command:
|
|
272
|
-
\`\`\`bash
|
|
273
|
-
npx servicemap --root <project-path>
|
|
274
|
-
\`\`\`
|
|
275
|
-
|
|
276
|
-
If your agent supports local skills, install the real skill folder with:
|
|
277
|
-
\`\`\`bash
|
|
278
|
-
npx servicemap install-skill --agent all
|
|
279
|
-
\`\`\`
|
|
280
|
-
`;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
async function writeGuidePacks(targets, options) {
|
|
284
|
-
const guideRoot = resolve(homedir(), '.servicemap/model-guides');
|
|
285
|
-
for (const target of guideTargets(targets)) {
|
|
286
|
-
const destination = join(guideRoot, target.id);
|
|
287
|
-
if (options.dryRun) {
|
|
288
|
-
console.log(`Would write Servicemap guide for ${target.label} to ${destination}`);
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
await mkdir(destination, { recursive: true });
|
|
292
|
-
await cp(resolve(packageRoot, 'servicemap/SKILL.md'), join(destination, 'SKILL.md'));
|
|
293
|
-
await mkdir(join(destination, 'servicemap'), { recursive: true });
|
|
294
|
-
await cp(resolve(packageRoot, 'servicemap'), join(destination, 'servicemap'), { recursive: true });
|
|
295
|
-
await writeFile(join(destination, 'README.md'), guideText(target));
|
|
296
|
-
console.log(`Wrote Servicemap guide for ${target.label} to ${destination}`);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
269
|
async function installSkill(options) {
|
|
301
270
|
const source = resolve(packageRoot, 'servicemap');
|
|
302
271
|
const selectedTargets = options.survey && !options.yes
|
|
303
|
-
? await runTargetSurvey()
|
|
272
|
+
? [{ id: 'universal', label: 'Universal', root: universalRoot }, ...await runTargetSurvey()]
|
|
304
273
|
: targetsForOptions(options);
|
|
305
274
|
const roots = skillRootsForTargets(options, selectedTargets);
|
|
306
275
|
|
|
@@ -308,11 +277,15 @@ async function installSkill(options) {
|
|
|
308
277
|
throw new Error(`Bundled skill folder not found: ${source}`);
|
|
309
278
|
}
|
|
310
279
|
|
|
280
|
+
if (roots.length === 0) {
|
|
281
|
+
console.log('No targets selected. Nothing was installed.');
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
311
285
|
if (options.dryRun) {
|
|
312
286
|
for (const root of roots) {
|
|
313
287
|
console.log(`Would install Servicemap skill to ${join(root, 'servicemap')}`);
|
|
314
288
|
}
|
|
315
|
-
await writeGuidePacks(selectedTargets, options);
|
|
316
289
|
return;
|
|
317
290
|
}
|
|
318
291
|
|
|
@@ -324,8 +297,6 @@ async function installSkill(options) {
|
|
|
324
297
|
console.log(`Installed Servicemap skill to ${destination}`);
|
|
325
298
|
}
|
|
326
299
|
|
|
327
|
-
await writeGuidePacks(selectedTargets, options);
|
|
328
|
-
|
|
329
300
|
console.log('Restart your AI app if /servicemap does not appear immediately.');
|
|
330
301
|
}
|
|
331
302
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "servicemap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Generate an internal service map page or desktop app for a software project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"README.md"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"test": "node bin/servicemap.mjs generate --root demo-project --allow-emails founder@example.com,ops@example.com --dry-run && node bin/servicemap.mjs --root no-website-demo --dry-run && node bin/servicemap.mjs --dry-run && HOME=\"$(mktemp -d)\" node bin/servicemap.mjs && node bin/servicemap.mjs install-skill --agent all --dry-run && node bin/servicemap.mjs install-skill --targets
|
|
15
|
+
"test": "node bin/servicemap.mjs generate --root demo-project --allow-emails founder@example.com,ops@example.com --dry-run && node bin/servicemap.mjs --root no-website-demo --dry-run && node bin/servicemap.mjs --dry-run && HOME=\"$(mktemp -d)\" node bin/servicemap.mjs && node bin/servicemap.mjs install-skill --agent all --dry-run && node bin/servicemap.mjs install-skill --targets universal,claude,grok --dry-run && node bin/servicemap.mjs install-skill --targets codex,chatgpt,gpt-6-astra --dry-run && node bin/servicemap.mjs skills install --agent claude --dry-run",
|
|
16
16
|
"validate:skill": "python3 /Users/dougie/.codex/skills/.system/skill-creator/scripts/quick_validate.py servicemap"
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|