luckiest-co 1.0.3 → 1.0.7
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 +65 -1
- package/commands/finish.md +10 -3
- package/commands/go.md +16 -7
- package/commands/plan.md +10 -8
- package/commands/status.md +1 -1
- package/package.json +1 -1
- package/references/project-key.md +24 -0
package/bin/install.js
CHANGED
|
@@ -195,6 +195,16 @@ function hasUnzip() {
|
|
|
195
195
|
* Never logs the key or the Authorization header.
|
|
196
196
|
*/
|
|
197
197
|
async function syncSkills() {
|
|
198
|
+
// Self-heal the /luckiest:luckiest:* duplicate: older installers copied
|
|
199
|
+
// commands into ~/.claude/commands/luckiest even when the marketplace
|
|
200
|
+
// plugin already registers them. Sync runs every session, so clean it here.
|
|
201
|
+
const globalDir = expandTilde(explicitConfigDir) || expandTilde(process.env.CLAUDE_CONFIG_DIR) || path.join(os.homedir(), '.claude');
|
|
202
|
+
const staleCommands = path.join(globalDir, 'commands', 'luckiest');
|
|
203
|
+
if (hasMarketplacePlugin(globalDir) && fs.existsSync(staleCommands)) {
|
|
204
|
+
fs.rmSync(staleCommands, { recursive: true, force: true });
|
|
205
|
+
console.log(` ${green}✓${reset} Removed duplicate commands/luckiest (plugin marketplace already provides them)`);
|
|
206
|
+
}
|
|
207
|
+
|
|
198
208
|
const key = readSavedKey();
|
|
199
209
|
if (!key) {
|
|
200
210
|
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}`);
|
|
@@ -288,6 +298,48 @@ async function syncSkills() {
|
|
|
288
298
|
console.log('');
|
|
289
299
|
}
|
|
290
300
|
|
|
301
|
+
/**
|
|
302
|
+
* True when the luckiest plugin is installed AND enabled through the Claude
|
|
303
|
+
* Code plugin marketplace (which registers commands itself). A disabled
|
|
304
|
+
* plugin still appears in installed_plugins.json but registers nothing, so
|
|
305
|
+
* treating it as present would delete the user's commands/luckiest copy and
|
|
306
|
+
* leave them with no /luckiest:* commands at all.
|
|
307
|
+
*/
|
|
308
|
+
function hasMarketplacePlugin(globalClaudeDir) {
|
|
309
|
+
try {
|
|
310
|
+
const manifest = JSON.parse(
|
|
311
|
+
fs.readFileSync(path.join(globalClaudeDir, 'plugins', 'installed_plugins.json'), 'utf8')
|
|
312
|
+
);
|
|
313
|
+
const entries = Object.entries(manifest.plugins || {}).filter(([k]) => k.startsWith('luckiest@'));
|
|
314
|
+
if (entries.length === 0) return false;
|
|
315
|
+
|
|
316
|
+
// Some Claude Code versions record enabled state on the install entry itself.
|
|
317
|
+
const entryDisabled = entries.every(([, v]) => {
|
|
318
|
+
const items = Array.isArray(v) ? v : [v];
|
|
319
|
+
return items.every((item) => item && typeof item === 'object' && item.enabled === false);
|
|
320
|
+
});
|
|
321
|
+
if (entryDisabled) return false;
|
|
322
|
+
|
|
323
|
+
// Others track it in settings.json under enabledPlugins ("name@marketplace": bool).
|
|
324
|
+
try {
|
|
325
|
+
const settings = JSON.parse(
|
|
326
|
+
fs.readFileSync(path.join(globalClaudeDir, 'settings.json'), 'utf8')
|
|
327
|
+
);
|
|
328
|
+
const enabledMap = settings.enabledPlugins || {};
|
|
329
|
+
const flags = entries
|
|
330
|
+
.map(([k]) => enabledMap[k])
|
|
331
|
+
.filter((flag) => typeof flag === 'boolean');
|
|
332
|
+
if (flags.length > 0 && flags.every((flag) => flag === false)) return false;
|
|
333
|
+
} catch {
|
|
334
|
+
// No readable settings.json — fall through to "installed means enabled".
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return true;
|
|
338
|
+
} catch {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
291
343
|
/**
|
|
292
344
|
* Install to the specified directory
|
|
293
345
|
*/
|
|
@@ -313,12 +365,24 @@ function install(isGlobal) {
|
|
|
313
365
|
// Copy flat commands/ into commands/luckiest so the subfolder becomes the
|
|
314
366
|
// `luckiest:` command namespace (=> /luckiest:plan). The repo keeps commands
|
|
315
367
|
// flat so the plugin install path yields the same single namespace.
|
|
368
|
+
//
|
|
369
|
+
// If the luckiest plugin is already installed via the Claude Code plugin
|
|
370
|
+
// marketplace, its commands are registered there; a file copy here would
|
|
371
|
+
// register them a second time and produce /luckiest:luckiest:* duplicates.
|
|
372
|
+
// In that case skip the copy and remove any stale copy from past installs.
|
|
316
373
|
const commandsDir = path.join(claudeDir, 'commands');
|
|
317
374
|
fs.mkdirSync(commandsDir, { recursive: true });
|
|
318
375
|
|
|
319
376
|
const commandsSrc = path.join(src, 'commands');
|
|
320
377
|
const commandsDest = path.join(commandsDir, 'luckiest');
|
|
321
|
-
if (
|
|
378
|
+
if (hasMarketplacePlugin(defaultGlobalDir)) {
|
|
379
|
+
if (fs.existsSync(commandsDest)) {
|
|
380
|
+
fs.rmSync(commandsDest, { recursive: true, force: true });
|
|
381
|
+
console.log(` ${green}✓${reset} Removed duplicate commands/luckiest (plugin marketplace already provides them)`);
|
|
382
|
+
} else {
|
|
383
|
+
console.log(` ${dim}Skipped commands/luckiest (plugin marketplace already provides them)${reset}`);
|
|
384
|
+
}
|
|
385
|
+
} else if (fs.existsSync(commandsSrc)) {
|
|
322
386
|
copyWithPathReplacement(commandsSrc, commandsDest, pathPrefix);
|
|
323
387
|
console.log(` ${green}✓${reset} Installed commands/luckiest`);
|
|
324
388
|
}
|
package/commands/finish.md
CHANGED
|
@@ -6,7 +6,9 @@ Read `references/vocabulary.md` first and follow it for all output in this comma
|
|
|
6
6
|
|
|
7
7
|
## Step 1: Check that everything is done
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
Call the `status` tool from the luckiest MCP server with that `project` value.
|
|
10
12
|
|
|
11
13
|
If any task is still open (not complete), list those tasks for the user and stop here. Do not proceed to the rest of this command. End your response with exactly one line, nothing after it:
|
|
12
14
|
|
|
@@ -28,7 +30,7 @@ After the recap, confirm with the AskUserQuestion tool so the user can click ins
|
|
|
28
30
|
Call the `finish` tool from the luckiest MCP server with:
|
|
29
31
|
|
|
30
32
|
```
|
|
31
|
-
{ decisions, keyFiles, deferred }
|
|
33
|
+
{ project: <the project key from Step 1>, decisions, keyFiles, deferred }
|
|
32
34
|
```
|
|
33
35
|
|
|
34
36
|
using the same lists from your recap. This awards Charms and archives the history automatically.
|
|
@@ -37,6 +39,11 @@ Report the Charms earned in friendly, plain terms, for example: "You earned 3 ch
|
|
|
37
39
|
|
|
38
40
|
## Step 4: Wrap up
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
Offer the next move with the AskUserQuestion tool so the user can click instead of retyping a command. Question: "What's next?" Options (keep the "Other" free-text choice available):
|
|
43
|
+
|
|
44
|
+
- "Plan the next piece" — on this pick, start the `/luckiest plan` flow now, fresh, as if newly invoked.
|
|
45
|
+
- "See where I stand" — on this pick, run the `/luckiest home` flow now.
|
|
46
|
+
|
|
47
|
+
Whichever they click, start that flow immediately in this session so they never have to type the command themselves. If they pick "Other" or dismiss, end with exactly one line, nothing after it:
|
|
41
48
|
|
|
42
49
|
Next: /luckiest plan for the next piece, or /luckiest home to see where you stand.
|
package/commands/go.md
CHANGED
|
@@ -6,17 +6,26 @@ Read `references/vocabulary.md` first and follow it for all output in this comma
|
|
|
6
6
|
|
|
7
7
|
## Step 1: Load the plan
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
Call the `status` tool from the luckiest MCP server with that `project` value. This is a zero-context resume, treat its result as the full picture of where things stand: don't assume anything about prior state beyond what it returns.
|
|
10
12
|
|
|
11
13
|
## Step 2: Pick how to run
|
|
12
14
|
|
|
13
15
|
Default: do all task work yourself, in this session, message by message. This is the safe, reviewable mode.
|
|
14
16
|
|
|
15
|
-
Fast mode (subagents): if the user wants tasks done faster, you may hand a ready task to a subagent instead of doing it yourself. Only do this when the user has asked to go fast, or says yes when you offer it.
|
|
17
|
+
Fast mode (subagents): if the user wants tasks done faster, you may hand a ready task to a subagent instead of doing it yourself. Only do this when the user has asked to go fast, or says yes when you offer it.
|
|
18
|
+
|
|
19
|
+
Before dispatching anything, check that you can actually assign agents: confirm the Agent (or Task) tool is available in this session. If it is not, say so plainly and fall back to default mode. Never claim work was handed off to a subagent you could not launch.
|
|
20
|
+
|
|
21
|
+
When you do run in fast mode, assign work top down in this order: subagents > tasks > skills > model.
|
|
22
|
+
|
|
23
|
+
- Subagents: one subagent owns a task. Independent tasks run in parallel; tasks that depend on an earlier one wait for it.
|
|
24
|
+
- Tasks: give each subagent one ready task from `status`, turned into a tight working prompt.
|
|
25
|
+
- Skills: inside its task, the subagent invokes the task's suggested skill via the Skill tool.
|
|
26
|
+
- Model: run each subagent on the task's `model` hint from `status` (light work like haiku, heavy work like opus), so each task uses the smallest model that fits and saves tokens.
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
- Independent tasks can run in parallel; tasks that depend on an earlier one wait for it.
|
|
19
|
-
- You still own Step 3's checks: read the subagent's result, hold it to the "done means..." line, and only then apply and verify.
|
|
28
|
+
You still own Step 3's checks: read the subagent's result, hold it to the "done means..." line, and only then apply and verify.
|
|
20
29
|
|
|
21
30
|
Research subagents (looking something up, exploring the codebase) are always allowed in either mode.
|
|
22
31
|
|
|
@@ -28,7 +37,7 @@ Take the ready tasks one at a time, in order. For each one:
|
|
|
28
37
|
2. Do the work using the task's suggested skill. If that skill is installed, invoke it via the Skill tool. If it isn't installed, do the work directly without it.
|
|
29
38
|
3. Check your result against the task's "done means..." line. Don't move on until it's actually met.
|
|
30
39
|
4. If the result is something the user can try themselves (a page, a feature, a flow), ask with the AskUserQuestion tool so they can click instead of typing. Question: "Try it yourself, does it work?" Options: "Works" and "Needs fixes" (keep the "Other" free-text choice available). Wait for their answer.
|
|
31
|
-
5. On a pass, call the `apply` tool with `{ taskId }` for that task, then call the `verify` tool with `{ results: [{ taskId, pass: true }] }`.
|
|
40
|
+
5. On a pass, call the `apply` tool with `{ project, taskId }` for that task, then call the `verify` tool with `{ project, results: [{ taskId, pass: true }] }`. Use the same `project` from Step 1.
|
|
32
41
|
6. On a fail, fix the problem before moving on to the next task. Only call `verify` with `pass: false` for that task if the user explicitly chooses to defer the fix instead of having you fix it now.
|
|
33
42
|
|
|
34
43
|
Only move to the next ready task once the current one is applied and verified (or deferred).
|
|
@@ -43,7 +52,7 @@ Pause and ask the user before doing any of the following, even if it seems like
|
|
|
43
52
|
- Adding a new dependency.
|
|
44
53
|
- Any schema change.
|
|
45
54
|
|
|
46
|
-
If the session has to end before the plan is done, call the `pause` tool with `{ reason }
|
|
55
|
+
If the session has to end before the plan is done, call the `pause` tool with `{ project, reason }` (same `project` from Step 1), a one-line reason describing where things were left.
|
|
47
56
|
|
|
48
57
|
## Step 5: Wrap up
|
|
49
58
|
|
package/commands/plan.md
CHANGED
|
@@ -10,7 +10,9 @@ Check whether `.luckiest/BRIEF.md` exists in the current project. If it exists,
|
|
|
10
10
|
|
|
11
11
|
## Step 2: Check for active work
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
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` here and `plan` in Step 5), so this project gets its own plan and does not collide with another project's.
|
|
14
|
+
|
|
15
|
+
Call the `status` tool from the luckiest MCP server with that `project` value.
|
|
14
16
|
|
|
15
17
|
- If the returned state's position is active (already in progress), stop here. Tell the user a plan is already active and offer `/luckiest go` to resume it instead. Do not stage a new plan over an active one unless the user explicitly confirms they want to replace it. If they confirm, continue to Step 3.
|
|
16
18
|
- Otherwise, continue to Step 3.
|
|
@@ -21,7 +23,7 @@ Open the interview with a short line that says no plan is active and you are sta
|
|
|
21
23
|
|
|
22
24
|
Then ask question 1 using the AskUserQuestion tool so the user can click an answer instead of typing one. Do not put the examples in plain text for them to copy. Present them as selectable options:
|
|
23
25
|
|
|
24
|
-
1.
|
|
26
|
+
1. Let's confirm what best describes the outcome you want
|
|
25
27
|
|
|
26
28
|
Build 3 to 4 options for that question. Draw them from their brief if one exists, otherwise use plausible outcomes for their project (for example: "About page rewritten to explain the full skills network, live on luckiest.co"). Keep the "Other" free-text choice available so they can still write their own outcome if none fit.
|
|
27
29
|
|
|
@@ -33,7 +35,9 @@ Include non-coding work too. Marketing, content, design, research, and ops tasks
|
|
|
33
35
|
|
|
34
36
|
For each draft task, call the `skill_router` tool from the luckiest MCP server. It returns `skills` (matching owned skills) and `who` (up to 3 tribe members who finished a similar task before). Attach the suggested skill(s) to the task, and attach `who` so the plan can carry who has done this kind of work.
|
|
35
37
|
|
|
36
|
-
For each task, state a one-line "done means..." in chat (not in the title, not stored anywhere) so the user sees what complete looks like for that task. When `who` is not empty, add a short line naming those people, for example "Done before by: Sam
|
|
38
|
+
For each task, state a one-line "done means..." in chat (not in the title, not stored anywhere) so the user sees what complete looks like for that task. When `who` is not empty, add a short line naming those people, for example "Done before by: **Sam**, **Alex**."
|
|
39
|
+
|
|
40
|
+
Whenever you name a skill or a person in your output, wrap it in markdown bold so it stands out, for example **Copywriting** or **Sam**. Bold the skill and person names everywhere they appear in this command, both in the draft plan and in the "done means..." and "done before by" lines.
|
|
37
41
|
|
|
38
42
|
## Step 4: Present the plan
|
|
39
43
|
|
|
@@ -52,15 +56,13 @@ Wait for the answer.
|
|
|
52
56
|
Call the `plan` tool from the luckiest MCP server with:
|
|
53
57
|
|
|
54
58
|
```
|
|
55
|
-
{ phase: <short phase name if any>, tasks: [{ title, skills, who }] }
|
|
59
|
+
{ project: <the project key from Step 2>, phase: <short phase name if any>, tasks: [{ title, skills, who }] }
|
|
56
60
|
```
|
|
57
61
|
|
|
58
62
|
Pass the `who` list you got from `skill_router` for each task so the plan keeps who has done this kind of work before.
|
|
59
63
|
|
|
60
64
|
Include at most 25 tasks. Each title must stay under 200 characters. Do not include acceptance criteria or "done means" text in any task field.
|
|
61
65
|
|
|
62
|
-
## Step 6:
|
|
63
|
-
|
|
64
|
-
End your response with exactly one line, nothing after it:
|
|
66
|
+
## Step 6: Start it
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
Once the plan is staged, do not make the user type the next command. Start the `/luckiest go` flow now, fresh, as if newly invoked, so the first task begins immediately in this session.
|
package/commands/status.md
CHANGED
|
@@ -6,7 +6,7 @@ Read `references/vocabulary.md` and `references/chart-renderer.md` first and fol
|
|
|
6
6
|
|
|
7
7
|
## Step 1: Check for a plan
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Derive the project key as described in `references/project-key.md`, then call the `status` tool from the luckiest MCP server with that `project` value so you read this project's plan and not another one.
|
|
10
10
|
|
|
11
11
|
If the returned state is null (no active plan), output nothing except these two lines, in order:
|
|
12
12
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Project key
|
|
2
|
+
|
|
3
|
+
The Luckiest plan tools (`status`, `plan`, `apply`, `verify`, `finish`, `pause`)
|
|
4
|
+
take an optional `project` value so each project keeps its own separate plan.
|
|
5
|
+
The MCP server is hosted and cannot see your folder, so you must compute this
|
|
6
|
+
key locally and pass it.
|
|
7
|
+
|
|
8
|
+
## How to derive it (do this once per session, before the first plan tool call)
|
|
9
|
+
|
|
10
|
+
1. Run: `git config --get remote.origin.url`
|
|
11
|
+
2. If it returns a URL, normalize it to `host/owner/repo`, lowercased, with any
|
|
12
|
+
`.git` suffix removed. Examples:
|
|
13
|
+
- `git@github.com:acme/app.git` becomes `github.com/acme/app`
|
|
14
|
+
- `https://github.com/acme/app.git` becomes `github.com/acme/app`
|
|
15
|
+
3. If step 1 returns nothing (not a git repo), run `pwd` and use that absolute
|
|
16
|
+
path as the key.
|
|
17
|
+
4. If there is no local shell at all (web chat, Cowork), omit `project` entirely.
|
|
18
|
+
Those surfaces share one plan, which is correct for them.
|
|
19
|
+
|
|
20
|
+
## How to use it
|
|
21
|
+
|
|
22
|
+
Pass the SAME `project` value on every plan tool call for the rest of the
|
|
23
|
+
session: `status`, `plan`, `apply`, `verify`, `finish`, `pause`. Do not change it
|
|
24
|
+
mid-session and do not recompute it per call. One project, one key, all session.
|