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 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,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: 'codex', label: 'OpenAI (Codex, ChatGPT, GPT models)', kind: 'skill', root: () => resolve(homedir(), '.agents/skills'), recommended: true },
17
- { id: 'claude', label: 'Anthropic (Claude Code, Claude models)', kind: 'skill', root: () => resolve(homedir(), '.claude/skills'), recommended: true },
18
- { id: 'grok', label: 'xAI (Grok)', kind: 'skill', root: () => resolve(homedir(), '.grok/skills'), recommended: true },
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' },
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
- ['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'],
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
- ['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'],
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
- return ids.map((id) => {
182
- const target = byId.get(targetAliases.get(id) ?? id);
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
- return target;
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 targetCatalog.filter((target) => target.kind === 'skill');
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
- 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');
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('Where should Servicemap be available?');
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
- const detail = target.kind === 'skill' ? 'skill folder' : 'setup notes';
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(targetCatalog.filter((target) => target.recommended));
230
+ return Promise.resolve([]);
222
231
  }
223
232
 
224
- const selected = new Set(targetCatalog.filter((target) => target.recommended).map((target) => target.id));
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.5",
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 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": [