sticky-note-cli 3.1.0 → 3.1.2

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.
Files changed (2) hide show
  1. package/bin/cli.js +146 -78
  2. package/package.json +6 -6
package/bin/cli.js CHANGED
@@ -574,6 +574,7 @@ async function cmdInit() {
574
574
  print("");
575
575
 
576
576
  let mcpServers, skills, conventions, staleDaysResolved, injectTokenBudgetResolved;
577
+ let wantsCloud = false;
577
578
 
578
579
  if (ciMode) {
579
580
  // CI mode: no prompts, use env vars
@@ -596,6 +597,7 @@ async function cmdInit() {
596
597
  conventions = convRaw ? convRaw.split(",").map(s => s.trim()).filter(Boolean) : [];
597
598
  staleDaysResolved = parseIntOr(staleDaysRaw, 14);
598
599
  injectTokenBudgetResolved = 1000;
600
+ wantsCloud = v3Mode; // --v3 in CI mode implies cloud
599
601
  } else {
600
602
  // Interactive prompts
601
603
  const rl = readline.createInterface({
@@ -623,6 +625,13 @@ async function cmdInit() {
623
625
  // Sync MCP servers from teammate's sticky-note config
624
626
  await syncMcpFromStickyNote(rl);
625
627
 
628
+ const cloudAnswer = await ask(
629
+ rl,
630
+ "Set up Cloudflare cloud backend for real-time team sync? (y/N)",
631
+ v3Mode ? "y" : "N"
632
+ );
633
+ wantsCloud = cloudAnswer.toLowerCase() === "y" || cloudAnswer.toLowerCase() === "yes";
634
+
626
635
  rl.close();
627
636
 
628
637
  // Build MCP server list: prefer detected objects, fall back to names
@@ -643,20 +652,61 @@ async function cmdInit() {
643
652
  injectTokenBudgetResolved = parseIntOr(injectTokenBudget, 1000);
644
653
  }
645
654
 
646
- // V3: write .env.sticky if STICKY_URL is available
647
- if (v3Mode || process.env.STICKY_URL) {
655
+ // Cloud backend: provision inline if requested, or write existing STICKY_URL from env
656
+ if (wantsCloud && !ciMode) {
657
+ print("\n Cloud Backend Setup...\n");
658
+ let cloudResult = provisionCloudBackend();
659
+
660
+ // wrangler not installed — guide the user through it inline
661
+ if (cloudResult === null) {
662
+ print(" wrangler CLI is not installed. Let's set it up.\n");
663
+ print(" Run these two commands in another terminal:\n");
664
+ print(" npm install -g wrangler");
665
+ print(" wrangler login\n");
666
+
667
+ const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
668
+ await ask(rl2, "Press Enter once wrangler is installed and you've logged in", "");
669
+ rl2.close();
670
+
671
+ print("");
672
+ cloudResult = provisionCloudBackend();
673
+
674
+ if (cloudResult === null) {
675
+ print(" [ERR] wrangler still not found in PATH.");
676
+ print(" Run `npx sticky-note deploy-backend` once it's installed.\n");
677
+ }
678
+ }
679
+
680
+ if (cloudResult && cloudResult.error) {
681
+ print(" [WARN] Cloud setup failed: " + cloudResult.error);
682
+ print(" Run `npx sticky-note deploy-backend` to retry.\n");
683
+ } else if (cloudResult && !cloudResult.error) {
684
+ if (cloudResult.secretSet) {
685
+ print("\n Share STICKY_URL and STICKY_API_KEY with teammates securely.");
686
+ print(" (e.g. org secrets, 1Password — do not commit .env.sticky)\n");
687
+ } else {
688
+ print("\n [!] API key written to .env.sticky but NOT yet set on the Worker.");
689
+ print(" Run the manual command shown above, then share with your team.\n");
690
+ }
691
+ }
692
+ } else if (wantsCloud && ciMode) {
693
+ // CI mode: can't pause for wrangler install, just try and warn on failure
694
+ print("\n Cloud Backend Setup...\n");
695
+ const cloudResult = provisionCloudBackend();
696
+ if (!cloudResult) {
697
+ print(" [ERR] `wrangler` CLI not found. Skipping cloud setup in CI mode.\n");
698
+ } else if (cloudResult.error) {
699
+ print(" [WARN] Cloud setup failed: " + cloudResult.error + "\n");
700
+ }
701
+ } else if (process.env.STICKY_URL) {
702
+ // Write existing STICKY_URL from environment into .env.sticky
648
703
  const envStickyPath = path.join(process.cwd(), ".env.sticky");
649
- const stickyUrl = process.env.STICKY_URL || "";
704
+ const stickyUrl = process.env.STICKY_URL;
650
705
  const stickyApiKey = process.env.STICKY_API_KEY || "";
651
- if (stickyUrl) {
652
- let content = `STICKY_URL=${stickyUrl}\n`;
653
- if (stickyApiKey) content += `STICKY_API_KEY=${stickyApiKey}\n`;
654
- fs.writeFileSync(envStickyPath, content, "utf-8");
655
- print(" [OK] .env.sticky (cloud backend configured)");
656
- } else if (v3Mode && !ciMode) {
657
- print(" [WARN] --v3 specified but STICKY_URL not set.");
658
- print(" Run `npx sticky-note deploy-backend` to provision a cloud backend.");
659
- }
706
+ let content = `STICKY_URL=${stickyUrl}\n`;
707
+ if (stickyApiKey) content += `STICKY_API_KEY=${stickyApiKey}\n`;
708
+ fs.writeFileSync(envStickyPath, content, "utf-8");
709
+ print(" [OK] .env.sticky (cloud backend configured)");
660
710
  }
661
711
 
662
712
  print("\n 📁 Creating files...\n");
@@ -2859,106 +2909,124 @@ function cmdGc() {
2859
2909
  // DEPLOY-BACKEND command (V3)
2860
2910
  // ──────────────────────────────────────────────
2861
2911
 
2862
- function cmdDeployBackend() {
2863
- printBanner();
2864
- print(" Deploy Cloudflare KV Backend\n");
2865
-
2912
+ // Shared provisioning logic used by both cmdInit and cmdDeployBackend.
2913
+ // Returns null if wrangler is missing.
2914
+ // Returns { workerUrl, apiKey, secretSet } on success.
2915
+ // Returns { error } on deployment failure.
2916
+ function provisionCloudBackend() {
2866
2917
  // Check wrangler
2867
2918
  try {
2868
2919
  execSync("wrangler --version", { stdio: "pipe" });
2869
2920
  } catch (_) {
2870
- print(" [ERR] `wrangler` CLI not found.");
2871
- print(" Install it: npm install -g wrangler");
2872
- print(" Then authenticate: wrangler login\n");
2873
- process.exit(1);
2921
+ return null; // caller prints the install message
2874
2922
  }
2875
2923
 
2876
2924
  const serverDir = path.join(__dirname, "..", "sticky-server");
2877
2925
  if (!fs.existsSync(path.join(serverDir, "worker.js"))) {
2878
- print(" [ERR] sticky-server/worker.js not found.");
2879
- print(" Make sure you have the full sticky-note package.\n");
2880
- process.exit(1);
2926
+ return { error: "sticky-server/worker.js not found. Make sure you have the full sticky-note package." };
2881
2927
  }
2882
2928
 
2929
+ // Step 1: KV namespace
2883
2930
  print(" Step 1: Creating KV namespace...");
2931
+ let kvResult;
2884
2932
  try {
2885
- const result = execSync("wrangler kv:namespace create STICKY_KV", {
2933
+ kvResult = execSync("wrangler kv:namespace create STICKY_KV", {
2886
2934
  cwd: serverDir,
2887
2935
  encoding: "utf-8",
2888
2936
  stdio: ["pipe", "pipe", "pipe"],
2889
2937
  });
2890
2938
  print(" [OK] KV namespace created");
2891
-
2892
- // Extract namespace ID from wrangler output
2893
- const idMatch = result.match(/id\s*=\s*"([^"]+)"/);
2894
- if (idMatch) {
2895
- const namespaceId = idMatch[1];
2896
- // Update wrangler.toml with real namespace ID
2897
- const tomlPath = path.join(serverDir, "wrangler.toml");
2898
- let toml = fs.readFileSync(tomlPath, "utf-8");
2899
- toml = toml.replace("PLACEHOLDER_KV_NAMESPACE_ID", namespaceId);
2900
- fs.writeFileSync(tomlPath, toml);
2901
- print(` [OK] wrangler.toml updated with namespace ID`);
2902
- }
2903
2939
  } catch (err) {
2904
- print(" [ERR] Failed to create KV namespace: " + (err.message || err));
2905
- print(" Make sure you're logged in: wrangler login\n");
2906
- process.exit(1);
2940
+ return { error: "Failed to create KV namespace: " + (err.message || err) + "\n Make sure you're logged in: wrangler login" };
2941
+ }
2942
+
2943
+ const idMatch = kvResult.match(/id\s*=\s*"([^"]+)"/);
2944
+ if (idMatch) {
2945
+ const tomlPath = path.join(serverDir, "wrangler.toml");
2946
+ let toml = fs.readFileSync(tomlPath, "utf-8");
2947
+ toml = toml.replace("PLACEHOLDER_KV_NAMESPACE_ID", idMatch[1]);
2948
+ fs.writeFileSync(tomlPath, toml);
2949
+ print(" [OK] wrangler.toml updated with namespace ID");
2907
2950
  }
2908
2951
 
2952
+ // Step 2: Deploy Worker
2909
2953
  print(" Step 2: Deploying Worker...");
2954
+ let deployResult;
2955
+ try {
2956
+ deployResult = execSync("wrangler deploy", {
2957
+ cwd: serverDir,
2958
+ encoding: "utf-8",
2959
+ stdio: ["pipe", "pipe", "pipe"],
2960
+ });
2961
+ } catch (err) {
2962
+ return { error: "Failed to deploy Worker: " + (err.message || err) };
2963
+ }
2964
+
2965
+ const urlMatch = deployResult.match(/(https:\/\/[^\s]+\.workers\.dev)/);
2966
+ const workerUrl = urlMatch ? urlMatch[1] : null;
2967
+ const apiKey = require("crypto").randomBytes(24).toString("base64url");
2968
+
2969
+ if (workerUrl) {
2970
+ print(` [OK] Worker deployed: ${workerUrl}`);
2971
+ } else {
2972
+ print(" [OK] Worker deployed (URL not detected — check wrangler output)");
2973
+ }
2974
+
2975
+ // Step 3: Set API key
2976
+ print(" Step 3: Setting API key on Worker...");
2977
+ let secretSet = false;
2910
2978
  try {
2911
- const result = execSync("wrangler deploy", {
2979
+ execSync("wrangler secret put STICKY_API_KEY", {
2912
2980
  cwd: serverDir,
2981
+ input: apiKey,
2913
2982
  encoding: "utf-8",
2914
2983
  stdio: ["pipe", "pipe", "pipe"],
2915
2984
  });
2916
- // Extract worker URL
2917
- const urlMatch = result.match(/(https:\/\/[^\s]+\.workers\.dev)/);
2918
- const workerUrl = urlMatch ? urlMatch[1] : null;
2985
+ print(" [OK] STICKY_API_KEY set on Worker");
2986
+ secretSet = true;
2987
+ } catch (err) {
2988
+ print(" [WARN] Could not set STICKY_API_KEY automatically: " + (err.message || err));
2989
+ if (workerUrl) print(` Set it manually: echo '${apiKey}' | wrangler secret put STICKY_API_KEY`);
2990
+ }
2919
2991
 
2920
- const apiKey = require("crypto").randomBytes(24).toString("base64url");
2992
+ // Write .env.sticky
2993
+ if (workerUrl) {
2994
+ const envPath = path.join(process.cwd(), ".env.sticky");
2995
+ fs.writeFileSync(envPath, `STICKY_URL=${workerUrl}\nSTICKY_API_KEY=${apiKey}\n`, "utf-8");
2996
+ print(" [OK] .env.sticky written with STICKY_URL and STICKY_API_KEY");
2997
+ }
2921
2998
 
2922
- if (workerUrl) {
2923
- print(` [OK] Worker deployed: ${workerUrl}`);
2999
+ return { workerUrl, apiKey, secretSet };
3000
+ }
2924
3001
 
2925
- print(" Step 3: Setting API key on Worker...");
2926
- let secretSet = false;
2927
- try {
2928
- execSync("wrangler secret put STICKY_API_KEY", {
2929
- cwd: serverDir,
2930
- input: apiKey,
2931
- encoding: "utf-8",
2932
- stdio: ["pipe", "pipe", "pipe"],
2933
- });
2934
- print(" [OK] STICKY_API_KEY secret set on Worker");
2935
- secretSet = true;
2936
- } catch (err) {
2937
- print(" [WARN] Could not set STICKY_API_KEY automatically: " + (err.message || err));
2938
- print(` Set it manually: echo '${apiKey}' | wrangler secret put STICKY_API_KEY`);
2939
- }
3002
+ function cmdDeployBackend() {
3003
+ printBanner();
3004
+ print(" Deploy Cloudflare KV Backend\n");
2940
3005
 
2941
- // Always write .env.sticky so the key is available even if secret-put failed
2942
- const envPath = path.join(process.cwd(), ".env.sticky");
2943
- fs.writeFileSync(envPath, `STICKY_URL=${workerUrl}\nSTICKY_API_KEY=${apiKey}\n`, "utf-8");
2944
- print(" [OK] .env.sticky written with STICKY_URL and STICKY_API_KEY");
3006
+ const result = provisionCloudBackend();
2945
3007
 
2946
- if (secretSet) {
2947
- print("\n Share STICKY_URL and STICKY_API_KEY with your team securely");
2948
- print(" (e.g. org secrets, 1Password). Do not commit .env.sticky.\n");
2949
- } else {
2950
- print("\n [!] API key is in .env.sticky but NOT yet set on the Worker.");
2951
- print(" Run the manual command above, then share with your team.\n");
2952
- }
2953
- } else {
2954
- print(" [OK] Worker deployed (URL not detected — check wrangler output)");
2955
- }
2956
- } catch (err) {
2957
- print(" [ERR] Failed to deploy Worker: " + (err.message || err));
3008
+ if (result === null) {
3009
+ print(" [ERR] `wrangler` CLI not found.");
3010
+ print(" Install it: npm install -g wrangler");
3011
+ print(" Then authenticate: wrangler login\n");
3012
+ process.exit(1);
3013
+ }
3014
+
3015
+ if (result.error) {
3016
+ print(" [ERR] " + result.error + "\n");
2958
3017
  process.exit(1);
2959
3018
  }
2960
3019
 
2961
- print("\n Backend ready! Run `npx sticky-note init --v3` in your repos.\n");
3020
+ const { secretSet } = result;
3021
+ if (secretSet) {
3022
+ print("\n Share STICKY_URL and STICKY_API_KEY with your team securely");
3023
+ print(" (e.g. org secrets, 1Password). Do not commit .env.sticky.\n");
3024
+ } else {
3025
+ print("\n [!] API key is in .env.sticky but NOT yet set on the Worker.");
3026
+ print(" Run the manual command above, then share with your team.\n");
3027
+ }
3028
+
3029
+ print("\n Backend ready! Run `npx sticky-note init` in your repos.\n");
2962
3030
  }
2963
3031
 
2964
3032
  // ──────────────────────────────────────────────
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "sticky-note-cli",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
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": {
7
- "sticky-note": "./bin/cli.js",
8
- "sticky-note-cli": "./bin/cli.js",
9
- "sticky-note-mcp": "./bin/mcp-server.js"
7
+ "sticky-note": "bin/cli.js",
8
+ "sticky-note-cli": "bin/cli.js",
9
+ "sticky-note-mcp": "bin/mcp-server.js"
10
10
  },
11
11
  "keywords": [
12
12
  "ai",
@@ -21,7 +21,7 @@
21
21
  "homepage": "https://github.com/BandaruDheeraj/sticky-note#readme",
22
22
  "repository": {
23
23
  "type": "git",
24
- "url": "https://github.com/BandaruDheeraj/sticky-note.git"
24
+ "url": "git+https://github.com/BandaruDheeraj/sticky-note.git"
25
25
  },
26
26
  "files": [
27
27
  "bin/",
@@ -34,6 +34,6 @@
34
34
  "test": "node test/smoke.test.js"
35
35
  },
36
36
  "devDependencies": {
37
- "sticky-note-cli": "^2.9.1"
37
+ "sticky-note-cli": "^3.1.1"
38
38
  }
39
39
  }