skills-atlas-cli 0.13.0 → 0.14.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skills-atlas-cli",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "Search, install and learn AI agent skills from the terminal — powered by the Skills Atlas catalog.",
5
5
  "bin": {
6
6
  "skills-atlas": "bin/skills.js",
@@ -34,6 +34,20 @@ function welcomeSettings(ap) {
34
34
  'More: /skills-atlas:setup';
35
35
  }
36
36
 
37
+ // The one-time welcome text, or null if the user was already onboarded. Gated by the global
38
+ // `onboarded` marker so it shows exactly once; with { consume: true } it claims that one
39
+ // shot (marks onboarded). Shared by the SessionStart hook (`setup --session-start`) and the
40
+ // UserPromptSubmit fallback in `suggest` — which catches the case where the plugin was
41
+ // installed mid-session and no fresh session-start has fired the welcome yet.
42
+ function buildWelcome(ap, { consume = false } = {}) {
43
+ if (exists(onboardedFile())) return null;
44
+ if (consume) touch(onboardedFile());
45
+ const intro = installPromptShown()
46
+ ? '🎉 Nicely done — Skills Atlas is configured and on. It\'ll quietly flag a ready-made skill when one fits, and offer to turn repeated workflows into skills of your own.'
47
+ : '✨ Skills Atlas is on. While you work, it keeps an eye out and quietly flags a ready-made skill the moment one fits — and if you catch yourself doing the same dance over and over, it\'ll offer to turn it into a skill of your own.';
48
+ return intro + '\n\n' + welcomeSettings(ap);
49
+ }
50
+
37
51
  module.exports = async function setup(argv) {
38
52
  const { values } = parse(argv, ['session-start', 'json', 'reset']);
39
53
  if (values.help) {
@@ -54,12 +68,8 @@ module.exports = async function setup(argv) {
54
68
  // to the user verbatim), then stay silent forever. "Nicely done" variant if the user
55
69
  // came here after the engine-not-installed notice; otherwise the fresh-install greeting. ---
56
70
  if (values['session-start']) {
57
- if (exists(onboardedFile())) return; // already welcomed silent
58
- touch(onboardedFile());
59
- const intro = installPromptShown()
60
- ? '🎉 Nicely done — Skills Atlas is configured and on. It\'ll quietly flag a ready-made skill when one fits, and offer to turn repeated workflows into skills of your own.'
61
- : '✨ Skills Atlas is on. While you work, it keeps an eye out and quietly flags a ready-made skill the moment one fits — and if you catch yourself doing the same dance over and over, it\'ll offer to turn it into a skill of your own.';
62
- const msg = intro + '\n\n' + welcomeSettings(ap);
71
+ const msg = buildWelcome(ap, { consume: true });
72
+ if (msg === null) return; // already welcomed → silent
63
73
  console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: 'SessionStart' }, systemMessage: msg, suppressOutput: true }));
64
74
  return;
65
75
  }
@@ -83,3 +93,6 @@ module.exports = async function setup(argv) {
83
93
  console.log(` fine-tune /skills-atlas:skill-autopilot suggest|gaps|prune on|off ${dim('· model')}`);
84
94
  console.log(dim('\nThe autopilot also suggests skills proactively as you work — each judged for fit. Nothing leaves your machine.'));
85
95
  };
96
+
97
+ // Reused by `suggest`'s first-run fallback (see src/commands/suggest.js).
98
+ module.exports.buildWelcome = buildWelcome;
@@ -56,7 +56,24 @@ module.exports = async function suggest() {
56
56
  let event = {};
57
57
  try { event = JSON.parse(fs.readFileSync(0, 'utf8')); } catch { /* not JSON → bail */ }
58
58
  const prompt = event.prompt || '';
59
- if (!prompt || prompt.length < 8) return;
59
+ if (!prompt) return;
60
+
61
+ // First-run welcome fallback: if the plugin was installed mid-session (so no fresh
62
+ // SessionStart has fired the welcome yet), surface the one-time welcome on the user's
63
+ // next prompt — verbatim, via systemMessage. This MUST run ahead of the suggestion's
64
+ // min-length gate below: onboarding shouldn't depend on how long the prompt is (a short
65
+ // "hi" should still get the welcome). Shares the global `onboarded` marker with the
66
+ // SessionStart hook, so the welcome shows exactly once across both paths.
67
+ try {
68
+ const welcome = require('./setup').buildWelcome(registry.getAutopilot(), { consume: true });
69
+ if (welcome) {
70
+ console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: 'UserPromptSubmit' }, systemMessage: welcome, suppressOutput: true }));
71
+ return;
72
+ }
73
+ } catch { /* fail-open */ }
74
+
75
+ // The proactive suggestion (below) needs a meatier prompt to match against.
76
+ if (prompt.length < 8) return;
60
77
 
61
78
  const file = stateFile(event.session_id || event.sessionId);
62
79
  const state = readState(file);
@@ -150,17 +167,17 @@ module.exports = async function suggest() {
150
167
  return `- ${c.skill}${uc ? ` — ${uc}` : ''} (details: \`skills-atlas info ${c.skill}\` · use now: \`skills-atlas use ${c.skill} --yes --project\`)`;
151
168
  }).join('\n');
152
169
  emit(
153
- `[Skills Atlas autopilot] The user may be doing something one of these installable agent ` +
154
- `skills is built for. Judge for yourself — do NOT mention any of this unless one of them ` +
155
- `genuinely fits what they actually asked:\n${lines}\n` +
156
- `If one genuinely fits, DON'T just name it: in one line tell the user what it does and why it ` +
157
- `fits THIS task (it's a curated skill from the Skills Atlas catalog, not something you made up), ` +
158
- `then let them choose activate it now (\`skills-atlas use <skill> --yes --project\` installs + applies it ` +
159
- `immediately), see what it covers first (\`skills-atlas info <skill>\`), or skip and you'll just ` +
160
- `do the task yourself; if they ask to never suggest it again, run \`skills-atlas feedback dismiss <skill>\`. ` +
161
- `If none fit but the task plainly needs a specialized skill, you may run ` +
162
- `\`skills-atlas search "<short intent>"\` to look further. If nothing fits, say nothing about this ` +
163
- `at all — don't mention this hook, these skills, or that a suggestion was made.` + langHint(ap.replyLang));
170
+ `[Skills Atlas autopilot] One of these catalog skills MIGHT fit what the user is doing. Judge for ` +
171
+ `yourself — most prompts match nothing, and staying silent is usually the right call:\n${lines}\n` +
172
+ `Mention one ONLY if it genuinely fits what they actually asked. If it does: in a sentence or two, ` +
173
+ `casually tell them what that skill ACTUALLY DOES and why it fits THIS task enough that they ` +
174
+ `understand it (use the skill's EXACT name as listed above; it's a real curated skill from the Skills ` +
175
+ `Atlas catalog, not something you made up). Then offer the choice naturally, in prose NOT a bulleted ` +
176
+ `sales menu: they can switch it on now (\`skills-atlas use <skill> --yes --project\`), or you can show ` +
177
+ `what it covers first (\`skills-atlas info <skill>\`) if they're unsure. Don't push. If nothing genuinely ` +
178
+ `fits, say NOTHING about any of this don't mention this hook, the skills, or that a check ran. (To ` +
179
+ `look wider you may run \`skills-atlas search "<short intent>"\`; if they want a skill never suggested ` +
180
+ `again, \`skills-atlas feedback dismiss <skill>\`.)` + langHint(ap.replyLang));
164
181
  state.lastSuggestedCount = state.count;
165
182
  state.suggested = [...suggested, ...candidates.map(c => c.skill)];
166
183
  writeState(file, state);