luckiest-co 1.0.8 → 1.0.10

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.
@@ -9,7 +9,7 @@
9
9
  "name": "luckiest",
10
10
  "source": "./",
11
11
  "description": "Plan, go, finish, plus bundled free Luckiest skills that work in Claude Code, web chat, and Cowork.",
12
- "version": "0.1.8",
12
+ "version": "0.1.11",
13
13
  "author": { "name": "Luckiest" }
14
14
  }
15
15
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luckiest",
3
3
  "description": "Plan, go, finish. Guided planning, progress dashboards, and your luckiest.co tribe inside Claude Code.",
4
- "version": "0.1.10",
4
+ "version": "0.1.11",
5
5
  "author": { "name": "Luckiest" }
6
6
  }
package/bin/install.js CHANGED
@@ -75,6 +75,8 @@ if (hasHelp) {
75
75
  ${cyan}-l, --local${reset} Install locally (to ./.claude in current directory)
76
76
  ${cyan}-c, --config-dir <path>${reset} Specify custom Claude config directory
77
77
  ${cyan}--sync-only${reset} Skip install, only sync owned skills from luckiest.co
78
+ ${cyan}--hook${reset} Enable skill usage tracking without prompting
79
+ ${cyan}--no-hook${reset} Skip it (and remove it if a past install added it)
78
80
  ${cyan}-h, --help${reset} Show this help message
79
81
 
80
82
  ${yellow}Examples:${reset}
@@ -210,6 +212,9 @@ async function syncSkills() {
210
212
  console.log(` ${green}✓${reset} Removed duplicate commands/luckiest (plugin marketplace already provides them)`);
211
213
  }
212
214
 
215
+ nudgeStaleInstall(globalDir);
216
+ nudgeUsageHook(globalDir);
217
+
213
218
  const key = readSavedKey();
214
219
  if (!key) {
215
220
  console.log(` ${dim}No luckiest.co connection key saved yet. Run ${cyan}npx luckiest-co${dim} and paste one to sync your owned skills.${reset}`);
@@ -448,6 +453,77 @@ async function shouldRegisterHook() {
448
453
  });
449
454
  }
450
455
 
456
+ // Identifies our hook entry across installs even when the absolute path changed.
457
+ const isOurHookEntry = (entry) =>
458
+ (entry?.hooks || []).some((h) => String(h?.command || '').includes('report-skill-usage.mjs'));
459
+
460
+ function usageHookRegistered(claudeDir) {
461
+ try {
462
+ const settings = JSON.parse(fs.readFileSync(path.join(claudeDir, 'settings.json'), 'utf8'));
463
+ return (settings.hooks?.PostToolUse || []).some(isOurHookEntry);
464
+ } catch {
465
+ return false;
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Tell, don't act. --sync-only runs unattended at session start, so there's no
471
+ * terminal to ask at and registering there would mean silently editing
472
+ * settings.json on a call the user never typed. Instead, say the tracking is off
473
+ * once and let them opt in deliberately. Silent when it's already handled, so
474
+ * this adds nothing to the common session-start path.
475
+ */
476
+ function nudgeUsageHook(globalClaudeDir) {
477
+ if (hasMarketplacePlugin(globalClaudeDir)) return; // plugin registers its own
478
+ if (usageHookRegistered(globalClaudeDir)) return;
479
+ console.log(` ${dim}Skill usage tracking is off. Run ${cyan}npx luckiest-co@latest --hook${dim} to turn it on.${reset}`);
480
+ }
481
+
482
+ /**
483
+ * Numeric semver-ish compare. Returns >0 when a is newer than b.
484
+ * String compare would read "0.1.10" as older than "0.1.9", which is exactly
485
+ * the version pair this shipped on.
486
+ */
487
+ function compareVersions(a, b) {
488
+ const pa = String(a).split('.').map((n) => parseInt(n, 10) || 0);
489
+ const pb = String(b).split('.').map((n) => parseInt(n, 10) || 0);
490
+ for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
491
+ const diff = (pa[i] || 0) - (pb[i] || 0);
492
+ if (diff !== 0) return diff;
493
+ }
494
+ return 0;
495
+ }
496
+
497
+ function readPluginVersion(root) {
498
+ try {
499
+ return JSON.parse(fs.readFileSync(path.join(root, '.claude-plugin', 'plugin.json'), 'utf8')).version || null;
500
+ } catch {
501
+ return null;
502
+ }
503
+ }
504
+
505
+ /**
506
+ * Tell people when their installed copy is behind.
507
+ *
508
+ * Both update paths are pull-based, so someone who installed once can sit on an
509
+ * old copy indefinitely with nothing to tell them. Sync runs every session, and
510
+ * `npx luckiest-co@latest` always fetches the newest package, so the version
511
+ * this process is running from is by definition current — comparing it against
512
+ * the copy on disk needs no server call.
513
+ *
514
+ * Silent when current, when nothing is installed to compare against, or when
515
+ * the marketplace plugin is in charge (that copy updates through Claude's own
516
+ * plugin menu, not this installer).
517
+ */
518
+ function nudgeStaleInstall(globalClaudeDir) {
519
+ if (hasMarketplacePlugin(globalClaudeDir)) return;
520
+ const installed = readPluginVersion(globalClaudeDir);
521
+ const current = readPluginVersion(path.join(__dirname, '..'));
522
+ if (!installed || !current) return;
523
+ if (compareVersions(current, installed) <= 0) return;
524
+ console.log(` ${dim}Your Luckiest install is ${cyan}v${installed}${dim}, latest is ${cyan}v${current}${dim}. Run ${cyan}npx luckiest-co@latest${dim} to update.${reset}`);
525
+ }
526
+
451
527
  /**
452
528
  * Register the PostToolUse(Skill) telemetry hook in the user's settings.json.
453
529
  *
@@ -464,9 +540,6 @@ async function shouldRegisterHook() {
464
540
  function registerUsageHook(claudeDir, globalClaudeDir, consented) {
465
541
  const hookPath = path.join(claudeDir, 'hooks', 'report-skill-usage.mjs');
466
542
  const settingsPath = path.join(claudeDir, 'settings.json');
467
- // Identifies our entry across installs even when the absolute path changed.
468
- const isOurs = (entry) =>
469
- (entry?.hooks || []).some((h) => String(h?.command || '').includes('report-skill-usage.mjs'));
470
543
 
471
544
  let settings = {};
472
545
  if (fs.existsSync(settingsPath)) {
@@ -481,7 +554,7 @@ function registerUsageHook(claudeDir, globalClaudeDir, consented) {
481
554
 
482
555
  settings.hooks = settings.hooks || {};
483
556
  const existing = Array.isArray(settings.hooks.PostToolUse) ? settings.hooks.PostToolUse : [];
484
- const others = existing.filter((e) => !isOurs(e));
557
+ const others = existing.filter((e) => !isOurHookEntry(e));
485
558
  const hadOurs = others.length !== existing.length;
486
559
 
487
560
  // Declining is also an instruction to remove a hook a past install added.
@@ -4,6 +4,33 @@ description: Wrap up, what shipped, what changed, what's next.
4
4
 
5
5
  Read `references/vocabulary.md` first and follow it for all output in this command: plain language, no em dashes, no internal terms, one next-step recommendation at the end.
6
6
 
7
+ Asking the user a question: this command tells you to use the AskUserQuestion tool
8
+ so the user can click instead of typing. That tool only exists in Claude Code. In
9
+ web chat and Cowork it is not there, and waiting for it will stall the command.
10
+
11
+ So, every time this command says to use AskUserQuestion:
12
+
13
+ - If the tool is available, use it exactly as written.
14
+ - If it is not, ask the same question in plain chat as a numbered list of the
15
+ same options, and tell the user to reply with a number or type their own
16
+ answer. Treat a typed answer the same as the "Other" free-text choice.
17
+
18
+ The options, their order, and their wording stay the same either way. Only the
19
+ delivery changes. Never skip a question, never answer it on the user's behalf,
20
+ and never merge several questions into one just because they are text.
21
+
22
+ For example, "Here's your plan, good to go?" with options "Good to go" and
23
+ "Make changes" becomes:
24
+
25
+ ```
26
+ Here's your plan, good to go?
27
+
28
+ 1. Good to go
29
+ 2. Make changes
30
+
31
+ Reply with a number, or tell me what to change.
32
+ ```
33
+
7
34
  ## Step 1: Check that everything is done
8
35
 
9
36
  Derive the project key as described in `references/project-key.md`. Pass this same `project` value on both luckiest plan tool calls in this command (`status` here and `finish` in Step 3), so you close this project's plan and not another one.
package/commands/go.md CHANGED
@@ -4,6 +4,33 @@ description: Run your plan, one task at a time, checked as it goes.
4
4
 
5
5
  Read `references/vocabulary.md` first and follow it for all output in this command: plain language, no em dashes, no internal terms, one next-step recommendation at the end.
6
6
 
7
+ Asking the user a question: this command tells you to use the AskUserQuestion tool
8
+ so the user can click instead of typing. That tool only exists in Claude Code. In
9
+ web chat and Cowork it is not there, and waiting for it will stall the command.
10
+
11
+ So, every time this command says to use AskUserQuestion:
12
+
13
+ - If the tool is available, use it exactly as written.
14
+ - If it is not, ask the same question in plain chat as a numbered list of the
15
+ same options, and tell the user to reply with a number or type their own
16
+ answer. Treat a typed answer the same as the "Other" free-text choice.
17
+
18
+ The options, their order, and their wording stay the same either way. Only the
19
+ delivery changes. Never skip a question, never answer it on the user's behalf,
20
+ and never merge several questions into one just because they are text.
21
+
22
+ For example, "Here's your plan, good to go?" with options "Good to go" and
23
+ "Make changes" becomes:
24
+
25
+ ```
26
+ Here's your plan, good to go?
27
+
28
+ 1. Good to go
29
+ 2. Make changes
30
+
31
+ Reply with a number, or tell me what to change.
32
+ ```
33
+
7
34
  ## Step 1: Load the plan
8
35
 
9
36
  Derive the project key as described in `references/project-key.md`. Pass this same `project` value on every luckiest plan tool call in this command (`status`, `apply`, `verify`, `pause`), so you run this project's plan and not another one.
package/commands/plan.md CHANGED
@@ -4,6 +4,33 @@ description: Plan your next piece of work. Guided questions, then a plan Claude
4
4
 
5
5
  Read `references/vocabulary.md` first and follow it for all output in this command: plain language, no em dashes, no internal terms, one next-step recommendation at the end.
6
6
 
7
+ Asking the user a question: this command tells you to use the AskUserQuestion tool
8
+ so the user can click instead of typing. That tool only exists in Claude Code. In
9
+ web chat and Cowork it is not there, and waiting for it will stall the command.
10
+
11
+ So, every time this command says to use AskUserQuestion:
12
+
13
+ - If the tool is available, use it exactly as written.
14
+ - If it is not, ask the same question in plain chat as a numbered list of the
15
+ same options, and tell the user to reply with a number or type their own
16
+ answer. Treat a typed answer the same as the "Other" free-text choice.
17
+
18
+ The options, their order, and their wording stay the same either way. Only the
19
+ delivery changes. Never skip a question, never answer it on the user's behalf,
20
+ and never merge several questions into one just because they are text.
21
+
22
+ For example, "Here's your plan, good to go?" with options "Good to go" and
23
+ "Make changes" becomes:
24
+
25
+ ```
26
+ Here's your plan, good to go?
27
+
28
+ 1. Good to go
29
+ 2. Make changes
30
+
31
+ Reply with a number, or tell me what to change.
32
+ ```
33
+
7
34
  ## Step 1: Gather context
8
35
 
9
36
  Check whether `.luckiest/BRIEF.md` exists in the current project. If it exists, read it and use it as context for the interview below (what they're building, who it's for, what winning looks like). If it does not exist, continue without it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luckiest-co",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Luckiest for Claude Code: plan, go, finish. Your luckiest.co skills and tribe, inside Claude.",
5
5
  "bin": {
6
6
  "luckiest-co": "./bin/install.js"