@davesheffer/hunch 1.41.1 → 1.41.3
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/dist/integrations/providers.js +29 -2
- package/dist/store/changeLedger.js +8 -3
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -26,6 +26,27 @@ import { renderHunchSection, stripManagedSection, upsertSection, updateClaudeMd
|
|
|
26
26
|
import { headFileContent, isGitCleanPath } from "../extractors/git.js";
|
|
27
27
|
import { parseJsonc } from "../core/jsonc.js";
|
|
28
28
|
import { parse as parseToml } from "smol-toml";
|
|
29
|
+
/** JSONC comments are user prose. A parsed object cannot retain their position,
|
|
30
|
+
* so refuse a write rather than reserialize the file without them. */
|
|
31
|
+
function hasJsoncComments(raw) {
|
|
32
|
+
let inString = false;
|
|
33
|
+
for (let i = 0; i < raw.length; i++) {
|
|
34
|
+
const char = raw[i];
|
|
35
|
+
if (inString) {
|
|
36
|
+
if (char === "\\")
|
|
37
|
+
i++;
|
|
38
|
+
else if (char === '"')
|
|
39
|
+
inString = false;
|
|
40
|
+
}
|
|
41
|
+
else if (char === '"') {
|
|
42
|
+
inString = true;
|
|
43
|
+
}
|
|
44
|
+
else if (char === "/" && (raw[i + 1] === "/" || raw[i + 1] === "*")) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
29
50
|
/** Read a JSON/JSONC object. Returns {} only for an ABSENT or empty file. A
|
|
30
51
|
* non-empty file we cannot parse THROWS — overwriting it would silently wipe the
|
|
31
52
|
* user's other MCP servers. */
|
|
@@ -35,15 +56,21 @@ function readJsonObj(file) {
|
|
|
35
56
|
const raw = readFileSync(file, "utf8");
|
|
36
57
|
if (!raw.trim())
|
|
37
58
|
return {};
|
|
59
|
+
let value;
|
|
38
60
|
try {
|
|
39
61
|
const v = parseJsonc(raw);
|
|
40
62
|
if (v && typeof v === "object" && !Array.isArray(v))
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
value = v;
|
|
64
|
+
else
|
|
65
|
+
throw new Error("not a JSON object");
|
|
43
66
|
}
|
|
44
67
|
catch (e) {
|
|
45
68
|
throw new Error(`refusing to overwrite ${file}: could not parse it (${e.message}). Fix or remove it, then re-run.`);
|
|
46
69
|
}
|
|
70
|
+
if (hasJsoncComments(raw)) {
|
|
71
|
+
throw new Error(`refusing to edit ${file}: JSONC comments cannot be preserved by this writer. Edit the Hunch entry manually or remove the comments, then re-run.`);
|
|
72
|
+
}
|
|
73
|
+
return value;
|
|
47
74
|
}
|
|
48
75
|
/** Render a string as a TOML value: a literal '…' when safe (no escaping needed —
|
|
49
76
|
* ideal for Windows backslash paths), else a basic "…" with escapes. */
|
|
@@ -170,8 +170,14 @@ export function mergeLedgers(base, ours, theirs) {
|
|
|
170
170
|
seen.set(k, e);
|
|
171
171
|
order.push(e);
|
|
172
172
|
} };
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
const ourIdentities = new Set(ours.events.map(eventIdentity));
|
|
174
|
+
const theirIdentities = new Set(theirs.events.map(eventIdentity));
|
|
175
|
+
// Base is a merge ancestor, not a source of history both clones have compacted away.
|
|
176
|
+
for (const e of base?.events ?? []) {
|
|
177
|
+
const key = eventIdentity(e);
|
|
178
|
+
if (ourIdentities.has(key) || theirIdentities.has(key) || (e.seq > ours.floor_seq && e.seq > theirs.floor_seq))
|
|
179
|
+
add(e);
|
|
180
|
+
}
|
|
175
181
|
for (const e of ours.events)
|
|
176
182
|
add(e);
|
|
177
183
|
for (const e of theirs.events)
|
|
@@ -180,7 +186,6 @@ export function mergeLedgers(base, ours, theirs) {
|
|
|
180
186
|
// side 1 iff theirs holds it and neither ours nor base does. Deciding by object identity
|
|
181
187
|
// (`includes`) would instead put anything ours compacted away — base's object, which `add`
|
|
182
188
|
// keeps — after its same-`at` siblings and silently reorder a batch both sides already had.
|
|
183
|
-
const ourIdentities = new Set(ours.events.map(eventIdentity));
|
|
184
189
|
const baseIdentities = new Set((base?.events ?? []).map(eventIdentity));
|
|
185
190
|
const theirsOnly = (e) => {
|
|
186
191
|
const k = eventIdentity(e);
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://www.hunchmemory.com",
|
|
10
|
-
"version": "1.41.
|
|
10
|
+
"version": "1.41.3",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@davesheffer/hunch",
|
|
16
|
-
"version": "1.41.
|
|
16
|
+
"version": "1.41.3",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"packageArguments": [
|
|
19
19
|
{
|