sticky-note-cli 3.1.11 → 3.1.13

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/bin/cli.js CHANGED
@@ -95,9 +95,8 @@ function resolveHookPaths(_obj, _rootDir) {
95
95
  return;
96
96
  }
97
97
 
98
- // Detects hook commands in an existing settings.json that point at an absolute
99
- // path (likely written by an older sticky-note-cli on another developer's
100
- // machine). Returns the list of stale command strings, or [] if all clean.
98
+ // Detects hook commands in an existing settings.json that are broken or stale.
99
+ // Returns the list of stale command strings, or [] if all clean.
101
100
  function findStaleHookPaths(settings) {
102
101
  const stale = [];
103
102
  const hooks = (settings && settings.hooks) || {};
@@ -107,9 +106,6 @@ function findStaleHookPaths(settings) {
107
106
  const cmd = h && h.command;
108
107
  if (typeof cmd !== "string") continue;
109
108
  // Absolute paths (Unix /Users, /home, /root or Windows C:/, D:/ etc.)
110
- // inside the hook command are the smoking gun. Match anywhere in the
111
- // string — the path may be preceded by other tokens (env vars, shell
112
- // prefixes) and may contain spaces.
113
109
  if (
114
110
  /["'][A-Za-z]:[\\/]/.test(cmd) ||
115
111
  /["']\/(?:Users|home|root|tmp)\//.test(cmd) ||
@@ -117,12 +113,9 @@ function findStaleHookPaths(settings) {
117
113
  ) {
118
114
  stale.push(cmd);
119
115
  }
120
- // Old relative-path format: node ".claude/hooks/foo.js"
121
- // This fails when Claude Code launches from a subdirectory of the
122
- // project root, because the path resolves relative to the launch cwd,
123
- // not relative to where .claude/settings.json lives. The fix is to
124
- // use `npx sticky-note run-hook foo` which walks up to find the hooks.
125
- if (/node\s+["']\.claude[\\/]hooks[\\/][^"']+\.js["']/.test(cmd)) {
116
+ // Broken `npx sticky-note run-hook X` — npm has no package named "sticky-note"
117
+ // (the package is "sticky-note-cli"). Written by earlier 3.x releases.
118
+ if (/^npx\s+sticky-note\s+run-hook\s+\S+/.test(cmd)) {
126
119
  stale.push(cmd);
127
120
  }
128
121
  }
@@ -3052,6 +3045,10 @@ function provisionCloudBackend() {
3052
3045
  if (namespaceId) {
3053
3046
  const tomlPath = path.join(serverDir, "wrangler.toml");
3054
3047
  let toml = fs.readFileSync(tomlPath, "utf-8");
3048
+ // Reset any previously-written ID back to placeholder first, then patch.
3049
+ // This makes re-runs idempotent even when provisionCloudBackend is called
3050
+ // multiple times in the same init (e.g. after auto-installing wrangler).
3051
+ toml = toml.replace(/id\s*=\s*"[^"]*"/, 'id = "PLACEHOLDER_KV_NAMESPACE_ID"');
3055
3052
  toml = toml.replace("PLACEHOLDER_KV_NAMESPACE_ID", namespaceId);
3056
3053
  fs.writeFileSync(tomlPath, toml);
3057
3054
  print(" [OK] wrangler.toml updated with namespace ID");
@@ -3073,7 +3070,20 @@ function provisionCloudBackend() {
3073
3070
 
3074
3071
  const urlMatch = deployResult.match(/(https:\/\/[^\s]+\.workers\.dev)/);
3075
3072
  const workerUrl = urlMatch ? urlMatch[1] : null;
3076
- const apiKey = require("crypto").randomBytes(24).toString("base64url");
3073
+
3074
+ // Reuse an existing API key from .env.sticky if present — prevents divergence
3075
+ // when init is re-run on an already-provisioned repo. Only generate a new key
3076
+ // if there isn't one already.
3077
+ const envPath = path.join(process.cwd(), ".env.sticky");
3078
+ let apiKey = null;
3079
+ try {
3080
+ const existing = fs.readFileSync(envPath, "utf-8");
3081
+ const m = existing.match(/^STICKY_API_KEY=(.+)$/m);
3082
+ if (m && m[1].trim()) apiKey = m[1].trim();
3083
+ } catch (_) { /* no existing .env.sticky */ }
3084
+ if (!apiKey) {
3085
+ apiKey = require("crypto").randomBytes(24).toString("base64url");
3086
+ }
3077
3087
 
3078
3088
  if (workerUrl) {
3079
3089
  print(` [OK] Worker deployed: ${workerUrl}`);
@@ -3081,7 +3091,7 @@ function provisionCloudBackend() {
3081
3091
  print(" [OK] Worker deployed (URL not detected — check wrangler output)");
3082
3092
  }
3083
3093
 
3084
- // Step 3: Set API key
3094
+ // Step 3: Set API key on Worker — always sync so Worker and .env.sticky agree
3085
3095
  print(" Step 3: Setting API key on Worker...");
3086
3096
  let secretSet = false;
3087
3097
  try {
@@ -3098,11 +3108,20 @@ function provisionCloudBackend() {
3098
3108
  if (workerUrl) print(` Set it manually: echo '${apiKey}' | wrangler secret put STICKY_API_KEY --name ${workerName}`);
3099
3109
  }
3100
3110
 
3101
- // Write .env.sticky
3102
- if (workerUrl) {
3103
- const envPath = path.join(process.cwd(), ".env.sticky");
3111
+ // Write .env.sticky only after the secret is confirmed set on the Worker,
3112
+ // so the local key always matches what the Worker expects.
3113
+ if (workerUrl && secretSet) {
3104
3114
  fs.writeFileSync(envPath, `STICKY_URL=${workerUrl}\nSTICKY_API_KEY=${apiKey}\n`, "utf-8");
3105
3115
  print(" [OK] .env.sticky written with STICKY_URL and STICKY_API_KEY");
3116
+ } else if (workerUrl && !secretSet) {
3117
+ // Secret set failed — still write .env.sticky with the key we tried,
3118
+ // and tell the user to set it manually so both sides match.
3119
+ fs.writeFileSync(envPath, `STICKY_URL=${workerUrl}\nSTICKY_API_KEY=${apiKey}\n`, "utf-8");
3120
+ print(" [WARN] .env.sticky written but STICKY_API_KEY was NOT set on the Worker.");
3121
+ print(` Sync manually: echo '${apiKey}' | wrangler secret put STICKY_API_KEY --name ${workerName}`);
3122
+ }
3123
+
3124
+ if (workerUrl) {
3106
3125
 
3107
3126
  // Write sticky_url to committed config so teammates get it on git pull
3108
3127
  const configPath = path.join(process.cwd(), ".sticky-note", "sticky-note-config.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sticky-note-cli",
3
- "version": "3.1.11",
3
+ "version": "3.1.13",
4
4
  "description": "Human-to-human handoff for AI coding assistants. Git-backed shared memory for Claude Code and Copilot CLI.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -157,13 +157,13 @@ function selfHealHookPaths(settingsPath) {
157
157
  try { settings = JSON.parse(raw); } catch (_) { return result; }
158
158
  if (!settings || !settings.hooks) return result;
159
159
 
160
- // Two stale patterns:
160
+ // Stale patterns that need rewriting to the canonical template format:
161
161
  // 1. Absolute paths (written by older sticky-note-cli or copied from another machine)
162
- // 2. Old relative form `node ".claude/hooks/foo.js"` — fails when Claude Code
163
- // launches from a subdirectory, because the path resolves relative to the
164
- // launch cwd, not the directory containing .claude/settings.json.
162
+ // 2. `npx sticky-note run-hook X` — broken: npm has no package named "sticky-note"
163
+ // (the package is "sticky-note-cli"). Written by earlier 3.x releases via the
164
+ // now-removed selfHeal rewrite. Canonical form is `node ".claude/hooks/X.js"`.
165
165
  const STALE_ABSOLUTE = /["'][A-Za-z]:[\\/]|["']\/(?:Users|home|root|tmp)\/|\$\(git rev-parse/;
166
- const STALE_RELATIVE = /^node\s+["']\.claude[\\/]hooks[\\/][^"']+\.js["']\s*$/;
166
+ const STALE_NPX = /^npx\s+sticky-note\s+run-hook\s+(\S+)/;
167
167
  // Match the script basename in either quoted or bare form.
168
168
  const BASENAME = /([A-Za-z0-9_.-]+)\.(?:js|sh|cjs|mjs)(?:["'`\s]|$)/;
169
169
 
@@ -174,12 +174,17 @@ function selfHealHookPaths(settingsPath) {
174
174
  for (const h of (entry && entry.hooks) || []) {
175
175
  const cmd = h && h.command;
176
176
  if (typeof cmd !== "string") continue;
177
- const isStale = STALE_ABSOLUTE.test(cmd) || STALE_RELATIVE.test(cmd);
178
- if (!isStale) continue;
179
- const m = cmd.match(BASENAME);
180
- if (!m) continue;
181
- const basename = m[1];
182
- const newCmd = `npx sticky-note run-hook ${basename}`;
177
+ let basename = null;
178
+ // Check for broken `npx sticky-note run-hook X` pattern first
179
+ const npxMatch = cmd.match(STALE_NPX);
180
+ if (npxMatch) {
181
+ basename = npxMatch[1];
182
+ } else if (STALE_ABSOLUTE.test(cmd)) {
183
+ const m = cmd.match(BASENAME);
184
+ if (m) basename = m[1];
185
+ }
186
+ if (!basename) continue;
187
+ const newCmd = `node ".claude/hooks/${basename}.js"`;
183
188
  result.push({ event, oldCommand: cmd, newCommand: newCmd });
184
189
  h.command = newCmd;
185
190
  changed = true;
@@ -192,9 +197,9 @@ function selfHealHookPaths(settingsPath) {
192
197
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
193
198
  try {
194
199
  const summary =
195
- `[STICKY-NOTE] ⚠️ Detected ${result.length} stale hook path(s) in ` +
200
+ `[STICKY-NOTE] ⚠️ Detected ${result.length} stale hook command(s) in ` +
196
201
  `${path.relative(process.cwd(), settingsPath) || settingsPath} — rewrote to ` +
197
- `"npx sticky-note run-hook <name>" so hooks work from any subdirectory. ` +
202
+ `"node \\".claude/hooks/<name>.js\\"" (canonical form). ` +
198
203
  `Commit the change to share the fix with your team.`;
199
204
  process.stderr.write(summary + "\n");
200
205
  for (const { event, oldCommand, newCommand } of result.slice(0, 5)) {