contextgit 0.0.1 → 0.0.2
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/.claude/settings.local.json +41 -0
- package/.contextgit/config.json +10 -0
- package/.contextgit/system-prompt.md +4 -0
- package/.github/workflows/contextgit-ci.yml +40 -0
- package/CLAUDE.md +123 -0
- package/CLAUDE.md.next +65 -0
- package/docs/ContextGit_ARCHITECTURE_v3.md +1141 -0
- package/docs/ContextGit_DELTA.md +84 -0
- package/docs/ContextGit_PHASE1_PLAN.md +177 -0
- package/docs/ContextGit_PHASE2_PLAN.md +535 -0
- package/docs/ContextGit_PRD_v4.md +488 -0
- package/docs/decisions.md +370 -0
- package/package.json +23 -8
- package/packages/api/package.json +25 -0
- package/packages/api/src/bootstrap.ts +64 -0
- package/packages/api/src/config.ts +45 -0
- package/packages/api/src/index.ts +17 -0
- package/packages/api/src/middleware/auth.test.ts +83 -0
- package/packages/api/src/middleware/auth.ts +41 -0
- package/packages/api/src/remote-store.test.ts +301 -0
- package/packages/api/src/router.ts +121 -0
- package/packages/api/src/server-config.ts +34 -0
- package/packages/api/src/server.ts +38 -0
- package/packages/api/src/store-router.ts +241 -0
- package/packages/api/tsconfig.json +8 -0
- package/packages/cli/bin/run.js +4 -0
- package/packages/cli/package.json +29 -0
- package/packages/cli/src/bootstrap.ts +68 -0
- package/packages/cli/src/commands/branch.ts +58 -0
- package/packages/cli/src/commands/claim.ts +58 -0
- package/packages/cli/src/commands/commit.ts +79 -0
- package/packages/cli/src/commands/context.ts +46 -0
- package/packages/cli/src/commands/doctor.ts +99 -0
- package/packages/cli/src/commands/init.ts +141 -0
- package/packages/cli/src/commands/keygen.ts +65 -0
- package/packages/cli/src/commands/log.ts +103 -0
- package/packages/cli/src/commands/merge.ts +36 -0
- package/packages/cli/src/commands/pull.ts +145 -0
- package/packages/cli/src/commands/push.ts +158 -0
- package/packages/cli/src/commands/remote-show.ts +87 -0
- package/packages/cli/src/commands/search.ts +54 -0
- package/packages/cli/src/commands/serve.ts +61 -0
- package/packages/cli/src/commands/set-remote.ts +30 -0
- package/packages/cli/src/commands/status.ts +62 -0
- package/packages/cli/src/commands/unclaim.ts +28 -0
- package/packages/cli/src/config.ts +64 -0
- package/packages/cli/src/git-hooks.ts +61 -0
- package/packages/cli/tsconfig.json +9 -0
- package/packages/core/package.json +28 -0
- package/packages/core/src/embeddings.test.ts +58 -0
- package/packages/core/src/embeddings.ts +75 -0
- package/packages/core/src/engine.ts +274 -0
- package/packages/core/src/index.ts +6 -0
- package/packages/core/src/snapshot.ts +82 -0
- package/packages/core/src/summarizer.test.ts +120 -0
- package/packages/core/src/summarizer.ts +113 -0
- package/packages/core/src/threads.ts +29 -0
- package/packages/core/src/types.ts +240 -0
- package/packages/core/tsconfig.json +9 -0
- package/packages/mcp/package.json +31 -0
- package/packages/mcp/src/auto-snapshot.ts +83 -0
- package/packages/mcp/src/config.ts +53 -0
- package/packages/mcp/src/git-sync.ts +94 -0
- package/packages/mcp/src/index.ts +19 -0
- package/packages/mcp/src/server.ts +377 -0
- package/packages/mcp/tsconfig.json +9 -0
- package/packages/store/package.json +30 -0
- package/packages/store/src/branch-merge.test.ts +127 -0
- package/packages/store/src/engine-integration.test.ts +93 -0
- package/packages/store/src/index.ts +3 -0
- package/packages/store/src/interface.ts +62 -0
- package/packages/store/src/local/claims.test.ts +190 -0
- package/packages/store/src/local/index.ts +380 -0
- package/packages/store/src/local/local-store.test.ts +164 -0
- package/packages/store/src/local/migrations.ts +99 -0
- package/packages/store/src/local/queries.ts +760 -0
- package/packages/store/src/local/schema.ts +157 -0
- package/packages/store/src/remote/index.ts +300 -0
- package/packages/store/tsconfig.json +9 -0
- package/pnpm-workspace.yaml +2 -0
- package/scripts/build.sh +28 -0
- package/tsconfig.base.json +14 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
# ContextGit — Phase 2 Implementation Plan
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
Phase 1 is complete: core engine, LocalStore (SQLite + sqlite-vec), RemoteStore (HTTP client), MCP server (3 tools), CLI (3 commands), and REST API are built and dogfooded on the ContextGit repo itself and on an external project (Loqally). All three Phase 1 gates passed.
|
|
6
|
+
|
|
7
|
+
Phase 2 (Weeks 5–8) makes ContextGit team-ready. It fills every Phase 1 gap and ships push/pull, auth, multi-agent scoped snapshots, git hooks, and CI integration.
|
|
8
|
+
|
|
9
|
+
**What Phase 1 left incomplete:**
|
|
10
|
+
- `gitCommitSha` / `gitBranch` fields exist in the DB schema and types but are never populated — agents write them manually
|
|
11
|
+
- `context_branch` and `context_merge` MCP tools were planned but never built (engine is done)
|
|
12
|
+
- CLI is missing: `branch`, `merge`, `search`, `status`, `push`, `pull`, `keygen`, `doctor`
|
|
13
|
+
- REST API `/v1/store` routes are NOT mounted in production `createApp()` — only in integration tests — so `RemoteStore` gets 404 in any real deployment
|
|
14
|
+
- No authentication on the REST API
|
|
15
|
+
- No git hook installer
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Monorepo Changes for Phase 2
|
|
20
|
+
|
|
21
|
+
No new packages. All work happens in existing packages:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
packages/
|
|
25
|
+
├── core/src/
|
|
26
|
+
│ ├── types.ts ← add remote?, ciRunId?, pipelineName?, gitCommitSha?, gitBranch? to config/input types
|
|
27
|
+
│ └── engine.ts ← pass new EngineCommitInput fields through to store
|
|
28
|
+
├── store/src/
|
|
29
|
+
│ ├── interface.ts ← add options? param to getSessionSnapshot
|
|
30
|
+
│ ├── local/
|
|
31
|
+
│ │ ├── queries.ts ← add selectRecentCommitsByRole prepared statement
|
|
32
|
+
│ │ └── index.ts ← role filter in getSessionSnapshot
|
|
33
|
+
│ └── remote/index.ts ← add apiKey constructor param, Authorization header
|
|
34
|
+
├── mcp/src/
|
|
35
|
+
│ ├── git-sync.ts ← CREATE: captureGitMetadata(), installGitHooks()
|
|
36
|
+
│ └── server.ts ← fix double loadConfig(), add context_branch + context_merge tools
|
|
37
|
+
├── cli/src/commands/
|
|
38
|
+
│ ├── init.ts ← add --hooks, --remote, --role flags
|
|
39
|
+
│ ├── commit.ts ← add --ci-run-id, --pipeline flags + captureGitMetadata
|
|
40
|
+
│ ├── branch.ts ← CREATE
|
|
41
|
+
│ ├── merge.ts ← CREATE
|
|
42
|
+
│ ├── search.ts ← CREATE
|
|
43
|
+
│ ├── status.ts ← CREATE
|
|
44
|
+
│ ├── push.ts ← CREATE
|
|
45
|
+
│ ├── pull.ts ← CREATE
|
|
46
|
+
│ ├── keygen.ts ← CREATE
|
|
47
|
+
│ └── doctor.ts ← CREATE
|
|
48
|
+
└── api/src/
|
|
49
|
+
├── server.ts ← mount /v1/store routes; add auth middleware
|
|
50
|
+
└── middleware/
|
|
51
|
+
└── auth.ts ← CREATE: Bearer token auth
|
|
52
|
+
|
|
53
|
+
.github/workflows/
|
|
54
|
+
└── contextgit-ci.yml ← CREATE: CI integration template
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Week 5 — Git Integration + MCP Completeness
|
|
60
|
+
|
|
61
|
+
**Goal:** Every context commit is automatically enriched with git metadata. MCP covers the full engine surface.
|
|
62
|
+
|
|
63
|
+
### 5.1 — `packages/mcp/src/git-sync.ts` (new file)
|
|
64
|
+
|
|
65
|
+
Two exports:
|
|
66
|
+
|
|
67
|
+
**`captureGitMetadata(cwd: string): Promise<{ sha: string; branch: string } | null>`**
|
|
68
|
+
- Calls `simpleGit(cwd).revparse(['HEAD'])` and `simpleGit(cwd).revparse(['--abbrev-ref', 'HEAD'])`
|
|
69
|
+
- Returns `null` on any error — git capture must never block a commit
|
|
70
|
+
- Used by both MCP `context_commit` handler and CLI `commit` command
|
|
71
|
+
|
|
72
|
+
**`installGitHooks(projectRoot: string): Promise<void>`**
|
|
73
|
+
- Writes hook scripts to `.git/hooks/` for `post-commit`, `post-checkout`, `post-merge`
|
|
74
|
+
- Idempotent: check for a `# contextgit` sentinel comment before appending
|
|
75
|
+
- Hook failures are completely silent — write errors to `~/.contextgit/hooks.log`, never stderr
|
|
76
|
+
- `post-commit` script: `contextgit commit -m "git: $(git log -1 --pretty=%s)" --git-capture`
|
|
77
|
+
- `post-checkout` script: `contextgit context --quiet` (confirm branch context loaded)
|
|
78
|
+
- `post-merge` script: `contextgit commit -m "Merged into $(git rev-parse --abbrev-ref HEAD)"`
|
|
79
|
+
|
|
80
|
+
### 5.2 — SQLite concurrency mitigation in `LocalStore`
|
|
81
|
+
|
|
82
|
+
`packages/store/src/local/index.ts` — alongside the existing WAL mode pragma, add:
|
|
83
|
+
```typescript
|
|
84
|
+
db.pragma('journal_mode = WAL')
|
|
85
|
+
db.pragma('busy_timeout = 5000') // Silently retry on concurrent write contention (1-in-1000 case)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This is a single-line addition with no interface changes. It ensures that concurrent writes (e.g., a git hook firing while a CLI command is running) queue and retry for up to 5 seconds rather than immediately throwing `SQLITE_BUSY`.
|
|
89
|
+
|
|
90
|
+
### 5.3 — Native git metadata on every commit
|
|
91
|
+
|
|
92
|
+
`packages/core/src/engine.ts`:
|
|
93
|
+
- Add `gitCommitSha?: string` and `gitBranch?: string` to `EngineCommitInput`
|
|
94
|
+
- Pass through to `EngineCommitStoreInput` → `CommitInput`
|
|
95
|
+
|
|
96
|
+
`packages/mcp/src/server.ts` (in `context_commit` handler):
|
|
97
|
+
```typescript
|
|
98
|
+
const git = await captureGitMetadata(process.cwd())
|
|
99
|
+
await engine.commit({ ..., gitCommitSha: git?.sha, gitBranch: git?.branch })
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`packages/cli/src/commands/commit.ts`:
|
|
103
|
+
- Same `captureGitMetadata()` call; import from `../../mcp/src/git-sync` or duplicate the 5-line helper inline
|
|
104
|
+
|
|
105
|
+
### 5.4 — Fix MCP server tech debt
|
|
106
|
+
|
|
107
|
+
`packages/mcp/src/server.ts`:
|
|
108
|
+
- **Double `loadConfig()` bug:** Called at bootstrap (~line 69) and again for `snapshotInterval` (~line 100). Fix: extend `ServerContext` to carry `config: ContextGitConfig`. `bootstrap()` returns it; remove the second `loadConfig()` call.
|
|
109
|
+
- **Deprecated 4-arg `server.tool()`:** If no clean fix in MCP SDK `^1.0.0`, add `// TODO: MCP SDK deprecated 4-arg form; no non-deprecated alternative available as of SDK ^1.0.0` inline comment.
|
|
110
|
+
|
|
111
|
+
### 5.5 — `context_branch` and `context_merge` MCP tools
|
|
112
|
+
|
|
113
|
+
`packages/mcp/src/server.ts` — add two new `server.tool()` calls:
|
|
114
|
+
|
|
115
|
+
**`context_branch`:**
|
|
116
|
+
- Input: `{ git_branch: string, name?: string }`
|
|
117
|
+
- Calls `engine.branch(gitBranch, name)` — engine method is already implemented
|
|
118
|
+
- Returns: `{ branchId, branchName }`
|
|
119
|
+
- Call `autoSnapshot.onToolCall('context_branch')` first
|
|
120
|
+
|
|
121
|
+
**`context_merge`:**
|
|
122
|
+
- Input: `{ source_branch_id: string }`
|
|
123
|
+
- Calls `engine.merge(sourceBranchId)`
|
|
124
|
+
- Returns: `{ commitId }`
|
|
125
|
+
- Call `autoSnapshot.onToolCall('context_merge')` first
|
|
126
|
+
|
|
127
|
+
### 5.6 — `contextgit init --hooks`
|
|
128
|
+
|
|
129
|
+
`packages/cli/src/commands/init.ts`:
|
|
130
|
+
- Add `--hooks` boolean flag
|
|
131
|
+
- When set, call `installGitHooks(process.cwd())` after project creation
|
|
132
|
+
- Without the flag, print: `Tip: run "contextgit init --hooks" to auto-capture context on every git commit`
|
|
133
|
+
|
|
134
|
+
**Week 5 Validation Gate:**
|
|
135
|
+
```bash
|
|
136
|
+
# In a test git repo with contextgit init --hooks:
|
|
137
|
+
echo "test" > file.txt && git add . && git commit -m "test"
|
|
138
|
+
# Verify: contextgit context shows a new commit with gitCommitSha populated
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Week 6 — CLI Completeness + Push/Pull
|
|
144
|
+
|
|
145
|
+
**Goal:** Complete CLI surface. Enable the core team-sharing workflow.
|
|
146
|
+
|
|
147
|
+
### 6.1 — Fix production API (critical bug)
|
|
148
|
+
|
|
149
|
+
`packages/api/src/server.ts` — in `createApp()`, after store is initialized:
|
|
150
|
+
```typescript
|
|
151
|
+
app.use('/v1/store', createStoreRouter(store))
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Without this, `RemoteStore` gets 404 on every call in production. This is the single most important fix for team use.
|
|
155
|
+
|
|
156
|
+
### 6.2 — Add `remote?` to config
|
|
157
|
+
|
|
158
|
+
`packages/core/src/types.ts`:
|
|
159
|
+
```typescript
|
|
160
|
+
interface ContextGitConfig {
|
|
161
|
+
// ... existing fields ...
|
|
162
|
+
remote?: string // URL for push/pull target (separate from store backend)
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
- `config.store = 'local'` — use local SQLite for reads/writes (default)
|
|
167
|
+
- `config.remote = 'http://...'` — remote API server for push/pull operations
|
|
168
|
+
|
|
169
|
+
`packages/cli/src/commands/init.ts` — add `--remote <url>` flag, write to config if provided.
|
|
170
|
+
|
|
171
|
+
### 6.3 — `contextgit branch` and `contextgit merge`
|
|
172
|
+
|
|
173
|
+
`packages/cli/src/commands/branch.ts`:
|
|
174
|
+
```
|
|
175
|
+
contextgit branch <git-branch-name> [--name <display-name>]
|
|
176
|
+
```
|
|
177
|
+
- Bootstrap engine, call `engine.branch(gitBranch, name)`, print new branch ID
|
|
178
|
+
|
|
179
|
+
`packages/cli/src/commands/merge.ts`:
|
|
180
|
+
```
|
|
181
|
+
contextgit merge <source-branch-id>
|
|
182
|
+
```
|
|
183
|
+
- Bootstrap engine, call `engine.merge(sourceBranchId)`, print merge commit ID
|
|
184
|
+
|
|
185
|
+
### 6.4 — `contextgit search`
|
|
186
|
+
|
|
187
|
+
`packages/cli/src/commands/search.ts`:
|
|
188
|
+
```
|
|
189
|
+
contextgit search <query> [--limit 5] [--format table|json]
|
|
190
|
+
```
|
|
191
|
+
- Run `store.fullTextSearch(query, projectId)` and `engine.semanticSearch(query, projectId, limit)` in parallel
|
|
192
|
+
- Merge results (deduplicate by commit ID, rank by score)
|
|
193
|
+
- Default output: a terminal table (respect `process.stdout.columns`)
|
|
194
|
+
- `--format json` for programmatic use
|
|
195
|
+
|
|
196
|
+
### 6.5 — `contextgit status`
|
|
197
|
+
|
|
198
|
+
`packages/cli/src/commands/status.ts`:
|
|
199
|
+
```
|
|
200
|
+
contextgit status
|
|
201
|
+
```
|
|
202
|
+
Output:
|
|
203
|
+
```
|
|
204
|
+
Project: my-project (proj_abc123)
|
|
205
|
+
Branch: feature/auth (ctx_branch_xyz)
|
|
206
|
+
HEAD: "Fixed the login bug" (2 hours ago)
|
|
207
|
+
Threads: 3 open
|
|
208
|
+
Store: local (~/.contextgit/projects/proj_abc123.db)
|
|
209
|
+
Remote: http://contextgit.company.com (configured)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 6.6 — `contextgit push` and `contextgit pull`
|
|
213
|
+
|
|
214
|
+
**Architecture:** Push/pull lives entirely in the CLI layer. Both `LocalStore` and `RemoteStore` implement `ContextStore`, so:
|
|
215
|
+
- Push = `listCommits(localStore)` → diff by ID vs `listCommits(remoteStore)` → `createCommit(remoteStore)` for missing ones
|
|
216
|
+
- Pull = reverse
|
|
217
|
+
|
|
218
|
+
**Conflict resolution:** Skip if commit ID already exists on target (append-only ledger; commits are immutable once written, so duplicate IDs = already synced).
|
|
219
|
+
|
|
220
|
+
`packages/cli/src/commands/push.ts`:
|
|
221
|
+
```
|
|
222
|
+
contextgit push [--branch <id>]
|
|
223
|
+
```
|
|
224
|
+
1. Load `LocalStore(projectId)` and `RemoteStore(config.remote)` from config
|
|
225
|
+
2. List local commits on current branch (all pages via pagination)
|
|
226
|
+
3. List remote commits on same branch
|
|
227
|
+
4. `createCommit(remote, c)` for each commit not on remote
|
|
228
|
+
5. `indexEmbedding(remote, commitId, vector)` for each pushed commit
|
|
229
|
+
6. Print: `Pushed N commits to <remote-url>`
|
|
230
|
+
|
|
231
|
+
`packages/cli/src/commands/pull.ts`:
|
|
232
|
+
```
|
|
233
|
+
contextgit pull [--branch <id>]
|
|
234
|
+
```
|
|
235
|
+
- Fetch all remote commits not present locally
|
|
236
|
+
- Wrap ALL inserts in a single `better-sqlite3` transaction: `db.transaction(() => { ... })()`. All commits land atomically or none do — if the process dies mid-pull, the local store stays clean.
|
|
237
|
+
- Re-index embeddings locally after the transaction commits (fire-and-forget)
|
|
238
|
+
- Update `headCommitId` on local branch after pull
|
|
239
|
+
|
|
240
|
+
**Week 6 Validation Gate:**
|
|
241
|
+
```bash
|
|
242
|
+
# dir1: contextgit commit -m "hello from dir1"
|
|
243
|
+
# dir1: contextgit push
|
|
244
|
+
# dir2: contextgit pull
|
|
245
|
+
# dir2: contextgit context → shows dir1 commit
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Week 7 — Auth + Multi-Agent
|
|
251
|
+
|
|
252
|
+
**Goal:** Make the REST API safe for shared use. Enable agent-role-filtered snapshots.
|
|
253
|
+
|
|
254
|
+
### 7.1 — API key authentication
|
|
255
|
+
|
|
256
|
+
**New DB:** `~/.contextgit/server.db` (separate from project DBs). One table:
|
|
257
|
+
```sql
|
|
258
|
+
CREATE TABLE api_keys (
|
|
259
|
+
id TEXT PRIMARY KEY,
|
|
260
|
+
name TEXT NOT NULL,
|
|
261
|
+
key_hash TEXT NOT NULL UNIQUE, -- SHA-256(raw_key)
|
|
262
|
+
created_at INTEGER NOT NULL,
|
|
263
|
+
last_used INTEGER
|
|
264
|
+
);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`packages/api/src/middleware/auth.ts`:
|
|
268
|
+
- Read `Authorization: Bearer <token>` header
|
|
269
|
+
- Hash the token with `crypto.createHash('sha256')`
|
|
270
|
+
- Look up `key_hash` in `api_keys` table
|
|
271
|
+
- 401 if missing or no match
|
|
272
|
+
- Update `last_used` on success
|
|
273
|
+
- Auth is only enforced when `CONTEXTGIT_AUTH=1` env var is set — local-only mode stays public
|
|
274
|
+
|
|
275
|
+
`packages/api/src/server.ts`:
|
|
276
|
+
```typescript
|
|
277
|
+
if (process.env.CONTEXTGIT_AUTH) {
|
|
278
|
+
app.use(authMiddleware(serverDb))
|
|
279
|
+
}
|
|
280
|
+
app.use('/v1', router)
|
|
281
|
+
app.use('/v1/store', createStoreRouter(store))
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
`packages/store/src/remote/index.ts`:
|
|
285
|
+
```typescript
|
|
286
|
+
constructor(baseUrl: string, private apiKey?: string) { ... }
|
|
287
|
+
// In req():
|
|
288
|
+
if (this.apiKey) headers['Authorization'] = `Bearer ${this.apiKey}`
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
`packages/cli/src/commands/keygen.ts`:
|
|
292
|
+
```
|
|
293
|
+
contextgit keygen [--name <label>]
|
|
294
|
+
```
|
|
295
|
+
- Generate 32-byte random key, base64url encode
|
|
296
|
+
- Store SHA-256 hash in `~/.contextgit/server.db`
|
|
297
|
+
- Print key once (never stored in plaintext): `cgk_<base64url>`
|
|
298
|
+
- Key goes in `config.apiKey` for CLI/MCP use
|
|
299
|
+
|
|
300
|
+
**Week 7 Gate (auth):**
|
|
301
|
+
```bash
|
|
302
|
+
CONTEXTGIT_AUTH=1 node packages/api/dist/server.js &
|
|
303
|
+
curl http://localhost:3141/v1/snapshot # → 401
|
|
304
|
+
curl -H "Authorization: Bearer cgk_..." http://localhost:3141/v1/snapshot # → 200
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### 7.2 — Multi-agent scoped snapshots
|
|
308
|
+
|
|
309
|
+
`packages/store/src/interface.ts`:
|
|
310
|
+
```typescript
|
|
311
|
+
getSessionSnapshot(
|
|
312
|
+
projectId: string,
|
|
313
|
+
branchId: string,
|
|
314
|
+
options?: { agentRole?: AgentRole }
|
|
315
|
+
): Promise<SessionSnapshot>
|
|
316
|
+
```
|
|
317
|
+
(Backward-compatible — `options` is optional.)
|
|
318
|
+
|
|
319
|
+
`packages/store/src/local/queries.ts`:
|
|
320
|
+
```typescript
|
|
321
|
+
selectRecentCommitsByRole: db.prepare(`
|
|
322
|
+
SELECT * FROM commits
|
|
323
|
+
WHERE branch_id = ? AND agent_role = ?
|
|
324
|
+
ORDER BY created_at DESC LIMIT 3
|
|
325
|
+
`)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
`packages/store/src/local/index.ts`:
|
|
329
|
+
- When `options?.agentRole` is set, use `selectRecentCommitsByRole` instead of the default `selectRecentCommits`
|
|
330
|
+
- `openThreads` are never role-filtered (threads cross agent boundaries by design)
|
|
331
|
+
|
|
332
|
+
`packages/mcp/src/server.ts` — `context_get` tool gains new parameter:
|
|
333
|
+
```typescript
|
|
334
|
+
agent_role: z.enum(['orchestrator','dev','test','review','background','ci','solo']).optional()
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
`packages/cli/src/commands/init.ts` — add `--role` flag:
|
|
338
|
+
```
|
|
339
|
+
contextgit init [--role orchestrator|dev|test|review|ci|solo]
|
|
340
|
+
```
|
|
341
|
+
Writes `agentRole` to `.contextgit/config.json`.
|
|
342
|
+
|
|
343
|
+
**Week 7 Gate (multi-agent):**
|
|
344
|
+
```bash
|
|
345
|
+
# Agent 1 (--role dev): contextgit commit -m "implement login"
|
|
346
|
+
# Agent 2 (--role test): contextgit commit -m "test login"
|
|
347
|
+
# context_get agent_role=dev → recentCommits shows only dev commits
|
|
348
|
+
# context_get → recentCommits shows all commits
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Week 8 — CI Integration + Polish
|
|
354
|
+
|
|
355
|
+
**Goal:** Ship a working CI story. Add diagnostics. Integration tests. Cut the Phase 2 release.
|
|
356
|
+
|
|
357
|
+
### 8.1 — CI metadata on commits
|
|
358
|
+
|
|
359
|
+
`packages/core/src/engine.ts`:
|
|
360
|
+
- Add `ciRunId?: string` and `pipelineName?: string` to `EngineCommitInput`
|
|
361
|
+
- Pass through to `CommitInput` (fields already exist in DB schema and `CommitInput` type)
|
|
362
|
+
|
|
363
|
+
`packages/cli/src/commands/commit.ts`:
|
|
364
|
+
- Add `--ci-run-id <id>` and `--pipeline <name>` flags
|
|
365
|
+
|
|
366
|
+
### 8.2 — GitHub Actions templates
|
|
367
|
+
|
|
368
|
+
`.github/workflows/contextgit-ci.yml` — template for users to copy into their repos:
|
|
369
|
+
```yaml
|
|
370
|
+
name: ContextGit CI
|
|
371
|
+
|
|
372
|
+
on: [push, pull_request]
|
|
373
|
+
|
|
374
|
+
jobs:
|
|
375
|
+
capture-context:
|
|
376
|
+
runs-on: ubuntu-latest
|
|
377
|
+
steps:
|
|
378
|
+
- uses: actions/checkout@v4
|
|
379
|
+
- uses: actions/setup-node@v4
|
|
380
|
+
with: { node-version: '20' }
|
|
381
|
+
- run: npx contextgit commit
|
|
382
|
+
-m "CI run ${{ github.run_number }}: ${{ github.event.head_commit.message }}"
|
|
383
|
+
--role ci
|
|
384
|
+
--ci-run-id ${{ github.run_id }}
|
|
385
|
+
--pipeline ${{ github.workflow }}
|
|
386
|
+
env:
|
|
387
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
388
|
+
CONTEXTGIT_REMOTE: ${{ secrets.CONTEXTGIT_REMOTE_URL }}
|
|
389
|
+
CONTEXTGIT_API_KEY: ${{ secrets.CONTEXTGIT_API_KEY }}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### 8.3 — `contextgit doctor`
|
|
393
|
+
|
|
394
|
+
`packages/cli/src/commands/doctor.ts`:
|
|
395
|
+
```
|
|
396
|
+
contextgit doctor
|
|
397
|
+
```
|
|
398
|
+
Checks and reports:
|
|
399
|
+
- [ ] Config file present and valid JSON
|
|
400
|
+
- [ ] DB reachable (local: file exists; remote: GET /v1/snapshot returns ≤ 401)
|
|
401
|
+
- [ ] Git hooks installed (check `.git/hooks/post-commit` for sentinel comment)
|
|
402
|
+
- [ ] API key configured (if `config.remote` is set)
|
|
403
|
+
- [ ] Embedding model downloaded (check `~/.cache/huggingface` or equiv)
|
|
404
|
+
- [ ] MCP server registered in `~/.claude.json` (check for `contextgit` entry)
|
|
405
|
+
- [ ] SQLite `busy_timeout` pragma set (open DB, run `PRAGMA busy_timeout`, verify result > 0)
|
|
406
|
+
|
|
407
|
+
### 8.4 — Integration test suite
|
|
408
|
+
|
|
409
|
+
New test files:
|
|
410
|
+
|
|
411
|
+
`packages/cli/src/push-pull.test.ts`:
|
|
412
|
+
- Two `LocalStore(':memory:')` instances bridged by an in-process Express server
|
|
413
|
+
- Commit to store A → push to server → pull to store B → assert both stores have same commits
|
|
414
|
+
- Embedding round-trip: index in A, pull to B, semantic search in B returns result
|
|
415
|
+
|
|
416
|
+
`packages/api/src/auth.test.ts`:
|
|
417
|
+
- Supertest against `createApp()` with `CONTEXTGIT_AUTH=1`
|
|
418
|
+
- 401 on missing Authorization header
|
|
419
|
+
- 401 on wrong key
|
|
420
|
+
- 200 on valid key
|
|
421
|
+
- `last_used` updates on successful auth
|
|
422
|
+
|
|
423
|
+
`packages/store/src/role-filter.test.ts`:
|
|
424
|
+
- Two agents write commits with different roles
|
|
425
|
+
- `getSessionSnapshot(projectId, branchId, { agentRole: 'dev' })` returns only dev commits in `recentCommits`
|
|
426
|
+
- `openThreads` remain unfiltered regardless of role
|
|
427
|
+
|
|
428
|
+
### Phase 2 Validation Gates
|
|
429
|
+
|
|
430
|
+
| Gate | What to verify |
|
|
431
|
+
|------|----------------|
|
|
432
|
+
| **1 — Git hooks** | `git commit` in a `--hooks` repo → context commit auto-written with `gitCommitSha` populated |
|
|
433
|
+
| **2 — Push/pull** | Commit in dir1, push, pull in dir2, `contextgit context` in dir2 shows dir1's commit |
|
|
434
|
+
| **3 — Auth** | API with `CONTEXTGIT_AUTH=1`: no unauthenticated request succeeds |
|
|
435
|
+
| **4 — CI** | GH Actions workflow writes `agentRole: ci` commit; `contextgit search "CI run"` finds it |
|
|
436
|
+
| **5 — Multi-agent** | `context_get agent_role=dev` returns only dev-role `recentCommits` |
|
|
437
|
+
| **6 — Claims** | `contextgit claim "task"` → `context_get` shows activeClaims → `contextgit commit` auto-releases claim |
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Phase 2 Delta — Coordination Primitives (added 2026-03-12)
|
|
442
|
+
|
|
443
|
+
**Status: IN PROGRESS — Phase 2 is NOT complete until this ships.**
|
|
444
|
+
|
|
445
|
+
Pre-launch dogfooding revealed task collision: two agents call `context_get`, both see the same next task, both start building it. Fix: `claims` table + claim/unclaim primitives.
|
|
446
|
+
|
|
447
|
+
See `docs/ContextGit_DELTA.md` for full decision log.
|
|
448
|
+
|
|
449
|
+
### New: `claims` table (DB migration v4)
|
|
450
|
+
|
|
451
|
+
```sql
|
|
452
|
+
CREATE TABLE claims (
|
|
453
|
+
id TEXT PRIMARY KEY,
|
|
454
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
455
|
+
branch_id TEXT NOT NULL REFERENCES branches(id),
|
|
456
|
+
task TEXT NOT NULL,
|
|
457
|
+
agent_id TEXT NOT NULL,
|
|
458
|
+
role TEXT NOT NULL,
|
|
459
|
+
claimed_at INTEGER NOT NULL,
|
|
460
|
+
status TEXT NOT NULL DEFAULT 'proposed',
|
|
461
|
+
ttl INTEGER NOT NULL,
|
|
462
|
+
released_at INTEGER
|
|
463
|
+
)
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### New types in `packages/core/src/types.ts`
|
|
467
|
+
- `ClaimStatus = 'proposed' | 'active' | 'released'`
|
|
468
|
+
- `Claim` entity
|
|
469
|
+
- `ClaimInput`
|
|
470
|
+
- `SessionSnapshot.activeClaims: Claim[]`
|
|
471
|
+
|
|
472
|
+
### New store methods in `packages/store/src/interface.ts`
|
|
473
|
+
- `claimTask(projectId, branchId, input): Promise<Claim>`
|
|
474
|
+
- `unclaimTask(claimId): Promise<void>`
|
|
475
|
+
- `listActiveClaims(projectId): Promise<Claim[]>`
|
|
476
|
+
|
|
477
|
+
Auto-release on `createCommit()` — branch-scoped: only releases this agent's claims on this branch.
|
|
478
|
+
|
|
479
|
+
TTL filter is in SQL (`claimed_at + ttl > now`) — not application code.
|
|
480
|
+
|
|
481
|
+
### New MCP tools
|
|
482
|
+
- `context_claim` — input: `task`, `ttl_hours` (default 2). Returns claim ID.
|
|
483
|
+
- `context_unclaim` — input: `claim_id`. Releases immediately.
|
|
484
|
+
|
|
485
|
+
### New CLI commands
|
|
486
|
+
- `contextgit claim "<task>"` — creates proposed claim
|
|
487
|
+
- `contextgit unclaim <claimId>` — releases claim manually
|
|
488
|
+
|
|
489
|
+
### Snapshot change
|
|
490
|
+
`context_get` output gains `## Active Claims` section showing all non-released, non-TTL-expired claims.
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Architecture Notes
|
|
495
|
+
|
|
496
|
+
### Dependency graph is unchanged
|
|
497
|
+
|
|
498
|
+
No new packages. The strict dep graph from Phase 1 is preserved:
|
|
499
|
+
```
|
|
500
|
+
core → @anthropic-ai/sdk, @xenova/transformers, nanoid
|
|
501
|
+
store → core (types only), better-sqlite3, sqlite-vec
|
|
502
|
+
mcp → core, store, @modelcontextprotocol/sdk, simple-git
|
|
503
|
+
cli → core, store, @oclif/core
|
|
504
|
+
api → core, store, express
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
`captureGitMetadata()` lives in `packages/mcp/src/git-sync.ts` (MCP already has `simple-git`). The CLI duplicates a minimal 5-line version in its bootstrap. Core stays pure business logic with no I/O deps.
|
|
508
|
+
|
|
509
|
+
### Config schema evolution
|
|
510
|
+
|
|
511
|
+
All three bootstrap files (`cli/src/bootstrap.ts`, `mcp/src/server.ts`, `api/src/bootstrap.ts`) have their own copy of the config loader. This is intentional to preserve the dep graph. When `ContextGitConfig` gains `remote?` and the `apiKey` field is wired, update all three copies in parallel.
|
|
512
|
+
|
|
513
|
+
### Push/pull append-only guarantee
|
|
514
|
+
|
|
515
|
+
Commits are immutable once written. "Conflicts" at the data level don't exist: if two agents wrote commits simultaneously, both are valid and both should exist in both stores after sync. The rolling summarizer naturally resolves semantic conflicts in subsequent summaries. No CRDT or OT needed.
|
|
516
|
+
|
|
517
|
+
### Auth is opt-in
|
|
518
|
+
|
|
519
|
+
Local-only workflows (solo developer, single machine) stay zero-config. Auth is enabled only when the `CONTEXTGIT_AUTH=1` env var is set on the API server. This preserves the Phase 1 DX for solo use while adding security for team deployments.
|
|
520
|
+
|
|
521
|
+
### Remote Hosting
|
|
522
|
+
|
|
523
|
+
Phase 2 validation uses localhost for the REST API server (`packages/api`). Deploying to a shared host (Railway, fly.io, etc.) is deferred to Phase 3 when the product goes public. For now, teams run the API server themselves on a machine they control.
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## What to Defer to Phase 3
|
|
528
|
+
|
|
529
|
+
| Feature | Why deferred |
|
|
530
|
+
|---------|-------------|
|
|
531
|
+
| **PostgreSQL + pgvector** | SQLite + WAL handles ~10 concurrent writers; build Postgres only when hitting real limits. Estimated 2–3 weeks of work. |
|
|
532
|
+
| **Conflict resolution beyond last-write-wins** | Append-only ledger means no data conflicts; semantic conflicts resolved by summarizer |
|
|
533
|
+
| **OpenAI embedding alternative** | `embeddingModel: 'openai'` is in the type but wiring it is a separate feature; Xenova local model works |
|
|
534
|
+
| **Web UI dashboard** | REST API + formatted snapshot already expose the data |
|
|
535
|
+
| **Multi-project API server mode** | Single-project-per-process is fine for Phase 2; multi-project routing is Phase 3 |
|