@ghostpaw/affinity 0.3.0 → 0.4.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/README.md +168 -9
- package/dist/cli.js +606 -137
- package/dist/index.cjs +38 -14
- package/dist/index.js +38 -14
- package/dist/mcp-server.js +52 -23
- package/dist/mcp.cjs +40 -16
- package/dist/mcp.d.cts +10 -0
- package/dist/mcp.d.ts +10 -0
- package/dist/mcp.js +40 -16
- package/package.json +17 -6
package/dist/index.cjs
CHANGED
|
@@ -788,13 +788,20 @@ function createContact(db, input) {
|
|
|
788
788
|
if (!KINDS.has(input.kind)) {
|
|
789
789
|
throw new AffinityValidationError("invalid contact kind");
|
|
790
790
|
}
|
|
791
|
+
if (input.isOwner !== void 0) {
|
|
792
|
+
throw new AffinityValidationError(
|
|
793
|
+
"isOwner is read-only; pass bootstrapOwner: true to create the owner contact"
|
|
794
|
+
);
|
|
795
|
+
}
|
|
791
796
|
const now = resolveNow(input.now);
|
|
792
797
|
if (input.bootstrapOwner) {
|
|
793
798
|
const existing = db.prepare(
|
|
794
|
-
"SELECT id FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
799
|
+
"SELECT id, name FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
795
800
|
).get();
|
|
796
801
|
if (existing !== void 0) {
|
|
797
|
-
throw new AffinityConflictError(
|
|
802
|
+
throw new AffinityConflictError(
|
|
803
|
+
`owner already exists: contact ${existing.id} (${existing.name}); bootstrapOwner can only be used once`
|
|
804
|
+
);
|
|
798
805
|
}
|
|
799
806
|
}
|
|
800
807
|
const isOwner = input.bootstrapOwner ? 1 : 0;
|
|
@@ -6453,17 +6460,6 @@ function unsetAttribute(db, target, name, removedAt) {
|
|
|
6453
6460
|
});
|
|
6454
6461
|
}
|
|
6455
6462
|
|
|
6456
|
-
// src/tools/tool_summary.ts
|
|
6457
|
-
function summarizeCount(count, singular) {
|
|
6458
|
-
if (count === 0) {
|
|
6459
|
-
return `No ${singular}s found.`;
|
|
6460
|
-
}
|
|
6461
|
-
if (count === 1) {
|
|
6462
|
-
return `Found 1 ${singular}.`;
|
|
6463
|
-
}
|
|
6464
|
-
return `Found ${count} ${singular}s.`;
|
|
6465
|
-
}
|
|
6466
|
-
|
|
6467
6463
|
// src/tools/tool_mutation.ts
|
|
6468
6464
|
function mutationToolResult(action, receipt, entitySummary) {
|
|
6469
6465
|
const changedCount = receipt.created.length + receipt.updated.length + receipt.archived.length + receipt.removed.length;
|
|
@@ -6477,7 +6473,7 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
6477
6473
|
affectedLinks: receipt.affectedLinks,
|
|
6478
6474
|
derivedEffects: receipt.derivedEffects
|
|
6479
6475
|
};
|
|
6480
|
-
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` :
|
|
6476
|
+
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` : describeChanges(receipt);
|
|
6481
6477
|
if (changedCount === 0) {
|
|
6482
6478
|
return toolNoOp(summary, data, {
|
|
6483
6479
|
entities: toReceiptEntityRefs(receipt)
|
|
@@ -6487,6 +6483,23 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
6487
6483
|
entities: toReceiptEntityRefs(receipt)
|
|
6488
6484
|
});
|
|
6489
6485
|
}
|
|
6486
|
+
var PLURALS = { identity: "identities" };
|
|
6487
|
+
function describeChanges(receipt) {
|
|
6488
|
+
const parts = [];
|
|
6489
|
+
for (const verb of ["created", "updated", "archived", "removed"]) {
|
|
6490
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6491
|
+
for (const ref of receipt[verb]) {
|
|
6492
|
+
counts.set(ref.kind, (counts.get(ref.kind) ?? 0) + 1);
|
|
6493
|
+
}
|
|
6494
|
+
if (counts.size === 0) continue;
|
|
6495
|
+
const items = [...counts].map(
|
|
6496
|
+
([kind, n]) => `${n} ${n === 1 ? kind : PLURALS[kind] ?? `${kind}s`}`
|
|
6497
|
+
);
|
|
6498
|
+
parts.push(`${verb} ${items.join(", ")}`);
|
|
6499
|
+
}
|
|
6500
|
+
const text = parts.join("; ");
|
|
6501
|
+
return `${text.charAt(0).toUpperCase()}${text.slice(1)}.`;
|
|
6502
|
+
}
|
|
6490
6503
|
|
|
6491
6504
|
// src/tools/manage_attribute_tool.ts
|
|
6492
6505
|
function contactLocatorSchema2(description) {
|
|
@@ -7868,6 +7881,17 @@ var recordEventTool = defineAffinityTool({
|
|
|
7868
7881
|
handler: recordEventToolHandler
|
|
7869
7882
|
});
|
|
7870
7883
|
|
|
7884
|
+
// src/tools/tool_summary.ts
|
|
7885
|
+
function summarizeCount(count, singular) {
|
|
7886
|
+
if (count === 0) {
|
|
7887
|
+
return `No ${singular}s found.`;
|
|
7888
|
+
}
|
|
7889
|
+
if (count === 1) {
|
|
7890
|
+
return `Found 1 ${singular}.`;
|
|
7891
|
+
}
|
|
7892
|
+
return `Found ${count} ${singular}s.`;
|
|
7893
|
+
}
|
|
7894
|
+
|
|
7871
7895
|
// src/tools/review_affinity_tool.ts
|
|
7872
7896
|
function buildReadOptions(input) {
|
|
7873
7897
|
return {
|
package/dist/index.js
CHANGED
|
@@ -786,13 +786,20 @@ function createContact(db, input) {
|
|
|
786
786
|
if (!KINDS.has(input.kind)) {
|
|
787
787
|
throw new AffinityValidationError("invalid contact kind");
|
|
788
788
|
}
|
|
789
|
+
if (input.isOwner !== void 0) {
|
|
790
|
+
throw new AffinityValidationError(
|
|
791
|
+
"isOwner is read-only; pass bootstrapOwner: true to create the owner contact"
|
|
792
|
+
);
|
|
793
|
+
}
|
|
789
794
|
const now = resolveNow(input.now);
|
|
790
795
|
if (input.bootstrapOwner) {
|
|
791
796
|
const existing = db.prepare(
|
|
792
|
-
"SELECT id FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
797
|
+
"SELECT id, name FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
793
798
|
).get();
|
|
794
799
|
if (existing !== void 0) {
|
|
795
|
-
throw new AffinityConflictError(
|
|
800
|
+
throw new AffinityConflictError(
|
|
801
|
+
`owner already exists: contact ${existing.id} (${existing.name}); bootstrapOwner can only be used once`
|
|
802
|
+
);
|
|
796
803
|
}
|
|
797
804
|
}
|
|
798
805
|
const isOwner = input.bootstrapOwner ? 1 : 0;
|
|
@@ -6451,17 +6458,6 @@ function unsetAttribute(db, target, name, removedAt) {
|
|
|
6451
6458
|
});
|
|
6452
6459
|
}
|
|
6453
6460
|
|
|
6454
|
-
// src/tools/tool_summary.ts
|
|
6455
|
-
function summarizeCount(count, singular) {
|
|
6456
|
-
if (count === 0) {
|
|
6457
|
-
return `No ${singular}s found.`;
|
|
6458
|
-
}
|
|
6459
|
-
if (count === 1) {
|
|
6460
|
-
return `Found 1 ${singular}.`;
|
|
6461
|
-
}
|
|
6462
|
-
return `Found ${count} ${singular}s.`;
|
|
6463
|
-
}
|
|
6464
|
-
|
|
6465
6461
|
// src/tools/tool_mutation.ts
|
|
6466
6462
|
function mutationToolResult(action, receipt, entitySummary) {
|
|
6467
6463
|
const changedCount = receipt.created.length + receipt.updated.length + receipt.archived.length + receipt.removed.length;
|
|
@@ -6475,7 +6471,7 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
6475
6471
|
affectedLinks: receipt.affectedLinks,
|
|
6476
6472
|
derivedEffects: receipt.derivedEffects
|
|
6477
6473
|
};
|
|
6478
|
-
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` :
|
|
6474
|
+
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` : describeChanges(receipt);
|
|
6479
6475
|
if (changedCount === 0) {
|
|
6480
6476
|
return toolNoOp(summary, data, {
|
|
6481
6477
|
entities: toReceiptEntityRefs(receipt)
|
|
@@ -6485,6 +6481,23 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
6485
6481
|
entities: toReceiptEntityRefs(receipt)
|
|
6486
6482
|
});
|
|
6487
6483
|
}
|
|
6484
|
+
var PLURALS = { identity: "identities" };
|
|
6485
|
+
function describeChanges(receipt) {
|
|
6486
|
+
const parts = [];
|
|
6487
|
+
for (const verb of ["created", "updated", "archived", "removed"]) {
|
|
6488
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6489
|
+
for (const ref of receipt[verb]) {
|
|
6490
|
+
counts.set(ref.kind, (counts.get(ref.kind) ?? 0) + 1);
|
|
6491
|
+
}
|
|
6492
|
+
if (counts.size === 0) continue;
|
|
6493
|
+
const items = [...counts].map(
|
|
6494
|
+
([kind, n]) => `${n} ${n === 1 ? kind : PLURALS[kind] ?? `${kind}s`}`
|
|
6495
|
+
);
|
|
6496
|
+
parts.push(`${verb} ${items.join(", ")}`);
|
|
6497
|
+
}
|
|
6498
|
+
const text = parts.join("; ");
|
|
6499
|
+
return `${text.charAt(0).toUpperCase()}${text.slice(1)}.`;
|
|
6500
|
+
}
|
|
6488
6501
|
|
|
6489
6502
|
// src/tools/manage_attribute_tool.ts
|
|
6490
6503
|
function contactLocatorSchema2(description) {
|
|
@@ -7866,6 +7879,17 @@ var recordEventTool = defineAffinityTool({
|
|
|
7866
7879
|
handler: recordEventToolHandler
|
|
7867
7880
|
});
|
|
7868
7881
|
|
|
7882
|
+
// src/tools/tool_summary.ts
|
|
7883
|
+
function summarizeCount(count, singular) {
|
|
7884
|
+
if (count === 0) {
|
|
7885
|
+
return `No ${singular}s found.`;
|
|
7886
|
+
}
|
|
7887
|
+
if (count === 1) {
|
|
7888
|
+
return `Found 1 ${singular}.`;
|
|
7889
|
+
}
|
|
7890
|
+
return `Found ${count} ${singular}s.`;
|
|
7891
|
+
}
|
|
7892
|
+
|
|
7869
7893
|
// src/tools/review_affinity_tool.ts
|
|
7870
7894
|
function buildReadOptions(input) {
|
|
7871
7895
|
return {
|
package/dist/mcp-server.js
CHANGED
|
@@ -5,8 +5,13 @@ import { homedir } from 'node:os';
|
|
|
5
5
|
|
|
6
6
|
var __defProp = Object.defineProperty;
|
|
7
7
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
-
var __esm = (fn, res) => function __init() {
|
|
9
|
-
|
|
8
|
+
var __esm = (fn, res, err) => function __init() {
|
|
9
|
+
if (err) throw err[0];
|
|
10
|
+
try {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
throw err = [e], e;
|
|
14
|
+
}
|
|
10
15
|
};
|
|
11
16
|
var __export = (target, all) => {
|
|
12
17
|
for (var name in all)
|
|
@@ -280,7 +285,7 @@ var init_mcp_stdio = __esm({
|
|
|
280
285
|
var affinityVersion;
|
|
281
286
|
var init_version = __esm({
|
|
282
287
|
"src/version.ts"() {
|
|
283
|
-
affinityVersion = "0.
|
|
288
|
+
affinityVersion = "0.4.1";
|
|
284
289
|
}
|
|
285
290
|
});
|
|
286
291
|
|
|
@@ -2757,21 +2762,6 @@ var init_unset_attribute = __esm({
|
|
|
2757
2762
|
}
|
|
2758
2763
|
});
|
|
2759
2764
|
|
|
2760
|
-
// src/tools/tool_summary.ts
|
|
2761
|
-
function summarizeCount(count, singular) {
|
|
2762
|
-
if (count === 0) {
|
|
2763
|
-
return `No ${singular}s found.`;
|
|
2764
|
-
}
|
|
2765
|
-
if (count === 1) {
|
|
2766
|
-
return `Found 1 ${singular}.`;
|
|
2767
|
-
}
|
|
2768
|
-
return `Found ${count} ${singular}s.`;
|
|
2769
|
-
}
|
|
2770
|
-
var init_tool_summary = __esm({
|
|
2771
|
-
"src/tools/tool_summary.ts"() {
|
|
2772
|
-
}
|
|
2773
|
-
});
|
|
2774
|
-
|
|
2775
2765
|
// src/tools/tool_mutation.ts
|
|
2776
2766
|
function mutationToolResult(action, receipt, entitySummary) {
|
|
2777
2767
|
const changedCount = receipt.created.length + receipt.updated.length + receipt.archived.length + receipt.removed.length;
|
|
@@ -2785,7 +2775,7 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
2785
2775
|
affectedLinks: receipt.affectedLinks,
|
|
2786
2776
|
derivedEffects: receipt.derivedEffects
|
|
2787
2777
|
};
|
|
2788
|
-
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` :
|
|
2778
|
+
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` : describeChanges(receipt);
|
|
2789
2779
|
if (changedCount === 0) {
|
|
2790
2780
|
return toolNoOp(summary, data, {
|
|
2791
2781
|
entities: toReceiptEntityRefs(receipt)
|
|
@@ -2795,11 +2785,28 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
2795
2785
|
entities: toReceiptEntityRefs(receipt)
|
|
2796
2786
|
});
|
|
2797
2787
|
}
|
|
2788
|
+
function describeChanges(receipt) {
|
|
2789
|
+
const parts = [];
|
|
2790
|
+
for (const verb of ["created", "updated", "archived", "removed"]) {
|
|
2791
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2792
|
+
for (const ref of receipt[verb]) {
|
|
2793
|
+
counts.set(ref.kind, (counts.get(ref.kind) ?? 0) + 1);
|
|
2794
|
+
}
|
|
2795
|
+
if (counts.size === 0) continue;
|
|
2796
|
+
const items = [...counts].map(
|
|
2797
|
+
([kind, n]) => `${n} ${n === 1 ? kind : PLURALS[kind] ?? `${kind}s`}`
|
|
2798
|
+
);
|
|
2799
|
+
parts.push(`${verb} ${items.join(", ")}`);
|
|
2800
|
+
}
|
|
2801
|
+
const text = parts.join("; ");
|
|
2802
|
+
return `${text.charAt(0).toUpperCase()}${text.slice(1)}.`;
|
|
2803
|
+
}
|
|
2804
|
+
var PLURALS;
|
|
2798
2805
|
var init_tool_mutation = __esm({
|
|
2799
2806
|
"src/tools/tool_mutation.ts"() {
|
|
2800
2807
|
init_tool_ref();
|
|
2801
|
-
init_tool_summary();
|
|
2802
2808
|
init_tool_types();
|
|
2809
|
+
PLURALS = { identity: "identities" };
|
|
2803
2810
|
}
|
|
2804
2811
|
});
|
|
2805
2812
|
|
|
@@ -4561,13 +4568,20 @@ function createContact(db, input) {
|
|
|
4561
4568
|
if (!KINDS.has(input.kind)) {
|
|
4562
4569
|
throw new AffinityValidationError("invalid contact kind");
|
|
4563
4570
|
}
|
|
4571
|
+
if (input.isOwner !== void 0) {
|
|
4572
|
+
throw new AffinityValidationError(
|
|
4573
|
+
"isOwner is read-only; pass bootstrapOwner: true to create the owner contact"
|
|
4574
|
+
);
|
|
4575
|
+
}
|
|
4564
4576
|
const now = resolveNow(input.now);
|
|
4565
4577
|
if (input.bootstrapOwner) {
|
|
4566
4578
|
const existing = db.prepare(
|
|
4567
|
-
"SELECT id FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
4579
|
+
"SELECT id, name FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
4568
4580
|
).get();
|
|
4569
4581
|
if (existing !== void 0) {
|
|
4570
|
-
throw new AffinityConflictError(
|
|
4582
|
+
throw new AffinityConflictError(
|
|
4583
|
+
`owner already exists: contact ${existing.id} (${existing.name}); bootstrapOwner can only be used once`
|
|
4584
|
+
);
|
|
4571
4585
|
}
|
|
4572
4586
|
}
|
|
4573
4587
|
const isOwner = input.bootstrapOwner ? 1 : 0;
|
|
@@ -7670,6 +7684,21 @@ var init_list_dismissed_duplicates = __esm({
|
|
|
7670
7684
|
}
|
|
7671
7685
|
});
|
|
7672
7686
|
|
|
7687
|
+
// src/tools/tool_summary.ts
|
|
7688
|
+
function summarizeCount(count, singular) {
|
|
7689
|
+
if (count === 0) {
|
|
7690
|
+
return `No ${singular}s found.`;
|
|
7691
|
+
}
|
|
7692
|
+
if (count === 1) {
|
|
7693
|
+
return `Found 1 ${singular}.`;
|
|
7694
|
+
}
|
|
7695
|
+
return `Found ${count} ${singular}s.`;
|
|
7696
|
+
}
|
|
7697
|
+
var init_tool_summary = __esm({
|
|
7698
|
+
"src/tools/tool_summary.ts"() {
|
|
7699
|
+
}
|
|
7700
|
+
});
|
|
7701
|
+
|
|
7673
7702
|
// src/tools/review_affinity_tool.ts
|
|
7674
7703
|
function buildReadOptions(input) {
|
|
7675
7704
|
return {
|
|
@@ -8485,7 +8514,7 @@ function affinityMcpOptions(context) {
|
|
|
8485
8514
|
annotations: (tool) => ({
|
|
8486
8515
|
readOnlyHint: tool.readOnly ?? false,
|
|
8487
8516
|
destructiveHint: tool.name === "merge_contacts" || /remove|unset|undismiss/.test(
|
|
8488
|
-
|
|
8517
|
+
String(tool.inputSchema.properties?.action?.enum ?? [])
|
|
8489
8518
|
),
|
|
8490
8519
|
idempotentHint: tool.readOnly ?? false,
|
|
8491
8520
|
openWorldHint: false
|
package/dist/mcp.cjs
CHANGED
|
@@ -5,7 +5,7 @@ var node_path = require('node:path');
|
|
|
5
5
|
var node_os = require('node:os');
|
|
6
6
|
|
|
7
7
|
// src/version.ts
|
|
8
|
-
var affinityVersion = "0.
|
|
8
|
+
var affinityVersion = "0.4.1";
|
|
9
9
|
|
|
10
10
|
// src/surface/affinity_mcp_instructions.ts
|
|
11
11
|
var affinityMcpInstructions = "Affinity is a local relationship model and social CRM. Search before creating contacts. Inspect before revising or merging. Record direct evidence honestly, keep identities and structural ties distinct from social links, and ask for clarification when a target is ambiguous. Review commitments and recurring dates regularly. Derived scores guide attention and do not prove relationship quality.";
|
|
@@ -2160,17 +2160,6 @@ function unsetAttribute(db, target, name, removedAt) {
|
|
|
2160
2160
|
});
|
|
2161
2161
|
}
|
|
2162
2162
|
|
|
2163
|
-
// src/tools/tool_summary.ts
|
|
2164
|
-
function summarizeCount(count, singular) {
|
|
2165
|
-
if (count === 0) {
|
|
2166
|
-
return `No ${singular}s found.`;
|
|
2167
|
-
}
|
|
2168
|
-
if (count === 1) {
|
|
2169
|
-
return `Found 1 ${singular}.`;
|
|
2170
|
-
}
|
|
2171
|
-
return `Found ${count} ${singular}s.`;
|
|
2172
|
-
}
|
|
2173
|
-
|
|
2174
2163
|
// src/tools/tool_mutation.ts
|
|
2175
2164
|
function mutationToolResult(action, receipt, entitySummary) {
|
|
2176
2165
|
const changedCount = receipt.created.length + receipt.updated.length + receipt.archived.length + receipt.removed.length;
|
|
@@ -2184,7 +2173,7 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
2184
2173
|
affectedLinks: receipt.affectedLinks,
|
|
2185
2174
|
derivedEffects: receipt.derivedEffects
|
|
2186
2175
|
};
|
|
2187
|
-
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` :
|
|
2176
|
+
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` : describeChanges(receipt);
|
|
2188
2177
|
if (changedCount === 0) {
|
|
2189
2178
|
return toolNoOp(summary, data, {
|
|
2190
2179
|
entities: toReceiptEntityRefs(receipt)
|
|
@@ -2194,6 +2183,23 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
2194
2183
|
entities: toReceiptEntityRefs(receipt)
|
|
2195
2184
|
});
|
|
2196
2185
|
}
|
|
2186
|
+
var PLURALS = { identity: "identities" };
|
|
2187
|
+
function describeChanges(receipt) {
|
|
2188
|
+
const parts = [];
|
|
2189
|
+
for (const verb of ["created", "updated", "archived", "removed"]) {
|
|
2190
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2191
|
+
for (const ref of receipt[verb]) {
|
|
2192
|
+
counts.set(ref.kind, (counts.get(ref.kind) ?? 0) + 1);
|
|
2193
|
+
}
|
|
2194
|
+
if (counts.size === 0) continue;
|
|
2195
|
+
const items = [...counts].map(
|
|
2196
|
+
([kind, n]) => `${n} ${n === 1 ? kind : PLURALS[kind] ?? `${kind}s`}`
|
|
2197
|
+
);
|
|
2198
|
+
parts.push(`${verb} ${items.join(", ")}`);
|
|
2199
|
+
}
|
|
2200
|
+
const text = parts.join("; ");
|
|
2201
|
+
return `${text.charAt(0).toUpperCase()}${text.slice(1)}.`;
|
|
2202
|
+
}
|
|
2197
2203
|
|
|
2198
2204
|
// src/tools/manage_attribute_tool.ts
|
|
2199
2205
|
function contactLocatorSchema2(description) {
|
|
@@ -3825,13 +3831,20 @@ function createContact(db, input) {
|
|
|
3825
3831
|
if (!KINDS.has(input.kind)) {
|
|
3826
3832
|
throw new AffinityValidationError("invalid contact kind");
|
|
3827
3833
|
}
|
|
3834
|
+
if (input.isOwner !== void 0) {
|
|
3835
|
+
throw new AffinityValidationError(
|
|
3836
|
+
"isOwner is read-only; pass bootstrapOwner: true to create the owner contact"
|
|
3837
|
+
);
|
|
3838
|
+
}
|
|
3828
3839
|
const now = resolveNow(input.now);
|
|
3829
3840
|
if (input.bootstrapOwner) {
|
|
3830
3841
|
const existing = db.prepare(
|
|
3831
|
-
"SELECT id FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
3842
|
+
"SELECT id, name FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
3832
3843
|
).get();
|
|
3833
3844
|
if (existing !== void 0) {
|
|
3834
|
-
throw new AffinityConflictError(
|
|
3845
|
+
throw new AffinityConflictError(
|
|
3846
|
+
`owner already exists: contact ${existing.id} (${existing.name}); bootstrapOwner can only be used once`
|
|
3847
|
+
);
|
|
3835
3848
|
}
|
|
3836
3849
|
}
|
|
3837
3850
|
const isOwner = input.bootstrapOwner ? 1 : 0;
|
|
@@ -6451,6 +6464,17 @@ function listDismissedDuplicates(db, options) {
|
|
|
6451
6464
|
}));
|
|
6452
6465
|
}
|
|
6453
6466
|
|
|
6467
|
+
// src/tools/tool_summary.ts
|
|
6468
|
+
function summarizeCount(count, singular) {
|
|
6469
|
+
if (count === 0) {
|
|
6470
|
+
return `No ${singular}s found.`;
|
|
6471
|
+
}
|
|
6472
|
+
if (count === 1) {
|
|
6473
|
+
return `Found 1 ${singular}.`;
|
|
6474
|
+
}
|
|
6475
|
+
return `Found ${count} ${singular}s.`;
|
|
6476
|
+
}
|
|
6477
|
+
|
|
6454
6478
|
// src/tools/review_affinity_tool.ts
|
|
6455
6479
|
function buildReadOptions(input) {
|
|
6456
6480
|
return {
|
|
@@ -7199,7 +7223,7 @@ function affinityMcpOptions(context) {
|
|
|
7199
7223
|
annotations: (tool) => ({
|
|
7200
7224
|
readOnlyHint: tool.readOnly ?? false,
|
|
7201
7225
|
destructiveHint: tool.name === "merge_contacts" || /remove|unset|undismiss/.test(
|
|
7202
|
-
|
|
7226
|
+
String(tool.inputSchema.properties?.action?.enum ?? [])
|
|
7203
7227
|
),
|
|
7204
7228
|
idempotentHint: tool.readOnly ?? false,
|
|
7205
7229
|
openWorldHint: false
|
package/dist/mcp.d.cts
CHANGED
|
@@ -19,6 +19,16 @@ interface JsonSchema {
|
|
|
19
19
|
}
|
|
20
20
|
interface SurfaceTool<TCtx> {
|
|
21
21
|
name: string;
|
|
22
|
+
cli?: {
|
|
23
|
+
noun?: string;
|
|
24
|
+
verb: string;
|
|
25
|
+
positionals?: string[];
|
|
26
|
+
action?: {
|
|
27
|
+
property: string;
|
|
28
|
+
verbs?: Record<string, string>;
|
|
29
|
+
positionals?: Record<string, string[]>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
22
32
|
title?: string;
|
|
23
33
|
description: string;
|
|
24
34
|
inputSchema: JsonSchema & {
|
package/dist/mcp.d.ts
CHANGED
|
@@ -19,6 +19,16 @@ interface JsonSchema {
|
|
|
19
19
|
}
|
|
20
20
|
interface SurfaceTool<TCtx> {
|
|
21
21
|
name: string;
|
|
22
|
+
cli?: {
|
|
23
|
+
noun?: string;
|
|
24
|
+
verb: string;
|
|
25
|
+
positionals?: string[];
|
|
26
|
+
action?: {
|
|
27
|
+
property: string;
|
|
28
|
+
verbs?: Record<string, string>;
|
|
29
|
+
positionals?: Record<string, string[]>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
22
32
|
title?: string;
|
|
23
33
|
description: string;
|
|
24
34
|
inputSchema: JsonSchema & {
|
package/dist/mcp.js
CHANGED
|
@@ -3,7 +3,7 @@ import { dirname, join } from 'node:path';
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
|
|
5
5
|
// src/version.ts
|
|
6
|
-
var affinityVersion = "0.
|
|
6
|
+
var affinityVersion = "0.4.1";
|
|
7
7
|
|
|
8
8
|
// src/surface/affinity_mcp_instructions.ts
|
|
9
9
|
var affinityMcpInstructions = "Affinity is a local relationship model and social CRM. Search before creating contacts. Inspect before revising or merging. Record direct evidence honestly, keep identities and structural ties distinct from social links, and ask for clarification when a target is ambiguous. Review commitments and recurring dates regularly. Derived scores guide attention and do not prove relationship quality.";
|
|
@@ -2158,17 +2158,6 @@ function unsetAttribute(db, target, name, removedAt) {
|
|
|
2158
2158
|
});
|
|
2159
2159
|
}
|
|
2160
2160
|
|
|
2161
|
-
// src/tools/tool_summary.ts
|
|
2162
|
-
function summarizeCount(count, singular) {
|
|
2163
|
-
if (count === 0) {
|
|
2164
|
-
return `No ${singular}s found.`;
|
|
2165
|
-
}
|
|
2166
|
-
if (count === 1) {
|
|
2167
|
-
return `Found 1 ${singular}.`;
|
|
2168
|
-
}
|
|
2169
|
-
return `Found ${count} ${singular}s.`;
|
|
2170
|
-
}
|
|
2171
|
-
|
|
2172
2161
|
// src/tools/tool_mutation.ts
|
|
2173
2162
|
function mutationToolResult(action, receipt, entitySummary) {
|
|
2174
2163
|
const changedCount = receipt.created.length + receipt.updated.length + receipt.archived.length + receipt.removed.length;
|
|
@@ -2182,7 +2171,7 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
2182
2171
|
affectedLinks: receipt.affectedLinks,
|
|
2183
2172
|
derivedEffects: receipt.derivedEffects
|
|
2184
2173
|
};
|
|
2185
|
-
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` :
|
|
2174
|
+
const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` : describeChanges(receipt);
|
|
2186
2175
|
if (changedCount === 0) {
|
|
2187
2176
|
return toolNoOp(summary, data, {
|
|
2188
2177
|
entities: toReceiptEntityRefs(receipt)
|
|
@@ -2192,6 +2181,23 @@ function mutationToolResult(action, receipt, entitySummary) {
|
|
|
2192
2181
|
entities: toReceiptEntityRefs(receipt)
|
|
2193
2182
|
});
|
|
2194
2183
|
}
|
|
2184
|
+
var PLURALS = { identity: "identities" };
|
|
2185
|
+
function describeChanges(receipt) {
|
|
2186
|
+
const parts = [];
|
|
2187
|
+
for (const verb of ["created", "updated", "archived", "removed"]) {
|
|
2188
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2189
|
+
for (const ref of receipt[verb]) {
|
|
2190
|
+
counts.set(ref.kind, (counts.get(ref.kind) ?? 0) + 1);
|
|
2191
|
+
}
|
|
2192
|
+
if (counts.size === 0) continue;
|
|
2193
|
+
const items = [...counts].map(
|
|
2194
|
+
([kind, n]) => `${n} ${n === 1 ? kind : PLURALS[kind] ?? `${kind}s`}`
|
|
2195
|
+
);
|
|
2196
|
+
parts.push(`${verb} ${items.join(", ")}`);
|
|
2197
|
+
}
|
|
2198
|
+
const text = parts.join("; ");
|
|
2199
|
+
return `${text.charAt(0).toUpperCase()}${text.slice(1)}.`;
|
|
2200
|
+
}
|
|
2195
2201
|
|
|
2196
2202
|
// src/tools/manage_attribute_tool.ts
|
|
2197
2203
|
function contactLocatorSchema2(description) {
|
|
@@ -3823,13 +3829,20 @@ function createContact(db, input) {
|
|
|
3823
3829
|
if (!KINDS.has(input.kind)) {
|
|
3824
3830
|
throw new AffinityValidationError("invalid contact kind");
|
|
3825
3831
|
}
|
|
3832
|
+
if (input.isOwner !== void 0) {
|
|
3833
|
+
throw new AffinityValidationError(
|
|
3834
|
+
"isOwner is read-only; pass bootstrapOwner: true to create the owner contact"
|
|
3835
|
+
);
|
|
3836
|
+
}
|
|
3826
3837
|
const now = resolveNow(input.now);
|
|
3827
3838
|
if (input.bootstrapOwner) {
|
|
3828
3839
|
const existing = db.prepare(
|
|
3829
|
-
"SELECT id FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
3840
|
+
"SELECT id, name FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
|
|
3830
3841
|
).get();
|
|
3831
3842
|
if (existing !== void 0) {
|
|
3832
|
-
throw new AffinityConflictError(
|
|
3843
|
+
throw new AffinityConflictError(
|
|
3844
|
+
`owner already exists: contact ${existing.id} (${existing.name}); bootstrapOwner can only be used once`
|
|
3845
|
+
);
|
|
3833
3846
|
}
|
|
3834
3847
|
}
|
|
3835
3848
|
const isOwner = input.bootstrapOwner ? 1 : 0;
|
|
@@ -6449,6 +6462,17 @@ function listDismissedDuplicates(db, options) {
|
|
|
6449
6462
|
}));
|
|
6450
6463
|
}
|
|
6451
6464
|
|
|
6465
|
+
// src/tools/tool_summary.ts
|
|
6466
|
+
function summarizeCount(count, singular) {
|
|
6467
|
+
if (count === 0) {
|
|
6468
|
+
return `No ${singular}s found.`;
|
|
6469
|
+
}
|
|
6470
|
+
if (count === 1) {
|
|
6471
|
+
return `Found 1 ${singular}.`;
|
|
6472
|
+
}
|
|
6473
|
+
return `Found ${count} ${singular}s.`;
|
|
6474
|
+
}
|
|
6475
|
+
|
|
6452
6476
|
// src/tools/review_affinity_tool.ts
|
|
6453
6477
|
function buildReadOptions(input) {
|
|
6454
6478
|
return {
|
|
@@ -7197,7 +7221,7 @@ function affinityMcpOptions(context) {
|
|
|
7197
7221
|
annotations: (tool) => ({
|
|
7198
7222
|
readOnlyHint: tool.readOnly ?? false,
|
|
7199
7223
|
destructiveHint: tool.name === "merge_contacts" || /remove|unset|undismiss/.test(
|
|
7200
|
-
|
|
7224
|
+
String(tool.inputSchema.properties?.action?.enum ?? [])
|
|
7201
7225
|
),
|
|
7202
7226
|
idempotentHint: tool.readOnly ?? false,
|
|
7203
7227
|
openWorldHint: false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostpaw/affinity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Relationship intelligence and social CRM model for Node.js — SQLite, zero dependencies, with a built-in LLM tool facade",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"bugs": {
|
|
16
16
|
"url": "https://github.com/GhostPawJS/affinity/issues"
|
|
17
17
|
},
|
|
18
|
-
"homepage": "https://
|
|
18
|
+
"homepage": "https://affinity.ghostpaw.dev/",
|
|
19
19
|
"funding": {
|
|
20
20
|
"type": "github",
|
|
21
21
|
"url": "https://github.com/sponsors/Anonyfox"
|
|
@@ -50,7 +50,12 @@
|
|
|
50
50
|
"mcp-server",
|
|
51
51
|
"model-context-protocol",
|
|
52
52
|
"agent-skills",
|
|
53
|
-
"cli"
|
|
53
|
+
"cli",
|
|
54
|
+
"ghostpaw",
|
|
55
|
+
"ai-agents",
|
|
56
|
+
"llm-tools",
|
|
57
|
+
"node-sqlite",
|
|
58
|
+
"local-first"
|
|
54
59
|
],
|
|
55
60
|
"files": ["dist", "skills", "SKILL.md"],
|
|
56
61
|
"main": "./dist/index.cjs",
|
|
@@ -109,13 +114,16 @@
|
|
|
109
114
|
"check:api": "node scripts/guard/api.mjs",
|
|
110
115
|
"check:consumer": "node scripts/guard/consumer.mjs",
|
|
111
116
|
"check:startup": "node scripts/guard/startup.mjs",
|
|
112
|
-
"mcpb": "node scripts/mcpb/build.mjs"
|
|
117
|
+
"mcpb": "node scripts/mcpb/build.mjs",
|
|
118
|
+
"sea": "node scripts/sea/build.mjs",
|
|
119
|
+
"sea:smoke": "node scripts/sea/smoke.mjs",
|
|
120
|
+
"sea:bench": "node scripts/sea/bench.mjs"
|
|
113
121
|
},
|
|
114
122
|
"devDependencies": {
|
|
115
123
|
"@biomejs/biome": "^1.9.4",
|
|
116
124
|
"@types/node": "^24.12.0",
|
|
117
125
|
"@types/sql.js": "^1.4.10",
|
|
118
|
-
"esbuild": "^0.
|
|
126
|
+
"esbuild": "^0.28.1",
|
|
119
127
|
"fflate": "0.8.3",
|
|
120
128
|
"preact": "^10.29.0",
|
|
121
129
|
"sql.js": "^1.14.1",
|
|
@@ -125,5 +133,8 @@
|
|
|
125
133
|
"bin": {
|
|
126
134
|
"ghostpaw-affinity": "./dist/cli.js"
|
|
127
135
|
},
|
|
128
|
-
"mcpName": "io.github.GhostPawJS/affinity"
|
|
136
|
+
"mcpName": "io.github.GhostPawJS/affinity",
|
|
137
|
+
"overrides": {
|
|
138
|
+
"esbuild": "$esbuild"
|
|
139
|
+
}
|
|
129
140
|
}
|