servicemap 0.1.7 → 0.1.9

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 CHANGED
@@ -13,7 +13,7 @@ npx servicemap
13
13
  ```
14
14
 
15
15
  That opens the interactive install survey.
16
- It lets users choose compatible AI coding agents and model environments.
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 codex,gemini,cursor
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 codex,gemini,cursor --dry-run
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
  ```
@@ -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, writeFile } from 'node:fs/promises';
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,89 @@ 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
+ 'Antigravity',
20
+ 'Antigravity CLI',
21
+ 'Cline',
22
+ 'Codex',
23
+ 'Cursor',
24
+ 'Deep Agents',
25
+ 'Dexto',
26
+ 'Droid',
27
+ 'Firebender',
28
+ 'Gemini CLI',
29
+ 'GitHub Copilot',
30
+ 'Kilo Code',
31
+ 'Kimi Code CLI',
32
+ 'Loaf',
33
+ 'OpenCode',
34
+ 'Sarvam Code',
35
+ 'Warp',
36
+ 'Zed',
37
+ ];
38
+
15
39
  const targetCatalog = [
16
- { id: 'codex', label: 'OpenAI (Codex, ChatGPT, GPT models)', kind: 'skill', root: () => resolve(homedir(), '.agents/skills') },
17
- { id: 'claude', label: 'Anthropic (Claude Code, Claude models)', kind: 'skill', root: () => resolve(homedir(), '.claude/skills') },
18
- { id: 'grok', label: 'xAI (Grok)', kind: 'skill', root: () => resolve(homedir(), '.grok/skills') },
19
- { id: 'gemini', label: 'Google (Gemini, Vertex AI)', kind: 'guide' },
20
- { id: 'cursor', label: 'Cursor', kind: 'guide' },
21
- { id: 'windsurf', label: 'Windsurf', kind: 'guide' },
22
- { id: 'copilot', label: 'GitHub Copilot', kind: 'guide' },
23
- { id: 'vscode-chat', label: 'VS Code Chat', kind: 'guide' },
24
- { id: 'continue', label: 'Continue.dev', kind: 'guide' },
25
- { id: 'aider', label: 'Aider', kind: 'guide' },
26
- { id: 'cline', label: 'Cline', kind: 'guide' },
27
- { id: 'roo-code', label: 'Roo Code', kind: 'guide' },
28
- { id: 'zed-ai', label: 'Zed AI', kind: 'guide' },
29
- { id: 'replit-agent', label: 'Replit Agent', kind: 'guide' },
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' },
40
+ { id: 'claude', label: 'Claude Code', root: () => resolve(homedir(), '.claude/skills') },
41
+ { id: 'grok', label: 'Grok', root: () => resolve(homedir(), '.grok/skills') },
42
+ { id: 'aiderdesk', label: 'AiderDesk', root: () => resolve(homedir(), '.aider-desk/skills') },
43
+ { id: 'astrbot', label: 'AstrBot', root: () => resolve(homedir(), '.astrbot/data/skills') },
44
+ { id: 'autohand', label: 'Autohand Code CLI', root: () => resolve(homedir(), '.autohand/skills') },
45
+ { id: 'augment', label: 'Augment', root: () => resolve(homedir(), '.augment/skills') },
46
+ { id: 'ibm-bob', label: 'IBM Bob', root: () => resolve(homedir(), '.bob/skills') },
47
+ { id: 'openclaw', label: 'OpenClaw', root: () => resolve(homedir(), '.openclaw/skills') },
48
+ { id: 'codearts', label: 'CodeArts Agent', root: () => resolve(homedir(), '.codeartsdoer/skills') },
49
+ { id: 'roo-code', label: 'Roo Code', root: () => resolve(homedir(), '.roo/skills') },
50
+ { id: 'continue', label: 'Continue', root: () => resolve(homedir(), '.continue/skills') },
51
+ { id: 'windsurf', label: 'Windsurf', root: () => resolve(homedir(), '.windsurf/skills') },
52
+ { id: 'replit-agent', label: 'Replit Agent', root: () => resolve(homedir(), '.replit/skills') },
53
+ { id: 'jetbrains-ai', label: 'JetBrains AI', root: () => resolve(homedir(), '.jetbrains-ai/skills') },
50
54
  ];
51
55
 
52
56
  const targetAliases = new Map([
53
- ['agents', 'codex'],
54
- ['chatgpt', 'codex'],
55
- ['openai', 'codex'],
56
- ['openai-api', 'codex'],
57
- ['gpt-6-astra', 'codex'],
58
- ['gpt-6-sol', 'codex'],
59
- ['gpt-6-luna', 'codex'],
60
- ['gpt-5.6-sol', 'codex'],
61
- ['gpt-5.6-terra', 'codex'],
62
- ['gpt-5.6-luna', 'codex'],
57
+ ['codex', 'universal'],
58
+ ['agents', 'universal'],
59
+ ['universal', 'universal'],
60
+ ['cursor', 'universal'],
61
+ ['cline', 'universal'],
62
+ ['antigravity', 'universal'],
63
+ ['antigravity-cli', 'universal'],
64
+ ['deep-agents', 'universal'],
65
+ ['deepagents', 'universal'],
66
+ ['dexto', 'universal'],
67
+ ['droid', 'universal'],
68
+ ['firebender', 'universal'],
69
+ ['opencode', 'universal'],
70
+ ['open-code', 'universal'],
71
+ ['zed', 'universal'],
72
+ ['github-copilot', 'universal'],
73
+ ['copilot', 'universal'],
74
+ ['gemini-cli', 'universal'],
75
+ ['kilo', 'universal'],
76
+ ['kilo-code', 'universal'],
77
+ ['kimi', 'universal'],
78
+ ['kimi-code', 'universal'],
79
+ ['kimi-code-cli', 'universal'],
80
+ ['loaf', 'universal'],
81
+ ['sarvam', 'universal'],
82
+ ['sarvam-code', 'universal'],
83
+ ['chatgpt', 'universal'],
84
+ ['openai', 'universal'],
85
+ ['openai-api', 'universal'],
86
+ ['gpt-6-astra', 'universal'],
87
+ ['gpt-6-sol', 'universal'],
88
+ ['gpt-6-luna', 'universal'],
89
+ ['gpt-5.6-sol', 'universal'],
90
+ ['gpt-5.6-terra', 'universal'],
91
+ ['gpt-5.6-luna', 'universal'],
63
92
  ['anthropic', 'claude'],
64
93
  ['anthropic-api', 'claude'],
65
94
  ['claude-opus', 'claude'],
66
95
  ['claude-sonnet', 'claude'],
67
96
  ['claude-haiku', 'claude'],
68
- ['google', 'gemini'],
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'],
97
+ ['xai', 'grok'],
76
98
  ]);
77
99
 
78
100
  function parseArgs() {
@@ -178,34 +200,48 @@ async function readScan(root) {
178
200
 
179
201
  function targetsByIds(ids) {
180
202
  const byId = new Map(targetCatalog.map((target) => [target.id, target]));
181
- return ids.map((id) => {
182
- const target = byId.get(targetAliases.get(id) ?? id);
203
+ const targets = [];
204
+ let includeUniversal = false;
205
+
206
+ for (const id of ids) {
207
+ const resolvedId = targetAliases.get(id) ?? id;
208
+ if (resolvedId === 'universal') {
209
+ includeUniversal = true;
210
+ continue;
211
+ }
212
+ const target = byId.get(resolvedId);
183
213
  if (!target) {
184
214
  throw new Error(`Unknown target "${id}". Use --survey to choose from the list.`);
185
215
  }
186
- return target;
187
- });
216
+ targets.push(target);
217
+ }
218
+
219
+ if (includeUniversal) {
220
+ targets.unshift({ id: 'universal', label: 'Universal', root: universalRoot });
221
+ }
222
+
223
+ return targets;
188
224
  }
189
225
 
190
226
  function targetsForOptions(options) {
191
227
  if (options.targets?.length) return targetsByIds(options.targets);
192
- if (options.agent === 'all') return targetCatalog.filter((target) => target.kind === 'skill');
228
+ if (options.agent === 'all') return [{ id: 'universal', label: 'Universal', root: universalRoot }, ...targetCatalog];
193
229
  return targetsByIds([options.agent]);
194
230
  }
195
231
 
196
232
  function skillRootsForTargets(options, targets) {
197
- const skillTargets = targets.filter((target) => target.kind === 'skill');
198
- if (options.skillsDir && skillTargets.length) return [options.skillsDir];
199
- return [...new Set(skillTargets.map((target) => target.root()))];
200
- }
201
-
202
- function guideTargets(targets) {
203
- return targets.filter((target) => target.kind === 'guide');
233
+ if (options.skillsDir && targets.length) return [options.skillsDir];
234
+ return [...new Set(targets.map((target) => target.root()))];
204
235
  }
205
236
 
206
237
  function renderSurvey(selected, cursor) {
207
238
  console.clear();
208
- console.log('Where should Servicemap be available?');
239
+ console.log('Which agents do you want to install Servicemap to?\n');
240
+ console.log('— Universal (.agents/skills) — always included —');
241
+ for (const agent of universalAgents) {
242
+ console.log(` • ${agent}`);
243
+ }
244
+ console.log('\n— Additional agents —');
209
245
  console.log('Use ↑/↓, Space to select, Enter to install.\n');
210
246
 
211
247
  targetCatalog.forEach((target, index) => {
@@ -256,59 +292,18 @@ function runTargetSurvey() {
256
292
  });
257
293
  }
258
294
 
259
- function guideText(target) {
260
- return `# Servicemap for ${target.label}
261
-
262
- These setup notes help ${target.label} use Servicemap when you want it to create a service map for a software project.
263
-
264
- Invoke: /servicemap
265
-
266
- Goal:
267
- 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.
268
- If no website can be safely found, create a desktop app fallback named servicemap-desktop.
269
-
270
- Recommended local command:
271
- \`\`\`bash
272
- npx servicemap --root <project-path>
273
- \`\`\`
274
-
275
- If your agent supports local skills, install the real skill folder with:
276
- \`\`\`bash
277
- npx servicemap install-skill --agent all
278
- \`\`\`
279
- `;
280
- }
281
-
282
- async function writeGuidePacks(targets, options) {
283
- const guideRoot = resolve(homedir(), '.servicemap/model-guides');
284
- for (const target of guideTargets(targets)) {
285
- const destination = join(guideRoot, target.id);
286
- if (options.dryRun) {
287
- console.log(`Would write Servicemap guide for ${target.label} to ${destination}`);
288
- continue;
289
- }
290
- await mkdir(destination, { recursive: true });
291
- await cp(resolve(packageRoot, 'servicemap/SKILL.md'), join(destination, 'SKILL.md'));
292
- await mkdir(join(destination, 'servicemap'), { recursive: true });
293
- await cp(resolve(packageRoot, 'servicemap'), join(destination, 'servicemap'), { recursive: true });
294
- await writeFile(join(destination, 'README.md'), guideText(target));
295
- console.log(`Wrote Servicemap guide for ${target.label} to ${destination}`);
296
- }
297
- }
298
-
299
295
  async function installSkill(options) {
300
296
  const source = resolve(packageRoot, 'servicemap');
301
297
  const selectedTargets = options.survey && !options.yes
302
- ? await runTargetSurvey()
298
+ ? [{ id: 'universal', label: 'Universal', root: universalRoot }, ...await runTargetSurvey()]
303
299
  : targetsForOptions(options);
304
300
  const roots = skillRootsForTargets(options, selectedTargets);
305
- const notes = guideTargets(selectedTargets);
306
301
 
307
302
  if (!existsSync(source)) {
308
303
  throw new Error(`Bundled skill folder not found: ${source}`);
309
304
  }
310
305
 
311
- if (roots.length === 0 && notes.length === 0) {
306
+ if (roots.length === 0) {
312
307
  console.log('No targets selected. Nothing was installed.');
313
308
  return;
314
309
  }
@@ -317,7 +312,6 @@ async function installSkill(options) {
317
312
  for (const root of roots) {
318
313
  console.log(`Would install Servicemap skill to ${join(root, 'servicemap')}`);
319
314
  }
320
- await writeGuidePacks(selectedTargets, options);
321
315
  return;
322
316
  }
323
317
 
@@ -329,11 +323,7 @@ async function installSkill(options) {
329
323
  console.log(`Installed Servicemap skill to ${destination}`);
330
324
  }
331
325
 
332
- await writeGuidePacks(selectedTargets, options);
333
-
334
- if (roots.length > 0) {
335
- console.log('Restart your AI app if /servicemap does not appear immediately.');
336
- }
326
+ console.log('Restart your AI app if /servicemap does not appear immediately.');
337
327
  }
338
328
 
339
329
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "servicemap",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
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 codex,gemini,cursor --dry-run && node bin/servicemap.mjs install-skill --targets chatgpt,gpt-6-astra,gemini-flash --dry-run && node bin/servicemap.mjs skills install --agent claude --dry-run",
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": [