luckiest-co 1.0.20 → 1.0.21

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.
@@ -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.19",
4
+ "version": "0.1.20",
5
5
  "author": { "name": "Luckiest" }
6
6
  }
@@ -46,6 +46,16 @@ Map each task's status to a glyph (these are the task status values, not the pla
46
46
  - "assist" -> `?`
47
47
  - "blocked" -> `✗`
48
48
 
49
+ ## Update notice
50
+
51
+ If earlier in this session the Luckiest session-start hook reported a newer version (a line like "Luckiest X is available (you have Y)"), add one line under the dashboard, outside the code block:
52
+
53
+ ```
54
+ Update available: Y → X. Update the luckiest plugin from /plugin.
55
+ ```
56
+
57
+ Otherwise add nothing. Do not call any tool for this.
58
+
49
59
  ## Step 3: Recommend next action
50
60
 
51
61
  End your response with exactly one line, nothing after it, derived from the MCP `nextAction` field:
package/hooks/hooks.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "Luckiest plugin hooks — deterministic skill-usage telemetry on every Luckiest skill run.",
2
+ "description": "Luckiest plugin hooks — deterministic skill-usage telemetry on every Luckiest skill run, and a weekly update notice at session start.",
3
3
  "hooks": {
4
4
  "PostToolUse": [
5
5
  {
@@ -11,6 +11,16 @@
11
11
  }
12
12
  ]
13
13
  }
14
+ ],
15
+ "SessionStart": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/update-notice.mjs\""
21
+ }
22
+ ]
23
+ }
14
24
  ]
15
25
  }
16
26
  }
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ // SessionStart hook: at most once every 7 days, compare this plugin's version to
3
+ // the published one and, only when behind, show one line in chat. Silent when
4
+ // current, when checked recently, or on any failure. Sends nothing: one GET of a
5
+ // public file.
6
+ // ponytail: marketplace installs only (registered via hooks.json). npx installs
7
+ // already self-update daily through `npx luckiest-co@latest --sync-only`, so the
8
+ // installer does not register this.
9
+
10
+ import { readFileSync, writeFileSync, mkdirSync, statSync } from "node:fs";
11
+ import { join, dirname } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { homedir } from "node:os";
14
+
15
+ const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
16
+ const MANIFEST_URL =
17
+ process.env.LUCKIEST_PLUGIN_MANIFEST_URL ||
18
+ "https://raw.githubusercontent.com/luckiestco/luckiest-plugin/main/.claude-plugin/plugin.json";
19
+ const STAMP = join(homedir(), ".luckiest", "last-update-check");
20
+
21
+ const root = process.env.CLAUDE_PLUGIN_ROOT || join(dirname(fileURLToPath(import.meta.url)), "..");
22
+
23
+ // Numeric major.minor.patch only, same rule as check_updates' cmpSemver.
24
+ export const newer = (a, b) => {
25
+ const pa = String(a).split(".").map((n) => parseInt(n, 10) || 0);
26
+ const pb = String(b).split(".").map((n) => parseInt(n, 10) || 0);
27
+ for (let i = 0; i < 3; i++) if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) > (pb[i] || 0);
28
+ return false;
29
+ };
30
+
31
+ async function main() {
32
+ try {
33
+ if (Date.now() - statSync(STAMP).mtimeMs < WEEK_MS) return;
34
+ } catch {
35
+ /* no stamp yet */
36
+ }
37
+ const installed = JSON.parse(readFileSync(join(root, ".claude-plugin", "plugin.json"), "utf8")).version;
38
+ const res = await fetch(MANIFEST_URL, { signal: AbortSignal.timeout(3000) });
39
+ if (!res.ok) return;
40
+ const latest = (await res.json()).version;
41
+ mkdirSync(dirname(STAMP), { recursive: true });
42
+ writeFileSync(STAMP, String(Date.now()));
43
+ if (!installed || !latest || !newer(latest, installed)) return;
44
+ const msg = `Luckiest ${latest} is available (you have ${installed}). Update the luckiest plugin from /plugin to get it.`;
45
+ process.stdout.write(
46
+ JSON.stringify({
47
+ systemMessage: msg,
48
+ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: msg },
49
+ }),
50
+ );
51
+ }
52
+
53
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
54
+ await main().catch(() => {});
55
+ process.exit(0);
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luckiest-co",
3
- "version": "1.0.20",
3
+ "version": "1.0.21",
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"
@@ -80,6 +80,16 @@ Map each task's status to a glyph (these are the task status values, not the pla
80
80
  - "assist" -> `?`
81
81
  - "blocked" -> `✗`
82
82
 
83
+ ## Update notice
84
+
85
+ If earlier in this session the Luckiest session-start hook reported a newer version (a line like "Luckiest X is available (you have Y)"), add one line under the dashboard, outside the code block:
86
+
87
+ ```
88
+ Update available: Y → X. Update the luckiest plugin from /plugin.
89
+ ```
90
+
91
+ Otherwise add nothing. Do not call any tool for this.
92
+
83
93
  ## Step 3: Recommend next action
84
94
 
85
95
  End your response with exactly one line, nothing after it, derived from the MCP `nextAction` field: