servicemap 0.1.24 → 0.1.26

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
@@ -41,6 +41,15 @@ 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
+ Symlink installs use a stable local clone at `~/.servicemap/source`, so updates can be pulled without reinstalling every agent folder.
45
+
46
+ To update that local skill source:
47
+
48
+ ```bash
49
+ npx servicemap update
50
+ ```
51
+
52
+ Running `npx servicemap` again also refreshes the stable source before installing.
44
53
 
45
54
  After a global install, the command is:
46
55
 
@@ -62,6 +71,8 @@ servicemap generate --root <project-path>
62
71
  servicemap --root <project-path>
63
72
  servicemap generate --root <project-path> --allow-emails founder@example.com,ops@example.com
64
73
  servicemap generate --root <project-path> --allow-email founder@example.com --allow-email ops@example.com
74
+ servicemap generate --root <project-path> --deploy
75
+ servicemap generate --root <project-path> --deploy-command "npm run deploy"
65
76
  servicemap --mode page
66
77
  servicemap --mode desktop
67
78
  servicemap --dry-run
@@ -73,8 +84,13 @@ servicemap install-skill --agent grok
73
84
  servicemap install-skill --agent all
74
85
  servicemap install-skill --targets universal,claude,grok
75
86
  servicemap skills install --agent codex
87
+ servicemap update
76
88
  ```
77
89
 
90
+ When a website page is generated, Servicemap prompts before deployment when it detects a deployment command.
91
+ It prefers an existing `npm run deploy` script, then provider config such as Vercel or Netlify.
92
+ Use `--no-deploy` to skip the prompt.
93
+
78
94
  ## Contents
79
95
 
80
96
  - `servicemap/`: installable skill folder
@@ -168,6 +168,8 @@ function parseArgs() {
168
168
  installMethod: null,
169
169
  allowedEmails: [],
170
170
  open: true,
171
+ deploy: null,
172
+ deployCommand: null,
171
173
  invokedWithoutArgs: args.length === 0,
172
174
  explicitCommand: false,
173
175
  generationRequested: false,
@@ -210,6 +212,9 @@ function parseArgs() {
210
212
  if (arg === '--yes' || arg === '-y') options.yes = true;
211
213
  if (arg === '--dry-run') options.dryRun = true;
212
214
  if (arg === '--no-open') options.open = false;
215
+ if (arg === '--deploy') options.deploy = true;
216
+ if (arg === '--no-deploy') options.deploy = false;
217
+ if (arg === '--deploy-command') options.deployCommand = args[++index] ?? null;
213
218
  if (arg === '--help' || arg === '-h') options.help = true;
214
219
  }
215
220
 
@@ -222,20 +227,24 @@ function help() {
222
227
 
223
228
  Usage:
224
229
  servicemap
225
- servicemap generate [--root <project>] [--mode auto|page|desktop] [--allow-emails <list>] [--dry-run] [--no-open]
226
- servicemap --root <project> [--mode auto|page|desktop] [--allow-emails <list>] [--dry-run] [--no-open]
230
+ servicemap generate [--root <project>] [--mode auto|page|desktop] [--allow-emails <list>] [--dry-run] [--no-open] [--deploy|--no-deploy]
231
+ servicemap --root <project> [--mode auto|page|desktop] [--allow-emails <list>] [--dry-run] [--no-open] [--deploy|--no-deploy]
227
232
  servicemap install-skill [--survey] [--agent codex|claude|grok|all] [--targets <ids>] [--install-method copy|symlink]
228
233
  servicemap skills install [--survey] [--agent codex|claude|grok|all]
234
+ servicemap update
229
235
 
230
236
  Examples:
231
237
  npx servicemap
232
238
  npx servicemap generate --root ../my-app --allow-emails founder@example.com,ops@example.com
239
+ npx servicemap generate --root ../my-app --deploy
240
+ npx servicemap generate --root ../my-app --deploy-command "npm run deploy"
233
241
  npx servicemap -- --root ../my-app
234
242
  npx servicemap install-skill
235
243
  npx servicemap install-skill --survey
236
244
  npx servicemap install-skill --agent all
237
245
  npx servicemap install-skill --targets codex,claude,cursor
238
246
  npx servicemap skills install --agent claude
247
+ npx servicemap update
239
248
  servicemap --mode desktop
240
249
  `);
241
250
  }
@@ -255,6 +264,23 @@ function run(command, args, options = {}) {
255
264
  });
256
265
  }
257
266
 
267
+ function runShell(command, options = {}) {
268
+ const shell = process.platform === 'win32' ? 'cmd' : 'sh';
269
+ const args = process.platform === 'win32' ? ['/c', command] : ['-lc', command];
270
+ return run(shell, args, options);
271
+ }
272
+
273
+ function askYesNo(question) {
274
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return Promise.resolve(false);
275
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
276
+ return new Promise((resolvePromise) => {
277
+ rl.question(`${question} [y/N] `, (answer) => {
278
+ rl.close();
279
+ resolvePromise(/^y(es)?$/i.test(answer.trim()));
280
+ });
281
+ });
282
+ }
283
+
258
284
  function openUrl(url) {
259
285
  const command = process.platform === 'darwin'
260
286
  ? 'open'
@@ -350,6 +376,47 @@ async function readScan(root) {
350
376
  return JSON.parse(await readFile(scanPath, 'utf8'));
351
377
  }
352
378
 
379
+ async function packageJsonFor(root) {
380
+ return JSON.parse(await readFile(resolve(root, 'package.json'), 'utf8').catch(() => '{}'));
381
+ }
382
+
383
+ async function detectDeployCommand(root, options) {
384
+ if (options.deployCommand) return options.deployCommand;
385
+ const packageJson = await packageJsonFor(root);
386
+ if (packageJson.scripts?.deploy) return 'npm run deploy';
387
+ if (existsSync(resolve(root, 'vercel.json')) || existsSync(resolve(root, '.vercel/project.json'))) {
388
+ return 'npx vercel --prod';
389
+ }
390
+ if (existsSync(resolve(root, 'netlify.toml'))) {
391
+ return 'npx netlify deploy --prod';
392
+ }
393
+ return null;
394
+ }
395
+
396
+ async function maybeDeploy(root, options) {
397
+ if (options.deploy === false) return;
398
+ const command = await detectDeployCommand(root, options);
399
+ if (!command) {
400
+ if (options.deploy === true) {
401
+ console.log('No deploy command was detected. Add a deploy script or pass --deploy-command "<command>".');
402
+ } else {
403
+ console.log('Generated /internalservicemap. Deploy manually, or pass --deploy-command next time.');
404
+ }
405
+ return;
406
+ }
407
+
408
+ const shouldDeploy = options.deploy === true
409
+ ? true
410
+ : await askYesNo(`Deploy /internalservicemap now with "${command}"?`);
411
+ if (!shouldDeploy) {
412
+ console.log(`Skipped deployment. To deploy later, run: ${command}`);
413
+ return;
414
+ }
415
+
416
+ console.log(`Deploying with: ${command}`);
417
+ await runShell(command, { cwd: root });
418
+ }
419
+
353
420
  function targetsByIds(ids) {
354
421
  const byId = new Map(targetCatalog.map((target) => [target.id, target]));
355
422
  const targets = [];
@@ -593,6 +660,11 @@ async function prepareStableSource(options) {
593
660
  return resolve(sourceRoot, 'servicemap');
594
661
  }
595
662
 
663
+ async function updateStableSource(options) {
664
+ const source = await prepareStableSource(options);
665
+ console.log(`Servicemap source is current at ${dirname(source)}`);
666
+ }
667
+
596
668
  async function installIntoRoot(root, source, method, options) {
597
669
  const destination = join(root, 'servicemap');
598
670
  if (options.dryRun) {
@@ -667,8 +739,13 @@ async function main() {
667
739
  return;
668
740
  }
669
741
 
742
+ if (options.command === 'update') {
743
+ await updateStableSource(options);
744
+ return;
745
+ }
746
+
670
747
  if (options.command !== 'generate') {
671
- throw new Error(`Unknown command "${options.command}". Use generate, install-skill, or skills install.`);
748
+ throw new Error(`Unknown command "${options.command}". Use generate, install-skill, update, or skills install.`);
672
749
  }
673
750
 
674
751
  if (!['auto', 'page', 'desktop'].includes(options.mode)) {
@@ -697,6 +774,7 @@ async function main() {
697
774
  } else {
698
775
  await run(process.execPath, [pageScript, '--root', options.root]);
699
776
  if (options.open) await openGeneratedPage(options.root, scan.project?.framework);
777
+ await maybeDeploy(options.root, options);
700
778
  }
701
779
  }
702
780
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "servicemap",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Generate an internal service map page or desktop app for a software project.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,16 +13,21 @@ If no website can be found, create a desktop app fallback at `servicemap-desktop
13
13
 
14
14
  ## Workflow
15
15
 
16
- 1. Inspect the project layout and current web framework.
17
- 2. If the user gives a list of emails that may access the generated web page, pass them to the scan command with `--allow-email <email>` for each address or `--allow-emails <comma-separated-list>`.
18
- 3. Run `scripts/scan-project.mjs --root <project-root>` to create `.servicemap/internal-servicemap.json`.
16
+ 1. If this skill is installed from the stable source clone at `~/.servicemap/source`, run `git -C ~/.servicemap/source pull --ff-only` before using the scripts.
17
+ If the update fails because the network is unavailable, continue with the installed local copy and report that the auto-update did not complete.
18
+ 2. Inspect the project layout and current web framework.
19
+ 3. If the user gives a list of emails that may access the generated web page, pass them to the scan command with `--allow-email <email>` for each address or `--allow-emails <comma-separated-list>`.
20
+ 4. Run `scripts/scan-project.mjs --root <project-root>` to create `.servicemap/internal-servicemap.json`.
19
21
  Include the access flags from the previous step when relevant.
20
- 4. Review the scan output for obvious false positives or missing high-confidence services.
21
- 5. If the scan detects a web framework or routeable website, run `scripts/generate-page.mjs --root <project-root>` to create the framework-specific `/internalservicemap` route.
22
- 6. If the scan reports `framework: static` or no website route can be safely attached, run `scripts/generate-desktop-app.mjs --root <project-root>` to create `servicemap-desktop`.
23
- 7. Adapt the generated surface to the project's existing design system only when the route is clear and the edits are low-risk.
24
- 8. Run the project's relevant build, lint, and focused tests.
25
- 9. Report the page or desktop app path, service count, validation run, any services marked low or medium confidence, and whether requested email access was enforced.
22
+ 5. Review the scan output for obvious false positives or missing high-confidence services.
23
+ 6. If the scan detects a web framework or routeable website, run `scripts/generate-page.mjs --root <project-root>` to create the framework-specific `/internalservicemap` route.
24
+ 7. If the scan reports `framework: static` or no website route can be safely attached, run `scripts/generate-desktop-app.mjs --root <project-root>` to create `servicemap-desktop`.
25
+ 8. Adapt the generated surface to the project's existing design system only when the route is clear and the edits are low-risk.
26
+ 9. Run the project's relevant build, lint, and focused tests.
27
+ 10. If a website page was created, tell the user it is only local until deployed and ask whether they want to deploy it now.
28
+ Do not deploy unless the user explicitly says yes.
29
+ If they approve, use the project's existing deployment path, preferring `npm run deploy` when present, then provider-specific project configuration such as Vercel or Netlify.
30
+ 11. Report the page or desktop app path, service count, validation run, any services marked low or medium confidence, whether requested email access was enforced, and whether deployment was skipped or completed.
26
31
 
27
32
  ## Safety Rules
28
33
 
@@ -30,7 +35,8 @@ Read environment variable names only.
30
35
  Never copy, expose, summarize, or commit secret values.
31
36
  The scan output may include env var names such as `STRIPE_SECRET_KEY`, but not their values.
32
37
 
33
- Do not add authentication, deployment, analytics, or external writes unless the user explicitly asks.
38
+ Do not add authentication, analytics, or external writes unless the user explicitly asks.
39
+ Deploy only after the generated website page exists and the user has confirmed deployment.
34
40
  If the repository already has route protection conventions for internal pages, follow them.
35
41
  When email access is requested, enforce it only through the app's existing server-side authentication.
36
42
  The built-in generator can enforce email allowlists for Next.js App Router projects using Clerk by checking `currentUser()` against the configured emails.