@ghostpaw/affinity 0.2.4 → 0.4.0

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 CHANGED
@@ -1,10 +1,14 @@
1
+ <a href="https://affinity.ghostpaw.dev/"><img src="https://raw.githubusercontent.com/GhostPawJS/affinity/main/.github/social-preview.png" alt="GhostPaw affinity: Who have you been meaning to call?" width="100%"></a>
2
+
1
3
  # @ghostpaw/affinity
2
4
 
3
- [![npm](https://img.shields.io/npm/v/@ghostpaw/affinity)](https://www.npmjs.com/package/@ghostpaw/affinity)
4
- [![node](https://img.shields.io/node/v/@ghostpaw/affinity)](https://nodejs.org)
5
- [![license](https://img.shields.io/npm/l/@ghostpaw/affinity)](LICENSE)
6
- [![dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](package.json)
7
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.8-blue?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
5
+ [![npm](https://img.shields.io/npm/v/@ghostpaw/affinity?style=flat-square&labelColor=0a1119&color=62e6f2&label=npm)](https://www.npmjs.com/package/@ghostpaw/affinity) [![CI](https://img.shields.io/github/actions/workflow/status/GhostPawJS/affinity/ci.yml?branch=main&style=flat-square&labelColor=0a1119&label=CI)](https://github.com/GhostPawJS/affinity/actions/workflows/ci.yml) [![site](https://img.shields.io/badge/site-affinity.ghostpaw.dev-62e6f2?style=flat-square&labelColor=0a1119)](https://affinity.ghostpaw.dev/) [![runtime deps](https://img.shields.io/badge/runtime_deps-0-7af0b4?style=flat-square&labelColor=0a1119)](package.json) [![MCP Registry](https://img.shields.io/badge/MCP_Registry-io.github.GhostPawJS%2Faffinity-b39dff?style=flat-square&labelColor=0a1119)](https://registry.modelcontextprotocol.io/v0.1/servers/io.github.GhostPawJS%2Faffinity/versions/latest) [![node](https://img.shields.io/badge/node-%E2%89%A524-93a5b9?style=flat-square&labelColor=0a1119)](https://nodejs.org) [![license](https://img.shields.io/npm/l/@ghostpaw/affinity?style=flat-square&labelColor=0a1119&color=93a5b9)](LICENSE)
6
+
7
+ **Agent contacts: people, promises and yearly dates, with a radar for who is drifting.**
8
+
9
+ ```bash
10
+ npm install @ghostpaw/affinity
11
+ ```
8
12
 
9
13
  A standalone social CRM core for Node.js, built on SQLite.
10
14
 
@@ -15,13 +19,211 @@ blob with zero runtime dependencies, designed for two audiences at once: human
15
19
  developers working directly in code, and LLM agents operating through a
16
20
  structured `soul` / `tools` / `skills` runtime.
17
21
 
18
- ## Install
22
+ ## MCP server
23
+
24
+ Every client below starts the same stdio server: `npx -y @ghostpaw/affinity mcp`.
25
+ Contacts live in `~/.ghostpaw/affinity.db` unless the `GHOSTPAW_AFFINITY_DB`
26
+ environment variable (absolute path) or `--db <path>` says otherwise.
27
+
28
+ **Claude Code**
19
29
 
20
30
  ```bash
21
- npm install @ghostpaw/affinity
31
+ claude mcp add ghostpaw-affinity -- npx -y @ghostpaw/affinity mcp
32
+ ```
33
+
34
+ or as a plugin, which also installs the skill:
35
+
36
+ ```bash
37
+ /plugin marketplace add GhostPawJS/affinity
38
+ /plugin install ghostpaw-affinity@ghostpaw-affinity
39
+ ```
40
+
41
+ **Codex CLI** (`~/.affinity/config.toml`)
42
+
43
+ ```toml
44
+ [mcp_servers.ghostpaw-affinity]
45
+ command = "npx"
46
+ args = ["-y", "@ghostpaw/affinity", "mcp"]
47
+ ```
48
+
49
+ **Cursor** (`.cursor/mcp.json`), **Claude Desktop** (`claude_desktop_config.json`)
50
+ or a project `.mcp.json`:
51
+
52
+ ```json
53
+ {
54
+ "mcpServers": {
55
+ "ghostpaw-affinity": {
56
+ "command": "npx",
57
+ "args": ["-y", "@ghostpaw/affinity", "mcp"]
58
+ }
59
+ }
60
+ }
22
61
  ```
23
62
 
24
- Requires **Node.js 24+** (uses the built-in `node:sqlite` module).
63
+ Claude Desktop users can also install `server.mcpb` from the
64
+ [latest GitHub release](https://github.com/GhostPawJS/affinity/releases/latest).
65
+ A Dockerfile is included for container hosts:
66
+ `docker build -t ghostpaw-affinity . && docker run -i --rm -v affinity:/data ghostpaw-affinity`.
67
+
68
+ The server speaks MCP 2026-07-28 (stateless, `server/discover`) and the
69
+ initialize-based revisions 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05.
70
+
71
+ ### CLI
72
+
73
+ ```bash
74
+ npx -y @ghostpaw/affinity tools
75
+ npx -y @ghostpaw/affinity call manage_contact '{"action":"create","input":{"name":"Sam","kind":"human"}}'
76
+ npx -y @ghostpaw/affinity call search_affinity '{"query":"Sam"}'
77
+ ```
78
+
79
+ Install globally for the `ghostpaw-affinity` binary: `npm i -g @ghostpaw/affinity`.
80
+ `ghostpaw-affinity mcp` starts the stdio server.
81
+
82
+ ### Agent Skill
83
+
84
+ `SKILL.md` (repo root) and `skills/affinity/SKILL.md` follow the
85
+ [Agent Skills](https://agentskills.io) format and ship in the npm package.
86
+ Copy `skills/affinity/` into your agent's skills folder, or print it:
87
+
88
+ ```bash
89
+ npx -y @ghostpaw/affinity skill > SKILL.md
90
+ ```
91
+
92
+ ## Install
93
+
94
+
95
+ The [npm package](https://www.npmjs.com/package/@ghostpaw/affinity) requires Node 24+:
96
+
97
+ ```sh
98
+ npm install -g @ghostpaw/affinity
99
+ npx -y @ghostpaw/affinity --help
100
+ ```
101
+
102
+ Standalone binaries are available from [GitHub Releases](https://github.com/GhostPawJS/affinity/releases)
103
+ for linux-x64, linux-arm64, darwin-arm64, darwin-x64 and windows-x64.
104
+
105
+ ```sh
106
+ curl -fsSL https://raw.githubusercontent.com/GhostPawJS/affinity/main/install.sh | sh
107
+ ```
108
+
109
+ ```powershell
110
+ irm https://raw.githubusercontent.com/GhostPawJS/affinity/main/install.ps1 | iex
111
+ ```
112
+
113
+ Other release installers:
114
+
115
+ ```sh
116
+ mise use -g github:GhostPawJS/affinity
117
+ ubi --project GhostPawJS/affinity --exe ghostpaw-affinity --in ~/.local/bin
118
+ eget GhostPawJS/affinity
119
+ ```
120
+
121
+ For [aqua](https://aquaproj.github.io/docs/reference/registry-config/), define a
122
+ `github_release` package in `aqua.yaml`:
123
+
124
+ ```yaml
125
+ registries:
126
+ - name: affinity
127
+ type: local
128
+ path: affinity-registry.yaml
129
+ packages:
130
+ - name: GhostPawJS/affinity@v<VERSION>
131
+ registry: affinity
132
+ ```
133
+
134
+ `affinity-registry.yaml`:
135
+
136
+ ```yaml
137
+ packages:
138
+ - type: github_release
139
+ repo_owner: GhostPawJS
140
+ repo_name: affinity
141
+ asset: ghostpaw-affinity-{{trimV .Version}}-{{.OS}}-{{.Arch}}.tar.gz
142
+ format: tar.gz
143
+ replacements: { amd64: x64 }
144
+ overrides:
145
+ - goos: windows
146
+ asset: ghostpaw-affinity-{{trimV .Version}}-windows-{{.Arch}}.zip
147
+ format: zip
148
+ files:
149
+ - name: ghostpaw-affinity
150
+ ```
151
+
152
+ Install scripts verify SHA256SUMS. To verify a downloaded archive's
153
+ [build attestation](https://cli.github.com/manual/gh_attestation_verify):
154
+
155
+ ```sh
156
+ gh attestation verify <archive> -R GhostPawJS/affinity
157
+ ```
158
+
159
+ Set `GHOSTPAW_VERIFY=attestation` for attestation verification in `install.sh`.
160
+ Binaries are unsigned; macOS binaries carry an ad-hoc signature. For a browser
161
+ download on macOS, remove quarantine with
162
+ `xattr -d com.apple.quarantine /path/to/ghostpaw-affinity`.
163
+ Linux binaries require glibc 2.28+; use npx on Alpine.
164
+ See the [binary build workflow](.github/workflows/release-binaries.yml).
165
+
166
+ <!-- sizes -->
167
+
168
+ The install check runs on ubuntu-24.04, macos-15 and windows-2025.
169
+
170
+ ## CLI
171
+
172
+ ```text
173
+ ghostpaw-affinity search <query>
174
+ ghostpaw-affinity review <view>
175
+ ghostpaw-affinity show <owner_profile|contact_profile|link> [--contact JSON --link JSON]
176
+ ghostpaw-affinity contact create <input-json>
177
+ ghostpaw-affinity contact revise <contact-json> --patch JSON
178
+ ghostpaw-affinity contact lifecycle <contact-json> <lifecycleState>
179
+ ghostpaw-affinity contact merge [flags]
180
+ ghostpaw-affinity identity add|revise|verify|remove [flags]
181
+ ghostpaw-affinity relationship add|bond|state|tie|untie [flags]
182
+ ghostpaw-affinity commitment record|resolve [flags]
183
+ ghostpaw-affinity attribute set|unset|replace [flags]
184
+ ghostpaw-affinity date add|revise|remove [flags]
185
+ ghostpaw-affinity event record [flags]
186
+ ghostpaw-affinity duplicate dismiss|undismiss [flags]
187
+ ghostpaw-affinity mcp|skill|tools|describe|call|completions|version|help
188
+ ```
189
+
190
+ There are 27 domain commands. `review` accepts the existing view names,
191
+ including `contacts.list`, `contacts.duplicates` and `dates.upcoming`.
192
+ `show` selects an inspection kind; contact and link locators are JSON objects.
193
+ Use `help contact` or `contact create --help` for schema-derived help.
194
+ Nested payloads use JSON or `@path` to read a file.
195
+
196
+ Global flags: `--db <path>`, `--json`, `--quiet`, `--compact`,
197
+ `--help`/`-h`, `--version`/`-V`/`-v`, `--no-color`.
198
+ `NO_COLOR` is accepted; output uses no terminal colors.
199
+ Noun-verb commands print the full tool result as JSON with `--json` or piped
200
+ stdout. Terminal output shows the summary and payload. `--quiet` suppresses
201
+ stdout. `--compact` removes JSON indentation.
202
+ Array flags repeat; kebab-case flags map to schema properties.
203
+ `--` ends flags. A positional `-`, or piped stdin with no input flags, reads a
204
+ JSON object. Explicit flags override stdin fields.
205
+ Database selection uses `--db`, `GHOSTPAW_AFFINITY_DB`, then
206
+ `~/.ghostpaw/affinity.db`. These commands work offline.
207
+
208
+ | Exit | Noun-verb result |
209
+ |---|---|
210
+ | 0 | Success, including no_op |
211
+ | 1 | Tool error |
212
+ | 2 | Usage or schema error |
213
+ | 3 | Clarification needed or not found |
214
+ | 4 | Conflict or version mismatch |
215
+ | 70 | Internal error |
216
+
217
+ Errors go to stderr. With `--json`, errors also print to stdout.
218
+ Legacy `tools [--json]`, `describe <tool>`, `call <tool> [JSON|-]` and
219
+ `<tool_name> --parameter value` retain their output and exit codes.
220
+ `completions bash|zsh|fish` prints shell scripts.
221
+
222
+ ```sh
223
+ ghostpaw-affinity contact create '{"name":"Sam","kind":"human"}' --db /tmp/affinity-example.db --json
224
+ ghostpaw-affinity search Sam --db /tmp/affinity-example.db --json
225
+ ghostpaw-affinity review contacts.list --db /tmp/affinity-example.db --json
226
+ ```
25
227
 
26
228
  ## Quick Start
27
229
 
@@ -64,6 +266,16 @@ const profile = read.getContactProfile(db, ada.id);
64
266
  const radar = read.listRadar(db);
65
267
  ```
66
268
 
269
+ ## Docs
270
+
271
+ The live demo runs the library in your browser against an in-memory SQLite database. Each view below opens a different part of it:
272
+
273
+ - [Dashboard](https://affinity.ghostpaw.dev/docs/): overview and session
274
+ - [Contacts](https://affinity.ghostpaw.dev/docs/#/contacts): people, companies, groups and pets you track
275
+ - [Relationships](https://affinity.ghostpaw.dev/docs/#/relationships): links, rank and bonds
276
+ - [Timeline](https://affinity.ghostpaw.dev/docs/#/timeline): events, commitments and date anchors
277
+ - [Insights](https://affinity.ghostpaw.dev/docs/#/insights): radar and analytics
278
+
67
279
  ## The Model
68
280
 
69
281
  Eight concepts, strict separation of concerns:
@@ -149,7 +361,7 @@ and skills.
149
361
 
150
362
  ## Tools
151
363
 
152
- Eleven tools shaped around operator intent, not raw storage operations:
364
+ Twelve tools shaped around operator intent, not raw storage operations:
153
365
 
154
366
  | Tool | What it does |
155
367
  |---|---|
@@ -158,6 +370,7 @@ Eleven tools shaped around operator intent, not raw storage operations:
158
370
  | `inspect_affinity_item` | Open one exact profile or link detail |
159
371
  | `manage_contact` | Create, revise, or change contact lifecycle |
160
372
  | `merge_contacts` | Merge duplicate contacts safely |
373
+ | `manage_duplicate_dismissal` | Dismiss or restore a suspected duplicate pair |
161
374
  | `manage_identity` | Add, revise, verify, or remove identities |
162
375
  | `manage_relationship` | Seed social links, revise bond/state, manage structural ties |
163
376
  | `record_event` | Record direct evidence, observations, milestones, or transactions |
package/SKILL.md ADDED
@@ -0,0 +1,283 @@
1
+ ---
2
+ name: affinity
3
+ description: "Search and maintain local contacts, identities, relationships, events, commitments, and recurring dates. Use when recording relationship evidence or reviewing a social CRM."
4
+ license: "MIT"
5
+ compatibility: "Node.js 24 or newer. MCP stdio or ghostpaw-affinity CLI."
6
+ ---
7
+
8
+ # affinity
9
+
10
+ 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.
11
+
12
+ ## Calling tools
13
+
14
+ MCP: connect to ghostpaw-affinity, then call a tool by name with its JSON arguments.
15
+
16
+ CLI: `ghostpaw-affinity call <tool> '<JSON object>'` or `ghostpaw-affinity <tool> --parameter value` (without a global install: `npx -y @ghostpaw/affinity <tool> ...`).
17
+
18
+ Inspect schemas with `ghostpaw-affinity describe <tool>`. Use `--db <path>` to select a database.
19
+
20
+ ## search_affinity
21
+
22
+ Search contacts by name or identity, including exact natural-key lookups like email-style identifiers.
23
+
24
+ Use when: Use this when you want to find contacts from free text or a known routing identity such as an email or URL-like handle.
25
+
26
+ Not for: Do not use this when you already know the exact contact you want to inspect in depth.
27
+
28
+ Read-only.
29
+
30
+ | Parameter | Type | Required | Values / description |
31
+ | --- | --- | --- | --- |
32
+ | query | string | yes | Free-text or natural-key query. |
33
+ | kind | string | no | human, group, company, team, pet, service, other. Optional contact kind filter. |
34
+ | lifecycleState | string | no | active, dormant, merged, lost. Optional lifecycle state filter. |
35
+ | includeOwner | boolean | no | Whether owner contacts are included. |
36
+ | includeDormant | boolean | no | Whether dormant contacts are included. |
37
+ | limit | integer | no | Optional result limit. |
38
+ | offset | integer | no | Optional result offset. |
39
+
40
+ ## review_affinity
41
+
42
+ Review one affinity surface such as contacts, journals, link queues, commitments, dates, graph state, or merge history.
43
+
44
+ Use when: Use this when you want a dashboard-style list or review view instead of inspecting one exact item.
45
+
46
+ Not for: Do not use this when you already know the exact contact or link you want to inspect in depth.
47
+
48
+ Read-only.
49
+
50
+ | Parameter | Type | Required | Values / description |
51
+ | --- | --- | --- | --- |
52
+ | view | string | yes | contacts.list, contacts.duplicates, events.contact_journal, events.link_timeline, events.moments, commitments.open, links.owner, links.observed, links.progression_readiness, links.radar, dates.upcoming, graph.chart, merges.history, merges.dismissed. Which review surface to return. |
53
+ | contact | object | no | Optional target contact locator. |
54
+ | link | object | no | Optional target link locator. |
55
+ | contactIds | array | no | Optional contact-id subset. |
56
+ | kind | string | no | Optional contact kind or link kind filter, depending on the view. |
57
+ | lifecycleState | string | no | active, dormant, merged, lost. Optional contact lifecycle-state filter. |
58
+ | linkState | string | no | active, dormant, strained, broken, archived. Optional link state filter. |
59
+ | includeOwner | boolean | no | Whether owner contacts are included. |
60
+ | includeDormant | boolean | no | Whether dormant rows are included. |
61
+ | includeArchived | boolean | no | Whether archived links are included. |
62
+ | includeObserved | boolean | no | Whether observed links are included. |
63
+ | eventTypes | array | no | Optional event-type filter. |
64
+ | momentKind | string | no | breakthrough, rupture, reconciliation, milestone, turning_point. Optional moment kind filter. |
65
+ | commitmentType | string | no | promise, agreement. Optional commitment type filter. |
66
+ | recurrenceKind | string | no | birthday, anniversary, renewal, memorial, custom_yearly. Optional recurrence-kind filter. |
67
+ | horizonDays | integer | no | Optional horizon-days filter. |
68
+ | exactOnly | boolean | no | Whether duplicate review is exact-only. |
69
+ | minScore | number | no | Optional duplicate minimum score. |
70
+ | includeDismissed | boolean | no | Whether dismissed pairs are included in duplicate review (marked with dismissed flag). |
71
+ | since | integer | no | Optional lower timestamp bound. |
72
+ | until | integer | no | Optional upper timestamp bound. |
73
+ | limit | integer | no | Optional result limit. |
74
+ | offset | integer | no | Optional result offset. |
75
+
76
+ ## inspect_affinity_item
77
+
78
+ Inspect one exact affinity item such as the owner profile, a contact profile, or one link detail.
79
+
80
+ Use when: Use this when you already know the exact item you want to inspect in depth.
81
+
82
+ Not for: Do not use this for search-style discovery or dashboard-like list reviews.
83
+
84
+ Read-only.
85
+
86
+ | Parameter | Type | Required | Values / description |
87
+ | --- | --- | --- | --- |
88
+ | kind | string | yes | owner_profile, contact_profile, link. What to inspect. |
89
+ | contact | object | no | Target contact. Required when kind=contact_profile. Provide contactId or identity. |
90
+ | link | object | no | Target link. Required when kind=link. Provide linkId or endpoints. |
91
+ | topLinksLimit | integer | no | Optional top-links cap. |
92
+ | recentEventsLimit | integer | no | Optional recent-events cap. |
93
+
94
+ ## manage_contact
95
+
96
+ Create a contact, revise one contact's core fields, or change lifecycle state. ALWAYS search_affinity first before creating — duplicates corrupt the graph.
97
+
98
+ Use when: Use this for contact creation and lifecycle-safe contact maintenance.
99
+
100
+ Not for: Do not use this for identities, links, attributes, or merges.
101
+
102
+ Writes: writes_state.
103
+
104
+ | Parameter | Type | Required | Values / description |
105
+ | --- | --- | --- | --- |
106
+ | action | string | yes | create, revise, set_lifecycle. Operation to perform. |
107
+ | input | object | no | New contact payload. Required when action=create. |
108
+ | contact | object | no | Target contact. Required when action=revise or set_lifecycle. Provide contactId or identity. |
109
+ | patch | object | no | Allowed contact patch. Required when action=revise. |
110
+ | lifecycleState | string | no | active, dormant, merged, lost. Target lifecycle state. Required when action=set_lifecycle. |
111
+ | options | object | no | Optional lifecycle metadata. |
112
+
113
+ ## merge_contacts
114
+
115
+ Merge one loser contact into one winner contact using explicit contact locators.
116
+
117
+ Use when: Use this only when two contacts are confirmed duplicates and the system should consolidate them.
118
+
119
+ Not for: Do not use this for routine contact edits or identity cleanup.
120
+
121
+ Writes: writes_state.
122
+
123
+ | Parameter | Type | Required | Values / description |
124
+ | --- | --- | --- | --- |
125
+ | winner | object | yes | Winner contact. Provide contactId or identity. |
126
+ | loser | object | yes | Loser contact. Provide contactId or identity. |
127
+ | reasonSummary | string | no | Optional merge reason. |
128
+ | now | integer | no | Optional merge timestamp. |
129
+
130
+ ## manage_duplicate_dismissal
131
+
132
+ Dismiss or un-dismiss a duplicate candidate pair so it does not resurface in listDuplicateCandidates.
133
+
134
+ Use when: Use dismiss after confirming two contacts are different people (not duplicates). Use undismiss to re-surface a pair for re-evaluation.
135
+
136
+ Not for: Do not use this to merge contacts — use merge_contacts for confirmed duplicates.
137
+
138
+ Writes: writes_state.
139
+
140
+ | Parameter | Type | Required | Values / description |
141
+ | --- | --- | --- | --- |
142
+ | action | string | yes | dismiss, undismiss. Action to perform. |
143
+ | left | object | yes | Left contact. Provide contactId or identity. |
144
+ | right | object | yes | Right contact. Provide contactId or identity. |
145
+ | reason | any | no | Optional reason for the dismissal. |
146
+
147
+ ## manage_identity
148
+
149
+ Add, revise, verify, or remove one routing identity on a contact. Use this for emails, phone numbers, social handles, and URLs — anything that could locate a person. Do not use manage_attribute for these.
150
+
151
+ Use when: Use this for contact identity management such as email, handle, or phone routing keys.
152
+
153
+ Not for: Do not use this for contact core fields, links, or attributes.
154
+
155
+ Writes: writes_state.
156
+
157
+ | Parameter | Type | Required | Values / description |
158
+ | --- | --- | --- | --- |
159
+ | action | string | yes | add, revise, verify, remove. Operation to perform. |
160
+ | contact | object | no | Target contact. Required when action=add. Provide contactId or identity. |
161
+ | input | object | no | New identity payload. Required when action=add. |
162
+ | identityId | integer | no | Identity id. Required when action=revise, verify, or remove. |
163
+ | patch | object | no | Identity patch. Required when action=revise. |
164
+ | verifiedAt | integer | no | Verification timestamp. Optional for action=verify. |
165
+ | removedAt | integer | no | Removal timestamp. Optional for action=remove. |
166
+
167
+ ## manage_relationship
168
+
169
+ Seed social links, revise bond and state, and manage structural ties. Seed a link as soon as a relationship is mentioned — do not wait for repeated evidence.
170
+
171
+ Use when: Use this for direct relationship modeling and link-state changes.
172
+
173
+ Not for: Do not use this for recording social evidence or resolving commitments.
174
+
175
+ Writes: writes_state.
176
+
177
+ | Parameter | Type | Required | Values / description |
178
+ | --- | --- | --- | --- |
179
+ | action | string | yes | seed_social_link, revise_bond, override_state, set_structural_tie, remove_structural_tie. Operation to perform. |
180
+ | from | object | no | Source contact. Required when action=seed_social_link or set_structural_tie. Provide contactId or identity. |
181
+ | to | object | no | Target contact. Required when action=seed_social_link or set_structural_tie. Provide contactId or identity. |
182
+ | kind | string | no | Link kind. Required for seed_social_link and set_structural_tie. Can be passed here or inside input. For seed_social_link: personal, family, professional, romantic, care, service, observed, other_relational. For set_structural_tie: works_at, manages, member_of, married_to, partner_of, parent_of, child_of, sibling_of, friend_of, client_of, vendor_of, reports_to, belongs_to, other_structural. |
183
+ | role | string | no | Optional role for seed_social_link or set_structural_tie. Can be passed here or inside input. |
184
+ | input | object | no | Full payload for seed_social_link or set_structural_tie. Alternative to top-level kind/role. |
185
+ | link | object | no | Target link. Required when action=revise_bond, override_state, or remove_structural_tie. Provide linkId or endpoints. |
186
+ | bond | any | no | Bond text. Required when action=revise_bond. Null to clear. |
187
+ | state | string | no | active, dormant, strained, broken, archived. Link state. Required when action=override_state. |
188
+ | options | object | no | Optional metadata. |
189
+ | removedAt | integer | no | Removal timestamp. Optional for action=remove_structural_tie. |
190
+
191
+ ## record_event
192
+
193
+ Record one social event such as an interaction, observation, milestone, or transaction. For interactions, milestones, and transactions the owner contact is auto-injected into participants if missing. Observations may omit the owner when recording third-party evidence.
194
+
195
+ Use when: Use this when you are capturing something that happened in the relationship graph.
196
+
197
+ Not for: Do not use this for commitments, date anchors, or direct relationship state overrides.
198
+
199
+ Writes: writes_state.
200
+
201
+ | Parameter | Type | Required | Values / description |
202
+ | --- | --- | --- | --- |
203
+ | kind | string | yes | interaction, observation, milestone, transaction. Event kind. |
204
+ | input | object | yes | Event payload. Always required. Interaction events also require `type`. |
205
+
206
+ ## manage_commitment
207
+
208
+ Record or resolve one commitment such as a promise or agreement. The owner contact is auto-injected into participants if missing.
209
+
210
+ Use when: Use this when the work is specifically about a commitment lifecycle rather than a general event.
211
+
212
+ Not for: Do not use this for ordinary interactions or direct relationship state changes.
213
+
214
+ Writes: writes_state.
215
+
216
+ | Parameter | Type | Required | Values / description |
217
+ | --- | --- | --- | --- |
218
+ | action | string | yes | record, resolve. Operation to perform. |
219
+ | input | object | no | Commitment payload. Required when action=record. |
220
+ | commitmentEventId | integer | no | Commitment event id. Required when action=resolve. |
221
+ | resolution | string | no | kept, cancelled, broken. How the commitment was resolved. Required when action=resolve. |
222
+ | options | object | no | Optional metadata. |
223
+
224
+ ## manage_date_anchor
225
+
226
+ Add, revise, or remove recurring date anchors on contacts or links. Use this for birthdays, anniversaries, and yearly events — not manage_attribute.
227
+
228
+ Use when: Use this for birthdays, anniversaries, renewals, memorials, and other recurring anchors.
229
+
230
+ Not for: Do not use this for one-off events or relationship state changes.
231
+
232
+ Writes: writes_state.
233
+
234
+ | Parameter | Type | Required | Values / description |
235
+ | --- | --- | --- | --- |
236
+ | action | string | yes | add, revise, remove. Operation to perform. |
237
+ | input | object | no | Date anchor payload. Required when action=add. |
238
+ | anchorEventId | integer | no | Anchor event id. Required when action=revise or remove. |
239
+ | patch | object | no | Anchor patch. Required when action=revise. |
240
+ | options | object | no | Optional metadata. |
241
+ | removedAt | integer | no | Removal timestamp. Optional for action=remove. |
242
+
243
+ ## manage_attribute
244
+
245
+ Set, unset, or replace custom metadata attributes on contacts and links. For emails, phones, handles, and URLs use manage_identity instead — those are routing keys, not attributes.
246
+
247
+ Use when: Use this for metadata such as tags, notes, or small labeled facts attached to a contact or link.
248
+
249
+ Not for: Do not use this for core contact fields, identities, or direct relationship mechanics.
250
+
251
+ Writes: writes_state.
252
+
253
+ | Parameter | Type | Required | Values / description |
254
+ | --- | --- | --- | --- |
255
+ | action | string | yes | set, unset, replace. Operation to perform. |
256
+ | target | object | no | Attribute target. Always required. |
257
+ | name | string | no | Attribute name. Required when action=set or unset. |
258
+ | value | any | no | Attribute value. Required when action=set. Null for presence/tag semantics. |
259
+ | entries | array | no | Key-value map to replace all attributes. Required when action=replace. |
260
+ | removedAt | integer | no | Removal timestamp. Optional for action=unset. |
261
+
262
+ ## Workflow guides
263
+
264
+ - [bootstrap-and-cold-start](skills/affinity/references/bootstrap-and-cold-start.md): Establish a clean Affinity baseline with one owner, one verified identity, and the minimum structure needed for later evidence and review work.
265
+ - [identify-and-locate-contacts](skills/affinity/references/identify-and-locate-contacts.md): Find the right contact reliably, prefer stable natural keys when possible, and avoid creating duplicates through weak lookup discipline.
266
+ - [import-history-without-faking-evidence](skills/affinity/references/import-history-without-faking-evidence.md): Import pre-existing relationship state honestly, using seeded links for known history instead of inventing fake journal evidence.
267
+ - [model-structure-orgs-and-households](skills/affinity/references/model-structure-orgs-and-households.md): Model factual structure such as employment, membership, hierarchy, and household ties without confusing it with live relational mechanics.
268
+ - [record-direct-evidence-well](skills/affinity/references/record-direct-evidence-well.md): Record direct owner-facing social evidence in a way that produces truthful mechanics, good moments, and useful future review surfaces.
269
+ - [record-observations-and-referrals](skills/affinity/references/record-observations-and-referrals.md): Capture third-party and referral truth without overclaiming intimacy, directness, or trust that the operator did not actually earn.
270
+ - [capture-transactions-and-commercial-events](skills/affinity/references/capture-transactions-and-commercial-events.md): Capture commercially meaningful interactions as transactions so operational and service relationships accumulate the right evidence shape.
271
+ - [manage-promises-and-agreements](skills/affinity/references/manage-promises-and-agreements.md): Track obligations explicitly from creation to resolution so open commitments remain actionable and trust consequences stay honest.
272
+ - [manage-recurring-dates-and-reminders](skills/affinity/references/manage-recurring-dates-and-reminders.md): Use date anchors for recurring salience so important birthdays, anniversaries, renewals, and memorials feed review surfaces cleanly.
273
+ - [review-radar-progression-and-graph](skills/affinity/references/review-radar-progression-and-graph.md): Use the review surfaces to prioritize maintenance, understand progression readiness, and read the shape of the relationship graph without guessing from raw rows.
274
+ - [reconcile-duplicates-and-merge-safely](skills/affinity/references/reconcile-duplicates-and-merge-safely.md): Review duplicate candidates carefully and merge only after the identity, history, and structural consequences are understood.
275
+
276
+ ## Principles
277
+
278
+ - Honor evidence before narrative.
279
+ - Clarify before you act.
280
+ - Keep truths in their proper layer.
281
+ - Read derived signals with judgment.
282
+ - Protect relationship integrity over time.
283
+ - Follow the operational protocol.