@vib795/agent-memory 0.1.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/HOWTO.md +368 -0
- package/LICENSE +21 -0
- package/README.md +302 -0
- package/install.ps1 +54 -0
- package/install.sh +36 -0
- package/package.json +47 -0
- package/scripts/postinstall.js +45 -0
- package/skills/handoff/SKILL.md +344 -0
- package/skills/recall/SKILL.md +125 -0
- package/skills/remember/SKILL.md +155 -0
- package/src/atomic.js +57 -0
- package/src/cli.js +558 -0
- package/src/compact.js +242 -0
- package/src/config.js +91 -0
- package/src/digest.js +199 -0
- package/src/graph.js +99 -0
- package/src/index-db.js +317 -0
- package/src/promptfile.js +78 -0
- package/src/redact.js +97 -0
- package/src/schema.js +98 -0
- package/src/setup.js +211 -0
- package/src/staleness.js +151 -0
- package/src/store.js +0 -0
- package/src/targets.js +139 -0
package/install.ps1
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Installs agent-memory and all three skills from a checkout.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
`npm install -g .` does this on its own via the postinstall hook. This script
|
|
7
|
+
exists for two cases: installing straight from a clone without npm, and finishing
|
|
8
|
+
the job when a managed npm config sets ignore-scripts=true and silently skips it.
|
|
9
|
+
|
|
10
|
+
The linking itself lives in src\setup.js, not here. That is deliberate: a PowerShell
|
|
11
|
+
reimplementation could only be tested on Windows, and the machine this was written
|
|
12
|
+
on is not Windows. One implementation, three entry points, no drift.
|
|
13
|
+
|
|
14
|
+
Skills are linked into both agent directories:
|
|
15
|
+
%USERPROFILE%\.agents\skills\<name> -> read by GitHub Copilot in every window
|
|
16
|
+
%USERPROFILE%\.claude\skills\<name> -> read by Claude Code
|
|
17
|
+
|
|
18
|
+
Directory junctions are used, which need neither admin rights nor Developer Mode.
|
|
19
|
+
They fail on a network-backed profile (FSLogix, roaming), and setup falls back to
|
|
20
|
+
copying and says so.
|
|
21
|
+
|
|
22
|
+
.EXAMPLE
|
|
23
|
+
powershell -ExecutionPolicy Bypass -File .\install.ps1
|
|
24
|
+
#>
|
|
25
|
+
|
|
26
|
+
$ErrorActionPreference = 'Stop'
|
|
27
|
+
|
|
28
|
+
$source = $PSScriptRoot
|
|
29
|
+
|
|
30
|
+
$node = Get-Command node -ErrorAction SilentlyContinue
|
|
31
|
+
if (-not $node) {
|
|
32
|
+
Write-Error "node not found on PATH. agent-memory needs Node >= 22.5."
|
|
33
|
+
exit 1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
& node (Join-Path $source 'src\cli.js') setup
|
|
37
|
+
|
|
38
|
+
Write-Host ""
|
|
39
|
+
Write-Host "Linking the CLI" -ForegroundColor Cyan
|
|
40
|
+
& npm install -g $source 2>&1 | Out-Null
|
|
41
|
+
if ($LASTEXITCODE -eq 0) {
|
|
42
|
+
Write-Host " [npm] agent-memory installed globally" -ForegroundColor Green
|
|
43
|
+
} else {
|
|
44
|
+
# A global install failing on a managed desktop is common and not worth aborting
|
|
45
|
+
# on. The skills are already linked; this one step can be finished by hand.
|
|
46
|
+
Write-Host " [npm] global install failed. Run this yourself:" -ForegroundColor Yellow
|
|
47
|
+
Write-Host " npm install -g `"$source`"" -ForegroundColor Yellow
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Write-Host ""
|
|
51
|
+
& node (Join-Path $source 'src\cli.js') doctor
|
|
52
|
+
|
|
53
|
+
Write-Host ""
|
|
54
|
+
Write-Host "Installed. Restart VS Code, then try /recall, /remember, or /handoff." -ForegroundColor Cyan
|
package/install.sh
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Installs agent-memory and all three skills from a checkout, on macOS/Linux.
|
|
3
|
+
#
|
|
4
|
+
# `npm install -g .` does this on its own via the postinstall hook. This script
|
|
5
|
+
# exists for two cases: installing straight from a clone without npm, and finishing
|
|
6
|
+
# the job when a managed npm config sets ignore-scripts=true and silently skips it.
|
|
7
|
+
#
|
|
8
|
+
# The linking itself lives in src/setup.js, not here. One implementation, three entry
|
|
9
|
+
# points, so this script and its PowerShell twin cannot drift from each other.
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
source_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
|
|
14
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
15
|
+
echo "node not found on PATH. agent-memory needs Node >= 22.5." >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
node "$source_dir/src/cli.js" setup
|
|
20
|
+
|
|
21
|
+
echo
|
|
22
|
+
echo "Linking the CLI"
|
|
23
|
+
if npm install -g "$source_dir" >/dev/null 2>&1; then
|
|
24
|
+
echo " [npm] agent-memory installed globally"
|
|
25
|
+
else
|
|
26
|
+
# A global install needing sudo is common and is not worth aborting on. The skills
|
|
27
|
+
# are already linked, and the user can finish this one step by hand.
|
|
28
|
+
echo " [npm] global install failed (permissions?). Run this yourself:"
|
|
29
|
+
echo " npm install -g \"$source_dir\""
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
echo
|
|
33
|
+
node "$source_dir/src/cli.js" doctor || true
|
|
34
|
+
|
|
35
|
+
echo
|
|
36
|
+
echo "Installed. Restart VS Code, then try /recall, /remember, or /handoff."
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vib795/agent-memory",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Durable cross-repo knowledge graph for GitHub Copilot and Claude Code. Markdown source of truth, disposable SQLite index, zero runtime dependencies.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"github-copilot",
|
|
7
|
+
"claude-code",
|
|
8
|
+
"agent-skills",
|
|
9
|
+
"memory",
|
|
10
|
+
"knowledge-graph",
|
|
11
|
+
"sqlite",
|
|
12
|
+
"zero-dependencies"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/vib795/agent-memory#readme",
|
|
15
|
+
"bugs": "https://github.com/vib795/agent-memory/issues",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/vib795/agent-memory.git"
|
|
19
|
+
},
|
|
20
|
+
"author": "Utkarsh Singh (https://github.com/vib795)",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"agent-memory": "src/cli.js"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=22.5.0"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"test": "node --test",
|
|
30
|
+
"postinstall": "node scripts/postinstall.js",
|
|
31
|
+
"setup": "node src/cli.js setup"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"src/",
|
|
35
|
+
"scripts/",
|
|
36
|
+
"skills/",
|
|
37
|
+
"install.sh",
|
|
38
|
+
"install.ps1",
|
|
39
|
+
"README.md",
|
|
40
|
+
"HOWTO.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Link the skills when the package is installed.
|
|
4
|
+
*
|
|
5
|
+
* This must never fail an install. A missing symlink is a nuisance; an `npm install`
|
|
6
|
+
* that exits non-zero on a managed desktop is the kind of thing that gets a tool
|
|
7
|
+
* banned. Every failure here is reported and swallowed, and `agent-memory setup`
|
|
8
|
+
* remains available to finish the job by hand.
|
|
9
|
+
*
|
|
10
|
+
* Note that managed npm configurations often set `ignore-scripts=true`, in which case
|
|
11
|
+
* this file never runs and nothing warns you. That is precisely why the same work is
|
|
12
|
+
* exposed as a command, and why `doctor` names it.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const say = (msg) => process.stdout.write(`${msg}\n`);
|
|
16
|
+
|
|
17
|
+
if (process.env.AGENT_MEMORY_SKIP_POSTINSTALL) {
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const [maj, min] = process.versions.node.split('.').map((s) => Number.parseInt(s, 10));
|
|
23
|
+
if (maj < 22 || (maj === 22 && min < 5)) {
|
|
24
|
+
say(`agent-memory: Node ${process.versions.node} is too old; needs >= 22.5 for node:sqlite.`);
|
|
25
|
+
say('agent-memory: skills not linked. Upgrade Node, then run: agent-memory setup');
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { setup } = await import('../src/setup.js');
|
|
30
|
+
const { compact } = await import('../src/compact.js');
|
|
31
|
+
const r = setup({ compactFn: () => compact() });
|
|
32
|
+
|
|
33
|
+
const links = r.installed.filter((s) => s.mode === 'link').length;
|
|
34
|
+
const copies = r.copies.length;
|
|
35
|
+
say(`agent-memory: linked ${links} skill${links === 1 ? '' : 's'}${copies ? `, copied ${copies}` : ''}.`);
|
|
36
|
+
say(`agent-memory: ${r.notes} notes indexed. Restart VS Code, then try /recall.`);
|
|
37
|
+
if (copies) {
|
|
38
|
+
say('agent-memory: copies happen on network-backed profiles; re-run `agent-memory setup` after upgrades.');
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
say(`agent-memory: automatic setup did not complete (${err.message}).`);
|
|
42
|
+
say('agent-memory: run `agent-memory setup` to finish. Nothing else is affected.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
process.exit(0);
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: handoff
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
description: Capture the working state of the current conversation into a portable handoff file, so an agent in a different VS Code window or a different repository can continue the work without the user re-explaining it. Use when the user says handoff, hand this off, save context, wrap this up, or continue this elsewhere.
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Bash
|
|
7
|
+
- Read
|
|
8
|
+
- Write
|
|
9
|
+
- Glob
|
|
10
|
+
triggers:
|
|
11
|
+
- handoff
|
|
12
|
+
- hand this off
|
|
13
|
+
- save context for another window
|
|
14
|
+
- continue this in another repo
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# handoff
|
|
18
|
+
|
|
19
|
+
Write one file that lets a different agent, in a different window, on a different
|
|
20
|
+
repo, pick up this thread cold.
|
|
21
|
+
|
|
22
|
+
You are not summarizing the conversation. You are reconstructing **working state**.
|
|
23
|
+
A transcript summary is a failure mode: it reads fine and still leaves the next
|
|
24
|
+
agent asking the questions the user is trying to avoid answering twice.
|
|
25
|
+
|
|
26
|
+
Do the whole job in **one pass**. Gather facts in a single terminal call, then
|
|
27
|
+
write. Do not loop, do not re-read source files, do not interrogate the user.
|
|
28
|
+
Every extra request costs the user credits, which is the reason this skill exists.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Step 0 — Resolve the store path
|
|
33
|
+
|
|
34
|
+
| Platform | Store |
|
|
35
|
+
|---|---|
|
|
36
|
+
| Windows | `$env:USERPROFILE\.agents\handoffs` |
|
|
37
|
+
| macOS / Linux | `$HOME/.agents/handoffs` |
|
|
38
|
+
|
|
39
|
+
The store lives outside every repository on purpose. That is what makes a handoff
|
|
40
|
+
readable from a window opened on a different project.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Step 1 — Gather deterministic facts (ONE terminal call)
|
|
45
|
+
|
|
46
|
+
Run one command that creates the store, reads the index, and collects git state.
|
|
47
|
+
Everything the deterministic layer contributes comes from this single call.
|
|
48
|
+
|
|
49
|
+
**PowerShell (Windows / AVD):**
|
|
50
|
+
|
|
51
|
+
```powershell
|
|
52
|
+
$store = Join-Path $env:USERPROFILE '.agents\handoffs'
|
|
53
|
+
New-Item -ItemType Directory -Force -Path $store | Out-Null
|
|
54
|
+
Write-Output "STORE=$store"
|
|
55
|
+
Write-Output "--- INDEX ---"
|
|
56
|
+
if (Test-Path (Join-Path $store 'index.md')) { Get-Content (Join-Path $store 'index.md') } else { Write-Output "(no index yet)" }
|
|
57
|
+
Write-Output "--- GIT ---"
|
|
58
|
+
Write-Output "ROOT=$(git rev-parse --show-toplevel 2>$null)"
|
|
59
|
+
Write-Output "BRANCH=$(git branch --show-current 2>$null)"
|
|
60
|
+
Write-Output "HEAD=$(git rev-parse --short HEAD 2>$null)"
|
|
61
|
+
Write-Output "--- STATUS ---"
|
|
62
|
+
git status --porcelain 2>$null
|
|
63
|
+
Write-Output "UTC=$([DateTime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ'))"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**bash (macOS / Linux):**
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
store="$HOME/.agents/handoffs"; mkdir -p "$store"
|
|
70
|
+
echo "STORE=$store"
|
|
71
|
+
echo "--- INDEX ---"; [ -f "$store/index.md" ] && cat "$store/index.md" || echo "(no index yet)"
|
|
72
|
+
echo "--- GIT ---"
|
|
73
|
+
echo "ROOT=$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
74
|
+
echo "BRANCH=$(git branch --show-current 2>/dev/null)"
|
|
75
|
+
echo "HEAD=$(git rev-parse --short HEAD 2>/dev/null)"
|
|
76
|
+
echo "--- STATUS ---"; git status --porcelain 2>/dev/null
|
|
77
|
+
echo "UTC=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
If the directory is not a git repository, `ROOT`/`BRANCH`/`HEAD` come back empty.
|
|
81
|
+
That is not an error. Record the working directory path and omit the git fields.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Step 2 — Identify the thread
|
|
86
|
+
|
|
87
|
+
Read the `--- INDEX ---` output and decide, **by judgment**, whether this
|
|
88
|
+
conversation continues an existing thread or starts a new one. Do not string-match
|
|
89
|
+
titles; a title drifts as work progresses and the thread is still the same thread.
|
|
90
|
+
|
|
91
|
+
- Same goal as an existing row, even under a different name → reuse that `id`.
|
|
92
|
+
- Same repo but a genuinely different goal → new `id`.
|
|
93
|
+
- Nothing close → new `id`.
|
|
94
|
+
|
|
95
|
+
New ids are kebab-case, 3 to 6 words, naming the **goal**, not the topic:
|
|
96
|
+
`migrate-orders-to-result-type`, not `orders-work`.
|
|
97
|
+
|
|
98
|
+
State your choice in the final output: "Updating thread `<id>`" or
|
|
99
|
+
"Creating thread `<id>`". If you guessed wrong the user corrects it in one line,
|
|
100
|
+
which is cheaper than asking.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Step 3 — Compose the handoff
|
|
105
|
+
|
|
106
|
+
Use this exact structure.
|
|
107
|
+
|
|
108
|
+
```markdown
|
|
109
|
+
---
|
|
110
|
+
id: <kebab-slug>
|
|
111
|
+
title: <one line, states the goal not the topic>
|
|
112
|
+
status: active | blocked | done
|
|
113
|
+
created: <ISO 8601 UTC>
|
|
114
|
+
updated: <ISO 8601 UTC>
|
|
115
|
+
repos:
|
|
116
|
+
- name: <repo name>
|
|
117
|
+
path: <absolute path>
|
|
118
|
+
branch: <branch>
|
|
119
|
+
head: <short sha>
|
|
120
|
+
agent: copilot | claude-code
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Orientation
|
|
124
|
+
|
|
125
|
+
3 to 5 sentences. What this thread is trying to accomplish and where it stands.
|
|
126
|
+
Written for a reader with zero prior context.
|
|
127
|
+
|
|
128
|
+
## Decisions
|
|
129
|
+
|
|
130
|
+
| # | Decision | Why | Alternatives rejected |
|
|
131
|
+
|---|----------|-----|----------------------|
|
|
132
|
+
|
|
133
|
+
## Constraints
|
|
134
|
+
|
|
135
|
+
Things not discoverable by reading the code: environment restrictions,
|
|
136
|
+
unavailable tooling, plan limits, deadlines, taste calls already settled.
|
|
137
|
+
|
|
138
|
+
## Rejected approaches
|
|
139
|
+
|
|
140
|
+
What was tried and why it failed. Mandatory. This is the section that stops the
|
|
141
|
+
next agent re-deriving known dead ends on the user's credits.
|
|
142
|
+
|
|
143
|
+
## Current task state
|
|
144
|
+
|
|
145
|
+
**Next action:** <one imperative sentence>
|
|
146
|
+
**Blocked on:** <specific blocker, or "nothing">
|
|
147
|
+
|
|
148
|
+
### Uncommitted work
|
|
149
|
+
|
|
150
|
+
| File | State | What changed |
|
|
151
|
+
|------|-------|--------------|
|
|
152
|
+
|
|
153
|
+
### Anchors
|
|
154
|
+
|
|
155
|
+
| Path | Why it matters |
|
|
156
|
+
|------|----------------|
|
|
157
|
+
|
|
158
|
+
## Open questions
|
|
159
|
+
|
|
160
|
+
Numbered. Only questions the user can answer.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`State` in Uncommitted work comes from `git status --porcelain`: `modified`,
|
|
164
|
+
`added`, `deleted`, `untracked`, `renamed`.
|
|
165
|
+
|
|
166
|
+
### Quality rules
|
|
167
|
+
|
|
168
|
+
These separate a useful handoff from a readable paragraph that still leaves questions.
|
|
169
|
+
|
|
170
|
+
1. Write for a reader with zero context. No pronoun without a stated antecedent.
|
|
171
|
+
2. Every decision carries its **why**. Rationale is what never survives
|
|
172
|
+
re-explanation and is the whole reason a handoff beats a transcript.
|
|
173
|
+
3. Never quote or paraphrase the transcript. Record conclusions, not the path to them.
|
|
174
|
+
4. Anchor claims to a file path or a decision number. "We refactored the service
|
|
175
|
+
layer" is a failure. "`src/services/order.ts:42` now returns `Result<T>` instead
|
|
176
|
+
of throwing" is not.
|
|
177
|
+
5. Record only what the conversation actually established. Prefix anything you
|
|
178
|
+
inferred with `inferred:` so the next agent knows to verify it.
|
|
179
|
+
6. Never inline a diff or a patch. List changed files with one line each.
|
|
180
|
+
7. Soft target 150 lines. On overflow, move detail into `<id>.detail.md` and
|
|
181
|
+
reference it. Never drop Decisions or Rejected approaches to hit the target.
|
|
182
|
+
8. Empty sections say `None.` They are never deleted. A missing section reads as
|
|
183
|
+
an oversight; an explicit `None.` reads as a fact.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Step 4 — Redact before writing
|
|
188
|
+
|
|
189
|
+
Scan the drafted body and replace any of these with `<redacted:kind>`:
|
|
190
|
+
|
|
191
|
+
API keys, access tokens, bearer tokens, passwords, connection strings, private
|
|
192
|
+
keys, session cookies, internal hostnames or IPs, and personal email addresses
|
|
193
|
+
that are not the user's own git identity.
|
|
194
|
+
|
|
195
|
+
The store is plaintext on a corporate machine. Never write the raw value, not even
|
|
196
|
+
once, not even to a temp file.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Step 5 — Write the files
|
|
201
|
+
|
|
202
|
+
Prefer your file-write tool targeting the absolute store path. If it refuses to
|
|
203
|
+
write outside the workspace root, fall back to the terminal.
|
|
204
|
+
|
|
205
|
+
Order matters:
|
|
206
|
+
|
|
207
|
+
1. If `<id>.md` exists, move it to `<id>.prev.md`, overwriting any existing `.prev`.
|
|
208
|
+
Exactly one prior version is kept. There is no version history.
|
|
209
|
+
2. Write the new body to `<id>.md.tmp`.
|
|
210
|
+
3. Rename `<id>.md.tmp` to `<id>.md`.
|
|
211
|
+
4. Only if the soft target overflowed, write `<id>.detail.md` the same way.
|
|
212
|
+
|
|
213
|
+
**PowerShell fallback.** Use a single-quoted here-string so `$` and backticks in
|
|
214
|
+
the content are not expanded:
|
|
215
|
+
|
|
216
|
+
```powershell
|
|
217
|
+
$store = Join-Path $env:USERPROFILE '.agents\handoffs'
|
|
218
|
+
$id = '<id>'
|
|
219
|
+
$cur = Join-Path $store "$id.md"
|
|
220
|
+
if (Test-Path $cur) { Move-Item -Force $cur (Join-Path $store "$id.prev.md") }
|
|
221
|
+
$body = @'
|
|
222
|
+
<the full handoff body>
|
|
223
|
+
'@
|
|
224
|
+
Set-Content -Path (Join-Path $store "$id.md.tmp") -Value $body -Encoding UTF8
|
|
225
|
+
Move-Item -Force (Join-Path $store "$id.md.tmp") $cur
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Step 6 — Update the index
|
|
231
|
+
|
|
232
|
+
`index.md` is what makes retrieval cheap later. Keep it to one row per thread.
|
|
233
|
+
|
|
234
|
+
```markdown
|
|
235
|
+
| id | title | status | repos | updated |
|
|
236
|
+
|----|-------|--------|-------|---------|
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Replace the row whose `id` matches. Append only when the id is new. Never let two
|
|
240
|
+
rows share an id.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Step 7 — Capture durable knowledge
|
|
245
|
+
|
|
246
|
+
Skip this step entirely if `agent-memory` is not installed. It is optional
|
|
247
|
+
machinery; the handoff above is complete without it.
|
|
248
|
+
|
|
249
|
+
A handoff carries this thread. The graph carries what stays true after the thread
|
|
250
|
+
ends. You already hold the whole conversation, so writing both costs the same single
|
|
251
|
+
request, which is the only reason this step belongs here rather than in its own skill.
|
|
252
|
+
|
|
253
|
+
### What is durable
|
|
254
|
+
|
|
255
|
+
<!-- extraction-rules:start -->
|
|
256
|
+
- A node is durable only if it will still be true next month. Task state is not
|
|
257
|
+
durable and belongs in a handoff file, not in the graph.
|
|
258
|
+
- Every `decision` node carries its why and its rejected alternatives, or it is not
|
|
259
|
+
written. Rationale is the thing that never survives re-explanation.
|
|
260
|
+
- Anything the conversation did not actually establish is `confidence: inferred`,
|
|
261
|
+
and the body says what would confirm it.
|
|
262
|
+
- Prefer updating an existing node over creating a near-duplicate. `write` returns
|
|
263
|
+
the existing id on a content-hash match.
|
|
264
|
+
- A decision that replaces a known prior sets `supersedes` to that prior's id.
|
|
265
|
+
- Constraints are the highest-value type. An environment restriction, a blocked
|
|
266
|
+
tool, a policy that forbids an approach: write it, because it is what stops a
|
|
267
|
+
future agent burning a retry loop on something that was never going to ship.
|
|
268
|
+
- Zero durable knowledge is a valid outcome. Writing nothing beats writing noise.
|
|
269
|
+
<!-- extraction-rules:end -->
|
|
270
|
+
|
|
271
|
+
Your Decisions table and Constraints section are usually already the durable part.
|
|
272
|
+
The Current task state section never is.
|
|
273
|
+
|
|
274
|
+
### Write it (ONE terminal call)
|
|
275
|
+
|
|
276
|
+
Types are `system`, `decision`, `convention`, `constraint`. Edge relations are
|
|
277
|
+
`depends-on`, `applies-to`, `supersedes`, `contradicts`, `evidence-for`.
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
tmp="$(mktemp)"
|
|
281
|
+
cat > "$tmp" <<'JSON'
|
|
282
|
+
{"nodes":[
|
|
283
|
+
{"id":"use-sessions","type":"decision",
|
|
284
|
+
"title":"Chose server sessions over JWT",
|
|
285
|
+
"body":"Why: revocation had to take effect immediately.\nRejected: short-TTL JWT, because logout would lag by the TTL.\nImplemented in src/auth/session.js:42.",
|
|
286
|
+
"edges":[{"rel":"evidence-for","dst":"auth-service"}]}
|
|
287
|
+
]}
|
|
288
|
+
JSON
|
|
289
|
+
agent-memory write --from-json "$tmp" --source handoff
|
|
290
|
+
rm -f "$tmp"
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
```powershell
|
|
294
|
+
$tmp = [System.IO.Path]::GetTempFileName()
|
|
295
|
+
@'
|
|
296
|
+
<the JSON>
|
|
297
|
+
'@ | Set-Content -Path $tmp -Encoding UTF8
|
|
298
|
+
agent-memory write --from-json $tmp --source handoff
|
|
299
|
+
Remove-Item -Force $tmp
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
`write` redacts before anything reaches disk and reports every validation error at
|
|
303
|
+
once. Surface its warnings verbatim; a `title collision` warning means two ids now
|
|
304
|
+
describe the same thing and the user should know.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Step 8 — Print the pickup line
|
|
309
|
+
|
|
310
|
+
End your reply with exactly this, and nothing after it:
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
Handoff written: <absolute path to id.md>
|
|
314
|
+
Thread: <id> (<created|updated>)
|
|
315
|
+
|
|
316
|
+
Paste this in the other window:
|
|
317
|
+
Read <absolute path to id.md> and continue this work. Follow the Next action.
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Self-check before you write
|
|
323
|
+
|
|
324
|
+
Answer all five. If any is no, fix the file before writing it.
|
|
325
|
+
|
|
326
|
+
1. Could a fresh agent execute **Next action** from this file alone?
|
|
327
|
+
2. Does every decision state why, and what was rejected?
|
|
328
|
+
3. Is Rejected approaches non-empty, or explicitly `None.`?
|
|
329
|
+
4. Is every claim anchored to a path or a decision number?
|
|
330
|
+
5. Is there a single secret, token, or connection string left in the body?
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Failure handling
|
|
335
|
+
|
|
336
|
+
- **File-write tool refuses paths outside the workspace.** Use the PowerShell or
|
|
337
|
+
bash fallback in Step 5. Expected on some configurations; not an error.
|
|
338
|
+
- **Terminal commands are approval-gated.** Proceed anyway. One approval click is
|
|
339
|
+
worth minutes of re-explanation. Do not ask the user to disable the gate.
|
|
340
|
+
- **Both write paths blocked.** Print the complete handoff body in the chat with
|
|
341
|
+
its intended absolute path and tell the user to save it manually. Never silently
|
|
342
|
+
fail, and never claim you wrote a file you did not write.
|
|
343
|
+
- **Not a git repository.** Omit git fields, record the working directory path,
|
|
344
|
+
continue.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: recall
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
description: "Durable project knowledge store. This line is regenerated by `agent-memory compact`. Use when you need to know how a system works, why a decision was made, what convention applies, or what the environment forbids."
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Bash
|
|
7
|
+
- Read
|
|
8
|
+
triggers:
|
|
9
|
+
- how does this work
|
|
10
|
+
- why did we
|
|
11
|
+
- what did we decide
|
|
12
|
+
- what is the convention
|
|
13
|
+
- recall
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# recall
|
|
17
|
+
|
|
18
|
+
Answer from what this project already knows, before deriving it again.
|
|
19
|
+
|
|
20
|
+
The description line above is regenerated by `agent-memory compact`. It is the only
|
|
21
|
+
part of this system loaded into every conversation, so it stays short on purpose.
|
|
22
|
+
Everything below is read only when you actually invoke the skill.
|
|
23
|
+
|
|
24
|
+
Do the whole job in **one pass**. The routing map and the notes are two terminal
|
|
25
|
+
calls inside a single turn. Do not loop, do not re-read, do not ask the user what
|
|
26
|
+
they already told a previous window.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Step 1 — Load the routing map (ONE terminal call)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
agent-memory tree
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`tree` scopes itself to the current repository and always includes globally scoped
|
|
37
|
+
notes, because a global constraint applies here too.
|
|
38
|
+
|
|
39
|
+
Output is one line per note: type, id, title.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
# memory: orders-api — 12 notes
|
|
43
|
+
constraint no-external-db No externally hosted databases
|
|
44
|
+
decision use-sessions Chose sessions over JWT
|
|
45
|
+
system auth-service Auth uses server sessions
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If the last line reads `N nodes not shown`, that is a real gap, not decoration.
|
|
49
|
+
Run `agent-memory tree --all` when the answer plausibly lives in what was dropped.
|
|
50
|
+
|
|
51
|
+
If the store is empty, say so in one line and answer from the code instead. Do not
|
|
52
|
+
invent notes and do not apologize at length.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Step 2 — Choose what to load
|
|
57
|
+
|
|
58
|
+
Read the titles and decide, **by judgment**, which notes bear on the question. You
|
|
59
|
+
are the router. There is no keyword matching underneath this and there should not
|
|
60
|
+
be: a title is a sentence, and matching sentences to a question is what you do well
|
|
61
|
+
and what a regex does badly.
|
|
62
|
+
|
|
63
|
+
- Pick 1 to 3 ids. More than 3 means the question is really several questions.
|
|
64
|
+
- Always include a `constraint` that touches the subject, even when the user did not
|
|
65
|
+
ask about limits. Constraints are what stop an approach that cannot ship.
|
|
66
|
+
- Nothing in the tree looks relevant → go to Step 4.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Step 3 — Load them (ONE terminal call)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
agent-memory get auth-service --depth 1
|
|
74
|
+
agent-memory get no-external-db --depth 1
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`--depth 1` returns the note plus everything one edge away. Use `--depth 2` only
|
|
78
|
+
when the question is about how parts fit together rather than about one part.
|
|
79
|
+
|
|
80
|
+
Two things in the output are load-bearing:
|
|
81
|
+
|
|
82
|
+
- `[captured N commits ago — verify before trusting]` means the note was written
|
|
83
|
+
against an older state of this repository. Say so when you use it.
|
|
84
|
+
- `N nodes omitted for budget` means the neighborhood was larger than the return
|
|
85
|
+
budget. The omitted ids are listed; ask for one directly if it matters.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Step 4 — Fall back to search
|
|
90
|
+
|
|
91
|
+
Only when the tree had nothing plausible:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
agent-memory search "session revocation"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Full text over titles and bodies. If this also misses, the store genuinely does not
|
|
98
|
+
know, and saying so plainly is the correct answer.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Step 5 — Answer
|
|
103
|
+
|
|
104
|
+
Rules that separate a useful recall from a confident wrong one:
|
|
105
|
+
|
|
106
|
+
1. **Cite the note id** for every claim you take from memory, and the `path:line`
|
|
107
|
+
inside the note when it has one.
|
|
108
|
+
2. **Repeat the staleness annotation** if the note carried one. A note captured 47
|
|
109
|
+
commits ago may still be right, but the user decides that, not you.
|
|
110
|
+
3. **The code wins.** If a note contradicts what you can read in the repository
|
|
111
|
+
right now, trust the repository, say which note is wrong, and suggest
|
|
112
|
+
`/remember` to correct it. A store that quietly rots is worse than no store.
|
|
113
|
+
4. **Never present an `inferred` note as established.** The note says which it is.
|
|
114
|
+
5. Answer the question. Do not summarize the store.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Failure handling
|
|
119
|
+
|
|
120
|
+
- **`agent-memory: command not found`.** The package is not installed or not on
|
|
121
|
+
PATH. Say so and answer from the code; do not attempt to install it.
|
|
122
|
+
- **Empty store.** Answer from the code, then mention `/remember` once.
|
|
123
|
+
- **`doctor` reports a problem.** Report it in one line and continue. `index.db` is
|
|
124
|
+
a cache; `agent-memory index` rebuilds it from the markdown, which is the source
|
|
125
|
+
of truth and is never lost.
|