contextgit 0.0.2 → 0.0.3
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/dist/bootstrap.d.ts +10 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +43 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/commands/branch.d.ts +13 -0
- package/dist/commands/branch.d.ts.map +1 -0
- package/dist/commands/branch.js +52 -0
- package/dist/commands/branch.js.map +1 -0
- package/dist/commands/claim.d.ts +13 -0
- package/dist/commands/claim.d.ts.map +1 -0
- package/dist/commands/claim.js +50 -0
- package/dist/commands/claim.js.map +1 -0
- package/dist/commands/commit.d.ts +14 -0
- package/dist/commands/commit.d.ts.map +1 -0
- package/dist/commands/commit.js +71 -0
- package/dist/commands/commit.js.map +1 -0
- package/dist/commands/context.d.ts +9 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +38 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +84 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +126 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/keygen.d.ts +10 -0
- package/dist/commands/keygen.d.ts.map +1 -0
- package/dist/commands/keygen.js +57 -0
- package/dist/commands/keygen.js.map +1 -0
- package/dist/commands/log.d.ts +13 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +91 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/merge.d.ts +12 -0
- package/dist/commands/merge.d.ts.map +1 -0
- package/dist/commands/merge.js +29 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/pull.d.ts +10 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +123 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +10 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +141 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/remote-show.d.ts +6 -0
- package/dist/commands/remote-show.d.ts.map +1 -0
- package/dist/commands/remote-show.js +71 -0
- package/dist/commands/remote-show.js.map +1 -0
- package/dist/commands/search.d.ts +11 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +47 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/serve.d.ts +9 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +51 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/set-remote.d.ts +9 -0
- package/dist/commands/set-remote.d.ts.map +1 -0
- package/dist/commands/set-remote.js +26 -0
- package/dist/commands/set-remote.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +54 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/unclaim.d.ts +9 -0
- package/dist/commands/unclaim.d.ts.map +1 -0
- package/dist/commands/unclaim.js +22 -0
- package/dist/commands/unclaim.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/git-hooks.d.ts +6 -0
- package/dist/git-hooks.d.ts.map +1 -0
- package/dist/git-hooks.js +58 -0
- package/dist/git-hooks.js.map +1 -0
- package/package.json +22 -19
- package/.claude/settings.local.json +0 -41
- package/.contextgit/config.json +0 -10
- package/.contextgit/system-prompt.md +0 -4
- package/.github/workflows/contextgit-ci.yml +0 -40
- package/CLAUDE.md +0 -123
- package/CLAUDE.md.next +0 -65
- package/docs/ContextGit_ARCHITECTURE_v3.md +0 -1141
- package/docs/ContextGit_DELTA.md +0 -84
- package/docs/ContextGit_PHASE1_PLAN.md +0 -177
- package/docs/ContextGit_PHASE2_PLAN.md +0 -535
- package/docs/ContextGit_PRD_v4.md +0 -488
- package/docs/decisions.md +0 -370
- package/packages/api/package.json +0 -25
- package/packages/api/src/bootstrap.ts +0 -64
- package/packages/api/src/config.ts +0 -45
- package/packages/api/src/index.ts +0 -17
- package/packages/api/src/middleware/auth.test.ts +0 -83
- package/packages/api/src/middleware/auth.ts +0 -41
- package/packages/api/src/remote-store.test.ts +0 -301
- package/packages/api/src/router.ts +0 -121
- package/packages/api/src/server-config.ts +0 -34
- package/packages/api/src/server.ts +0 -38
- package/packages/api/src/store-router.ts +0 -241
- package/packages/api/tsconfig.json +0 -8
- package/packages/cli/package.json +0 -29
- package/packages/cli/src/bootstrap.ts +0 -68
- package/packages/cli/src/commands/branch.ts +0 -58
- package/packages/cli/src/commands/claim.ts +0 -58
- package/packages/cli/src/commands/commit.ts +0 -79
- package/packages/cli/src/commands/context.ts +0 -46
- package/packages/cli/src/commands/doctor.ts +0 -99
- package/packages/cli/src/commands/init.ts +0 -141
- package/packages/cli/src/commands/keygen.ts +0 -65
- package/packages/cli/src/commands/log.ts +0 -103
- package/packages/cli/src/commands/merge.ts +0 -36
- package/packages/cli/src/commands/pull.ts +0 -145
- package/packages/cli/src/commands/push.ts +0 -158
- package/packages/cli/src/commands/remote-show.ts +0 -87
- package/packages/cli/src/commands/search.ts +0 -54
- package/packages/cli/src/commands/serve.ts +0 -61
- package/packages/cli/src/commands/set-remote.ts +0 -30
- package/packages/cli/src/commands/status.ts +0 -62
- package/packages/cli/src/commands/unclaim.ts +0 -28
- package/packages/cli/src/config.ts +0 -64
- package/packages/cli/src/git-hooks.ts +0 -61
- package/packages/cli/tsconfig.json +0 -9
- package/packages/core/package.json +0 -28
- package/packages/core/src/embeddings.test.ts +0 -58
- package/packages/core/src/embeddings.ts +0 -75
- package/packages/core/src/engine.ts +0 -274
- package/packages/core/src/index.ts +0 -6
- package/packages/core/src/snapshot.ts +0 -82
- package/packages/core/src/summarizer.test.ts +0 -120
- package/packages/core/src/summarizer.ts +0 -113
- package/packages/core/src/threads.ts +0 -29
- package/packages/core/src/types.ts +0 -240
- package/packages/core/tsconfig.json +0 -9
- package/packages/mcp/package.json +0 -31
- package/packages/mcp/src/auto-snapshot.ts +0 -83
- package/packages/mcp/src/config.ts +0 -53
- package/packages/mcp/src/git-sync.ts +0 -94
- package/packages/mcp/src/index.ts +0 -19
- package/packages/mcp/src/server.ts +0 -377
- package/packages/mcp/tsconfig.json +0 -9
- package/packages/store/package.json +0 -30
- package/packages/store/src/branch-merge.test.ts +0 -127
- package/packages/store/src/engine-integration.test.ts +0 -93
- package/packages/store/src/index.ts +0 -3
- package/packages/store/src/interface.ts +0 -62
- package/packages/store/src/local/claims.test.ts +0 -190
- package/packages/store/src/local/index.ts +0 -380
- package/packages/store/src/local/local-store.test.ts +0 -164
- package/packages/store/src/local/migrations.ts +0 -99
- package/packages/store/src/local/queries.ts +0 -760
- package/packages/store/src/local/schema.ts +0 -157
- package/packages/store/src/remote/index.ts +0 -300
- package/packages/store/tsconfig.json +0 -9
- package/pnpm-workspace.yaml +0 -2
- package/scripts/build.sh +0 -28
- package/tsconfig.base.json +0 -14
- package/vitest.config.ts +0 -15
- /package/{packages/cli/bin → bin}/run.js +0 -0
package/docs/decisions.md
DELETED
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
# ContextGit — Session Decisions Log
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
## Session: Day 1–4 Foundation (2026-03-10)
|
|
6
|
-
|
|
7
|
-
**Built:**
|
|
8
|
-
- `packages/core/package.json` + `tsconfig.json` — `@contextgit/core` package wired into monorepo
|
|
9
|
-
- `packages/store/package.json` + `tsconfig.json` — `@contextgit/store` with `better-sqlite3`, `sqlite-vec`, `nanoid`
|
|
10
|
-
- `packages/store/src/interface.ts` — full `ContextStore` interface (the contract all storage backends must satisfy)
|
|
11
|
-
- `packages/store/src/local/schema.ts` — DDL for 5 tables (`projects`, `branches`, `commits`, `threads`, `agents`), `vec0` virtual table, FTS5 index, and 4 covering indexes
|
|
12
|
-
- `packages/store/src/local/migrations.ts` — versioned migration runner with `_migrations` tracking table; v1=core schema, v2=FTS5+vec0 (vec0 creation wrapped in try/catch)
|
|
13
|
-
- `packages/store/src/local/queries.ts` — `Queries` class with all prepared statements, row→domain type converters, full CRUD + snapshot + search helpers
|
|
14
|
-
- `packages/store/src/local/index.ts` — `LocalStore` implementing `ContextStore`; sync `better-sqlite3` calls wrapped in `Promise.resolve()` at the interface boundary
|
|
15
|
-
- `packages/core/src/index.ts`, `packages/store/src/index.ts` — barrel exports
|
|
16
|
-
- `vitest.config.ts` — root-level Vitest covering all `packages/*/src/**/*.test.ts`
|
|
17
|
-
- `packages/store/src/local/local-store.test.ts` — 6 smoke tests (project, branch, commit, threads open/close, snapshot, merge with thread carry-forward)
|
|
18
|
-
- Root `package.json` — added `pnpm.onlyBuiltDependencies` for `better-sqlite3`/`sqlite-vec`/`esbuild`; fixed `skipLibChecks` typo → `skipLibCheck`
|
|
19
|
-
|
|
20
|
-
**Decided:**
|
|
21
|
-
- **TEXT primary keys via `nanoid()`** — never auto-increment integers. Keeps IDs portable and opaque.
|
|
22
|
-
- **Sync SQLite wrapped in `Promise.resolve()` at the interface boundary** — `ContextStore` interface returns Promises so Phase 2 `RemoteStore` (async Postgres) can slot in without changing callers. `LocalStore` internals stay sync (better-sqlite3's strength).
|
|
23
|
-
- **sqlite-vec loaded via `createRequire` shim** — package is ESM, `sqlite-vec` is CJS. `createRequire(import.meta.url)` gives a sync CJS `require()` suitable for use in the constructor. Load failure is silently caught — semantic search disabled gracefully.
|
|
24
|
-
- **Migration v2 wraps vec0 creation in try/catch** — sqlite-vec is optional at runtime; FTS5 and KNN queries both return empty arrays if the extension isn't loaded. Semantic search wired in Week 4 only.
|
|
25
|
-
- **Inline snapshot formatter in `LocalStore`** — minimal `text`/`agents-md`/`json` formatting lives in `store/local/index.ts` for now. Full `SnapshotFormatter` moves to `core/src/snapshot.ts` in Days 5–7 without changing the `ContextStore` interface.
|
|
26
|
-
- **`mergeBranch` attributes merge commits to `system`/`orchestrator`** — the interface signature `(sourceBranchId, targetBranchId, summary)` matches the architecture doc exactly; attribution is a placeholder until `ContextEngine.merge()` is built.
|
|
27
|
-
- **DB path: `~/.contextgit/projects/<projectId>.db`** — pass `':memory:'` for tests.
|
|
28
|
-
- **ABI compatibility confirmed:** `better-sqlite3` compiled from source for Node 22.22.0 / arm64 (no prebuilt available). `sqlite-vec` v0.1.6 loads and `vec0` virtual tables create cleanly against SQLite 3.45.3.
|
|
29
|
-
|
|
30
|
-
**Unresolved:**
|
|
31
|
-
- `pnpm approve-builds` is interactive in pnpm 10 — worked around via `pnpm.onlyBuiltDependencies` in root `package.json` + `pnpm install --force` to trigger the native build. Subsequent installs should be clean, but CI will need the same config.
|
|
32
|
-
- `getFormattedSnapshot` in `LocalStore` does inline formatting. Once `core/src/snapshot.ts` is written (Days 5–7), decide whether to call the formatter from the store or move `getFormattedSnapshot` up to `ContextEngine`.
|
|
33
|
-
- `semanticSearch` on `LocalStore` currently returns `[]` unconditionally — the interface method exists but the embedding vector must be passed in from `core/src/embeddings.ts` (Week 4). The `Queries.semanticSearch()` helper is wired and ready.
|
|
34
|
-
- No project-references in `tsconfig.json` — store must typecheck after core is built. Consider adding `composite: true` + `references` to avoid the build-first requirement.
|
|
35
|
-
|
|
36
|
-
**Next:**
|
|
37
|
-
- Start Days 5–7: `packages/core/src/engine.ts` — `ContextEngine.commit()` with placeholder string-truncation summarizer, `ContextEngine.context('global')` returning a `SessionSnapshot`. Wire `LocalStore` into the engine via constructor injection. Validate: create project → 2 commits (one with open thread) → `context('global')` → snapshot shows thread + both commits.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Session: Days 5–7 — ContextEngine (2026-03-10) #2
|
|
42
|
-
|
|
43
|
-
**Built:**
|
|
44
|
-
- `packages/core/src/summarizer.ts` — `RollingSummarizer` class; Week 1 placeholder: appends new content to previous summary, truncates from the start to keep the most-recent work. Interface is stable for Week 2 Claude Haiku swap.
|
|
45
|
-
- `packages/core/src/snapshot.ts` — `SnapshotFormatter` class; all 3 formats (`text`, `agents-md`, `json`). Moved out of the inline function in `LocalStore`.
|
|
46
|
-
- `packages/core/src/threads.ts` — `ThreadManager` class; read-side helper enforcing the open-thread immune-to-compression boundary. Write-side (open/close) stays in `store.createCommit()` for transactional correctness.
|
|
47
|
-
- `packages/core/src/engine.ts` — `ContextEngine`: `init(projectId, branchId)`, `commit(input)`, `context(scope)`. Uses `EngineStore` structural interface so core never imports from store (no circular deps).
|
|
48
|
-
- `packages/core/src/index.ts` — updated barrel exports for all 4 new modules.
|
|
49
|
-
- `packages/store/src/local/index.ts` — removed inline `formatSnapshot`; now imports and uses `SnapshotFormatter` from `@contextgit/core`.
|
|
50
|
-
- `packages/store/src/engine-integration.test.ts` — 4 integration tests covering the Week 1 validation scenario, rolling summary accumulation, `context('branch')` parity, and uninitialized guard.
|
|
51
|
-
|
|
52
|
-
**Decided:**
|
|
53
|
-
- **`ContextEngine` accepts `EngineStore` structural interface** — core defines only the subset of methods it needs (`getBranch`, `getCommit`, `createCommit`, `getSessionSnapshot`, `upsertAgent`). `LocalStore` satisfies it without a direct import, preserving the strict dependency graph (`core` never imports `store`).
|
|
54
|
-
- **`SnapshotFormatter` moved to core, `LocalStore` imports it** — store already depends on core for types; this is the correct layering. `getFormattedSnapshot` stays on `ContextStore` interface (callers don't change).
|
|
55
|
-
- **`ThreadManager` is read-only** — write-side (open/close) is a transactional concern that belongs inside `store.createCommit()`. `ThreadManager` is purely a query helper.
|
|
56
|
-
- **`context('branch')` delegates to same `getSessionSnapshot` as `'global'`** — branch-scoped view is distinct only in Week 2+ when branch summaries are computed separately. For now both return identical data.
|
|
57
|
-
|
|
58
|
-
**Unresolved:**
|
|
59
|
-
- `semanticSearch` still returns `[]` — needs `EmbeddingService` from `core/src/embeddings.ts` (Week 4).
|
|
60
|
-
- `RollingSummarizer.summarize()` uses string truncation — Week 2 replaces with Claude Haiku + graceful fallback; no caller changes required.
|
|
61
|
-
- `context('search' | 'commit' | 'raw')` scopes throw "not implemented" — Week 3/4 work.
|
|
62
|
-
- No project-references in tsconfig — build-order dependency remains manual (`pnpm build` must run core before store).
|
|
63
|
-
|
|
64
|
-
**Next:**
|
|
65
|
-
- Start Days 8–9 (Week 2): `packages/core/src/summarizer.ts` — replace string truncation with `claude-haiku-4-5-20251001` via `@anthropic-ai/sdk`. Graceful fallback: if API call fails, revert to string truncation. Never let summarizer failure propagate to the caller. Add `ANTHROPIC_API_KEY` handling. Test: mock API failure → fallback summary still returned.
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## Session: Days 8–9 — Claude Haiku Summarizer (2026-03-10) #3
|
|
70
|
-
|
|
71
|
-
**Built:**
|
|
72
|
-
- `packages/core/src/summarizer.ts` — `RollingSummarizer.summarize()` is now async; uses `claude-haiku-4-5` via `@anthropic-ai/sdk` when `ANTHROPIC_API_KEY` is present. Graceful fallback to string truncation on any error (API failure, no key, network issue). Response is sliced to `maxChars` budget after the Claude call.
|
|
73
|
-
- `packages/core/src/summarizer.test.ts` — 7 tests: Claude success path, previous summary passed through, API failure → fallback, never throws, no-key → fallback, truncation tail-keeps-newest, Claude response truncated to budget.
|
|
74
|
-
- `packages/core/package.json` — added `@anthropic-ai/sdk: ^0.40.1` to `dependencies`.
|
|
75
|
-
- `packages/core/src/engine.ts` — `summarizer.summarize()` call updated to `await`.
|
|
76
|
-
- `packages/store/src/local/queries.ts` — `selectCommits` and `selectLastCommit` sort keys extended with `rowid DESC` tiebreaker to fix non-deterministic ordering when two commits share the same millisecond timestamp.
|
|
77
|
-
|
|
78
|
-
**Decided:**
|
|
79
|
-
- **Inject `Anthropic` client via `SummarizerOptions.client`** — avoids environment coupling in tests; production path creates the client from `ANTHROPIC_API_KEY` automatically.
|
|
80
|
-
- **`model: 'claude-haiku-4-5'`** — cheapest/fastest model, appropriate for compression; no thinking or streaming needed.
|
|
81
|
-
- **`max_tokens: 1024`** — sufficient for summaries; both budget sizes (2000 / 8000 chars) fit within this token ceiling.
|
|
82
|
-
- **`rowid DESC` tiebreaker on commit sort** — `Date.now()` collides at millisecond resolution in fast in-memory tests. `rowid` is always monotonically increasing so insertion order is preserved as a stable secondary sort.
|
|
83
|
-
- **No changes to `ContextStore` interface** — `getFormattedSnapshot` and `createCommit` signatures unchanged; callers are unaffected by the sync→async transition inside `ContextEngine`.
|
|
84
|
-
|
|
85
|
-
**Unresolved:**
|
|
86
|
-
- `semanticSearch` still returns `[]` — needs `EmbeddingService` from `core/src/embeddings.ts` (Week 4).
|
|
87
|
-
- `context('search' | 'commit' | 'raw')` scopes throw "not implemented" — Week 3/4 work.
|
|
88
|
-
- No project-references in tsconfig — build-order dependency remains manual.
|
|
89
|
-
|
|
90
|
-
**Next:**
|
|
91
|
-
- Days 10–11 (Week 2 continued): wire the MCP server skeleton — `packages/mcp/src/server.ts` implementing the three core tools (`context`, `commit`, `search`) via `@modelcontextprotocol/sdk`. Validate with `mcp dev` inspector: tool list returned, `commit` persists a record, `context` returns a snapshot.
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## Session: Days 10–11 — MCP Server Skeleton (2026-03-10) #4
|
|
96
|
-
|
|
97
|
-
**Built:**
|
|
98
|
-
- `packages/mcp/package.json` — `@contextgit/mcp` workspace package; deps: `@modelcontextprotocol/sdk ^1.0.0`, `simple-git ^3.27.0`, `zod ^3.23.0`, `@contextgit/core`, `@contextgit/store`.
|
|
99
|
-
- `packages/mcp/tsconfig.json` — extends `tsconfig.base.json`, same pattern as core/store.
|
|
100
|
-
- `packages/mcp/src/config.ts` — `loadConfig()` searches CWD upward for `.contextgit/config.json`, validates required `projectId`/`project` fields. Exports `ConfigNotFoundError`.
|
|
101
|
-
- `packages/mcp/src/server.ts` — `createServer()` bootstraps `LocalStore` + `ContextEngine` then registers 3 tools on `McpServer`:
|
|
102
|
-
- `context_get` — calls `store.getFormattedSnapshot(projectId, branchId, format)`. Params: `scope` (global|branch, default global), `format` (agents-md|json|text, default agents-md).
|
|
103
|
-
- `context_commit` — calls `engine.commit({ message, content, threads })`. Params: `message`, `content`, optional `open_threads[]`, `close_thread_ids[]`.
|
|
104
|
-
- `context_search` — calls `store.fullTextSearch(query, projectId)`, slices to `limit`. Params: `query`, `limit` (1–20, default 5).
|
|
105
|
-
- `packages/mcp/src/index.ts` — entry point; connects `McpServer` to `StdioServerTransport`, exits on fatal error.
|
|
106
|
-
- All 17 existing tests still pass. `pnpm build` and `pnpm typecheck` clean.
|
|
107
|
-
|
|
108
|
-
**Decided:**
|
|
109
|
-
- **`McpServer` from `@modelcontextprotocol/sdk/server/mcp.js`** — high-level API with Zod schemas. The 4-arg `(name, description, schema, cb)` overload is deprecated (hint only, not a build error); the non-deprecated 3-arg `(name, schema, annotations, cb)` form has no `description` field in annotations. Kept the deprecated form because it's the only way to pass a description string.
|
|
110
|
-
- **Bootstrap at startup, not per-request** — `LocalStore`, `ContextEngine`, `projectId`, `branchId` all resolved once at process start. Git branch detected via `simple-git`. Context branch auto-created if not found in DB.
|
|
111
|
-
- **Agent ID: `${hostname}-mcp-claude-code-interactive`** — deterministic, no auth. Matches Phase 1 agent identity strategy.
|
|
112
|
-
- **`context_get` ignores `scope` for now** — always calls `getFormattedSnapshot` on the current branch; scope differentiation deferred to Week 3 when `engine.branch()` and proper multi-branch context are built.
|
|
113
|
-
- **`context_search` uses `fullTextSearch` only** — semantic search still returns `[]`; FTS5 is ready and functional.
|
|
114
|
-
|
|
115
|
-
**Unresolved:**
|
|
116
|
-
- MCP server not yet validated with `mcp dev` inspector — requires a live `.contextgit/config.json` + project in DB. Validation deferred to when `contextgit init` CLI command is built (Days 18–19).
|
|
117
|
-
- `server.tool()` 4-arg form is deprecated in `@modelcontextprotocol/sdk ^1.0.0` but no clean alternative supports a description string. May need SDK upgrade or monkey-patch workaround in Week 3.
|
|
118
|
-
- `context_get` `scope` param is accepted but unused — full scope routing (branch-only vs global) is Week 3 work.
|
|
119
|
-
- `semanticSearch` still returns `[]` — needs `EmbeddingService` (Week 4).
|
|
120
|
-
|
|
121
|
-
**Next:**
|
|
122
|
-
- Days 12–13 (Week 2): `engine.branch()` — `store.createBranch()` + `branch-init` commit carrying parent HEAD summary forward. `engine.merge()` — merge commit, carry forward open threads from source branch, mark source branch `status: 'merged'`. Integration test: branch → 2 commits → merge → snapshot shows merged state and all threads.
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Session: Days 12–13 — engine.branch() + engine.merge() (2026-03-10) #5
|
|
127
|
-
|
|
128
|
-
**Built:**
|
|
129
|
-
- `packages/core/src/engine.ts` — two new `ContextEngine` methods:
|
|
130
|
-
- `branch(gitBranch, name?)` — calls `store.createBranch()` with `parentBranchId = this.branchId`, then writes a `branch-init` commit on the new branch carrying the parent HEAD summary forward (no rolling summarization on init — summary passed through verbatim).
|
|
131
|
-
- `merge(sourceBranchId)` — fetches source + target HEAD summaries, calls `summarizer.summarize(mergeContent, targetSummary, 'branch')` to produce the merge summary, then delegates to `store.mergeBranch()`. Thread carry-forward and branch status update handled transactionally inside `LocalStore.mergeBranch()`.
|
|
132
|
-
- `EngineStore` interface extended with `createBranch(EngineBranchInput)` and `mergeBranch(sourceBranchId, targetBranchId, summary)`. `getBranch` return type widened to include `gitBranch?` and `name?` (needed for merge message). `LocalStore` satisfies structurally — no changes to store code required.
|
|
133
|
-
- `packages/store/src/branch-merge.test.ts` — 3 integration tests:
|
|
134
|
-
1. Full flow: main commit + thread → branch → 2 feature commits + thread → merge → snapshot shows merge commit + both open threads carried to main.
|
|
135
|
-
2. `branch-init` commit carries exact parent HEAD summary into new branch.
|
|
136
|
-
3. Merge commit content includes source branch content; summary is non-empty.
|
|
137
|
-
- All 20 tests pass; `pnpm build` and `pnpm typecheck` clean.
|
|
138
|
-
|
|
139
|
-
**Decided:**
|
|
140
|
-
- **`branch-init` summary passed through verbatim** — no summarization for init commits. The parent summary is already compressed; re-summarizing an empty "new" content with the parent as base would just return the parent unchanged anyway. Verbatim is cleaner and avoids an async call.
|
|
141
|
-
- **`EngineStore` structural widening is non-breaking** — adding optional fields (`gitBranch?`, `name?`) to the `getBranch` return type is structurally safe; `LocalStore.getBranch()` returns `Branch` which has both fields.
|
|
142
|
-
- **Thread carry-forward stays in `LocalStore.mergeBranch()`** — it's a transactional concern; engine should not orchestrate individual thread reassignments.
|
|
143
|
-
- **`engine.merge(sourceBranchId)` not `engine.merge(sourceBranchId, targetBranchId)`** — the engine is already bound to a branch via `init()`; the target is always `this.branchId`. Explicit target would be redundant and error-prone.
|
|
144
|
-
|
|
145
|
-
**Unresolved:**
|
|
146
|
-
- `semanticSearch` still returns `[]` — needs `EmbeddingService` (Week 4).
|
|
147
|
-
- `context('search' | 'commit' | 'raw')` scopes still throw "not implemented".
|
|
148
|
-
- No project-references in tsconfig — build order still manual.
|
|
149
|
-
- `server.tool()` 4-arg form in MCP SDK still deprecated (carried from Day 10).
|
|
150
|
-
|
|
151
|
-
**Next:**
|
|
152
|
-
- Days 14–15 (Week 3): `packages/cli/src/` — oclif CLI skeleton. Commands: `init` (creates `.contextgit/config.json` + project in DB), `commit` (engine.commit from CLI args), `context` (print formatted snapshot). Validate: `contextgit init`, `contextgit commit -m "msg"`, `contextgit context` all run end-to-end.
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## Session: Days 14–15 — oclif CLI Skeleton (2026-03-10) #6
|
|
157
|
-
|
|
158
|
-
**Built:**
|
|
159
|
-
- `packages/cli/package.json` — `@contextgit/cli` workspace package; deps: `@oclif/core ^3.27.0`, `nanoid ^5.0.0`, `simple-git ^3.27.0`, `@contextgit/core`, `@contextgit/store`.
|
|
160
|
-
- `packages/cli/tsconfig.json` — extends `tsconfig.base.json`, same pattern as other packages.
|
|
161
|
-
- `packages/cli/bin/run.js` — oclif ESM entry point; calls `run(argv, import.meta.url)` + `flush()`.
|
|
162
|
-
- `packages/cli/src/config.ts` — `loadConfig()` / `findConfigPath()` (same logic as `packages/mcp/src/config.ts`; duplicated to keep `cli → core, store` dep graph clean).
|
|
163
|
-
- `packages/cli/src/bootstrap.ts` — shared setup for commit/context commands: loads config, opens `LocalStore`, detects git branch via `simple-git`, creates branch if missing, inits `ContextEngine`. Returns `{ engine, store, projectId, branchId }`.
|
|
164
|
-
- `packages/cli/src/commands/init.ts` — `contextgit init [--name <name>]`: generates a `nanoid()` projectId, opens `LocalStore(projectId)`, calls `store.createProject({ id: projectId, name })` (same ID for DB path and project entity), creates initial branch, writes `.contextgit/config.json`. Guards against double-init.
|
|
165
|
-
- `packages/cli/src/commands/commit.ts` — `contextgit commit -m <msg> [-c <content>] [-t <thread>...] [--close <id>...]`: bootstraps engine, calls `engine.commit()`.
|
|
166
|
-
- `packages/cli/src/commands/context.ts` — `contextgit context [-f agents-md|json|text]`: loads config, opens store, detects branch, prints `store.getFormattedSnapshot()`.
|
|
167
|
-
- `packages/core/src/types.ts` — added `id?: string` to `ProjectInput` (backward-compatible).
|
|
168
|
-
- `packages/store/src/local/index.ts` — `createProject` now uses `input.id ?? nanoid()` so callers can supply a specific ID.
|
|
169
|
-
- All 20 existing tests still pass; `pnpm build` and `pnpm typecheck` clean.
|
|
170
|
-
- E2E validated: `contextgit init` → `contextgit commit -m "First commit"` → `contextgit context` all run end-to-end in a fresh tmp directory.
|
|
171
|
-
|
|
172
|
-
**Decided:**
|
|
173
|
-
- **`config.ts` duplicated in CLI** — MCP and CLI both need config loading, but `cli → mcp` is not in the allowed dep graph. The file is 50 lines; duplication is cheaper than a shared package or a cross-boundary import.
|
|
174
|
-
- **`ProjectInput.id?` added to core types** — necessary for `init` to ensure the DB path key matches the project entity ID. `LocalStore.createProject` falls back to `nanoid()` if omitted, so all existing callers (tests, MCP) are unaffected.
|
|
175
|
-
- **`bootstrap()` shared helper** — `commit` and `context` both need the same setup; extracted to avoid duplication. `init` does not use bootstrap (no config yet at that point).
|
|
176
|
-
- **`context` command does not go through engine** — reads directly from `store.getFormattedSnapshot()` since no engine-level logic is needed for a read. Matches `context_get` MCP tool pattern.
|
|
177
|
-
- **`commit --content` defaults to `--message`** — simple CLI ergonomics; power users can add richer content via `-c`.
|
|
178
|
-
|
|
179
|
-
**Unresolved:**
|
|
180
|
-
- `semanticSearch` still returns `[]` — needs `EmbeddingService` (Week 4).
|
|
181
|
-
- `context('search' | 'commit' | 'raw')` scopes still throw "not implemented".
|
|
182
|
-
- `server.tool()` 4-arg form in MCP SDK still deprecated (carried from Day 10).
|
|
183
|
-
- CLI not yet installable globally (`npm link` / `pnpm link`) — needs bin shebang permissions (`chmod +x bin/run.js`) which aren't set by tsc.
|
|
184
|
-
|
|
185
|
-
**Next:**
|
|
186
|
-
- Days 16–17 (Week 3): `packages/api/src/` — Express REST API skeleton. Routes: `POST /commits` (engine.commit), `GET /snapshot` (formatted snapshot), `GET /search?q=` (FTS). Validate with curl end-to-end.
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
## Session: Days 16–17 — Express REST API Skeleton (2026-03-10) #7
|
|
191
|
-
|
|
192
|
-
**Built:**
|
|
193
|
-
- `packages/api/package.json` — `@contextgit/api` workspace package; deps: `express ^4.19.0`, `simple-git ^3.27.0`, `@contextgit/core`, `@contextgit/store`.
|
|
194
|
-
- `packages/api/tsconfig.json` — extends `tsconfig.base.json`, same pattern as other packages.
|
|
195
|
-
- `packages/api/src/config.ts` — `loadConfig()` / `findConfigPath()` (same logic as `mcp` and `cli`; duplicated to preserve dep graph: `api → core, store` only).
|
|
196
|
-
- `packages/api/src/bootstrap.ts` — opens `LocalStore`, detects git branch via `simple-git`, resolves/creates context branch, inits `ContextEngine`. Agent ID: `${hostname}-api-server`.
|
|
197
|
-
- `packages/api/src/router.ts` — Express `Router` with 3 routes:
|
|
198
|
-
- `POST /commits` — validates `message`/`content`, calls `engine.commit()`, returns `{ id, message, createdAt }` with 201.
|
|
199
|
-
- `GET /snapshot` — `?format=agents-md|json|text` (default: agents-md). Returns `text/plain` for text/agents-md, `application/json` for json.
|
|
200
|
-
- `GET /search` — `?q=<query>&limit=<n>` (default 5, max 20). Returns `{ query, total, results[] }`.
|
|
201
|
-
- `packages/api/src/server.ts` — `createApp()`: bootstraps context, mounts router, adds 404 handler. Separated from `index.ts` so tests can import without binding to a port.
|
|
202
|
-
- `packages/api/src/index.ts` — entry point; listens on `PORT` env var (default 3141).
|
|
203
|
-
- All 20 existing tests still pass; `pnpm build` and `pnpm typecheck` clean.
|
|
204
|
-
- E2E validated with curl: `POST /commits` → 201 + ID, `GET /snapshot?format=text` → formatted snapshot, `GET /snapshot?format=json` → full JSON, `GET /search?q=API` → results array.
|
|
205
|
-
|
|
206
|
-
**Decided:**
|
|
207
|
-
- **`createApp()` separated from `index.ts`** — allows future supertest integration tests to import the app without starting a live server. Same pattern used by most production Express apps.
|
|
208
|
-
- **`config.ts` again duplicated in api package** — `api → mcp` and `api → cli` are both outside the allowed dep graph. 50-line file; duplication is the right trade-off.
|
|
209
|
-
- **Flat routes (`/commits`, `/snapshot`, `/search`)** — project and branch resolved at startup from config, not from URL params. Matches MCP + CLI single-project-per-process model. URL-scoped routes (`/v1/projects/:id/...`) deferred to Week 4 when multi-project API is planned.
|
|
210
|
-
- **`PORT` env var default: 3141** — avoids common conflicts (3000, 8080, 8000); easy to override.
|
|
211
|
-
- **`GET /search` returns 0 results immediately after a fresh `POST /commits`** — SQLite FTS5 index (`commits_fts`) is updated synchronously inside `createCommit`, but the query ran before the server saw a commit. Confirmed not a bug; repeated queries return results correctly.
|
|
212
|
-
|
|
213
|
-
**Unresolved:**
|
|
214
|
-
- `semanticSearch` still returns `[]` — needs `EmbeddingService` (Week 4).
|
|
215
|
-
- Supertest integration tests for the API — Week 4 work alongside the full route set.
|
|
216
|
-
- `GET /search` returns 0 results for a brand-new DB with only one commit — investigation needed to confirm FTS5 trigger fires on first insert (may need a test).
|
|
217
|
-
- `server.tool()` 4-arg form in MCP SDK still deprecated (carried from Day 10).
|
|
218
|
-
- CLI `init` guard bails when config.json already exists but DB is empty — no self-healing path. Edge case when config is committed to repo but DB is machine-local.
|
|
219
|
-
|
|
220
|
-
**Next:**
|
|
221
|
-
- Days 18–19 (Week 3): `contextgit init` system-prompt fragment — print to stdout and write `.contextgit/system-prompt.md` with the agent instruction fragment. Then: `AutoSnapshotManager` skeleton in `packages/mcp/src/auto-snapshot.ts` — counter that fires `engine.commit()` every N=10 tool calls. Begin dogfooding: run `contextgit init` on this repo (fix the empty-DB edge case first), configure Claude Code MCP.
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## Session: Days 18–19 — System-Prompt Fragment + AutoSnapshotManager (2026-03-10) #8
|
|
226
|
-
|
|
227
|
-
**Built:**
|
|
228
|
-
- `packages/cli/src/commands/init.ts` — self-heal: when `config.json` exists but DB is empty, reads the config, detects git branch, recreates project + branch in DB instead of bailing. Fixes the edge case where config is committed to the repo but DB is machine-local.
|
|
229
|
-
- `packages/cli/src/commands/init.ts` — system-prompt fragment: after fresh init (or self-heal), writes `.contextgit/system-prompt.md` and prints the fragment to stdout. Fragment instructs agents to call `context_get scope=global` at session start and `context_commit` after significant work.
|
|
230
|
-
- `packages/mcp/src/auto-snapshot.ts` — `AutoSnapshotManager` class: counts tool calls, fires `engine.commit({ commitType: 'auto' })` every N=10 non-commit calls. `context_commit` resets the counter. Auto-commit failures are swallowed — tool calls are never blocked.
|
|
231
|
-
- `packages/mcp/src/server.ts` — `AutoSnapshotManager` wired in: instantiated after bootstrap, `autoSnapshot.onToolCall(toolName)` called in each of the three tool handlers.
|
|
232
|
-
- **Dogfooded:** ran `contextgit init` on this repo (self-heal path triggered), then `contextgit commit` and `contextgit context` — full round-trip confirmed in the ContextGit DB.
|
|
233
|
-
|
|
234
|
-
**Decided:**
|
|
235
|
-
- **Self-heal uses `getBranchByGitName` as the DB health check** — if the git branch's context branch is found, initialization is complete; if not, recreate project + branch. This handles the common case (machine-local DB wiped or repo cloned fresh) without requiring a separate `getProject` method on the store interface.
|
|
236
|
-
- **`AutoSnapshotManager` counts all tool calls except `context_commit`** — `context_get` and `context_search` count toward the interval because they indicate an active session. Only a manual `context_commit` (or auto-commit) resets the counter.
|
|
237
|
-
- **`loadConfig()` called twice in `createServer()`** — once inside `bootstrap()` and once to read `snapshotInterval` for `AutoSnapshotManager`. Acceptable for a short-lived startup path; would refactor if config parsing became expensive.
|
|
238
|
-
- **`commitType: 'auto'`** — auto-commits are distinguished from manual ones in the DB for observability. No other behavior change.
|
|
239
|
-
|
|
240
|
-
**Unresolved:**
|
|
241
|
-
- `semanticSearch` still returns `[]` — needs `EmbeddingService` (Week 4).
|
|
242
|
-
- MCP server not yet validated with `mcp dev` inspector or Claude Code MCP config — the `.contextgit/system-prompt.md` exists but hasn't been added to Claude Code's MCP settings yet.
|
|
243
|
-
- `server.tool()` 4-arg form in MCP SDK still deprecated (carried from Day 10).
|
|
244
|
-
- `loadConfig()` called twice in `createServer()` — minor, deferred.
|
|
245
|
-
|
|
246
|
-
**Next:**
|
|
247
|
-
- Days 20–21 (Week 3 wrap-up): configure Claude Code MCP to load `packages/mcp/src/index.ts` for this repo. Validate `context_get` and `context_commit` work inside Claude Code. Then begin Week 4: `EmbeddingService` in `core/src/embeddings.ts` using `@xenova/transformers all-MiniLM-L6-v2`, wire into `engine.commit()`, enable `semanticSearch` in `LocalStore`.
|
|
248
|
-
|
|
249
|
-
---
|
|
250
|
-
|
|
251
|
-
## Session: Days 20–21 — MCP Config + EmbeddingService (2026-03-10) #9
|
|
252
|
-
|
|
253
|
-
**Built:**
|
|
254
|
-
- `~/.claude.json` — registered `contextgit` MCP server for the `/Users/mendetrajovski/contextgit` project: `node packages/mcp/dist/index.js` via stdio. Claude Code will now load the MCP server when working in this repo.
|
|
255
|
-
- `packages/core/src/embeddings.ts` — `EmbeddingService` class: lazy-loads `Xenova/all-MiniLM-L6-v2` via `@xenova/transformers`, returns `Float32Array(384)` or `null` on any error. Pipeline loaded once and reused across all `embed()` calls.
|
|
256
|
-
- `packages/core/src/embeddings.test.ts` — 5 tests: success path, load failure → null, inference failure → null, never throws, pipeline loaded only once.
|
|
257
|
-
- `packages/core/src/engine.ts` — `ContextEngine` extended: `EngineOptions.embeddingService?`, `EngineStore.indexEmbedding()` + `EngineStore.semanticSearch()`. After `createCommit()`, embedding generated asynchronously (fire-and-forget, never blocks commit). New `engine.semanticSearch(query, projectId, limit)` method generates vector and delegates to store.
|
|
258
|
-
- `packages/store/src/interface.ts` — `ContextStore.semanticSearch` signature changed from `(query: string, ...)` to `(vector: Float32Array, ...)`; `indexEmbedding(commitId, vector)` added.
|
|
259
|
-
- `packages/store/src/local/index.ts` — `LocalStore.indexEmbedding()` calls `queries.insertEmbedding()`; `LocalStore.semanticSearch()` now calls `queries.semanticSearch()` with the provided vector (no longer returns `[]`).
|
|
260
|
-
- `packages/mcp/src/server.ts` — `EmbeddingService` injected into `ContextEngine` at bootstrap; `context_search` tool now runs semantic + FTS in parallel, merges and deduplicates results by commit ID.
|
|
261
|
-
- All 25 tests pass; `pnpm build` and `pnpm typecheck` clean.
|
|
262
|
-
|
|
263
|
-
**Decided:**
|
|
264
|
-
- **`semanticSearch` on `ContextStore` takes `Float32Array`, not `string`** — embedding generation lives in `core`; `store → core (types only)` constraint means the store cannot call `EmbeddingService`. The vector is produced by the engine and passed to the store. `Float32Array` is a native type — no import required.
|
|
265
|
-
- **Embedding indexing is fire-and-forget in `engine.commit()`** — a `.then().catch()` chain ensures: (a) the commit is returned immediately, (b) indexing failures are swallowed, (c) the `never fail a COMMIT` invariant is preserved.
|
|
266
|
-
- **`EmbeddingService.pipelineFactory` is injectable** — allows tests to inject a fake pipeline without loading the real model. Real model only loaded when `ANTHROPIC_API_KEY`-style lazy init is triggered in production.
|
|
267
|
-
- **`context_search` merges semantic + FTS, deduplicates by `commit.id`** — semantic results ranked first (higher precision), FTS fills in remaining slots. `limit` applied after merge.
|
|
268
|
-
- **`@xenova/transformers ^2.17.2`** added to `packages/core` dependencies.
|
|
269
|
-
|
|
270
|
-
**Unresolved:**
|
|
271
|
-
- MCP server not live-validated inside Claude Code yet — requires a Claude Code restart to pick up the new MCP config in `~/.claude.json`.
|
|
272
|
-
- `server.tool()` 4-arg form in MCP SDK still deprecated (carried from Day 10).
|
|
273
|
-
- `loadConfig()` called twice in `createServer()` — minor, deferred.
|
|
274
|
-
- First call to `engine.semanticSearch()` will trigger model download (~25 MB) on cold start — no progress indicator; may time out on slow connections.
|
|
275
|
-
|
|
276
|
-
**Next:**
|
|
277
|
-
- Days 22–23 (Week 4 continued): validate MCP server live inside Claude Code (restart required). Then: `RemoteStore` stub in `packages/store/src/remote/` — implement `ContextStore` interface backed by HTTP fetch against the Express API. Wire into CLI and MCP via config `store: "http://..."`. Add integration test: LocalStore and RemoteStore produce identical snapshots for the same sequence of commits.
|
|
278
|
-
|
|
279
|
-
---
|
|
280
|
-
|
|
281
|
-
## Session: Days 22–23 — RemoteStore + FTS5 Fix (2026-03-11) #10
|
|
282
|
-
|
|
283
|
-
**Built:**
|
|
284
|
-
- `packages/api/src/store-router.ts` — Express router (`createStoreRouter(store)`) exposing all 20 `ContextStore` methods as HTTP endpoints at `/v1/store/...`. Covers projects, branches, commits, snapshots, threads, embeddings, search, and agents. Float32Array serialized as `number[]` for JSON transport.
|
|
285
|
-
- `packages/store/src/remote/index.ts` — `RemoteStore` implementing `ContextStore` via `fetch` against the store router. Includes date-parsing helpers (JSON returns ISO strings; RemoteStore converts them back to `Date`). `Float32Array` serialized/deserialized for `indexEmbedding` and `semanticSearch`.
|
|
286
|
-
- `packages/store/src/index.ts` — updated barrel export to include `RemoteStore`.
|
|
287
|
-
- `packages/api/src/remote-store.test.ts` — 7 integration tests: project/branch CRUD, commit retrieval, formatted snapshot identical to LocalStore output, open thread round-trip, FTS search, mergeBranch thread carry-forward. All run against a live `http.createServer` on a random port backed by in-memory LocalStore.
|
|
288
|
-
- `packages/store/src/local/schema.ts` — added `CREATE_FTS_TRIGGER` and `SCHEMA_V3_DDL`: `AFTER INSERT ON commits` trigger that inserts into `commits_fts` to maintain the FTS index.
|
|
289
|
-
- `packages/store/src/local/migrations.ts` — migration v3 `fts_trigger`: applies the trigger and runs `INSERT INTO commits_fts(commits_fts) VALUES('rebuild')` to index any pre-existing rows.
|
|
290
|
-
- `packages/store/src/local/queries.ts` — fixed `fullTextSearch` query: changed `JOIN commits c ON c.id = commits_fts.commit_id` → `ON c.rowid = commits_fts.rowid`. The content FTS5 table cannot read `commit_id` from `commits` (column named `id`, not `commit_id`); rowid-based join is correct for content tables.
|
|
291
|
-
- **MCP confirmed live**: `mcp__contextgit__context_get`, `mcp__contextgit__context_commit`, `mcp__contextgit__context_search` visible in this Claude Code session — Gate 1 MCP validation passing.
|
|
292
|
-
- All 32 tests pass; `pnpm build` and `pnpm typecheck` clean.
|
|
293
|
-
|
|
294
|
-
**Decided:**
|
|
295
|
-
- **`createStoreRouter(store)` in `packages/api`** — not mounted in the existing `createApp()` (which is project/branch-scoped at startup). The store router is project/branch-agnostic (callers pass IDs). Kept as a separate factory for use by integration tests and future multi-project server mode.
|
|
296
|
-
- **Integration test in `packages/api/src/`** — needs both `express` (api dep) and `RemoteStore`/`LocalStore` (store dep). Keeping in api avoids adding express to store devDependencies. Cross-package relative imports avoided; `@contextgit/store` resolved from workspace dist after `pnpm build`.
|
|
297
|
-
- **`afterAll` uses Promise-based cleanup** — Vitest's TypeScript types don't support the `done` callback pattern; `new Promise<void>(resolve => server.close(...resolve))` is the correct form.
|
|
298
|
-
- **FTS5 content table bug fixed** — The original query joined on `commits_fts.commit_id` but `commits_fts` is a content table referencing `commits`; SQLite FTS5 fetches column values from the content table by column name, so `commit_id` was unresolvable (commits has `id`, not `commit_id`). Fix: join on `c.rowid = commits_fts.rowid` — always correct for content tables.
|
|
299
|
-
- **Migration v3 adds trigger** — rather than an explicit FTS insert in `queries.insertCommit()`, a trigger covers ALL INSERT paths into `commits` (including `mergeBranch`). The `rebuild` command indexes pre-existing rows in upgraded DBs.
|
|
300
|
-
|
|
301
|
-
**Unresolved:**
|
|
302
|
-
- `RemoteStore` not yet wired into CLI/MCP bootstrap via `config.store = "http://..."`. Config type (`ContextGitConfig.store`) already supports this; bootstrap functions need a factory that checks `config.store !== 'local'` and creates `RemoteStore`. Deferred to Day 24.
|
|
303
|
-
- `server.tool()` 4-arg form in MCP SDK still deprecated (carried from Day 10).
|
|
304
|
-
- `loadConfig()` called twice in `createServer()` — minor, deferred.
|
|
305
|
-
- Phase 1 gates (Gate 2 Ralph Loop, Gate 3 REST CI) not yet validated end-to-end.
|
|
306
|
-
|
|
307
|
-
**Next:**
|
|
308
|
-
- Day 24 (Week 4): wire `RemoteStore` into CLI/MCP bootstrap — check `config.store !== 'local'`, create `RemoteStore(config.store)` instead of `LocalStore`. Then run Phase 1 validation gates: Gate 2 (ralph-loop CLI), Gate 3 (REST API curl). Update npx packaging (`contextgit` bin entry in root package.json, `scripts/build.sh`).
|
|
309
|
-
|
|
310
|
-
---
|
|
311
|
-
|
|
312
|
-
## Session: Day 24 — RemoteStore wiring + npx packaging + Phase 1 gates (2026-03-11) #11
|
|
313
|
-
|
|
314
|
-
**Built:**
|
|
315
|
-
- `packages/cli/src/bootstrap.ts` — checks `config.store !== 'local'`; creates `RemoteStore(config.store)` when a URL is configured, `LocalStore(projectId)` otherwise.
|
|
316
|
-
- `packages/mcp/src/server.ts` — same RemoteStore/LocalStore switch in `bootstrap()`.
|
|
317
|
-
- `scripts/build.sh` — builds all 5 packages in dependency order via `pnpm --filter`. Executable.
|
|
318
|
-
- Root `package.json` — added `"bin": { "contextgit": "./packages/cli/bin/run.js" }` so `npx contextgit` works from the monorepo root.
|
|
319
|
-
- **Gate 2 PASS (ralph-loop CLI):** 3 iterations of `contextgit context -f agents-md` → `contextgit commit`. All 4 sections present in agents-md output every iteration: `## Project State`, `## Current Branch`, `## Recent Activity`, `## Open Threads`.
|
|
320
|
-
- **Gate 3 PASS (REST API):** `GET /snapshot?format=agents-md` returns `## Project State` section; `POST /commits` returns `{ id, message, createdAt }` with 201.
|
|
321
|
-
- All 32 tests pass; `pnpm build` and `pnpm typecheck` clean.
|
|
322
|
-
|
|
323
|
-
**Decided:**
|
|
324
|
-
- **`config.store && config.store !== 'local'` guard** — catches both missing field (old configs pre-dating the field) and the explicit `'local'` value. Any other non-empty string is treated as a URL for `RemoteStore`.
|
|
325
|
-
- **Root `package.json` bin entry** — points to `./packages/cli/bin/run.js` (the oclif ESM entry point). The monorepo root is named `contextgit` so `npx contextgit` routes here. No separate publish step needed for local dev; `pnpm link` or `npm link` will expose the bin globally.
|
|
326
|
-
- **`scripts/build.sh` uses `pnpm --filter`** — explicit per-package ordering guarantees `core` before `store` before the rest. Safer than `pnpm -r build` which may parallelize incorrectly on first install.
|
|
327
|
-
- **Gate 3 uses flat routes** (`/commits`, `/snapshot`) not `/v1/projects/:id/...` — the API is single-project-per-process (resolved at startup). The plan doc showed the multi-project URL schema planned for a future phase; the flat routes are the correct target for Phase 1 Gate 3.
|
|
328
|
-
|
|
329
|
-
**Unresolved:**
|
|
330
|
-
- `server.tool()` 4-arg form in MCP SDK still deprecated (carried from Day 10).
|
|
331
|
-
- `loadConfig()` called twice in MCP `createServer()` — minor, deferred.
|
|
332
|
-
- npx packaging not tested in a clean temp dir (no `pnpm link` validation run yet).
|
|
333
|
-
- Gate 1 (interactive MCP session cold-start) not formally re-run — MCP tools are live in Claude Code (confirmed Day 22) but cold-start AGENTS.md read not re-validated after RemoteStore wiring.
|
|
334
|
-
|
|
335
|
-
**Next:**
|
|
336
|
-
- Days 25–26: npx clean-install validation — `cd /tmp && npx contextgit init` in a fresh dir. Fix any path/shebang issues. Then run Gate 1 formal cold-start: Claude Code session start → `context_get scope=global` auto-called via system prompt → snapshot returned with no prior context loaded.
|
|
337
|
-
**Tokens:** TBD
|
|
338
|
-
**Ramp-up:** 0
|
|
339
|
-
**Time to first code:** ~3 min
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
## Session: Phase 1 Complete (2026-03-11)
|
|
343
|
-
**Built:** Full Phase 1 — engine, LocalStore, MCP, CLI, REST API
|
|
344
|
-
**Gates:** Gate 1 ✅ Gate 2 ✅ Gate 3 ✅ npx clean-install ✅
|
|
345
|
-
**Renamed:** contexthub → contextgit, npm name claimed
|
|
346
|
-
**Slipped to Phase 2:** git hooks, semantic search e2e validation
|
|
347
|
-
**Next:** Plan Phase 2 — RemoteStore, multi-agent, team support
|
|
348
|
-
**Ramp-up:** 0
|
|
349
|
-
**Time to first code:** ~3 min
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
## Session: 2026-03-11 #N
|
|
353
|
-
**Built:** Validated contextgit on Loqally. Initial architecture commit captured 7 open threads.
|
|
354
|
-
**Decided:** CLAUDE.md session discipline added to loqally. claude-flow/ruv-swarm/flow-nexus removed.
|
|
355
|
-
**Decided:** context_commit messages should include branch name and git commit hash manually until native git metadata capture is built into the MCP server (auto git rev-parse on commit — small task, ~30min).
|
|
356
|
-
**Unresolved:** Native git metadata on context commits (branch + hash stored automatically in DB)
|
|
357
|
-
**Next:** Run first full session in loqally using contextgit. Validate session 2 picks up from session 1 with zero ramp-up questions.
|
|
358
|
-
|
|
359
|
-
## Session: 2026-03-11 Phase 2 Kickoff
|
|
360
|
-
**Built:** Nothing yet — planning session
|
|
361
|
-
**Decided:** Phase 2 plan written and reviewed. Three updates added:
|
|
362
|
-
busy_timeout mitigation, atomic pull transaction, doctor check, localhost hosting note.
|
|
363
|
-
CLAUDE.md updated to point at Phase 2 plan.
|
|
364
|
-
**Decided:** Remote hosting deferred to Phase 3 (Railway/fly.io). Localhost sufficient for Phase 2 validation.
|
|
365
|
-
**Unresolved:** None — plan is locked and ready to build
|
|
366
|
-
**Next:** Start Week 5. Build packages/mcp/src/git-sync.ts (captureGitMetadata + installGitHooks).
|
|
367
|
-
Then wire gitCommitSha and gitBranch into engine.commit() via MCP and CLI.
|
|
368
|
-
Then fix the double loadConfig() bug in mcp/src/server.ts.
|
|
369
|
-
Then add context_branch and context_merge MCP tools.
|
|
370
|
-
**Next:** Run first full session in loqally using contextgit. Validate session 2 picks up from session 1 with zero ramp-up questions.
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@contextgit/api",
|
|
3
|
-
"version": "0.0.1",
|
|
4
|
-
"type": "module",
|
|
5
|
-
"main": "./dist/server.js",
|
|
6
|
-
"exports": {
|
|
7
|
-
".": "./dist/server.js",
|
|
8
|
-
"./bin": "./dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"typecheck": "tsc --noEmit"
|
|
13
|
-
},
|
|
14
|
-
"dependencies": {
|
|
15
|
-
"@contextgit/core": "workspace:*",
|
|
16
|
-
"@contextgit/store": "workspace:*",
|
|
17
|
-
"express": "^4.19.0",
|
|
18
|
-
"simple-git": "^3.27.0"
|
|
19
|
-
},
|
|
20
|
-
"devDependencies": {
|
|
21
|
-
"@types/express": "^4.17.21",
|
|
22
|
-
"@types/node": "^20.0.0",
|
|
23
|
-
"typescript": "^5.4.0"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// bootstrap.ts — shared setup: load config, open store, detect git branch, init engine.
|
|
2
|
-
|
|
3
|
-
import os from 'os'
|
|
4
|
-
import { simpleGit } from 'simple-git'
|
|
5
|
-
import { ContextEngine } from '@contextgit/core'
|
|
6
|
-
import { LocalStore } from '@contextgit/store'
|
|
7
|
-
import type { ContextStore } from '@contextgit/store'
|
|
8
|
-
import { loadConfig } from './config.js'
|
|
9
|
-
|
|
10
|
-
export interface ApiContext {
|
|
11
|
-
engine: ContextEngine
|
|
12
|
-
store: ContextStore
|
|
13
|
-
projectId: string
|
|
14
|
-
branchId: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function detectGitBranch(): Promise<string> {
|
|
18
|
-
try {
|
|
19
|
-
const git = simpleGit(process.cwd())
|
|
20
|
-
const result = await git.revparse(['--abbrev-ref', 'HEAD'])
|
|
21
|
-
return result.trim()
|
|
22
|
-
} catch {
|
|
23
|
-
return 'main'
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function resolveContextBranch(
|
|
28
|
-
store: ContextStore,
|
|
29
|
-
projectId: string,
|
|
30
|
-
gitBranch: string,
|
|
31
|
-
): Promise<string> {
|
|
32
|
-
const existing = await store.getBranchByGitName(projectId, gitBranch)
|
|
33
|
-
if (existing) return existing.id
|
|
34
|
-
|
|
35
|
-
const created = await store.createBranch({
|
|
36
|
-
projectId,
|
|
37
|
-
name: `Context: ${gitBranch}`,
|
|
38
|
-
gitBranch,
|
|
39
|
-
})
|
|
40
|
-
return created.id
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export async function bootstrap(): Promise<ApiContext> {
|
|
44
|
-
const config = loadConfig()
|
|
45
|
-
const { projectId } = config
|
|
46
|
-
|
|
47
|
-
const store = new LocalStore(projectId)
|
|
48
|
-
const gitBranch = await detectGitBranch()
|
|
49
|
-
const branchId = await resolveContextBranch(store, projectId, gitBranch)
|
|
50
|
-
|
|
51
|
-
const hostname = os.hostname()
|
|
52
|
-
const agentId = `${hostname}-api-server`
|
|
53
|
-
|
|
54
|
-
const engine = new ContextEngine(
|
|
55
|
-
store,
|
|
56
|
-
agentId,
|
|
57
|
-
config.agentRole ?? 'solo',
|
|
58
|
-
'contextgit-api',
|
|
59
|
-
config.workflowType ?? 'interactive',
|
|
60
|
-
)
|
|
61
|
-
await engine.init(projectId, branchId)
|
|
62
|
-
|
|
63
|
-
return { engine, store, projectId, branchId }
|
|
64
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// config.ts — load and validate .contextgit/config.json
|
|
2
|
-
// Searches from CWD upwards until it finds the config file.
|
|
3
|
-
|
|
4
|
-
import { readFileSync } from 'fs'
|
|
5
|
-
import { join, dirname } from 'path'
|
|
6
|
-
import type { ContextGitConfig } from '@contextgit/core'
|
|
7
|
-
|
|
8
|
-
export class ConfigNotFoundError extends Error {
|
|
9
|
-
constructor(startDir: string) {
|
|
10
|
-
super(`No .contextgit/config.json found searching upward from: ${startDir}`)
|
|
11
|
-
this.name = 'ConfigNotFoundError'
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function findConfigPath(startDir: string = process.cwd()): string {
|
|
16
|
-
let current = startDir
|
|
17
|
-
while (true) {
|
|
18
|
-
const candidate = join(current, '.contextgit', 'config.json')
|
|
19
|
-
try {
|
|
20
|
-
readFileSync(candidate)
|
|
21
|
-
return candidate
|
|
22
|
-
} catch {
|
|
23
|
-
const parent = dirname(current)
|
|
24
|
-
if (parent === current) {
|
|
25
|
-
throw new ConfigNotFoundError(startDir)
|
|
26
|
-
}
|
|
27
|
-
current = parent
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function loadConfig(startDir?: string): ContextGitConfig {
|
|
33
|
-
const configPath = findConfigPath(startDir)
|
|
34
|
-
const raw = readFileSync(configPath, 'utf-8')
|
|
35
|
-
const config = JSON.parse(raw) as ContextGitConfig
|
|
36
|
-
|
|
37
|
-
if (!config.projectId) {
|
|
38
|
-
throw new Error(`Invalid config at ${configPath}: missing required field 'projectId'`)
|
|
39
|
-
}
|
|
40
|
-
if (!config.project) {
|
|
41
|
-
throw new Error(`Invalid config at ${configPath}: missing required field 'project'`)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return config
|
|
45
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// index.ts — ContextGit REST API entry point
|
|
2
|
-
// Starts the Express server on PORT (default 3141).
|
|
3
|
-
|
|
4
|
-
import { createApp } from './server.js'
|
|
5
|
-
|
|
6
|
-
const PORT = parseInt(process.env['PORT'] ?? '3141', 10)
|
|
7
|
-
|
|
8
|
-
createApp()
|
|
9
|
-
.then(app => {
|
|
10
|
-
app.listen(PORT, () => {
|
|
11
|
-
console.log(`ContextGit API listening on http://localhost:${PORT}`)
|
|
12
|
-
})
|
|
13
|
-
})
|
|
14
|
-
.catch(err => {
|
|
15
|
-
console.error('Failed to start ContextGit API:', err)
|
|
16
|
-
process.exit(1)
|
|
17
|
-
})
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { createHash } from 'crypto'
|
|
3
|
-
import express from 'express'
|
|
4
|
-
import http from 'http'
|
|
5
|
-
import type { AddressInfo } from 'net'
|
|
6
|
-
import { createAuthMiddleware } from './auth.js'
|
|
7
|
-
|
|
8
|
-
function sha256hex(input: string): string {
|
|
9
|
-
return createHash('sha256').update(input, 'utf8').digest('hex')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async function startServer(apiKey: string | undefined): Promise<{ url: string; server: http.Server }> {
|
|
13
|
-
const app = express()
|
|
14
|
-
app.use(createAuthMiddleware(apiKey ? sha256hex(apiKey) : undefined))
|
|
15
|
-
app.get('/ping', (_req, res) => res.json({ ok: true }))
|
|
16
|
-
|
|
17
|
-
const server = http.createServer(app)
|
|
18
|
-
const port = await new Promise<number>(resolve =>
|
|
19
|
-
server.listen(0, () => resolve((server.address() as AddressInfo).port)),
|
|
20
|
-
)
|
|
21
|
-
return { url: `http://localhost:${port}`, server }
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function get(url: string, headers: Record<string, string> = {}): Promise<{ status: number; body: unknown }> {
|
|
25
|
-
const res = await fetch(url, { headers })
|
|
26
|
-
const body = await res.json()
|
|
27
|
-
return { status: res.status, body }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
describe('createAuthMiddleware', () => {
|
|
31
|
-
it('passes all requests when no API key is configured (dev mode)', async () => {
|
|
32
|
-
const { url, server } = await startServer(undefined)
|
|
33
|
-
try {
|
|
34
|
-
const { status } = await get(`${url}/ping`)
|
|
35
|
-
expect(status).toBe(200)
|
|
36
|
-
} finally {
|
|
37
|
-
await new Promise<void>(r => server.close(() => r()))
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('returns 401 when Authorization header is missing', async () => {
|
|
42
|
-
const { url, server } = await startServer('my-secret-key')
|
|
43
|
-
try {
|
|
44
|
-
const { status, body } = await get(`${url}/ping`)
|
|
45
|
-
expect(status).toBe(401)
|
|
46
|
-
expect(body).toMatchObject({ error: 'Authorization required' })
|
|
47
|
-
} finally {
|
|
48
|
-
await new Promise<void>(r => server.close(() => r()))
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('returns 401 when Authorization header is not Bearer scheme', async () => {
|
|
53
|
-
const { url, server } = await startServer('my-secret-key')
|
|
54
|
-
try {
|
|
55
|
-
const { status } = await get(`${url}/ping`, { Authorization: 'Basic abc123' })
|
|
56
|
-
expect(status).toBe(401)
|
|
57
|
-
} finally {
|
|
58
|
-
await new Promise<void>(r => server.close(() => r()))
|
|
59
|
-
}
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('returns 401 for a wrong Bearer token', async () => {
|
|
63
|
-
const { url, server } = await startServer('my-secret-key')
|
|
64
|
-
try {
|
|
65
|
-
const { status, body } = await get(`${url}/ping`, { Authorization: 'Bearer wrong-key' })
|
|
66
|
-
expect(status).toBe(401)
|
|
67
|
-
expect(body).toMatchObject({ error: 'Invalid API key' })
|
|
68
|
-
} finally {
|
|
69
|
-
await new Promise<void>(r => server.close(() => r()))
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it('passes request with the correct Bearer token', async () => {
|
|
74
|
-
const { url, server } = await startServer('my-secret-key')
|
|
75
|
-
try {
|
|
76
|
-
const { status, body } = await get(`${url}/ping`, { Authorization: 'Bearer my-secret-key' })
|
|
77
|
-
expect(status).toBe(200)
|
|
78
|
-
expect(body).toMatchObject({ ok: true })
|
|
79
|
-
} finally {
|
|
80
|
-
await new Promise<void>(r => server.close(() => r()))
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
})
|