claude-priors 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/bin/cli.js ADDED
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const PRIORS_MARKER = '## Priors';
7
+ const VERSION = '0.1.0';
8
+
9
+ // Resolve paths to bundled assets (works both locally and via npx)
10
+ const PKG_ROOT = path.resolve(__dirname, '..');
11
+ const SKILL_SRC = path.resolve(PKG_ROOT, '..', 'skill', 'SKILL.md');
12
+ const BOOTSTRAP_SRC = path.resolve(PKG_ROOT, '..', 'bootstrap', 'CLAUDE.md.snippet');
13
+
14
+ // Fallback: when installed as npm package, skill/ and bootstrap/ are siblings to bin/
15
+ const SKILL_SRC_ALT = path.resolve(PKG_ROOT, 'skill', 'SKILL.md');
16
+ const BOOTSTRAP_SRC_ALT = path.resolve(PKG_ROOT, 'bootstrap', 'CLAUDE.md.snippet');
17
+
18
+ function resolveAsset(primary, fallback) {
19
+ if (fs.existsSync(primary)) return primary;
20
+ if (fs.existsSync(fallback)) return fallback;
21
+ return null;
22
+ }
23
+
24
+ function readAsset(primary, fallback) {
25
+ const resolved = resolveAsset(primary, fallback);
26
+ if (!resolved) {
27
+ console.error(`Error: Could not find asset. Looked in:\n ${primary}\n ${fallback}`);
28
+ process.exit(1);
29
+ }
30
+ return fs.readFileSync(resolved, 'utf8');
31
+ }
32
+
33
+ function printUsage() {
34
+ console.log(`
35
+ claude-priors v${VERSION} — Persistent agent memory for AI coding assistants
36
+
37
+ Usage:
38
+ claude-priors init [options] Bootstrap priors in current directory
39
+ claude-priors status Show priors stats
40
+ claude-priors help Show this help
41
+
42
+ Options:
43
+ --shared Git-track priors (default: git-ignored)
44
+ --global Install skill to ~/.claude/skills/ instead of repo
45
+ `);
46
+ }
47
+
48
+ function init(args) {
49
+ const cwd = process.cwd();
50
+ const shared = args.includes('--shared');
51
+ const global = args.includes('--global');
52
+
53
+ console.log(`\nclaude-priors v${VERSION}`);
54
+ console.log(`Initializing in: ${cwd}\n`);
55
+
56
+ // 1. Create .claude/priors/
57
+ const priorsDir = path.join(cwd, '.claude', 'priors');
58
+ if (!fs.existsSync(priorsDir)) {
59
+ fs.mkdirSync(priorsDir, { recursive: true });
60
+ fs.writeFileSync(path.join(priorsDir, '.gitkeep'), '');
61
+ console.log(' Created .claude/priors/');
62
+ } else {
63
+ console.log(' .claude/priors/ already exists');
64
+ }
65
+
66
+ // 2. Copy SKILL.md
67
+ const skillContent = readAsset(SKILL_SRC, SKILL_SRC_ALT);
68
+ let skillDir;
69
+ if (global) {
70
+ skillDir = path.join(
71
+ process.env.HOME || process.env.USERPROFILE,
72
+ '.claude', 'skills', 'knowledge-extraction'
73
+ );
74
+ } else {
75
+ skillDir = path.join(cwd, '.claude', 'skills', 'knowledge-extraction');
76
+ }
77
+
78
+ if (!fs.existsSync(skillDir)) {
79
+ fs.mkdirSync(skillDir, { recursive: true });
80
+ }
81
+
82
+ const skillDest = path.join(skillDir, 'SKILL.md');
83
+ if (!fs.existsSync(skillDest)) {
84
+ fs.writeFileSync(skillDest, skillContent);
85
+ console.log(` Created ${global ? '~/.claude/skills' : '.claude/skills'}/knowledge-extraction/SKILL.md`);
86
+ } else {
87
+ // Check version
88
+ const existing = fs.readFileSync(skillDest, 'utf8');
89
+ if (existing === skillContent) {
90
+ console.log(` SKILL.md already up to date`);
91
+ } else {
92
+ fs.writeFileSync(skillDest, skillContent);
93
+ console.log(` Updated SKILL.md to v${VERSION}`);
94
+ }
95
+ }
96
+
97
+ // 3. Append bootstrap to CLAUDE.md (or AGENTS.md)
98
+ const bootstrapContent = readAsset(BOOTSTRAP_SRC, BOOTSTRAP_SRC_ALT);
99
+
100
+ // Detect which instruction file exists
101
+ let instructionFile = null;
102
+ const candidates = ['CLAUDE.md', 'AGENTS.md'];
103
+ for (const candidate of candidates) {
104
+ if (fs.existsSync(path.join(cwd, candidate))) {
105
+ instructionFile = candidate;
106
+ break;
107
+ }
108
+ }
109
+
110
+ if (!instructionFile) {
111
+ instructionFile = 'CLAUDE.md';
112
+ }
113
+
114
+ const instructionPath = path.join(cwd, instructionFile);
115
+
116
+ if (fs.existsSync(instructionPath)) {
117
+ const existing = fs.readFileSync(instructionPath, 'utf8');
118
+ if (existing.includes(PRIORS_MARKER)) {
119
+ console.log(` ${instructionFile} already has priors bootstrap`);
120
+ } else {
121
+ fs.writeFileSync(instructionPath, existing.trimEnd() + '\n\n' + bootstrapContent.trim() + '\n');
122
+ console.log(` Appended priors bootstrap to ${instructionFile}`);
123
+ }
124
+ } else {
125
+ fs.writeFileSync(instructionPath, bootstrapContent.trim() + '\n');
126
+ console.log(` Created ${instructionFile} with priors bootstrap`);
127
+ }
128
+
129
+ // 4. Handle .gitignore
130
+ const gitignorePath = path.join(cwd, '.gitignore');
131
+ const priorsGlob = '.claude/priors/';
132
+
133
+ if (!shared) {
134
+ if (fs.existsSync(gitignorePath)) {
135
+ const gitignore = fs.readFileSync(gitignorePath, 'utf8');
136
+ if (!gitignore.includes(priorsGlob)) {
137
+ fs.writeFileSync(gitignorePath, gitignore.trimEnd() + '\n\n# Agent priors (local knowledge)\n' + priorsGlob + '\n');
138
+ console.log(' Added .claude/priors/ to .gitignore');
139
+ } else {
140
+ console.log(' .gitignore already excludes priors');
141
+ }
142
+ } else {
143
+ fs.writeFileSync(gitignorePath, '# Agent priors (local knowledge)\n' + priorsGlob + '\n');
144
+ console.log(' Created .gitignore with priors exclusion');
145
+ }
146
+ } else {
147
+ console.log(' --shared: priors will be git-tracked');
148
+ }
149
+
150
+ console.log('\nDone! Your agent will now learn from every conversation.\n');
151
+ }
152
+
153
+ function status() {
154
+ const cwd = process.cwd();
155
+ const priorsDir = path.join(cwd, '.claude', 'priors');
156
+
157
+ if (!fs.existsSync(priorsDir)) {
158
+ console.log('No priors directory found. Run `claude-priors init` first.');
159
+ return;
160
+ }
161
+
162
+ const files = fs.readdirSync(priorsDir).filter(f => f.endsWith('.md'));
163
+
164
+ if (files.length === 0) {
165
+ console.log('Priors directory exists but is empty. The agent will start writing priors as it learns.\n');
166
+ return;
167
+ }
168
+
169
+ console.log(`\nPriors: ${priorsDir}\n`);
170
+
171
+ let totalEntries = 0;
172
+
173
+ for (const file of files) {
174
+ const content = fs.readFileSync(path.join(priorsDir, file), 'utf8');
175
+
176
+ // Parse frontmatter
177
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
178
+ let topic = file.replace('.md', '');
179
+ let summary = '';
180
+ let entries = 0;
181
+ let lastUpdated = '';
182
+
183
+ if (fmMatch) {
184
+ const fm = fmMatch[1];
185
+ const topicMatch = fm.match(/topic:\s*(.+)/);
186
+ const summaryMatch = fm.match(/summary:\s*(.+)/);
187
+ const entriesMatch = fm.match(/entries:\s*(\d+)/);
188
+ const dateMatch = fm.match(/last_updated:\s*(.+)/);
189
+
190
+ if (topicMatch) topic = topicMatch[1].trim();
191
+ if (summaryMatch) summary = summaryMatch[1].trim();
192
+ if (entriesMatch) entries = parseInt(entriesMatch[1]);
193
+ if (dateMatch) lastUpdated = dateMatch[1].trim();
194
+ }
195
+
196
+ totalEntries += entries;
197
+ console.log(` ${file}`);
198
+ console.log(` Topic: ${topic} | Entries: ${entries} | Updated: ${lastUpdated || 'unknown'}`);
199
+ if (summary) console.log(` ${summary}`);
200
+ console.log();
201
+ }
202
+
203
+ console.log(`Total: ${files.length} files, ${totalEntries} entries\n`);
204
+ }
205
+
206
+ // --- Main ---
207
+ const args = process.argv.slice(2);
208
+ const command = args[0];
209
+
210
+ switch (command) {
211
+ case 'init':
212
+ init(args.slice(1));
213
+ break;
214
+ case 'status':
215
+ status();
216
+ break;
217
+ case 'help':
218
+ case '--help':
219
+ case '-h':
220
+ case undefined:
221
+ printUsage();
222
+ break;
223
+ default:
224
+ console.error(`Unknown command: ${command}`);
225
+ printUsage();
226
+ process.exit(1);
227
+ }
@@ -0,0 +1,8 @@
1
+
2
+ ## Priors
3
+
4
+ This project uses agent priors (`.claude/priors/`) — persistent knowledge the agent accumulates across sessions.
5
+
6
+ **Start of every task:** List `.claude/priors/` and read each file's YAML frontmatter (`topic`, `summary`). Load full content only for files relevant to the current task.
7
+
8
+ **End of every conversation** where you were corrected, discovered something unexpected, or the user provided context you couldn't derive from code: load the `knowledge-extraction` skill from `.claude/skills/` and extract priors.
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "claude-priors",
3
+ "version": "0.1.0",
4
+ "description": "Persistent agent memory for AI coding assistants. One command to enable your agent to learn from every conversation.",
5
+ "bin": {
6
+ "claude-priors": "./bin/cli.js"
7
+ },
8
+ "keywords": [
9
+ "claude",
10
+ "ai",
11
+ "agent",
12
+ "memory",
13
+ "priors",
14
+ "coding-assistant",
15
+ "opencode",
16
+ "cursor",
17
+ "mcp"
18
+ ],
19
+ "author": "Jiahao Li",
20
+ "license": "MIT",
21
+ "homepage": "https://github.com/jiahaoli/claude-priors#readme",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/jiahaoli/claude-priors.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/jiahaoli/claude-priors/issues"
28
+ },
29
+ "files": [
30
+ "bin/",
31
+ "skill/",
32
+ "bootstrap/"
33
+ ]
34
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: knowledge-extraction
3
+ description: Detailed instructions for the agent priors system — how to extract, format, store, consume, and manage priors files in .claude/priors/. Load this skill when you need to write new priors, reorganize existing priors, or understand the priors file format. The always-on bootstrap lives in CLAUDE.md; this skill provides the full reference.
4
+ ---
5
+
6
+ # Knowledge Extraction — Priors System Reference
7
+
8
+ This skill is the detailed reference for the priors system. The always-on bootstrap in `CLAUDE.md` tells the agent to scan and use priors every session. This skill is loaded on demand when the agent needs to **write, format, or manage** priors.
9
+
10
+ ## Skills vs Priors
11
+
12
+ - **Skills** (`.claude/skills/`) — Explicit, user-provided and user-maintained. **Never write agent-learned knowledge into these files.**
13
+ - **Priors** (`.claude/priors/`) — Implicit, agent-learned and agent-maintained. The agent's working memory persisted to disk.
14
+
15
+ ## Agent Autonomy Over Priors
16
+
17
+ Priors are fully agent-managed. The agent has **unconditional permission** to create, edit, rewrite, merge, split, delete, and reorganize priors files and directory structure. No user confirmation needed. Optimize purely for agent utility: fast scanning, low token cost, high signal density.
18
+
19
+ ## When to Extract
20
+
21
+ After completing a task, extract if the conversation contained:
22
+
23
+ 1. **User correction** — User pointed out something wrong
24
+ 2. **Unexpected discovery** — Something behaved differently than assumed
25
+ 3. **Domain context the user provided** — System behavior, business logic, conventions not in code
26
+ 4. **Multi-step debugging** — Root cause of a back-and-forth diagnosis
27
+ 5. **Workaround or gotcha** — Non-obvious solution or edge case
28
+ 6. **User-volunteered context** — User proactively provided info (strong non-derivable signal)
29
+
30
+ Do NOT extract when:
31
+ - Task completed smoothly on first attempt
32
+ - Knowledge already exists in skills or priors
33
+ - Insight is too one-time-specific to reuse
34
+
35
+ ## Derivable vs Non-Derivable Context
36
+
37
+ - **Derivable** — Discoverable from codebase (schemas, file structure). Don't persist unless expensive to re-derive.
38
+ - **Non-derivable** — Exists only in user's head or external systems (timezone configs, business events, product behavior). Always persist.
39
+
40
+ When a user volunteers information unprompted, it's almost certainly non-derivable — they're telling you because they know you can't find it. Always capture it.
41
+
42
+ ## Priors File Format
43
+
44
+ ### Frontmatter (required)
45
+
46
+ Every priors file has YAML frontmatter for progressive disclosure — the agent scans frontmatter to decide relevance before loading the body.
47
+
48
+ ```yaml
49
+ ---
50
+ topic: <short topic name>
51
+ summary: <one-line description — used for relevance scanning without loading body>
52
+ entries: <count>
53
+ last_updated: <YYYY-MM-DD>
54
+ ---
55
+ ```
56
+
57
+ ### Body
58
+
59
+ ```markdown
60
+ # <Topic>
61
+
62
+ ## <Section>
63
+
64
+ ### <Fact title>
65
+ <!-- Learned: YYYY-MM-DD -->
66
+ <1-3 sentences: fact, consequence, correct approach. Max 5 lines.>
67
+ ```
68
+
69
+ ### Entry rules
70
+
71
+ - State the **fact**, not the discovery story
72
+ - Include **consequence** of getting it wrong
73
+ - Include the **correct approach**
74
+ - Max 5 lines per entry
75
+ - Code snippets only when they save more words than they cost
76
+ - Newest entries first within each section
77
+
78
+ ## Where to Store
79
+
80
+ All priors: `<repo>/.claude/priors/`. Organize by **topic domain**:
81
+
82
+ ```
83
+ .claude/priors/
84
+ ├── data-analysis.md
85
+ ├── schema.md
86
+ ├── deployment.md
87
+ └── product.md
88
+ ```
89
+
90
+ Decision tree:
91
+ 1. `.claude/priors/` doesn't exist? Create it.
92
+ 2. Relevant file exists? Append. If not, create with frontmatter.
93
+ 3. Relevant `##` section exists? Append under it. If not, create one.
94
+ 4. Update frontmatter (`entries`, `last_updated`) after every edit.
95
+
96
+ ## Execution (Writing Priors)
97
+
98
+ 1. Identify extractable lessons from conversation
99
+ 2. Draft compact entries
100
+ 3. Read target priors file (or create with frontmatter)
101
+ 4. Append under appropriate section
102
+ 5. Update frontmatter
103
+ 6. Briefly confirm to user what was captured
104
+
105
+ Do this automatically at conversation end without being asked.
106
+
107
+ ## Execution (Reading Priors)
108
+
109
+ 1. List `.claude/priors/`
110
+ 2. Read frontmatter only (first lines up to closing `---`)
111
+ 3. Based on `topic` and `summary`, decide relevance to current task
112
+ 4. Load full body only for relevant files
113
+
114
+ ## Maintenance
115
+
116
+ Proactively maintain quality:
117
+ - **Merge** overlapping files
118
+ - **Split** files exceeding ~50 entries
119
+ - **Rewrite** verbose or unclear entries
120
+ - **Delete** outdated entries or entries now covered by explicit skills
121
+ - **Update frontmatter** after every change