repoctx 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/README.md +202 -0
- package/dist/cache.d.ts +61 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +155 -0
- package/dist/cache.js.map +1 -0
- package/dist/checkpoint.d.ts +9 -0
- package/dist/checkpoint.d.ts.map +1 -0
- package/dist/checkpoint.js +30 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/diff.d.ts +4 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +76 -0
- package/dist/diff.js.map +1 -0
- package/dist/get.d.ts +10 -0
- package/dist/get.d.ts.map +1 -0
- package/dist/get.js +119 -0
- package/dist/get.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/dist/onboarding.d.ts +2 -0
- package/dist/onboarding.d.ts.map +1 -0
- package/dist/onboarding.js +123 -0
- package/dist/onboarding.js.map +1 -0
- package/dist/save.d.ts +13 -0
- package/dist/save.d.ts.map +1 -0
- package/dist/save.js +53 -0
- package/dist/save.js.map +1 -0
- package/dist/saveSymbol.d.ts +13 -0
- package/dist/saveSymbol.d.ts.map +1 -0
- package/dist/saveSymbol.js +33 -0
- package/dist/saveSymbol.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# repoctx
|
|
2
|
+
|
|
3
|
+
LLMs are probabilistic reasoners.
|
|
4
|
+
Repositories are deterministic systems.
|
|
5
|
+
|
|
6
|
+
Deterministic context should be extracted outside the LLM.
|
|
7
|
+
|
|
8
|
+
repoctx precomputes repository context — diffs, summaries, symbol cards —
|
|
9
|
+
so the assistant reasons instead of explores.
|
|
10
|
+
|
|
11
|
+
Fast. Local. Minimal.
|
|
12
|
+
No server. No DB. No embeddings. No magic.
|
|
13
|
+
|
|
14
|
+
## The problem
|
|
15
|
+
|
|
16
|
+
Every new session, your agent reads the same files again to understand the
|
|
17
|
+
codebase. In large repos this means dozens of tool calls before writing a
|
|
18
|
+
single line. The context is thrown away when the session closes.
|
|
19
|
+
|
|
20
|
+
## How it works
|
|
21
|
+
|
|
22
|
+
- You (or your agent) run `repoctx save <file> "<summary>"` after working on a file
|
|
23
|
+
- repoctx stores the summary, exported symbols, keywords, and a content hash in `.repoctx/`
|
|
24
|
+
- Next session: `repoctx get --keyword auth` returns all indexed modules tagged with that keyword — **0 file reads**
|
|
25
|
+
- If a file changed since the last save, repoctx flags it as stale
|
|
26
|
+
|
|
27
|
+
The index survives session resets. Context is never lost.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# From npm (recommended)
|
|
33
|
+
npm install -g repoctx
|
|
34
|
+
|
|
35
|
+
# From source
|
|
36
|
+
git clone https://github.com/Ezequiiel98/repoctx.git
|
|
37
|
+
cd repoctx
|
|
38
|
+
npm install
|
|
39
|
+
npm run build
|
|
40
|
+
npm install -g .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
cd your-repo
|
|
47
|
+
repoctx init # creates .repoctx/, adds to .gitignore
|
|
48
|
+
repoctx onboarding >> CLAUDE.md # tells Claude how to use repoctx
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
That's it. From now on Claude can save context as it works and retrieve it at the start of each session.
|
|
52
|
+
|
|
53
|
+
## Token savings
|
|
54
|
+
|
|
55
|
+
Measured on a real Node.js service (checkout API, 341 source files):
|
|
56
|
+
|
|
57
|
+
| Scenario | Chars read | Tokens (~) | File reads |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| Without repoctx | 3,731 | ~933 | 4 |
|
|
60
|
+
| repoctx (layer not indexed yet) | 3,396 | ~849 | 2 |
|
|
61
|
+
| repoctx (layer fully indexed) | **1,139** | **~285** | **0** |
|
|
62
|
+
|
|
63
|
+
Tokens estimated using ~4 chars per token heuristic.
|
|
64
|
+
|
|
65
|
+
First session costs the same — you're reading the files anyway to work on them.
|
|
66
|
+
From the second session onwards, you pay ~285 tokens instead of ~933 for the
|
|
67
|
+
same context. The savings compound across sessions and teammates.
|
|
68
|
+
|
|
69
|
+
## Commands
|
|
70
|
+
|
|
71
|
+
### `repoctx save <file> "<summary>" [options]`
|
|
72
|
+
|
|
73
|
+
Save context for a file. Run this when a file's public contract changes.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
repoctx save src/users/dal.js "MongoDB DAL for users. CRUD operations." \
|
|
77
|
+
--symbols "createUser,getUser,deleteUser" \
|
|
78
|
+
--keywords "dal,users,mongodb" \
|
|
79
|
+
--deps "userSchema,sessionSchema" \
|
|
80
|
+
--footguns "deleteUser is soft-delete only" \
|
|
81
|
+
--delta "Added deleteUser (soft delete)"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Option | Description |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `--symbols` | Comma-separated exported functions/classes |
|
|
87
|
+
| `--keywords` | Tags for filtering with `get --keyword`. **Always include these.** |
|
|
88
|
+
| `--deps` | Dependencies this module relies on |
|
|
89
|
+
| `--footguns` | Things that break if touched wrong |
|
|
90
|
+
| `--delta` | What changed in this save |
|
|
91
|
+
| `--meta` | Virtual entry — no real file (for patterns, glossary, folder maps) |
|
|
92
|
+
|
|
93
|
+
### `repoctx save-symbol <name> "<purpose>" --file <path> [options]`
|
|
94
|
+
|
|
95
|
+
Save a Symbol Card for a specific function or class.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
repoctx save-symbol deleteUser "Hard delete a user from MongoDB" \
|
|
99
|
+
--file src/users/dal.js \
|
|
100
|
+
--kind function \
|
|
101
|
+
--signature "({ userSchema }) => async ({ id }) => Promise<DeleteResult>" \
|
|
102
|
+
--related "softDeleteUser:soft-delete variant" \
|
|
103
|
+
--keywords "dal,users,delete"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `repoctx get [path] [--keyword k1,k2] [--symbol name]`
|
|
107
|
+
|
|
108
|
+
Print saved context. **Prefer `--keyword` over bare `repoctx get`** — it uses
|
|
109
|
+
a keyword index (O(1)) and returns only what's relevant.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
repoctx get --keyword auth # modules tagged with "auth"
|
|
113
|
+
repoctx get --keyword dal,users # OR logic across keywords
|
|
114
|
+
repoctx get src/users/ # everything indexed under a path
|
|
115
|
+
repoctx get --symbol deleteUser # look up a specific function
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### `repoctx stale`
|
|
119
|
+
|
|
120
|
+
List all files whose content changed since the last `save`.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
repoctx stale
|
|
124
|
+
# ⚠ src/users/dal.js (file content changed)
|
|
125
|
+
# → repoctx save src/users/dal.js "<summary>" --delta "what changed"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### `repoctx checkpoint` / `repoctx diff [--top N]`
|
|
129
|
+
|
|
130
|
+
Save the current git HEAD as a baseline, then show what changed since then —
|
|
131
|
+
formatted for Claude to understand without reading full diffs.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
repoctx checkpoint
|
|
135
|
+
# ... work for a few days ...
|
|
136
|
+
repoctx diff --top 5
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `repoctx init`
|
|
140
|
+
|
|
141
|
+
Initialize repoctx in the current repo. Creates `.repoctx/` and adds it to
|
|
142
|
+
`.gitignore`.
|
|
143
|
+
|
|
144
|
+
### `repoctx onboarding`
|
|
145
|
+
|
|
146
|
+
Print agent instructions ready to paste into `CLAUDE.md` or `AGENTS.md`.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
repoctx onboarding >> CLAUDE.md
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## When to save
|
|
153
|
+
|
|
154
|
+
**Do save** when the public contract changed:
|
|
155
|
+
- Exported functions, classes, or constants
|
|
156
|
+
- Route handlers or API surface
|
|
157
|
+
- DAL/service patterns (factory signatures, argument shapes)
|
|
158
|
+
- Business logic behavior (soft-delete vs hard-delete, idempotency, etc.)
|
|
159
|
+
- A file is moved, renamed, or becomes a new entrypoint
|
|
160
|
+
|
|
161
|
+
**Don't save** for:
|
|
162
|
+
- Literals, logs, comments, or formatting changes
|
|
163
|
+
- Internal refactors with no public interface change
|
|
164
|
+
- Test-only changes
|
|
165
|
+
|
|
166
|
+
## Meta entries (patterns, glossary, folder map)
|
|
167
|
+
|
|
168
|
+
For knowledge not tied to a single file:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Codebase conventions
|
|
172
|
+
repoctx save .meta/patterns \
|
|
173
|
+
"DAL pattern: ({ schema }) => async (args) => ... Exports at bottom: fn: fn(models)." \
|
|
174
|
+
--keywords "pattern,convention,dal" --meta
|
|
175
|
+
|
|
176
|
+
# Domain glossary
|
|
177
|
+
repoctx save .meta/glossary \
|
|
178
|
+
"user = registered account. session = active login token." \
|
|
179
|
+
--keywords "glossary,domain" --meta
|
|
180
|
+
|
|
181
|
+
# Folder ownership
|
|
182
|
+
repoctx save .meta/structure \
|
|
183
|
+
"src/users = user domain. src/auth = authentication. src/api = route handlers." \
|
|
184
|
+
--keywords "structure,folders" --meta
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Sharing context with your team
|
|
188
|
+
|
|
189
|
+
`.repoctx/` is added to `.gitignore` by default (`repoctx init`). Context is
|
|
190
|
+
local per developer.
|
|
191
|
+
|
|
192
|
+
If you want to share context across the team, remove `.repoctx` from
|
|
193
|
+
`.gitignore` and commit it. Anyone who clones the repo gets the full index
|
|
194
|
+
immediately — no re-indexing needed.
|
|
195
|
+
|
|
196
|
+
## Maintenance
|
|
197
|
+
|
|
198
|
+
Built for my workflow; support is best-effort. PRs welcome.
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type ExportEntry = {
|
|
2
|
+
name: string;
|
|
3
|
+
kind: "function" | "class" | "constant" | "type" | "other";
|
|
4
|
+
};
|
|
5
|
+
export type RepoctxFileCache = {
|
|
6
|
+
path: string;
|
|
7
|
+
hash: string;
|
|
8
|
+
publicSurfaceHash?: string | undefined;
|
|
9
|
+
summary: string;
|
|
10
|
+
symbols: string[];
|
|
11
|
+
exports?: ExportEntry[] | undefined;
|
|
12
|
+
keywords?: string[] | undefined;
|
|
13
|
+
dependencies?: string[] | undefined;
|
|
14
|
+
footguns?: string | undefined;
|
|
15
|
+
delta?: string | undefined;
|
|
16
|
+
repoHeadAtSave?: string | undefined;
|
|
17
|
+
updatedAt: string;
|
|
18
|
+
};
|
|
19
|
+
export type RepoctxIndex = {
|
|
20
|
+
version: 1;
|
|
21
|
+
metaVersion?: number | undefined;
|
|
22
|
+
files: Record<string, {
|
|
23
|
+
hash: string;
|
|
24
|
+
ref: string;
|
|
25
|
+
}>;
|
|
26
|
+
keywordIndex: Record<string, string[]>;
|
|
27
|
+
};
|
|
28
|
+
export type SymbolRelation = {
|
|
29
|
+
symbol: string;
|
|
30
|
+
relation: string;
|
|
31
|
+
};
|
|
32
|
+
export type SymbolCard = {
|
|
33
|
+
symbol: string;
|
|
34
|
+
kind: "function" | "class" | "constant" | "type" | "other";
|
|
35
|
+
file: string;
|
|
36
|
+
signature?: string | undefined;
|
|
37
|
+
purpose: string;
|
|
38
|
+
related?: SymbolRelation[] | undefined;
|
|
39
|
+
keywords?: string[] | undefined;
|
|
40
|
+
updatedAt: string;
|
|
41
|
+
};
|
|
42
|
+
export type SymbolsIndex = {
|
|
43
|
+
version: 1;
|
|
44
|
+
symbols: Record<string, string>;
|
|
45
|
+
};
|
|
46
|
+
export declare function repoctxDir(): string;
|
|
47
|
+
export declare function ensureDirs(): Promise<void>;
|
|
48
|
+
export declare function fileHash(file: string): Promise<string>;
|
|
49
|
+
export declare function computePublicSurfaceHash(symbols: string[]): string;
|
|
50
|
+
export declare function loadIndex(): Promise<RepoctxIndex>;
|
|
51
|
+
export declare function saveIndex(idx: RepoctxIndex): Promise<void>;
|
|
52
|
+
export declare function loadFileCache(relativePath: string): Promise<RepoctxFileCache | null>;
|
|
53
|
+
export declare function saveFileCache(relativePath: string, data: RepoctxFileCache): Promise<void>;
|
|
54
|
+
/** O(1) keyword lookup using the index */
|
|
55
|
+
export declare function lookupByKeywords(keywords: string[]): Promise<string[]>;
|
|
56
|
+
export declare function loadSymbolsIndex(): Promise<SymbolsIndex>;
|
|
57
|
+
export declare function saveSymbolCard(data: SymbolCard): Promise<void>;
|
|
58
|
+
export declare function loadAllSymbols(): Promise<SymbolCard[]>;
|
|
59
|
+
export declare function getGitHead(): string | null;
|
|
60
|
+
export declare function getGitBranch(): string | null;
|
|
61
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
5
|
+
// ── Paths ─────────────────────────────────────────────────────────────────────
|
|
6
|
+
function repoRoot() {
|
|
7
|
+
return process.cwd();
|
|
8
|
+
}
|
|
9
|
+
export function repoctxDir() {
|
|
10
|
+
return path.join(repoRoot(), ".repoctx");
|
|
11
|
+
}
|
|
12
|
+
function filesDir() {
|
|
13
|
+
return path.join(repoctxDir(), "files");
|
|
14
|
+
}
|
|
15
|
+
function symbolsDir() {
|
|
16
|
+
return path.join(repoctxDir(), "symbols");
|
|
17
|
+
}
|
|
18
|
+
function indexFile() {
|
|
19
|
+
return path.join(repoctxDir(), "index.json");
|
|
20
|
+
}
|
|
21
|
+
function symbolsIndexFile() {
|
|
22
|
+
return path.join(repoctxDir(), "symbols-index.json");
|
|
23
|
+
}
|
|
24
|
+
export async function ensureDirs() {
|
|
25
|
+
await fs.mkdir(filesDir(), { recursive: true });
|
|
26
|
+
await fs.mkdir(symbolsDir(), { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
// ── Hashing ───────────────────────────────────────────────────────────────────
|
|
29
|
+
export async function fileHash(file) {
|
|
30
|
+
const buf = await fs.readFile(file);
|
|
31
|
+
return crypto.createHash("sha1").update(buf).digest("hex");
|
|
32
|
+
}
|
|
33
|
+
/** Stable short filename derived from the path (not content) */
|
|
34
|
+
function refFromPath(p) {
|
|
35
|
+
return crypto.createHash("sha1").update(p).digest("hex").slice(0, 12) + ".json";
|
|
36
|
+
}
|
|
37
|
+
export function computePublicSurfaceHash(symbols) {
|
|
38
|
+
return crypto.createHash("sha1").update(symbols.slice().sort().join(",")).digest("hex").slice(0, 12);
|
|
39
|
+
}
|
|
40
|
+
// ── Module Card I/O ───────────────────────────────────────────────────────────
|
|
41
|
+
export async function loadIndex() {
|
|
42
|
+
await ensureDirs();
|
|
43
|
+
try {
|
|
44
|
+
const raw = await fs.readFile(indexFile(), "utf8");
|
|
45
|
+
const parsed = JSON.parse(raw);
|
|
46
|
+
// back-compat: old index had no keywordIndex
|
|
47
|
+
if (!parsed.keywordIndex)
|
|
48
|
+
parsed.keywordIndex = {};
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return { version: 1, files: {}, keywordIndex: {} };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function saveIndex(idx) {
|
|
56
|
+
await fs.writeFile(indexFile(), JSON.stringify(idx, null, 2), "utf8");
|
|
57
|
+
}
|
|
58
|
+
export async function loadFileCache(relativePath) {
|
|
59
|
+
const idx = await loadIndex();
|
|
60
|
+
const entry = idx.files[relativePath];
|
|
61
|
+
if (!entry)
|
|
62
|
+
return null;
|
|
63
|
+
try {
|
|
64
|
+
const raw = await fs.readFile(path.join(filesDir(), entry.ref), "utf8");
|
|
65
|
+
return JSON.parse(raw);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export async function saveFileCache(relativePath, data) {
|
|
72
|
+
const idx = await loadIndex();
|
|
73
|
+
const ref = refFromPath(relativePath);
|
|
74
|
+
await fs.writeFile(path.join(filesDir(), ref), JSON.stringify(data, null, 2), "utf8");
|
|
75
|
+
// Update file entry
|
|
76
|
+
idx.files[relativePath] = { hash: data.hash, ref };
|
|
77
|
+
// Rebuild keyword index for this path
|
|
78
|
+
const keywords = data.keywords ?? [];
|
|
79
|
+
// Remove this path from all existing keyword entries first
|
|
80
|
+
for (const kw of Object.keys(idx.keywordIndex)) {
|
|
81
|
+
idx.keywordIndex[kw] = (idx.keywordIndex[kw] ?? []).filter((p) => p !== relativePath);
|
|
82
|
+
if ((idx.keywordIndex[kw] ?? []).length === 0)
|
|
83
|
+
delete idx.keywordIndex[kw];
|
|
84
|
+
}
|
|
85
|
+
// Add to new keywords
|
|
86
|
+
for (const kw of keywords) {
|
|
87
|
+
if (!idx.keywordIndex[kw])
|
|
88
|
+
idx.keywordIndex[kw] = [];
|
|
89
|
+
if (!idx.keywordIndex[kw].includes(relativePath)) {
|
|
90
|
+
idx.keywordIndex[kw].push(relativePath);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
await saveIndex(idx);
|
|
94
|
+
}
|
|
95
|
+
/** O(1) keyword lookup using the index */
|
|
96
|
+
export async function lookupByKeywords(keywords) {
|
|
97
|
+
const idx = await loadIndex();
|
|
98
|
+
const sets = keywords.map((k) => new Set(idx.keywordIndex[k] ?? []));
|
|
99
|
+
// OR logic: union of all sets
|
|
100
|
+
const union = new Set();
|
|
101
|
+
for (const s of sets)
|
|
102
|
+
for (const p of s)
|
|
103
|
+
union.add(p);
|
|
104
|
+
return [...union];
|
|
105
|
+
}
|
|
106
|
+
// ── Symbol Card I/O ───────────────────────────────────────────────────────────
|
|
107
|
+
export async function loadSymbolsIndex() {
|
|
108
|
+
await ensureDirs();
|
|
109
|
+
try {
|
|
110
|
+
const raw = await fs.readFile(symbolsIndexFile(), "utf8");
|
|
111
|
+
return JSON.parse(raw);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return { version: 1, symbols: {} };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export async function saveSymbolCard(data) {
|
|
118
|
+
const idx = await loadSymbolsIndex();
|
|
119
|
+
const fname = refFromPath("symbol:" + data.symbol);
|
|
120
|
+
await fs.writeFile(path.join(symbolsDir(), fname), JSON.stringify(data, null, 2), "utf8");
|
|
121
|
+
idx.symbols[data.symbol] = fname;
|
|
122
|
+
await fs.writeFile(symbolsIndexFile(), JSON.stringify(idx, null, 2), "utf8");
|
|
123
|
+
}
|
|
124
|
+
export async function loadAllSymbols() {
|
|
125
|
+
const idx = await loadSymbolsIndex();
|
|
126
|
+
const results = [];
|
|
127
|
+
for (const fname of Object.values(idx.symbols)) {
|
|
128
|
+
try {
|
|
129
|
+
const raw = await fs.readFile(path.join(symbolsDir(), fname), "utf8");
|
|
130
|
+
results.push(JSON.parse(raw));
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// skip corrupt entries
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return results;
|
|
137
|
+
}
|
|
138
|
+
// ── Git helpers ───────────────────────────────────────────────────────────────
|
|
139
|
+
export function getGitHead() {
|
|
140
|
+
try {
|
|
141
|
+
return execSync("git rev-parse HEAD", { encoding: "utf8" }).trim();
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export function getGitBranch() {
|
|
148
|
+
try {
|
|
149
|
+
return execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf8" }).trim();
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Checkpoint = {
|
|
2
|
+
head: string;
|
|
3
|
+
branch: string;
|
|
4
|
+
at: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function saveCheckpoint(): Promise<Checkpoint>;
|
|
7
|
+
export declare function loadCheckpoint(): Promise<Checkpoint | null>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=checkpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../src/checkpoint.ts"],"names":[],"mappings":"AAKA,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAWF,wBAAsB,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC,CAc1D;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAQjE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { repoctxDir, ensureDirs } from "./cache.js";
|
|
5
|
+
function stateFile() {
|
|
6
|
+
return path.join(repoctxDir(), "state.json");
|
|
7
|
+
}
|
|
8
|
+
export async function saveCheckpoint() {
|
|
9
|
+
const head = execSync("git rev-parse HEAD", { encoding: "utf8" }).trim();
|
|
10
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf8" }).trim();
|
|
11
|
+
const at = new Date().toISOString();
|
|
12
|
+
const state = {
|
|
13
|
+
repoRoot: process.cwd(),
|
|
14
|
+
lastCheckpoint: { head, branch, at },
|
|
15
|
+
};
|
|
16
|
+
await ensureDirs();
|
|
17
|
+
await fs.writeFile(stateFile(), JSON.stringify(state, null, 2), "utf8");
|
|
18
|
+
return { head, branch, at };
|
|
19
|
+
}
|
|
20
|
+
export async function loadCheckpoint() {
|
|
21
|
+
try {
|
|
22
|
+
const raw = await fs.readFile(stateFile(), "utf8");
|
|
23
|
+
const state = JSON.parse(raw);
|
|
24
|
+
return state.lastCheckpoint ?? null;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=checkpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../src/checkpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAapD,SAAS,SAAS;IAChB,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,YAAY,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,IAAI,GAAG,QAAQ,CAAC,oBAAoB,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACzE,MAAM,MAAM,GAAG,QAAQ,CAAC,iCAAiC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxF,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEpC,MAAM,KAAK,GAAiB;QAC1B,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE;QACvB,cAAc,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;KACrC,CAAC;IAEF,MAAM,UAAU,EAAE,CAAC;IACnB,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAExE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;QACnD,MAAM,KAAK,GAAiB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5C,OAAO,KAAK,CAAC,cAAc,IAAI,IAAI,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/dist/diff.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAoBA,wBAAsB,OAAO,CAAC,EAAE,GAAG,EAAE,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,mBAyE3D"}
|
package/dist/diff.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { loadCheckpoint } from "./checkpoint.js";
|
|
3
|
+
function parseNumstat(output) {
|
|
4
|
+
const results = [];
|
|
5
|
+
for (const line of output.trim().split("\n").filter(Boolean)) {
|
|
6
|
+
const parts = line.split("\t");
|
|
7
|
+
const file = parts[2];
|
|
8
|
+
if (!file)
|
|
9
|
+
continue;
|
|
10
|
+
results.push({ file, additions: Number(parts[0]), deletions: Number(parts[1]) });
|
|
11
|
+
}
|
|
12
|
+
return results;
|
|
13
|
+
}
|
|
14
|
+
export async function runDiff({ top } = {}) {
|
|
15
|
+
const checkpoint = await loadCheckpoint();
|
|
16
|
+
if (!checkpoint) {
|
|
17
|
+
return "No checkpoint found. Run `repoctx checkpoint` first.";
|
|
18
|
+
}
|
|
19
|
+
const { head, at } = checkpoint;
|
|
20
|
+
const shortHead = head.slice(0, 7);
|
|
21
|
+
let currentHead;
|
|
22
|
+
try {
|
|
23
|
+
currentHead = execSync("git rev-parse HEAD", { encoding: "utf8" }).trim();
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return "Not in a git repository.";
|
|
27
|
+
}
|
|
28
|
+
if (currentHead === head) {
|
|
29
|
+
return `No changes since checkpoint (${shortHead}).`;
|
|
30
|
+
}
|
|
31
|
+
let numstatOutput;
|
|
32
|
+
try {
|
|
33
|
+
numstatOutput = execSync(`git diff ${head}..HEAD --numstat`, { encoding: "utf8" });
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return `Could not compute diff from ${shortHead}.`;
|
|
37
|
+
}
|
|
38
|
+
let stats = parseNumstat(numstatOutput);
|
|
39
|
+
stats.sort((a, b) => (b.additions + b.deletions) - (a.additions + a.deletions));
|
|
40
|
+
const topStats = top ? stats.slice(0, top) : stats;
|
|
41
|
+
const lines = [];
|
|
42
|
+
lines.push(`# Repoctx Diff (since ${shortHead} — ${at})`);
|
|
43
|
+
lines.push("");
|
|
44
|
+
lines.push(`Files changed (${stats.length}):`);
|
|
45
|
+
lines.push("");
|
|
46
|
+
for (const s of topStats) {
|
|
47
|
+
lines.push(`- ${s.file} (+${s.additions} -${s.deletions})`);
|
|
48
|
+
}
|
|
49
|
+
if (top && stats.length > top) {
|
|
50
|
+
lines.push(`... and ${stats.length - top} more files`);
|
|
51
|
+
}
|
|
52
|
+
lines.push("");
|
|
53
|
+
lines.push("---");
|
|
54
|
+
lines.push("");
|
|
55
|
+
for (const s of topStats) {
|
|
56
|
+
try {
|
|
57
|
+
const fileDiff = execSync(`git diff ${head}..HEAD -- "${s.file}"`, {
|
|
58
|
+
encoding: "utf8",
|
|
59
|
+
});
|
|
60
|
+
if (!fileDiff.trim())
|
|
61
|
+
continue;
|
|
62
|
+
lines.push(`[${s.file}]`);
|
|
63
|
+
const diffLines = fileDiff
|
|
64
|
+
.split("\n")
|
|
65
|
+
.filter((l) => (l.startsWith("+") || l.startsWith("-")) && !l.startsWith("+++") && !l.startsWith("---"))
|
|
66
|
+
.slice(0, 40);
|
|
67
|
+
lines.push(...diffLines);
|
|
68
|
+
lines.push("");
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// skip if diff fails for a specific file
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=diff.js.map
|
package/dist/diff.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"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"}
|
package/dist/get.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function getContext({ filterPath, keywords, symbol, }?: {
|
|
2
|
+
filterPath?: string;
|
|
3
|
+
keywords?: string[];
|
|
4
|
+
symbol?: string;
|
|
5
|
+
}): Promise<string>;
|
|
6
|
+
export declare function getStale(): Promise<{
|
|
7
|
+
path: string;
|
|
8
|
+
reason: string;
|
|
9
|
+
}[]>;
|
|
10
|
+
//# sourceMappingURL=get.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../src/get.ts"],"names":[],"mappings":"AASA,wBAAsB,UAAU,CAAC,EAC/B,UAAU,EACV,QAAQ,EACR,MAAM,GACP,GAAE;IACD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACZ,mBAsGL;AAID,wBAAsB,QAAQ,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAmB5E"}
|
package/dist/get.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { loadIndex, loadFileCache, fileHash, loadAllSymbols, lookupByKeywords, } from "./cache.js";
|
|
3
|
+
export async function getContext({ filterPath, keywords, symbol, } = {}) {
|
|
4
|
+
const lines = [];
|
|
5
|
+
// ── Symbol lookup ──────────────────────────────────────────────────────────
|
|
6
|
+
if (symbol) {
|
|
7
|
+
const allSymbols = await loadAllSymbols();
|
|
8
|
+
const match = allSymbols.find((s) => s.symbol.toLowerCase() === symbol.toLowerCase());
|
|
9
|
+
if (!match)
|
|
10
|
+
return `No symbol found: "${symbol}"`;
|
|
11
|
+
lines.push(`[symbol: ${match.symbol}]`);
|
|
12
|
+
lines.push(`Kind: ${match.kind}`);
|
|
13
|
+
lines.push(`File: ${match.file}`);
|
|
14
|
+
lines.push(`Purpose: ${match.purpose}`);
|
|
15
|
+
if (match.signature)
|
|
16
|
+
lines.push(`Signature: ${match.signature}`);
|
|
17
|
+
if (match.related?.length) {
|
|
18
|
+
lines.push(`Related:`);
|
|
19
|
+
for (const r of match.related)
|
|
20
|
+
lines.push(` - ${r.symbol} (${r.relation})`);
|
|
21
|
+
}
|
|
22
|
+
if (match.keywords?.length)
|
|
23
|
+
lines.push(`Keywords: ${match.keywords.join(", ")}`);
|
|
24
|
+
lines.push(`Updated: ${match.updatedAt}`);
|
|
25
|
+
return lines.join("\n");
|
|
26
|
+
}
|
|
27
|
+
// ── Determine which paths to show ─────────────────────────────────────────
|
|
28
|
+
let targetPaths = null; // null = show all
|
|
29
|
+
if (keywords && keywords.length > 0) {
|
|
30
|
+
// O(1) lookup via keyword index
|
|
31
|
+
targetPaths = await lookupByKeywords(keywords);
|
|
32
|
+
}
|
|
33
|
+
// ── Module cards ───────────────────────────────────────────────────────────
|
|
34
|
+
const idx = await loadIndex();
|
|
35
|
+
const relFilter = filterPath
|
|
36
|
+
? path.relative(process.cwd(), path.resolve(filterPath)).replaceAll("\\", "/")
|
|
37
|
+
: undefined;
|
|
38
|
+
const pathsToShow = targetPaths ?? Object.keys(idx.files);
|
|
39
|
+
for (const rel of pathsToShow) {
|
|
40
|
+
if (relFilter) {
|
|
41
|
+
const isCwd = relFilter === "" || relFilter === ".";
|
|
42
|
+
const isExact = rel === relFilter;
|
|
43
|
+
const isUnder = rel.startsWith(relFilter + "/");
|
|
44
|
+
if (!isCwd && !isExact && !isUnder)
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const cache = await loadFileCache(rel);
|
|
48
|
+
if (!cache)
|
|
49
|
+
continue;
|
|
50
|
+
lines.push(`[${rel}]`);
|
|
51
|
+
lines.push(cache.summary);
|
|
52
|
+
if (cache.symbols.length > 0)
|
|
53
|
+
lines.push(`Symbols: ${cache.symbols.join(", ")}`);
|
|
54
|
+
if (cache.exports?.length) {
|
|
55
|
+
lines.push(`Exports: ${cache.exports.map((e) => `${e.name}(${e.kind})`).join(", ")}`);
|
|
56
|
+
}
|
|
57
|
+
if (cache.keywords?.length)
|
|
58
|
+
lines.push(`Keywords: ${cache.keywords.join(", ")}`);
|
|
59
|
+
if (cache.dependencies?.length)
|
|
60
|
+
lines.push(`Deps: ${cache.dependencies.join(", ")}`);
|
|
61
|
+
if (cache.footguns)
|
|
62
|
+
lines.push(`⚠ Footguns: ${cache.footguns}`);
|
|
63
|
+
if (cache.delta)
|
|
64
|
+
lines.push(`Δ Last change: ${cache.delta}`);
|
|
65
|
+
if (cache.hash !== "meta") {
|
|
66
|
+
try {
|
|
67
|
+
const currentHash = await fileHash(path.resolve(rel));
|
|
68
|
+
if (currentHash !== cache.hash) {
|
|
69
|
+
lines.push("⚠ Context outdated (file changed since last save)");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
lines.push("⚠ File not found (may have been deleted or moved)");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
lines.push("");
|
|
77
|
+
}
|
|
78
|
+
// ── Symbol cards ───────────────────────────────────────────────────────────
|
|
79
|
+
const allSymbols = await loadAllSymbols();
|
|
80
|
+
let symbolsToShow = allSymbols;
|
|
81
|
+
if (keywords && keywords.length > 0) {
|
|
82
|
+
symbolsToShow = allSymbols.filter((s) => keywords.some((k) => s.keywords?.includes(k)));
|
|
83
|
+
}
|
|
84
|
+
if (symbolsToShow.length > 0 && !symbol) {
|
|
85
|
+
lines.push("── Symbols ──────────────────────────────────────────────");
|
|
86
|
+
for (const s of symbolsToShow) {
|
|
87
|
+
lines.push(`[symbol: ${s.symbol}] (${s.kind}) ${s.file}`);
|
|
88
|
+
lines.push(` ${s.purpose}`);
|
|
89
|
+
if (s.signature)
|
|
90
|
+
lines.push(` Sig: ${s.signature}`);
|
|
91
|
+
if (s.related?.length) {
|
|
92
|
+
lines.push(` Related: ${s.related.map((r) => `${r.symbol} (${r.relation})`).join(", ")}`);
|
|
93
|
+
}
|
|
94
|
+
lines.push("");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return lines.join("\n");
|
|
98
|
+
}
|
|
99
|
+
// ── Stale check ───────────────────────────────────────────────────────────────
|
|
100
|
+
export async function getStale() {
|
|
101
|
+
const idx = await loadIndex();
|
|
102
|
+
const stale = [];
|
|
103
|
+
for (const rel of Object.keys(idx.files)) {
|
|
104
|
+
const cache = await loadFileCache(rel);
|
|
105
|
+
if (!cache || cache.hash === "meta")
|
|
106
|
+
continue;
|
|
107
|
+
try {
|
|
108
|
+
const currentHash = await fileHash(path.resolve(rel));
|
|
109
|
+
if (currentHash !== cache.hash) {
|
|
110
|
+
stale.push({ path: rel, reason: "file content changed" });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
stale.push({ path: rel, reason: "file not found" });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return stale;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=get.js.map
|
package/dist/get.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get.js","sourceRoot":"","sources":["../src/get.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { saveManual } from "./save.js";
|
|
6
|
+
import { saveSymbol, parseRelated } from "./saveSymbol.js";
|
|
7
|
+
import { getContext, getStale } from "./get.js";
|
|
8
|
+
import { saveCheckpoint } from "./checkpoint.js";
|
|
9
|
+
import { runDiff } from "./diff.js";
|
|
10
|
+
import { getOnboardingText } from "./onboarding.js";
|
|
11
|
+
import { repoctxDir } from "./cache.js";
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name("repoctx")
|
|
15
|
+
.description("Structured context layer for AI coding assistants")
|
|
16
|
+
.version("0.1.0");
|
|
17
|
+
// ── repoctx save ──────────────────────────────────────────────────────────────
|
|
18
|
+
program
|
|
19
|
+
.command("save")
|
|
20
|
+
.description("Save context for a file. Always include --keywords so future agents can find this module with repoctx get --keyword <topic>")
|
|
21
|
+
.argument("<file>", "File path (or virtual key with --meta)")
|
|
22
|
+
.argument("<summary>", "What this file does, its role, and conventions")
|
|
23
|
+
.option("--symbols <symbols>", "Comma-separated exported symbols/functions", "")
|
|
24
|
+
.option("--keywords <keywords>", "REQUIRED in practice: comma-separated tags (e.g. dal,payments,http). Used by repoctx get --keyword", "")
|
|
25
|
+
.option("--deps <deps>", "Comma-separated dependencies of this module", "")
|
|
26
|
+
.option("--footguns <text>", "Gotchas: things that break if touched wrong (e.g. soft-delete vs hard-delete)")
|
|
27
|
+
.option("--delta <text>", "What changed in this save — short description of the diff")
|
|
28
|
+
.option("--meta", "Virtual entry — no real file, skips hash check. Use for patterns, glossary, folder maps")
|
|
29
|
+
.action(async (file, summary, opts) => {
|
|
30
|
+
const split = (s) => s ? s.split(",").map((x) => x.trim()).filter(Boolean) : [];
|
|
31
|
+
const rel = await saveManual({
|
|
32
|
+
filePath: file,
|
|
33
|
+
summary,
|
|
34
|
+
symbols: split(opts.symbols),
|
|
35
|
+
keywords: split(opts.keywords),
|
|
36
|
+
dependencies: split(opts.deps),
|
|
37
|
+
footguns: opts.footguns,
|
|
38
|
+
delta: opts.delta,
|
|
39
|
+
meta: !!opts.meta,
|
|
40
|
+
});
|
|
41
|
+
console.log(`✓ Saved context for ${rel}`);
|
|
42
|
+
});
|
|
43
|
+
// ── repoctx save-symbol ───────────────────────────────────────────────────────
|
|
44
|
+
program
|
|
45
|
+
.command("save-symbol")
|
|
46
|
+
.description("Save a Symbol Card for a specific function, class, or constant. Enables repoctx get --symbol <name> lookup")
|
|
47
|
+
.argument("<symbol>", "Symbol name (e.g. removeCharge)")
|
|
48
|
+
.argument("<purpose>", "One-line description of what this symbol does")
|
|
49
|
+
.requiredOption("--file <file>", "File where the symbol lives")
|
|
50
|
+
.option("--kind <kind>", "function | class | constant | type | other", "function")
|
|
51
|
+
.option("--signature <sig>", "Full signature (e.g. '({ chargeSchema }) => async ({ id }) => Promise<Result>')")
|
|
52
|
+
.option("--related <pairs>", "Typed relations: 'symbol:relation,symbol:relation' (e.g. 'deleteCharge:soft-delete variant')", "")
|
|
53
|
+
.option("--keywords <keywords>", "Same tags as the parent module so --keyword lookups include this symbol too", "")
|
|
54
|
+
.action(async (symbol, purpose, opts) => {
|
|
55
|
+
const split = (s) => s ? s.split(",").map((x) => x.trim()).filter(Boolean) : [];
|
|
56
|
+
await saveSymbol({
|
|
57
|
+
symbol,
|
|
58
|
+
purpose,
|
|
59
|
+
file: opts.file,
|
|
60
|
+
kind: opts.kind,
|
|
61
|
+
signature: opts.signature,
|
|
62
|
+
related: opts.related ? parseRelated(opts.related) : [],
|
|
63
|
+
keywords: split(opts.keywords),
|
|
64
|
+
});
|
|
65
|
+
console.log(`✓ Saved symbol card for ${symbol}`);
|
|
66
|
+
});
|
|
67
|
+
// ── repoctx get ───────────────────────────────────────────────────────────────
|
|
68
|
+
program
|
|
69
|
+
.command("get")
|
|
70
|
+
.description("Print saved context. Prefer --keyword over bare get — it's faster, cheaper, and less noisy")
|
|
71
|
+
.argument("[path]", "Filter by file or directory path")
|
|
72
|
+
.option("--keyword <keywords>", "Filter by keyword(s), comma-separated OR logic. e.g. --keyword dal,payments. Use this before reaching for Read on a source file")
|
|
73
|
+
.option("--symbol <name>", "Look up a specific symbol card by name")
|
|
74
|
+
.action(async (filterPath, opts) => {
|
|
75
|
+
const keywords = opts.keyword
|
|
76
|
+
? opts.keyword.split(",").map((s) => s.trim()).filter(Boolean)
|
|
77
|
+
: undefined;
|
|
78
|
+
const output = await getContext({
|
|
79
|
+
...(filterPath !== undefined && { filterPath }),
|
|
80
|
+
...(keywords !== undefined && { keywords }),
|
|
81
|
+
...(opts.symbol !== undefined && { symbol: opts.symbol }),
|
|
82
|
+
});
|
|
83
|
+
if (!output.trim()) {
|
|
84
|
+
console.log("No context found. Use `repoctx save` to add context.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
process.stdout.write(output + "\n");
|
|
88
|
+
});
|
|
89
|
+
// ── repoctx stale ─────────────────────────────────────────────────────────────
|
|
90
|
+
program
|
|
91
|
+
.command("stale")
|
|
92
|
+
.description("List all indexed files whose content changed since last save")
|
|
93
|
+
.action(async () => {
|
|
94
|
+
const results = await getStale();
|
|
95
|
+
if (results.length === 0) {
|
|
96
|
+
console.log("✓ All context is up to date.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
console.log(`${results.length} stale file(s):\n`);
|
|
100
|
+
for (const { path: p, reason } of results) {
|
|
101
|
+
console.log(` ⚠ ${p} (${reason})`);
|
|
102
|
+
console.log(` → repoctx save ${p} "<updated summary>" --keywords "..." --delta "what changed"`);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
// ── repoctx checkpoint ────────────────────────────────────────────────────────
|
|
106
|
+
program
|
|
107
|
+
.command("checkpoint")
|
|
108
|
+
.description("Save current git HEAD as baseline for future diffs")
|
|
109
|
+
.action(async () => {
|
|
110
|
+
try {
|
|
111
|
+
const cp = await saveCheckpoint();
|
|
112
|
+
console.log(`✓ Checkpoint saved: ${cp.branch} @ ${cp.head.slice(0, 7)} (${cp.at})`);
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
console.error(`Error: ${e?.message ?? e}`);
|
|
116
|
+
console.error("Make sure you are inside a git repository.");
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// ── repoctx diff ──────────────────────────────────────────────────────────────
|
|
121
|
+
program
|
|
122
|
+
.command("diff")
|
|
123
|
+
.description("Show changes since last checkpoint, formatted for Claude")
|
|
124
|
+
.option("--top <n>", "Show only top N changed files by lines modified")
|
|
125
|
+
.action(async (opts) => {
|
|
126
|
+
const top = opts.top ? Number(opts.top) : undefined;
|
|
127
|
+
const output = await runDiff(top !== undefined ? { top } : {});
|
|
128
|
+
process.stdout.write(output + "\n");
|
|
129
|
+
});
|
|
130
|
+
// ── repoctx init ──────────────────────────────────────────────────────────────
|
|
131
|
+
program
|
|
132
|
+
.command("init")
|
|
133
|
+
.description("Initialize repoctx in the current repo: creates .repoctx/ and adds it to .gitignore")
|
|
134
|
+
.action(async () => {
|
|
135
|
+
await fs.mkdir(repoctxDir(), { recursive: true });
|
|
136
|
+
// Add .repoctx to .gitignore if not already there
|
|
137
|
+
const gitignorePath = path.join(process.cwd(), ".gitignore");
|
|
138
|
+
let alreadyIgnored = false;
|
|
139
|
+
try {
|
|
140
|
+
const content = await fs.readFile(gitignorePath, "utf8");
|
|
141
|
+
alreadyIgnored = content.split("\n").some((l) => l.trim() === ".repoctx");
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// no .gitignore yet, will create
|
|
145
|
+
}
|
|
146
|
+
if (!alreadyIgnored) {
|
|
147
|
+
await fs.appendFile(gitignorePath, "\n# repoctx context index\n.repoctx\n");
|
|
148
|
+
console.log("✓ Added .repoctx to .gitignore");
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.log("✓ .repoctx already in .gitignore");
|
|
152
|
+
}
|
|
153
|
+
console.log("✓ repoctx initialized. Run `repoctx onboarding` to get CLAUDE.md instructions.");
|
|
154
|
+
});
|
|
155
|
+
// ── repoctx onboarding ────────────────────────────────────────────────────────
|
|
156
|
+
program
|
|
157
|
+
.command("onboarding")
|
|
158
|
+
.description("Print instructions for AI agents (paste into CLAUDE.md)")
|
|
159
|
+
.action(() => {
|
|
160
|
+
process.stdout.write(getOnboardingText() + "\n");
|
|
161
|
+
});
|
|
162
|
+
program.parseAsync(process.argv).catch((e) => {
|
|
163
|
+
console.error(e?.message ?? e);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
});
|
|
166
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,IAAI,MAAM,CAyH1C"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export function getOnboardingText() {
|
|
2
|
+
return `
|
|
3
|
+
# repoctx — Agent Instructions
|
|
4
|
+
|
|
5
|
+
repoctx is a persistent context layer for this repo. It stores module
|
|
6
|
+
summaries, symbol cards, patterns, and domain knowledge in .repoctx/.
|
|
7
|
+
|
|
8
|
+
The index survives session resets — you never lose context when a
|
|
9
|
+
conversation closes and reopens. Always read from repoctx first; it
|
|
10
|
+
prevents hallucinations by grounding your understanding in committed,
|
|
11
|
+
structured facts instead of memory.
|
|
12
|
+
|
|
13
|
+
## Always start a session with
|
|
14
|
+
|
|
15
|
+
\`\`\`bash
|
|
16
|
+
repoctx get --keyword <topic> # targeted: only what you need
|
|
17
|
+
repoctx diff --top 8 # what changed since the last checkpoint
|
|
18
|
+
\`\`\`
|
|
19
|
+
|
|
20
|
+
Prefer \`--keyword\` over a bare \`repoctx get\` (which dumps everything).
|
|
21
|
+
Use keywords to fetch only the modules relevant to the current task.
|
|
22
|
+
This keeps context small and responses fast.
|
|
23
|
+
|
|
24
|
+
## Targeted lookups — use these instead of reading source files
|
|
25
|
+
|
|
26
|
+
\`\`\`bash
|
|
27
|
+
repoctx get --keyword dal,users # modules related to a topic
|
|
28
|
+
repoctx get --keyword auth # modules in the auth domain
|
|
29
|
+
repoctx get --symbol deleteUser # look up a specific function
|
|
30
|
+
repoctx get src/users/ # everything indexed under a path
|
|
31
|
+
\`\`\`
|
|
32
|
+
|
|
33
|
+
Keywords are OR-filtered: \`--keyword dal,charge\` returns anything tagged
|
|
34
|
+
with "dal" OR "charge". Each module and symbol has its own keyword list —
|
|
35
|
+
use them to navigate without reading files.
|
|
36
|
+
|
|
37
|
+
## Speed note
|
|
38
|
+
|
|
39
|
+
Every file you skip reading = one fewer tool call = faster response.
|
|
40
|
+
A \`repoctx get --keyword X\` is one call. Reading three source files is
|
|
41
|
+
three calls. Always check repoctx before reaching for Read.
|
|
42
|
+
|
|
43
|
+
## When to run \`repoctx save\` (public contract changed)
|
|
44
|
+
|
|
45
|
+
Run it when any of these change:
|
|
46
|
+
- Exported functions, classes, or constants
|
|
47
|
+
- Module.exports / ESM exports
|
|
48
|
+
- Route handlers or API surface
|
|
49
|
+
- DAL/service patterns (factory signatures, argument shapes)
|
|
50
|
+
- Business logic behavior (soft-delete vs hard-delete, idempotency, etc.)
|
|
51
|
+
- A file is moved, renamed, or becomes a new entrypoint
|
|
52
|
+
- A new convention or pattern is introduced
|
|
53
|
+
|
|
54
|
+
Always include \`--keywords\` matching the domain and layer so future
|
|
55
|
+
lookups via \`--keyword\` can find this module:
|
|
56
|
+
|
|
57
|
+
\`\`\`bash
|
|
58
|
+
repoctx save src/users/dal.js \\
|
|
59
|
+
"MongoDB DAL for users. CRUD operations." \\
|
|
60
|
+
--symbols "createUser,getUser,deleteUser" \\
|
|
61
|
+
--keywords "dal,users,mongodb" \\
|
|
62
|
+
--delta "Added deleteUser (soft delete)"
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
Use \`--footguns\` to warn future agents about tricky behavior:
|
|
66
|
+
|
|
67
|
+
\`\`\`bash
|
|
68
|
+
--footguns "deleteUser = soft delete. removeUser = hard delete. Don't confuse them."
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
## When NOT to run \`repoctx save\`
|
|
72
|
+
|
|
73
|
+
Skip it for:
|
|
74
|
+
- Literals, log messages, comments, or formatting
|
|
75
|
+
- Internal refactors with no change to the public interface
|
|
76
|
+
- Test-only changes
|
|
77
|
+
|
|
78
|
+
## Symbol Cards (fine-grained lookup)
|
|
79
|
+
|
|
80
|
+
For key functions worth tracking individually:
|
|
81
|
+
|
|
82
|
+
\`\`\`bash
|
|
83
|
+
repoctx save-symbol deleteUser "Hard delete a user from MongoDB" \\
|
|
84
|
+
--file src/users/dal.js \\
|
|
85
|
+
--kind function \\
|
|
86
|
+
--signature "({ userSchema }) => async ({ id }) => Promise<DeleteResult>" \\
|
|
87
|
+
--related "softDeleteUser:soft-delete variant" \\
|
|
88
|
+
--keywords "dal,users,delete"
|
|
89
|
+
\`\`\`
|
|
90
|
+
|
|
91
|
+
Then look it up with: \`repoctx get --symbol deleteUser\`
|
|
92
|
+
|
|
93
|
+
## Meta entries (patterns, glossary, folder map)
|
|
94
|
+
|
|
95
|
+
For knowledge not tied to a single file, use \`--meta\`:
|
|
96
|
+
|
|
97
|
+
\`\`\`bash
|
|
98
|
+
repoctx save .meta/patterns "DAL pattern: ({ schema }) => async (args) => ..." \\
|
|
99
|
+
--keywords "pattern,convention,dal" --meta
|
|
100
|
+
|
|
101
|
+
repoctx save .meta/glossary "user = registered account. session = active login token." \\
|
|
102
|
+
--keywords "glossary,domain" --meta
|
|
103
|
+
|
|
104
|
+
repoctx save .meta/structure "src/users = user domain. src/auth = authentication. src/api = route handlers." \\
|
|
105
|
+
--keywords "structure,folders" --meta
|
|
106
|
+
\`\`\`
|
|
107
|
+
|
|
108
|
+
## Freshness
|
|
109
|
+
|
|
110
|
+
If \`repoctx get\` shows \`⚠ Context outdated\` for a file, read it and
|
|
111
|
+
run \`repoctx save\` with an updated summary before working on it.
|
|
112
|
+
Refresh only the stale file — do not re-index the whole repo.
|
|
113
|
+
|
|
114
|
+
## Output discipline
|
|
115
|
+
|
|
116
|
+
- Always try \`repoctx get --keyword <topic>\` before reaching for Read.
|
|
117
|
+
- If repoctx can answer your question, stop there.
|
|
118
|
+
- If you must read a file, prefer a targeted slice (10–30 lines) to
|
|
119
|
+
confirm exact syntax, not to understand the module from scratch.
|
|
120
|
+
- After a meaningful change, update repoctx for that module.
|
|
121
|
+
`.trim();
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=onboarding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB;IAC/B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuHR,CAAC,IAAI,EAAE,CAAC;AACT,CAAC"}
|
package/dist/save.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ExportEntry } from "./cache.js";
|
|
2
|
+
export declare function saveManual({ filePath, summary, symbols, exports: exportsArg, keywords, dependencies, footguns, delta, meta, }: {
|
|
3
|
+
filePath: string;
|
|
4
|
+
summary: string;
|
|
5
|
+
symbols: string[];
|
|
6
|
+
exports?: ExportEntry[];
|
|
7
|
+
keywords: string[];
|
|
8
|
+
dependencies?: string[];
|
|
9
|
+
footguns?: string;
|
|
10
|
+
delta?: string;
|
|
11
|
+
meta?: boolean;
|
|
12
|
+
}): Promise<string>;
|
|
13
|
+
//# sourceMappingURL=save.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.d.ts","sourceRoot":"","sources":["../src/save.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAqBpB,wBAAsB,UAAU,CAAC,EAC/B,QAAQ,EACR,OAAO,EACP,OAAO,EACP,OAAO,EAAE,UAAU,EACnB,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,KAAK,EACL,IAAY,GACb,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,mBA+BA"}
|
package/dist/save.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { fileHash, saveFileCache, getGitHead, computePublicSurfaceHash, } from "./cache.js";
|
|
4
|
+
function findWhereUsed(relPath) {
|
|
5
|
+
try {
|
|
6
|
+
const basename = path.basename(relPath, path.extname(relPath));
|
|
7
|
+
const raw = execSync(`grep -r --include="*.js" --include="*.ts" -l "${basename}" . 2>/dev/null || true`, { encoding: "utf8", cwd: process.cwd() });
|
|
8
|
+
return raw
|
|
9
|
+
.trim()
|
|
10
|
+
.split("\n")
|
|
11
|
+
.filter(Boolean)
|
|
12
|
+
.filter((f) => !f.includes("node_modules") && !f.includes(".repoctx"))
|
|
13
|
+
.filter((f) => f !== `./${relPath}` && f !== relPath)
|
|
14
|
+
.slice(0, 5);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function saveManual({ filePath, summary, symbols, exports: exportsArg, keywords, dependencies, footguns, delta, meta = false, }) {
|
|
21
|
+
const rel = meta
|
|
22
|
+
? filePath.replaceAll("\\", "/")
|
|
23
|
+
: path.relative(process.cwd(), path.resolve(filePath)).replaceAll("\\", "/");
|
|
24
|
+
const hash = meta ? "meta" : await fileHash(path.resolve(filePath));
|
|
25
|
+
const publicSurfaceHash = symbols.length > 0 ? computePublicSurfaceHash(symbols) : undefined;
|
|
26
|
+
const head = getGitHead();
|
|
27
|
+
const whereUsed = meta ? [] : findWhereUsed(rel);
|
|
28
|
+
const entry = {
|
|
29
|
+
path: rel,
|
|
30
|
+
hash,
|
|
31
|
+
summary: whereUsed.length > 0
|
|
32
|
+
? `${summary}\nUsed in: ${whereUsed.join(", ")}`
|
|
33
|
+
: summary,
|
|
34
|
+
symbols,
|
|
35
|
+
keywords,
|
|
36
|
+
updatedAt: new Date().toISOString(),
|
|
37
|
+
};
|
|
38
|
+
if (publicSurfaceHash)
|
|
39
|
+
entry.publicSurfaceHash = publicSurfaceHash;
|
|
40
|
+
if (exportsArg && exportsArg.length > 0)
|
|
41
|
+
entry.exports = exportsArg;
|
|
42
|
+
if (dependencies && dependencies.length > 0)
|
|
43
|
+
entry.dependencies = dependencies;
|
|
44
|
+
if (footguns)
|
|
45
|
+
entry.footguns = footguns;
|
|
46
|
+
if (delta)
|
|
47
|
+
entry.delta = delta;
|
|
48
|
+
if (head)
|
|
49
|
+
entry.repoHeadAtSave = head;
|
|
50
|
+
await saveFileCache(rel, entry);
|
|
51
|
+
return rel;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=save.js.map
|
package/dist/save.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.js","sourceRoot":"","sources":["../src/save.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type SymbolCard, type SymbolRelation } from "./cache.js";
|
|
2
|
+
export declare function saveSymbol({ symbol, kind, file, signature, purpose, related, keywords, }: {
|
|
3
|
+
symbol: string;
|
|
4
|
+
kind: SymbolCard["kind"];
|
|
5
|
+
file: string;
|
|
6
|
+
signature?: string;
|
|
7
|
+
purpose: string;
|
|
8
|
+
related?: SymbolRelation[];
|
|
9
|
+
keywords?: string[];
|
|
10
|
+
}): Promise<string>;
|
|
11
|
+
/** Parse "symbol:relation,symbol:relation" format from CLI */
|
|
12
|
+
export declare function parseRelated(raw: string): SymbolRelation[];
|
|
13
|
+
//# sourceMappingURL=saveSymbol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saveSymbol.d.ts","sourceRoot":"","sources":["../src/saveSymbol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAElF,wBAAsB,UAAU,CAAC,EAC/B,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,OAAO,EACP,OAAO,EACP,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB,mBAeA;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAY1D"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { saveSymbolCard } from "./cache.js";
|
|
2
|
+
export async function saveSymbol({ symbol, kind, file, signature, purpose, related, keywords, }) {
|
|
3
|
+
const card = {
|
|
4
|
+
symbol,
|
|
5
|
+
kind,
|
|
6
|
+
file,
|
|
7
|
+
purpose,
|
|
8
|
+
updatedAt: new Date().toISOString(),
|
|
9
|
+
};
|
|
10
|
+
if (signature)
|
|
11
|
+
card.signature = signature;
|
|
12
|
+
if (related && related.length > 0)
|
|
13
|
+
card.related = related;
|
|
14
|
+
if (keywords && keywords.length > 0)
|
|
15
|
+
card.keywords = keywords;
|
|
16
|
+
await saveSymbolCard(card);
|
|
17
|
+
return symbol;
|
|
18
|
+
}
|
|
19
|
+
/** Parse "symbol:relation,symbol:relation" format from CLI */
|
|
20
|
+
export function parseRelated(raw) {
|
|
21
|
+
return raw
|
|
22
|
+
.split(",")
|
|
23
|
+
.map((s) => s.trim())
|
|
24
|
+
.filter(Boolean)
|
|
25
|
+
.map((s) => {
|
|
26
|
+
const [symbol, ...rest] = s.split(":");
|
|
27
|
+
return {
|
|
28
|
+
symbol: symbol.trim(),
|
|
29
|
+
relation: rest.join(":").trim() || "related",
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=saveSymbol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saveSymbol.js","sourceRoot":"","sources":["../src/saveSymbol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAwC,MAAM,YAAY,CAAC;AAElF,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAC/B,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,OAAO,EACP,OAAO,EACP,QAAQ,GAST;IACC,MAAM,IAAI,GAAe;QACvB,MAAM;QACN,IAAI;QACJ,IAAI;QACJ,OAAO;QACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IAEF,IAAI,SAAS;QAAE,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC1C,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC1D,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAE9D,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvC,OAAO;YACL,MAAM,EAAE,MAAO,CAAC,IAAI,EAAE;YACtB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS;SAC7C,CAAC;IACJ,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "repoctx",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Structured context layer for AI coding assistants. Reduces token cost and exploration overhead by storing file summaries, symbol cards and git diffs in .repoctx/",
|
|
6
|
+
"bin": { "repoctx": "dist/index.js" },
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"files": ["dist"],
|
|
9
|
+
"engines": { "node": ">=18" },
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc -p tsconfig.json",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"claude",
|
|
18
|
+
"llm",
|
|
19
|
+
"context",
|
|
20
|
+
"tokens",
|
|
21
|
+
"developer-tools",
|
|
22
|
+
"cli",
|
|
23
|
+
"codebase",
|
|
24
|
+
"agent"
|
|
25
|
+
],
|
|
26
|
+
"author": {
|
|
27
|
+
"name": "Ezequiel",
|
|
28
|
+
"url": "https://github.com/Ezequiiel98"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/Ezequiiel98/repoctx"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/Ezequiiel98/repoctx#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/Ezequiiel98/repoctx/issues"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"commander": "^14.0.3"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^25.2.0",
|
|
44
|
+
"tsx": "^4.21.0",
|
|
45
|
+
"typescript": "^5.9.3"
|
|
46
|
+
}
|
|
47
|
+
}
|