sticky-note-cli 2.6.16 → 2.7.1

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/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "sticky-note-cli",
3
- "version": "2.6.16",
3
+ "version": "2.7.1",
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"
7
+ "sticky-note": "./bin/cli.js",
8
+ "sticky-note-mcp": "./bin/mcp-server.js"
8
9
  },
9
10
  "keywords": [
10
11
  "ai",
@@ -186,33 +186,54 @@ after. A raw `git checkout` or `git switch` will fail if sticky-note
186
186
  files have uncommitted changes. The alias `git sw <branch>` also works
187
187
  if set up by `npx sticky-note init`.
188
188
 
189
- ### ⚠️ Overlap warnings (CRITICAL — always surface these)
189
+ ### ⚠️ Overlap warnings
190
190
 
191
- Sticky-note hooks detect when your files overlap with other teammates'
192
- open or stuck threads. Overlap warnings arrive through TWO channels:
191
+ Sticky-note detects when your files overlap with other teammates'
192
+ open or stuck threads. When the MCP server is available, use the
193
+ `check_overlaps` tool before editing files for the most reliable detection.
194
+
195
+ Hooks also inject overlap context through two fallback channels:
193
196
 
194
197
  1. **Injected context** — `[STICKY-NOTE] ⚠️ OVERLAP DETECTED` appears in
195
198
  your hook output. This fires on every user prompt.
196
199
  2. **Tool call deny** — Your first tool call may be denied with a reason
197
- containing "CRITICAL INSTRUCTION" and the overlap details.
200
+ containing overlap details (Copilot CLI only).
198
201
 
199
- When you receive an overlap warning through EITHER channel, you **MUST**
200
- start your response with this exact banner format before doing ANYTHING else:
202
+ When you detect an overlap (via MCP tool or hook), surface it to the user
203
+ using this styled banner format:
201
204
 
202
205
  ```
203
- ⚠️ Overlap detected: [user] is working on [files] — [narrative summary]
204
- Status: [STUCK/OPEN] | Resume: npx sticky-note resume [id]
206
+ ⚠️ ━━━ OVERLAP DETECTED ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
207
+
208
+ 🔴 [user] (STUCK) · [branch]
209
+ Files: [file1], [file2], [file3]
210
+ "[narrative summary]"
211
+ → Resume: npx sticky-note resume [id]
212
+
213
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
205
214
  ```
206
215
 
207
- Rules:
208
- - This banner MUST be the **very first thing** in your response
209
- - Do NOT bury it at the end or in a footnote
210
- - Do NOT rephrase it as "Also noted..." or "By the way..."
211
- - If a tool call was denied with overlap info, display the warning then proceed
212
- - Then continue with the user's actual request normally
216
+ Use 🔴 for STUCK threads, 🟡 for OPEN threads. For multiple overlaps,
217
+ add additional entries between the bars.
218
+
219
+ ## Sticky Note MCP Server
220
+
221
+ You have access to a `sticky-note` MCP server. Use it:
222
+
223
+ 1. **At session start**: Call `get_stuck_threads()` and `get_environment_status()`
224
+ to check for team blockers and environment changes. Surface any warnings.
225
+
226
+ 2. **Before editing files**: Call `check_overlaps(files)` with the files you
227
+ plan to edit. If overlaps exist, warn the user before proceeding.
228
+
229
+ 3. **For prior work context**: Call `get_thread_context_for_files(files)` or
230
+ `search_threads(query)` to find relevant thread history.
231
+
232
+ 4. **For audit history**: Call `get_audit_trail(file)` to understand who
233
+ changed a file and when.
213
234
 
214
- This pattern exists to prevent two teammates from wasting hours on the
215
- same problem. Treat it like a system alert, not optional context.
235
+ These tools are the primary way sticky-note communicates with you.
236
+ Hook-injected context supplements but does not replace MCP tool calls.
216
237
 
217
238
  ### `[STICKY-NOTE]` tags
218
239
 
@@ -2,7 +2,34 @@
2
2
  # Sticky Note — Copilot CLI Instructions
3
3
 
4
4
  This repository uses **Sticky Note** for team handoff context.
5
- All session threads are stored in `.sticky-note/sticky-note.json`.
5
+
6
+ ## ⚠️ MANDATORY: Sticky Note MCP Tools (DO THIS FIRST)
7
+
8
+ You have a `sticky-note` MCP server connected. You MUST call these tools:
9
+
10
+ ### On EVERY session — before doing any work:
11
+ ```
12
+ get_stuck_threads() → surface stuck threads to user
13
+ get_environment_status() → check for missing MCP servers/secrets
14
+ ```
15
+
16
+ ### Before EVERY file edit:
17
+ ```
18
+ check_overlaps(files) → detect if teammates are working on same files
19
+ ```
20
+ If overlaps are found, display the overlap banner BEFORE proceeding.
21
+
22
+ ### When you need context:
23
+ ```
24
+ get_thread_context_for_files(files) → who worked on these files, what happened
25
+ search_threads(query) → keyword search across all threads
26
+ get_session_context(id) → full thread payload by ID
27
+ get_audit_trail(file, user, since) → query per-user audit logs
28
+ get_presence() → who is currently active
29
+ ```
30
+
31
+ These MCP tools are the PRIMARY way sticky-note communicates with you.
32
+ Do NOT skip them. Do NOT substitute with manual file reads.
6
33
 
7
34
  ## When asked about threads, sessions, or teammate activity
8
35
 
@@ -17,20 +44,18 @@ git history, your own session memory, or any other source.
17
44
  - [CLOSED] `closed` — completed
18
45
  - [EXPIRED] `expired` — tombstoned, minimal data remains
19
46
 
20
- ### Proactive context injection (V2.5)
47
+ ### Fallback: Manual context injection (if MCP is unavailable)
21
48
 
22
- Copilot CLI does not have lifecycle hooks, so **you must self-serve context**.
23
- Follow these two rules every session:
49
+ If the sticky-note MCP server is not connected, fall back to self-serve:
24
50
 
25
- #### 1. Eager: At session start, check for stuck/open threads
51
+ #### 1. At session start, check for stuck/open threads
26
52
 
27
- Before doing any work, read `.sticky-note/sticky-note.json` and check for
28
- threads with status `stuck` or `open`. If any exist, briefly summarize them
29
- to the user (what's blocked, what files are involved).
53
+ Read `.sticky-note/sticky-note.json` and check for threads with status
54
+ `stuck` or `open`. If any exist, briefly summarize them to the user.
30
55
 
31
- #### 2. Lazy: Before editing a file, check for prior thread context
56
+ #### 2. Before editing a file, check for prior thread context
32
57
 
33
- **The first time you edit or read a file in a session**, run:
58
+ **The first time you edit a file in a session**, run:
34
59
  ```bash
35
60
  npx sticky-note get-line-attribution --file <path>
36
61
  ```
@@ -164,41 +189,35 @@ after. A raw `git checkout` or `git switch` will fail if sticky-note
164
189
  files have uncommitted changes. The alias `git sw <branch>` also works
165
190
  if set up by `npx sticky-note init`.
166
191
 
167
- ### ⚠️ Overlap warnings (CRITICAL — always surface these)
192
+ ### ⚠️ Overlap warnings
193
+
194
+ Sticky-note detects when your files overlap with other teammates'
195
+ open or stuck threads. When the MCP server is available, use the
196
+ `check_overlaps` tool before editing files for the most reliable detection.
168
197
 
169
- Sticky-note hooks detect when your files overlap with other teammates'
170
- open or stuck threads. Overlap warnings arrive through TWO channels:
198
+ Hooks also inject overlap context through two fallback channels:
171
199
 
172
200
  1. **Injected context** — `[STICKY-NOTE] ⚠️ OVERLAP DETECTED` appears in
173
201
  your `additionalContext`. This fires on every user prompt.
174
- 2. **Tool call deny** — Your first tool call may be denied with a
175
- `permissionDecisionReason` containing "CRITICAL INSTRUCTION" and the
176
- overlap details. After the deny, the tool is auto-retried.
202
+ 2. **Tool call deny** — Your first tool call may be denied with overlap
203
+ details (Copilot CLI only). After the deny, the tool is auto-retried.
177
204
 
178
- When you receive an overlap warning through EITHER channel, you **MUST**
179
- start your response with this exact banner format before doing ANYTHING else:
205
+ When you detect an overlap (via MCP tool or hook), surface it to the user
206
+ using this styled banner format:
180
207
 
181
208
  ```
182
- ⚠️ Overlap detected: [user] is working on [files] — [narrative summary]
183
- Status: [STUCK/OPEN] | Resume: npx sticky-note resume [id]
184
- ```
209
+ ⚠️ ━━━ OVERLAP DETECTED ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
185
210
 
186
- Example:
187
- ```
188
- ⚠️ Overlap detected: alice is working on session-start.js, cli.js — fixing auth token refresh
189
- Status: STUCK (tried retry logic, tokens expire mid-request) | Resume: npx sticky-note resume demo-ali
190
- ```
211
+ 🔴 [user] (STUCK) · [branch]
212
+ Files: [file1], [file2], [file3]
213
+ "[narrative summary]"
214
+ → Resume: npx sticky-note resume [id]
191
215
 
192
- Rules:
193
- - This banner MUST be the **very first thing** in your response
194
- - Show it BEFORE your thinking indicators (◐, ●, etc.)
195
- - Do NOT bury it at the end or in a footnote
196
- - Do NOT rephrase it as "Also noted..." or "By the way..."
197
- - If a tool call was denied with overlap info, display the warning then proceed
198
- - Then continue with the user's actual request normally
216
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
217
+ ```
199
218
 
200
- This pattern exists to prevent two teammates from wasting hours on the
201
- same problem. Treat it like a system alert, not optional context.
219
+ Use 🔴 for STUCK threads, 🟡 for OPEN threads. For multiple overlaps,
220
+ add additional entries between the bars.
202
221
 
203
222
  ### `[STICKY-NOTE]` tags
204
223
 
@@ -218,4 +237,19 @@ when sticky-note is acting on their behalf.
218
237
  ### Team config
219
238
 
220
239
  Check `.sticky-note/sticky-note-config.json` for team conventions and settings.
240
+
241
+ ### Team environment sync
242
+
243
+ The team's vibe coding environment is defined in `.sticky-note/environment/`.
244
+ Skills, agents, commands, MCP servers, and permissions are auto-provisioned
245
+ by the session-start hook — no manual setup needed.
246
+
247
+ - **Skills:** `.sticky-note/environment/skills/*.md` → auto-copied to plugin dirs
248
+ - **Agents:** `.sticky-note/environment/agents/*.md` → auto-copied to plugin dirs
249
+ - **Commands:** `.sticky-note/environment/commands/*.md` → auto-copied to plugin dirs
250
+ - **MCP servers:** Defined in `manifest.json` → secret-free servers auto-provisioned
251
+ - **Permissions:** Defined in `manifest.json` → merged into settings
252
+
253
+ Use `get_environment_status()` MCP tool to check what's provisioned vs missing.
254
+ If MCP servers need secrets, tell the user to run `npx sticky-note bootstrap`.
221
255
  <!-- sticky-note:end -->
@@ -0,0 +1,37 @@
1
+ {
2
+ "version": "1",
3
+
4
+ "mcp_servers": {
5
+ "context7": {
6
+ "type": "stdio",
7
+ "command": "npx",
8
+ "args": ["-y", "@upstash/context7-mcp@latest"],
9
+ "description": "Fetch up-to-date docs for any library",
10
+ "required": true
11
+ },
12
+ "github": {
13
+ "type": "stdio",
14
+ "command": "npx",
15
+ "args": ["-y", "@modelcontextprotocol/server-github"],
16
+ "env": {
17
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
18
+ },
19
+ "description": "GitHub API access",
20
+ "required": true
21
+ }
22
+ },
23
+
24
+ "permissions": [
25
+ "Bash(npx sticky-note:*)",
26
+ "Bash(npm test:*)",
27
+ "Bash(node:*)"
28
+ ],
29
+
30
+ "env_vars": {
31
+ "GITHUB_TOKEN": {
32
+ "description": "GitHub PAT with repo scope",
33
+ "docs_url": "https://github.com/settings/tokens",
34
+ "required": true
35
+ }
36
+ }
37
+ }
@@ -149,56 +149,75 @@ function detectAndFormatOverlaps(threads, currentUser) {
149
149
  return b.overlap.length - a.overlap.length;
150
150
  });
151
151
 
152
+ const bar = "━".repeat(52);
152
153
  const lines = [
153
154
  "## [STICKY-NOTE] ⚠️ OVERLAP DETECTED\n",
154
- "**CRITICAL INSTRUCTION: Display the overlap warning below as the FIRST line of your response.",
155
- "Do NOT skip this. Do NOT rephrase it. Do NOT bury it.**\n",
156
- "Someone else is working on files you're touching:\n",
155
+ "**You MUST display the banner below as the FIRST thing in your response.**\n",
156
+ "```",
157
+ `⚠️ ━━━ OVERLAP DETECTED ${bar.substring(0, 28)}`,
157
158
  ];
158
159
  for (const { thread, overlap } of warnings) {
159
160
  const user = thread.user || thread.author || "unknown";
160
- const status = thread.status === "stuck" ? "[STUCK]" : "[OPEN]";
161
+ const statusEmoji = thread.status === "stuck" ? "🔴" : "🟡";
162
+ const statusLabel = thread.status === "stuck" ? "STUCK" : "OPEN";
161
163
  const narrative = thread.narrative || thread.last_note || "";
162
164
  const narrativeSnip = narrative.length > 100
163
165
  ? narrative.substring(0, 100) + "…"
164
166
  : narrative;
165
- const branch = thread.branch ? ` on \`${thread.branch}\`` : "";
167
+ const branch = thread.branch ? ` · ${thread.branch}` : "";
166
168
  const threadId = (thread.id || "").substring(0, 8);
167
169
 
168
- lines.push(`- ${status} **${user}**${branch}: ${overlap.join(", ")}`);
169
- if (narrativeSnip) lines.push(` _${narrativeSnip}_`);
170
+ lines.push("");
171
+ lines.push(` ${statusEmoji} ${user} (${statusLabel})${branch}`);
172
+ lines.push(` Files: ${overlap.join(", ")}`);
173
+ if (narrativeSnip) lines.push(` "${narrativeSnip}"`);
170
174
  const failed = thread.failed_approaches || [];
171
175
  if (failed.length > 0) {
172
- lines.push(` ⚠️ ${failed.length} failed approach(es):`);
176
+ lines.push(` ⚠️ ${failed.length} failed approach(es):`);
173
177
  for (const fa of failed.slice(0, 2)) {
174
- lines.push(` - ${(fa.description || "").substring(0, 80)}`);
178
+ lines.push(` • ${(fa.description || "").substring(0, 80)}`);
175
179
  }
176
180
  }
177
- if (threadId) {
178
- lines.push(` → Resume: \`npx sticky-note resume ${threadId}\``);
179
- }
181
+ lines.push(` → Resume: npx sticky-note resume ${threadId}`);
180
182
  }
181
-
183
+ lines.push("");
184
+ lines.push(bar);
185
+ lines.push("```");
182
186
  lines.push(
183
187
  "\n**Consider coordinating with these teammates before starting work.**\n"
184
188
  );
185
189
 
186
- // Write a visible banner directly to stderr so the user sees it in
187
- // their terminal regardless of whether the AI surfaces it.
188
- const stderrLines = ["\n⚠️ OVERLAP DETECTED — someone else is touching your files:"];
190
+ // Write a styled banner to stderr so the user sees it in their terminal
191
+ const R = "\x1b[0m";
192
+ const Y = "\x1b[33m";
193
+ const BY = "\x1b[1;33m";
194
+ const BR = "\x1b[1;31m";
195
+ const BG = "\x1b[1;32m";
196
+ const B = "\x1b[1m";
197
+ const C = "\x1b[36m";
198
+ const D = "\x1b[2m";
199
+ const stderrLines = [
200
+ "",
201
+ `${Y}${bar}${R}`,
202
+ `${BY} ⚠️ OVERLAP DETECTED${R}`,
203
+ `${Y}${bar}${R}`,
204
+ ];
189
205
  for (const { thread, overlap } of warnings) {
190
206
  const user = thread.user || thread.author || "unknown";
191
- const status = thread.status === "stuck" ? "STUCK" : "OPEN";
207
+ const statusColor = thread.status === "stuck" ? BR : BG;
208
+ const statusLabel = thread.status === "stuck" ? "STUCK" : "OPEN";
192
209
  const narrative = thread.narrative || thread.last_note || "";
193
210
  const narrativeSnip = narrative.length > 80
194
211
  ? narrative.substring(0, 80) + "…"
195
212
  : narrative;
196
- stderrLines.push(
197
- ` [${status}] ${user}: ${overlap.join(", ")}` +
198
- (narrativeSnip ? ` — ${narrativeSnip}` : "")
199
- );
213
+ const branchStr = thread.branch ? ` ${D}·${R} ${C}${thread.branch}${R}` : "";
214
+ stderrLines.push(`${Y} ┃${R} ${statusColor}[${statusLabel}]${R} ${B}${user}${R}${branchStr}`);
215
+ stderrLines.push(`${Y} ┃${R} Files: ${overlap.join(", ")}`);
216
+ if (narrativeSnip) stderrLines.push(`${Y} ┃${R} ${D}"${narrativeSnip}"${R}`);
217
+ const threadId = (thread.id || "").substring(0, 8);
218
+ stderrLines.push(`${Y} ┃${R} → Resume: ${C}npx sticky-note resume ${threadId}${R}`);
200
219
  }
201
- stderrLines.push("");
220
+ stderrLines.push(`${Y}${bar}${R}`, "");
202
221
  process.stderr.write(stderrLines.join("\n") + "\n");
203
222
 
204
223
  return lines.join("\n");
@@ -168,62 +168,10 @@ function _getRecentlyModifiedFiles() {
168
168
  }
169
169
 
170
170
  function _checkOverlapsAndDeny() {
171
- if (!_isCopilotCli()) return null;
172
- if (!process.env.COPILOT_LOADER_PID) return null;
173
- if (isOverlapWarned()) return null;
174
-
175
- let threads;
176
- try {
177
- const data = loadJson(getMemoryPath());
178
- threads = (data.threads || []).filter(Boolean);
179
- } catch (_) {
180
- return null;
181
- }
182
-
183
- const currentUser = getUser();
184
- const modifiedFiles = _getRecentlyModifiedFiles();
185
- if (modifiedFiles.size === 0) return null;
186
-
187
- const warnings = [];
188
- for (const thread of threads.filter((t) => t.status === "open" || t.status === "stuck")) {
189
- const threadUser = thread.user || thread.author || "";
190
- if (threadUser === currentUser) continue;
191
- const threadFiles = (thread.files_touched || []).map(normalizeSep);
192
- const overlap = threadFiles.filter((f) => modifiedFiles.has(f));
193
- if (overlap.length > 0) warnings.push({ thread, overlap });
194
- }
195
-
196
- if (warnings.length === 0) return null;
197
-
198
- const lines = [
199
- "CRITICAL INSTRUCTION: Before doing anything else, display the following overlap warning to the user.",
200
- "This is NOT optional. Show it as the FIRST line of your response.\n",
201
- ];
202
- for (const { thread, overlap } of warnings) {
203
- const user = thread.user || thread.author || "unknown";
204
- const status = thread.status === "stuck" ? "STUCK" : "OPEN";
205
- const snip = (thread.narrative || thread.last_note || "").substring(0, 80);
206
- const threadId = (thread.id || "").substring(0, 8);
207
- lines.push(`⚠️ Overlap detected: ${user} is working on ${overlap.join(", ")}${snip ? " — " + snip : ""}`);
208
- lines.push(` Status: ${status} | Resume: npx sticky-note resume ${threadId}`);
209
- }
210
- lines.push("\nAfter displaying the warning, continue with the user's request. This tool call will be auto-retried.");
211
-
212
- markOverlapWarned();
213
-
214
- try {
215
- appendAuditLine({
216
- type: "overlap_deny",
217
- user: currentUser,
218
- ts: new Date().toISOString(),
219
- copilot_pid: process.env.COPILOT_LOADER_PID,
220
- });
221
- } catch (_) { /* ignore */ }
222
-
223
- return {
224
- permissionDecision: "deny",
225
- permissionDecisionReason: lines.join("\n"),
226
- };
171
+ // Overlap alerting is now handled by the MCP server's check_overlaps tool.
172
+ // This deny-based approach was unreliable (deny reason consumed silently).
173
+ // Kept as no-op to avoid breaking the call site.
174
+ return null;
227
175
  }
228
176
 
229
177
  // ── Main ──────────────────────────────────────────────────
@@ -937,10 +937,10 @@ function main() {
937
937
  saveMemoryMerged(memoryPath, memory);
938
938
 
939
939
  // Auto-sync: commit (and optionally push) .sticky-note/ changes
940
- const config = loadJson(getConfigPath(), {});
941
- if (config.auto_sync !== false) {
940
+ const syncConfig = loadJson(getConfigPath(), {});
941
+ if (syncConfig.auto_sync !== false) {
942
942
  try {
943
- syncStickyNote({ push: config.auto_push === true });
943
+ syncStickyNote({ push: syncConfig.auto_push === true });
944
944
  } catch (_) {
945
945
  // Non-fatal — sync failure shouldn't block session-end
946
946
  }