sticky-note-cli 3.1.42 → 3.1.43
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 +23 -3
- package/package.json +1 -1
- package/templates/hooks/session-end.js +17 -12
- package/templates/hooks/session-start.js +30 -7
- package/templates/hooks/sticky-utils.js +14 -0
package/bin/cli.js
CHANGED
|
@@ -1071,10 +1071,11 @@ async function cmdInit() {
|
|
|
1071
1071
|
print(" ⏭️ .gitignore already configured");
|
|
1072
1072
|
}
|
|
1073
1073
|
|
|
1074
|
-
// Add .gitattributes merge strategy
|
|
1074
|
+
// Add .gitattributes merge strategy for both thread data and config
|
|
1075
1075
|
const gitattrsPath = path.join(process.cwd(), ".gitattributes");
|
|
1076
1076
|
const oldMergeRule = ".sticky-note/sticky-note.json merge=union";
|
|
1077
1077
|
const mergeRule = ".sticky-note/sticky-note.json merge=sticky-note";
|
|
1078
|
+
const configMergeRule = ".sticky-note/sticky-note-config.json merge=sticky-note";
|
|
1078
1079
|
let gitattrsContent = "";
|
|
1079
1080
|
if (fs.existsSync(gitattrsPath)) {
|
|
1080
1081
|
gitattrsContent = fs.readFileSync(gitattrsPath, "utf-8");
|
|
@@ -1089,10 +1090,18 @@ async function cmdInit() {
|
|
|
1089
1090
|
? `\n# Sticky Note - JSON-aware thread merge\n${mergeRule}\n`
|
|
1090
1091
|
: `\n\n# Sticky Note - JSON-aware thread merge\n${mergeRule}\n`;
|
|
1091
1092
|
fs.appendFileSync(gitattrsPath, addition);
|
|
1093
|
+
gitattrsContent += addition;
|
|
1092
1094
|
print(" [OK] .gitattributes updated (merge=sticky-note)");
|
|
1093
1095
|
} else {
|
|
1094
1096
|
print(" ⏭️ .gitattributes already configured");
|
|
1095
1097
|
}
|
|
1098
|
+
// Also protect sticky-note-config.json — merge conflicts here silently break
|
|
1099
|
+
// cloud transport and hook loading for the entire team.
|
|
1100
|
+
if (!gitattrsContent.includes(configMergeRule)) {
|
|
1101
|
+
const addition = `${configMergeRule}\n`;
|
|
1102
|
+
fs.appendFileSync(gitattrsPath, addition);
|
|
1103
|
+
print(" [OK] .gitattributes: sticky-note-config.json protected from merge conflicts");
|
|
1104
|
+
}
|
|
1096
1105
|
|
|
1097
1106
|
// Install merge driver script and git config
|
|
1098
1107
|
const mergeDriverSrc = path.join(TEMPLATES_DIR, "merge-driver.js");
|
|
@@ -1452,14 +1461,25 @@ async function cmdUpdate() {
|
|
|
1452
1461
|
}
|
|
1453
1462
|
}
|
|
1454
1463
|
|
|
1455
|
-
|
|
1456
|
-
let gitattrsContent = fs.
|
|
1464
|
+
{
|
|
1465
|
+
let gitattrsContent = fs.existsSync(gitattrsPath)
|
|
1466
|
+
? fs.readFileSync(gitattrsPath, "utf-8") : "";
|
|
1457
1467
|
const oldRule = ".sticky-note/sticky-note.json merge=union";
|
|
1458
1468
|
const newRule = ".sticky-note/sticky-note.json merge=sticky-note";
|
|
1469
|
+
const configRule = ".sticky-note/sticky-note-config.json merge=sticky-note";
|
|
1459
1470
|
if (gitattrsContent.includes(oldRule)) {
|
|
1460
1471
|
gitattrsContent = gitattrsContent.replace(oldRule, newRule);
|
|
1461
1472
|
fs.writeFileSync(gitattrsPath, gitattrsContent, "utf-8");
|
|
1462
1473
|
print(" [OK] .gitattributes upgraded (merge=union -> merge=sticky-note)");
|
|
1474
|
+
} else if (!gitattrsContent.includes(newRule)) {
|
|
1475
|
+
const addition = `\n# Sticky Note - JSON-aware thread merge\n${newRule}\n`;
|
|
1476
|
+
fs.appendFileSync(gitattrsPath, addition);
|
|
1477
|
+
gitattrsContent += addition;
|
|
1478
|
+
print(" [OK] .gitattributes updated (merge=sticky-note)");
|
|
1479
|
+
}
|
|
1480
|
+
if (!gitattrsContent.includes(configRule)) {
|
|
1481
|
+
fs.appendFileSync(gitattrsPath, `${configRule}\n`);
|
|
1482
|
+
print(" [OK] .gitattributes: sticky-note-config.json protected from merge conflicts");
|
|
1463
1483
|
}
|
|
1464
1484
|
}
|
|
1465
1485
|
|
package/package.json
CHANGED
|
@@ -954,7 +954,7 @@ function _collectDirFiles(dir, ext, prefix, fileMap) {
|
|
|
954
954
|
}
|
|
955
955
|
|
|
956
956
|
function commitAndPushDataBranch() {
|
|
957
|
-
if (!dataBranch) return;
|
|
957
|
+
if (!dataBranch) return null;
|
|
958
958
|
try {
|
|
959
959
|
const fileMap = {};
|
|
960
960
|
const memPath = getMemoryPath();
|
|
@@ -966,7 +966,7 @@ function commitAndPushDataBranch() {
|
|
|
966
966
|
_collectDirFiles(getPresenceDir(), ".json", "presence/", fileMap);
|
|
967
967
|
_collectDirFiles(getTranscriptsDir(), ".jsonl", "transcripts/", fileMap);
|
|
968
968
|
|
|
969
|
-
if (Object.keys(fileMap).length === 0) return;
|
|
969
|
+
if (Object.keys(fileMap).length === 0) return null;
|
|
970
970
|
|
|
971
971
|
dataBranch.commitFilesToBranch(dataBranch.DATA_BRANCH, fileMap);
|
|
972
972
|
|
|
@@ -976,16 +976,18 @@ function commitAndPushDataBranch() {
|
|
|
976
976
|
remote, dataBranch.DATA_BRANCH, 3, getMemoryPath(), loadJson, saveJson
|
|
977
977
|
);
|
|
978
978
|
if (!pushResult.ok) {
|
|
979
|
-
|
|
980
|
-
"
|
|
981
|
-
|
|
982
|
-
|
|
979
|
+
const hint = pushResult.error && pushResult.error.includes("authentication")
|
|
980
|
+
? " Add STICKY_PUSH_TOKEN=<github_pat> to .env.sticky to fix."
|
|
981
|
+
: "";
|
|
982
|
+
const msg = `[STICKY-NOTE] ⚠️ data branch push failed — ${pushResult.error}${hint}`;
|
|
983
|
+
logHookError("data-branch-push", new Error(pushResult.error));
|
|
984
|
+
return msg;
|
|
983
985
|
}
|
|
984
986
|
}
|
|
987
|
+
return null;
|
|
985
988
|
} catch (err) {
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
);
|
|
989
|
+
logHookError("data-branch-sync", err);
|
|
990
|
+
return `[STICKY-NOTE] ⚠️ data branch sync failed — ${err.message}`;
|
|
989
991
|
}
|
|
990
992
|
}
|
|
991
993
|
|
|
@@ -1306,7 +1308,9 @@ async function main() {
|
|
|
1306
1308
|
if (cloud) {
|
|
1307
1309
|
const threadToSync = threads.find(t => t.session_id === sessionId);
|
|
1308
1310
|
if (threadToSync) {
|
|
1309
|
-
cloudWriteThread(threadToSync).catch(
|
|
1311
|
+
cloudWriteThread(threadToSync).catch(err => {
|
|
1312
|
+
logHookError("session-end:cloud-write", err);
|
|
1313
|
+
});
|
|
1310
1314
|
}
|
|
1311
1315
|
}
|
|
1312
1316
|
|
|
@@ -1320,14 +1324,15 @@ async function main() {
|
|
|
1320
1324
|
}
|
|
1321
1325
|
|
|
1322
1326
|
// Commit current data to sticky-note/data branch and push
|
|
1323
|
-
commitAndPushDataBranch();
|
|
1327
|
+
const dataBranchWarning = commitAndPushDataBranch();
|
|
1324
1328
|
|
|
1325
1329
|
const fileCount = filesTouched.length;
|
|
1326
1330
|
const commitCount = commitShas.length;
|
|
1327
1331
|
const statusLabel = isCopilotCli ? "updated" : "closed";
|
|
1328
1332
|
const statusMsg = `[STICKY-NOTE] Session ${statusLabel} - thread ${isCopilotCli ? "updated" : "created"} (${fileCount} file${fileCount !== 1 ? "s" : ""}${commitCount > 0 ? ", " + commitCount + " commit" + (commitCount !== 1 ? "s" : "") : ""})`;
|
|
1333
|
+
const fullOutput = dataBranchWarning ? `${statusMsg}\n${dataBranchWarning}` : statusMsg;
|
|
1329
1334
|
try {
|
|
1330
|
-
process.stdout.write(JSON.stringify({ output:
|
|
1335
|
+
process.stdout.write(JSON.stringify({ output: fullOutput }) + "\n");
|
|
1331
1336
|
} catch (_) {
|
|
1332
1337
|
process.stdout.write('{"output":""}\n');
|
|
1333
1338
|
}
|
|
@@ -69,15 +69,38 @@ function _installedCliVersion() {
|
|
|
69
69
|
}
|
|
70
70
|
function formatVersionWarning(config) {
|
|
71
71
|
try {
|
|
72
|
-
const min = config && config.min_version;
|
|
73
|
-
if (!min) return "";
|
|
74
72
|
const installed = _installedCliVersion();
|
|
75
73
|
if (!installed) return "";
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
const warnings = [];
|
|
75
|
+
|
|
76
|
+
// Check 1: installed version is below the project's required minimum.
|
|
77
|
+
const min = config && config.min_version;
|
|
78
|
+
if (min && _cmpSemver(installed, min) < 0) {
|
|
79
|
+
warnings.push(
|
|
80
|
+
`[STICKY-NOTE] ⚠️ sticky-note-cli v${installed} is below this project's ` +
|
|
81
|
+
`min_version v${min}. Run \`npm i -g sticky-note-cli@latest\` to update — ` +
|
|
82
|
+
`features and bug fixes are missing until you do.`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check 2: hook scripts in .claude/hooks/ are older than the installed CLI.
|
|
87
|
+
// This means the teammate installed a newer CLI but never ran `npx sticky-note update`
|
|
88
|
+
// to refresh the hook files — they're running old code with new expectations.
|
|
89
|
+
const hookVer = config && config.hook_version;
|
|
90
|
+
if (hookVer && _cmpSemver(installed, hookVer) > 0) {
|
|
91
|
+
const iv = _parseSemver(installed), hv = _parseSemver(hookVer);
|
|
92
|
+
// Warn on any minor or major gap — patch-only differences are usually safe.
|
|
93
|
+
if (iv && hv && (iv[0] !== hv[0] || iv[1] !== hv[1])) {
|
|
94
|
+
warnings.push(
|
|
95
|
+
`[STICKY-NOTE] ⚠️ Hook scripts are v${hookVer} but sticky-note-cli v${installed} ` +
|
|
96
|
+
`is installed. Run \`npx sticky-note update\` to get the latest fixes ` +
|
|
97
|
+
`(data branch push, identity, credential handling).`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (warnings.length === 0) return "";
|
|
103
|
+
const msg = warnings.join("\n");
|
|
81
104
|
try { process.stderr.write(msg + "\n"); } catch (_) { /* non-fatal */ }
|
|
82
105
|
return msg;
|
|
83
106
|
} catch (_) {
|
|
@@ -450,6 +450,20 @@ function clearResumeSignal() {
|
|
|
450
450
|
function loadJson(filePath, defaultVal) {
|
|
451
451
|
try {
|
|
452
452
|
const raw = fs.readFileSync(filePath, "utf-8");
|
|
453
|
+
// Detect unresolved git merge conflict markers — JSON.parse would silently
|
|
454
|
+
// swallow the SyntaxError and return the default, hiding the real problem.
|
|
455
|
+
// This breaks useCloud(), thread loading, and config reading with no trace.
|
|
456
|
+
if (raw.includes("<<<<<<<") || raw.includes(">>>>>>>")) {
|
|
457
|
+
logHookError(
|
|
458
|
+
"loadJson",
|
|
459
|
+
new Error(
|
|
460
|
+
`Unresolved git merge conflict in ${filePath}. ` +
|
|
461
|
+
`Resolve the conflict and commit, or restore the file with ` +
|
|
462
|
+
`\`git checkout HEAD -- ${path.relative(process.cwd(), filePath) || filePath}\``
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
return defaultVal !== undefined ? defaultVal : {};
|
|
466
|
+
}
|
|
453
467
|
return JSON.parse(raw);
|
|
454
468
|
} catch (_) {
|
|
455
469
|
return defaultVal !== undefined ? defaultVal : {};
|