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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/bin/install.js +77 -4
- package/commands/finish.md +27 -0
- package/commands/go.md +27 -0
- package/commands/plan.md +27 -0
- package/package.json +1 -1
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) => !
|
|
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.
|
package/commands/finish.md
CHANGED
|
@@ -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.
|