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.
- package/bin/cli.js +146 -78
- 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
|
-
//
|
|
647
|
-
if (
|
|
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
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2923
|
-
|
|
2999
|
+
return { workerUrl, apiKey, secretSet };
|
|
3000
|
+
}
|
|
2924
3001
|
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
8
|
-
"sticky-note-cli": "
|
|
9
|
-
"sticky-note-mcp": "
|
|
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": "^
|
|
37
|
+
"sticky-note-cli": "^3.1.1"
|
|
38
38
|
}
|
|
39
39
|
}
|