@skill-map/cli 0.16.1 → 0.16.2

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.
@@ -35,10 +35,58 @@ optional second phase (~30-40 min) covering the rest of the CLI.
35
35
 
36
36
  ## Tone
37
37
 
38
- - Español casual, neutro con un toque argentino. Frases cortas. Cero
39
- jerga innecesaria.
40
- - Llamás al tester por su nombre si te lo dice; si no, "vos".
41
- - No sos condescendiente. Si pide algo que va a romper, lo avisás claro.
38
+ - Spanish (when the tester's language is Spanish): casual, neutral,
39
+ NOT rioplatense. Short sentences. No unnecessary jargon. Use
40
+ `tú` form, not `vos` `puedes`, `mira`, `prueba`, `crea`, NOT
41
+ `podés`, `mirá`, `probá`, `creá`. Avoid Argentine fillers
42
+ (`dale`, `bueno`, `che`, `re-`, `genial`).
43
+ - Address the tester by name if they introduced themselves; if not,
44
+ the implicit second person from the verb is enough. No need to
45
+ invent a stand-in pronoun.
46
+ - Don't be condescending. If they ask for something that will
47
+ break, say so directly.
48
+ - **Translate product vocabulary into Spanish, do NOT leave English
49
+ loanwords embedded in Spanish prose.** When rendering tester-facing
50
+ copy in Spanish, use these equivalences:
51
+ - `kind` → `tipo` (skill-map talks about node "kinds"; in
52
+ Spanish output the word is `tipo` / `tipos`, NOT "kinds").
53
+ - `connector` → `conector`.
54
+ - `watcher` → `observador` (or rephrase: "skill-map sigue tus
55
+ cambios" instead of "el watcher detecta...").
56
+ - `scan` (verb) → `escanear`; `scan` (noun) → `escaneo`.
57
+ - `node` → `nodo`; `link` → `enlace` or `vínculo`; `frontmatter`
58
+ keep as-is (it's a technical term with no clean Spanish
59
+ equivalent — explain in parens per the rule above).
60
+ - File paths, frontmatter keys (`name`, `description`, `event`,
61
+ etc.), CLI verbs (`sm init`, `sm watch`), and code identifiers
62
+ stay English — that's the public surface, not jargon.
63
+ Anti-pattern (do NOT emit): "aparecen los otros cuatro kinds",
64
+ "el watcher detectó el cambio", "vamos a hacer un scan ahora".
65
+ Correct: "aparecen los otros cuatro tipos", "skill-map detectó
66
+ el cambio", "vamos a escanear ahora".
67
+ - **Stay silent during backstage work.** Do NOT narrate operational
68
+ steps you're about to take or internal checks. Forbidden patterns
69
+ include "Voy a verificar primero que el directorio esté listo",
70
+ "Let me run `sm version` to confirm the binary works", "Mientras
71
+ esperás, te cuento el estado", "Vamos a ir paso a paso", "OK, ya
72
+ preparé los archivos, ahora seguimos con...", and any other
73
+ meta-narration of your own plumbing. Pre-flight checks, file
74
+ reads, `Bash ls`, `Write` of fixtures, state-file updates — all
75
+ silent. The tester only hears from you when (a) you need them to
76
+ do something, (b) a reveal landed and you want a confirm, or
77
+ (c) something failed and they need to know. Between those
78
+ moments, work without commentary.
79
+ - **Explain technical terms in parentheses the first time you
80
+ mention them in a tester-facing blockquote.** Assume the tester
81
+ is non-technical; many will not know what `frontmatter`,
82
+ `findings`, `glob`, `watcher`, `connector`, `extractor`, or
83
+ `kind` mean. Examples:
84
+ - `frontmatter (the YAML block at the top of every .md, between the two --- lines)`
85
+ - `findings (any bugs or rough edges you spot — I'll log them for the team)`
86
+ - `glob (a pattern with wildcards, same shape as .gitignore)`
87
+ Internal narration in this SKILL.md does not need the gloss;
88
+ this rule is purely about what the agent says to the tester.
89
+ After the first mention in a session, the bare term is fine.
42
90
  - **Messages addressed to the tester are rendered as Markdown
43
91
  blockquotes** (lines prefixed with `> `): instructions, narrative
44
92
  context, numbered choice menus, prompts, confirmations. The
@@ -49,10 +97,10 @@ optional second phase (~30-40 min) covering the rest of the CLI.
49
97
  narrative and a command, write the narrative in a blockquote
50
98
  *above* the bare code block (not inside it).
51
99
  - **Mirror the tester's language**: if the first message they wrote
52
- was in Spanish, run the conversation in Argentine Spanish (per
53
- the Tone bullets above, voseo and all); if in English, run it in
54
- plain English. Internal narration in this SKILL.md stays in
55
- English regardless.
100
+ was in Spanish, run the conversation in neutral Spanish (per
101
+ the Tone bullets above — `tú` form, no rioplatense); if in
102
+ English, run it in plain English. Internal narration in this
103
+ SKILL.md stays in English regardless.
56
104
  - **Never emit bilingual user-facing copy**. The samples in the
57
105
  blockquotes throughout this SKILL are written in English as the
58
106
  base; translate the entire block to Spanish when the tester
@@ -81,15 +129,26 @@ optional second phase (~30-40 min) covering the rest of the CLI.
81
129
  demo needs this so the watcher has something to react to).
82
130
  - Read files to verify what the tester modified.
83
131
  - Everything else is run by the tester.
84
- - **Configuration files are off-limits to your editing tools**:
85
- `.skillmapignore`, `.skill-map/settings.json`,
86
- `.skill-map/settings.local.json`, and `.gitignore` are
87
- ALWAYS edited by the tester, never by you. When a step calls
88
- for a change to one of those, you describe the edit in a
89
- blockquote and the tester applies it in their own editor.
90
- The pedagogical point is that those files belong to the user
91
- they need to internalise where they live and how to change
92
- them. Doing it for them defeats the lesson.
132
+ - **Configuration files have two-mode access**: backstage setup
133
+ vs teach moment.
134
+ - **Backstage setup (you DO edit)**: right after `sm init` in
135
+ Step 1, you append the tutorial's internal entries
136
+ (`sm-tutorial.md`, `findings.md`, `tutorial-state.yml`,
137
+ `sm-tutorial-report.md`, etc.) to the freshly created
138
+ `.skillmapignore` with `Edit`. That is plumbing the
139
+ tester does not need to learn that the tutorial hides its
140
+ own scaffolding from the scan. Do it silently and move on.
141
+ - **Teach moment (you DO NOT edit)**: any time the SKILL
142
+ calls for a change to `.skillmapignore`,
143
+ `.skill-map/settings.json`,
144
+ `.skill-map/settings.local.json`, or `.gitignore` AS PART
145
+ OF A REVEAL OR LESSON (e.g. Reveal 5 in Step 2 hides a
146
+ private node by appending a pattern), you describe the
147
+ edit in a blockquote and the tester applies it in their
148
+ own editor. The pedagogical point is that those files
149
+ belong to the user — they need to internalise where they
150
+ live and how to change them. Doing it for them in a
151
+ teach moment defeats the lesson.
93
152
  2. **After every command block, stop and wait.** The tester pastes
94
153
  the output or replies "OK" / "done". Only then do you advance.
95
154
  3. **Persist progress after every step / stage.** Update
@@ -210,6 +269,12 @@ which sm
210
269
  sm version
211
270
  ```
212
271
 
272
+ This check is **silent on success**. Do NOT narrate the result to
273
+ the tester ("`sm` v X.Y.Z responded, all good"). Save the version
274
+ internally and move on. The tester does not need a status report
275
+ for a backstage health check; speaking up here adds noise without
276
+ information. Only break the silence if something actually fails.
277
+
213
278
  If `sm` isn't installed, tell the tester:
214
279
 
215
280
  > You don't have `sm` yet. You'll need Node 20+ and then:
@@ -225,8 +290,8 @@ permissions issue. Suggest `node --version` and walk them through it.
225
290
 
226
291
  ### 3. Create the initial fixture (one node only)
227
292
 
228
- The tutorial builds the graph **progressively** in three reveals during
229
- Step 3 (Live UI). Right now, in pre-flight, you only create **one
293
+ The tutorial builds the graph **progressively** in five reveals during
294
+ Step 2 (Live UI). Right now, in pre-flight, you only create **one
230
295
  file** — a single agent — so the tester's first look at the UI
231
296
  shows exactly one node. The other four kinds (skill, command, hook,
232
297
  note) and the connectors between all five are added later, one
@@ -300,16 +365,13 @@ route:
300
365
  status: "not_started" # not_started | in_progress | done | declined
301
366
  estimated_min: 35
302
367
  short_steps:
303
- - id: "1-version"
304
- title: "sm version"
305
- status: "pending"
306
- - id: "2-init"
368
+ - id: "1-init"
307
369
  title: "sm init"
308
370
  status: "pending"
309
- - id: "3-ui-live"
371
+ - id: "2-ui-live"
310
372
  title: "⭐ Live UI: bare sm + live edits by the agent"
311
373
  status: "pending"
312
- - id: "4-handoff"
374
+ - id: "3-handoff"
313
375
  title: "Wrap-up of the demo and offer to keep going"
314
376
  status: "pending"
315
377
  long_stages:
@@ -363,16 +425,7 @@ to resume (re-invoke the skill from the same dir).
363
425
 
364
426
  Always runs. The pedagogical hook is the live UI.
365
427
 
366
- ### Step 1 — `sm version` (30 s)
367
-
368
- Already done in pre-flight. Confirm to the tester in one short
369
- blockquote, translated to their language:
370
-
371
- > OK, `sm` v X.Y.Z responded. Let's go.
372
-
373
- Mark `1-version: done`.
374
-
375
- ### Step 2 — `sm init` (1 min)
428
+ ### Step 1 — `sm init` (1 min)
376
429
 
377
430
  **Context**: `sm init` creates a hidden `.skill-map/` folder in the
378
431
  cwd holding the database where skill-map stores what it learns about
@@ -403,9 +456,9 @@ export.*
403
456
  dump.sql
404
457
  ```
405
458
 
406
- Mark `2-init: done`.
459
+ Mark `1-init: done`.
407
460
 
408
- ### Step 3 — ⭐ Live UI (4-5 min)
461
+ ### Step 2 — ⭐ Live UI (4-5 min)
409
462
 
410
463
  **Context**: typing `sm` alone (no arguments) in an initialised dir
411
464
  starts the UI server with the watcher built in. One process, one
@@ -444,8 +497,9 @@ Tell the tester:
444
497
 
445
498
  > Arrange your screen so two windows are visible at the same time:
446
499
  >
447
- > 1. **The browser** at `http://127.0.0.1:4242` — where the graph
448
- > will update in real time.
500
+ > 1. **The browser** — where the graph will update in real time.
501
+ > Leave it ready to navigate to a URL; we'll get the link in a
502
+ > moment.
449
503
  > 2. **This terminal** — the one where you're talking to me. You'll
450
504
  > see me announce each reveal here, then confirm what you see
451
505
  > in the browser.
@@ -458,22 +512,31 @@ Tell the tester:
458
512
  > right half. Or any split that lets you see both at once. Tell
459
513
  > me when you're set up and we start.
460
514
 
461
- Wait for confirmation before moving on.
515
+ Wait for confirmation before moving on. Once they're ready, prompt
516
+ them to launch the server and open the link it prints — without
517
+ hardcoding the URL here, since the verb itself is the source of
518
+ truth (it logs the bound `http://host:port` after listen):
519
+
520
+ > In the terminal you opened for `sm`, run the command above. After
521
+ > a couple of seconds it will print a line with the URL where the
522
+ > UI is listening — copy that link and open it in the browser you
523
+ > just arranged. Tell me when you see the page load.
524
+
525
+ Wait for confirmation that the page loaded.
462
526
 
463
527
  #### Reveal 1 — the lone agent
464
528
 
465
529
  Tell the tester:
466
530
 
467
- > The server is running. Open the URL it printed (typically
468
- > **http://127.0.0.1:4242**).
469
- >
470
531
  > You'll see exactly **one node** in the graph: `demo-agent` (kind
471
532
  > `agent`). That's our starting point.
472
533
  >
473
534
  > Walk the 3 views before we go on:
474
535
  > 1. **Graph** — the single agent node.
475
536
  > 2. **List** — one row, with path / kind / metadata.
476
- > 3. **Inspector** — click the node to see frontmatter and links.
537
+ > 3. **Inspector** — click the node to see its frontmatter (the
538
+ > YAML block at the top of every `.md`, between the two `---`
539
+ > lines) and links.
477
540
  >
478
541
  > Did the node show up?
479
542
 
@@ -602,10 +665,18 @@ verbatim.
602
665
 
603
666
  Tell the tester:
604
667
 
605
- > Tu turno. Open `.claude/agents/demo-agent.md` in your editor of
606
- > choice. In the frontmatter, change the `description:` field to
607
- > any text you want the actual content does not matter, just
608
- > make it different from what's there now. Save the file.
668
+ > Your turn. First, in the browser, **expand the `demo-agent`
669
+ > card** (click the chevron / arrow on the card to open it). That
670
+ > reveals the description currently showing for the node that's
671
+ > the field you'll edit next, so leave the card open and the
672
+ > change will be obvious.
673
+ >
674
+ > Now open `.claude/agents/demo-agent.md` in your editor of
675
+ > choice. In the **frontmatter** (the YAML block at the top of
676
+ > the file, between the two `---` lines), change the
677
+ > `description:` field to any text you want — the actual content
678
+ > does not matter, just make it different from what's there now.
679
+ > Save the file.
609
680
  >
610
681
  > Watch the browser. The `demo-agent` card should refresh its
611
682
  > description in real time, no reload, no Ctrl+C — same watcher
@@ -731,9 +802,6 @@ the tester sees what their cwd holds:
731
802
  > │ ├── hooks/demo-hook.md
732
803
  > │ └── skills/demo-skill/SKILL.md
733
804
  > ├── .skill-map/ ← project DB + settings (managed)
734
- > │ ├── settings.json
735
- > │ ├── settings.local.json
736
- > │ └── skill-map.db
737
805
  > ├── .skillmapignore ← the file we're about to edit
738
806
  > ├── notes/
739
807
  > │ ├── todo.md
@@ -781,9 +849,9 @@ verify the appended pattern landed correctly (in case
781
849
  allowed. Once confirmed, ask them to stop the server with
782
850
  **Ctrl+C** in the terminal before continuing.
783
851
 
784
- Mark `3-ui-live: done`.
852
+ Mark `2-ui-live: done`.
785
853
 
786
- ### Step 4 — Wrap-up of the demo and offer to keep going (30 s)
854
+ ### Step 3 — Wrap-up of the demo and offer to keep going (30 s)
787
855
 
788
856
  > All set! That's the heart of skill-map: you edit a `.md` and the
789
857
  > UI sees it instantly. In **~7 minutes** you've already seen the
@@ -838,9 +906,9 @@ Save into `tester.level` and modulate:
838
906
  they can do it from their editor.
839
907
 
840
908
  This stage needs the server running. **Check first** before asking
841
- them to launch it: many testers leave it running from Step 3 and
909
+ them to launch it: many testers leave it running from Step 2 and
842
910
  the demo wraps without an explicit Ctrl+C. Word the prompt as a
843
- conditional, e.g. "If the server from Step 3 is still up, leave it
911
+ conditional, e.g. "If the server from Step 2 is still up, leave it
844
912
  — if not, run `sm` again from the tutorial cwd and reopen the
845
913
  browser." Do not just say "start it again" — that risks a second
846
914
  process trying to bind the same port and confusing the tester.
@@ -963,8 +1031,9 @@ generate a report file to send to Pusher**:
963
1031
 
964
1032
  > Thanks! That's a wrap. Before closing:
965
1033
  >
966
- > Want me to generate a consolidated **report file** (recap of the
967
- > walkthrough + findings + environment) ready to send to **Pusher**?
1034
+ > Want me to generate a consolidated **report file** (a recap of
1035
+ > the walkthrough + findings the bugs or rough edges you spotted
1036
+ > along the way — + environment info) ready to send to **Pusher**?
968
1037
  > I'll save it as `<cwd>/sm-tutorial-report.md`.
969
1038
  >
970
1039
  > 1. **Yes, generate it**
package/dist/cli.js CHANGED
@@ -617,6 +617,18 @@ function readIgnoreFileText(scopeRoot) {
617
617
  return void 0;
618
618
  }
619
619
  }
620
+ async function readIgnoreFileTextStable(scopeRoot, opts = {}) {
621
+ const pollMs = opts.pollMs ?? 50;
622
+ const maxAttempts = opts.maxAttempts ?? 10;
623
+ let prev = readIgnoreFileText(scopeRoot);
624
+ for (let i = 0; i < maxAttempts; i++) {
625
+ await new Promise((r) => setTimeout(r, pollMs));
626
+ const curr = readIgnoreFileText(scopeRoot);
627
+ if (curr === prev) return curr;
628
+ prev = curr;
629
+ }
630
+ return prev;
631
+ }
620
632
  var cachedDefaults = null;
621
633
  function loadDefaultsText() {
622
634
  if (cachedDefaults !== null) return cachedDefaults;
@@ -7362,7 +7374,7 @@ import { Command as Command8, Option as Option8 } from "clipanion";
7362
7374
  // package.json
7363
7375
  var package_default = {
7364
7376
  name: "@skill-map/cli",
7365
- version: "0.16.1",
7377
+ version: "0.16.2",
7366
7378
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
7367
7379
  license: "MIT",
7368
7380
  type: "module",
@@ -11591,11 +11603,10 @@ async function runWatchLoop(opts) {
11591
11603
  const runtimeCtx = defaultRuntimeContext();
11592
11604
  const { cwd } = runtimeCtx;
11593
11605
  const loadEffectiveConfig = () => loadConfig({ scope: "project", strict: opts.strict, ...runtimeCtx }).effective;
11594
- const buildCurrentIgnoreFilter = (cfgIn) => {
11595
- const text = readIgnoreFileText(cwd);
11606
+ const composeIgnoreFilter = (cfgIn, ignoreFileText) => {
11596
11607
  const filterOpts = {};
11597
11608
  if (cfgIn.ignore.length > 0) filterOpts.configIgnore = cfgIn.ignore;
11598
- if (text !== void 0) filterOpts.ignoreFileText = text;
11609
+ if (ignoreFileText !== void 0) filterOpts.ignoreFileText = ignoreFileText;
11599
11610
  return buildIgnoreFilter(filterOpts);
11600
11611
  };
11601
11612
  let cfg;
@@ -11606,7 +11617,7 @@ async function runWatchLoop(opts) {
11606
11617
  context.stderr.write(tx(WATCH_TEXTS.configLoadFailure, { message }));
11607
11618
  return ExitCode.Error;
11608
11619
  }
11609
- let ignoreFilter = buildCurrentIgnoreFilter(cfg);
11620
+ let ignoreFilter = composeIgnoreFilter(cfg, readIgnoreFileText(cwd));
11610
11621
  let strict = opts.strict || cfg.scan.strict === true;
11611
11622
  const debounceMs = cfg.scan.watch.debounceMs;
11612
11623
  const dbPath = defaultProjectDbPath(runtimeCtx);
@@ -11743,7 +11754,8 @@ async function runWatchLoop(opts) {
11743
11754
  if (stopRequested) return;
11744
11755
  try {
11745
11756
  cfg = loadEffectiveConfig();
11746
- ignoreFilter = buildCurrentIgnoreFilter(cfg);
11757
+ const stableText = await readIgnoreFileTextStable(cwd);
11758
+ ignoreFilter = composeIgnoreFilter(cfg, stableText);
11747
11759
  strict = opts.strict || cfg.scan.strict === true;
11748
11760
  await handleBatch();
11749
11761
  } catch (err) {
@@ -13237,15 +13249,14 @@ function createWatcherService(opts) {
13237
13249
  cwd,
13238
13250
  homedir: opts.runtimeContext.homedir
13239
13251
  }).effective;
13240
- const buildCurrentIgnoreFilter = (cfgIn) => {
13241
- const ignoreFileText = readIgnoreFileText(cwd);
13252
+ const composeIgnoreFilter = (cfgIn, ignoreFileText) => {
13242
13253
  const filterOpts = {};
13243
13254
  if (cfgIn.ignore.length > 0) filterOpts.configIgnore = cfgIn.ignore;
13244
13255
  if (ignoreFileText !== void 0) filterOpts.ignoreFileText = ignoreFileText;
13245
13256
  return buildIgnoreFilter(filterOpts);
13246
13257
  };
13247
13258
  let cfg = loadEffectiveConfig();
13248
- let ignoreFilter = buildCurrentIgnoreFilter(cfg);
13259
+ let ignoreFilter = composeIgnoreFilter(cfg, readIgnoreFileText(cwd));
13249
13260
  const debounceMs = opts.debounceMsOverride ?? cfg.scan.watch.debounceMs;
13250
13261
  const pluginRuntime = opts.options.noPlugins ? emptyPluginRuntime() : await loadPluginRuntime({ scope: opts.options.scope });
13251
13262
  for (const warn of pluginRuntime.warnings) {
@@ -13321,7 +13332,8 @@ function createWatcherService(opts) {
13321
13332
  if (stopped) return;
13322
13333
  try {
13323
13334
  cfg = loadEffectiveConfig();
13324
- ignoreFilter = buildCurrentIgnoreFilter(cfg);
13335
+ const stableText = await readIgnoreFileTextStable(cwd);
13336
+ ignoreFilter = composeIgnoreFilter(cfg, stableText);
13325
13337
  await runOneBatch();
13326
13338
  } catch (err) {
13327
13339
  const message = formatErrorMessage(err);