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.
@@ -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.0",
12
+ "version": "0.1.8",
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.2",
4
+ "version": "0.1.8",
5
5
  "author": { "name": "Luckiest" }
6
6
  }
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 (fs.existsSync(commandsSrc)) {
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
  }
@@ -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
- Call the `status` tool from the luckiest MCP server.
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
- End your response with exactly one line, nothing after it:
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
- Call the `status` tool from the luckiest MCP server. 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.
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. When you dispatch a subagent for a task:
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
- - Run it 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.
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 }`, a one-line reason describing where things were left.
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
- Call the `status` tool from the luckiest MCP server.
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. What do you want to tackle most on this task?
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, Alex."
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: Wrap up
63
-
64
- End your response with exactly one line, nothing after it:
66
+ ## Step 6: Start it
65
67
 
66
- Next: /luckiest go to start.
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.
@@ -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
- Call the `status` tool from the luckiest MCP server.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luckiest-co",
3
- "version": "1.0.3",
3
+ "version": "1.0.7",
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"
@@ -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.