contextgit 0.0.2 → 0.0.4

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.
Files changed (163) hide show
  1. package/dist/bootstrap.d.ts +10 -0
  2. package/dist/bootstrap.d.ts.map +1 -0
  3. package/dist/bootstrap.js +43 -0
  4. package/dist/bootstrap.js.map +1 -0
  5. package/dist/commands/branch.d.ts +13 -0
  6. package/dist/commands/branch.d.ts.map +1 -0
  7. package/dist/commands/branch.js +52 -0
  8. package/dist/commands/branch.js.map +1 -0
  9. package/dist/commands/claim.d.ts +15 -0
  10. package/dist/commands/claim.d.ts.map +1 -0
  11. package/dist/commands/claim.js +60 -0
  12. package/dist/commands/claim.js.map +1 -0
  13. package/dist/commands/commit.d.ts +17 -0
  14. package/dist/commands/commit.d.ts.map +1 -0
  15. package/dist/commands/commit.js +83 -0
  16. package/dist/commands/commit.js.map +1 -0
  17. package/dist/commands/context.d.ts +9 -0
  18. package/dist/commands/context.d.ts.map +1 -0
  19. package/dist/commands/context.js +38 -0
  20. package/dist/commands/context.js.map +1 -0
  21. package/dist/commands/doctor.d.ts +6 -0
  22. package/dist/commands/doctor.d.ts.map +1 -0
  23. package/dist/commands/doctor.js +84 -0
  24. package/dist/commands/doctor.js.map +1 -0
  25. package/dist/commands/init.d.ts +10 -0
  26. package/dist/commands/init.d.ts.map +1 -0
  27. package/dist/commands/init.js +184 -0
  28. package/dist/commands/init.js.map +1 -0
  29. package/dist/commands/keygen.d.ts +10 -0
  30. package/dist/commands/keygen.d.ts.map +1 -0
  31. package/dist/commands/keygen.js +57 -0
  32. package/dist/commands/keygen.js.map +1 -0
  33. package/dist/commands/log.d.ts +13 -0
  34. package/dist/commands/log.d.ts.map +1 -0
  35. package/dist/commands/log.js +91 -0
  36. package/dist/commands/log.js.map +1 -0
  37. package/dist/commands/merge.d.ts +12 -0
  38. package/dist/commands/merge.d.ts.map +1 -0
  39. package/dist/commands/merge.js +29 -0
  40. package/dist/commands/merge.js.map +1 -0
  41. package/dist/commands/pull.d.ts +10 -0
  42. package/dist/commands/pull.d.ts.map +1 -0
  43. package/dist/commands/pull.js +123 -0
  44. package/dist/commands/pull.js.map +1 -0
  45. package/dist/commands/push.d.ts +10 -0
  46. package/dist/commands/push.d.ts.map +1 -0
  47. package/dist/commands/push.js +141 -0
  48. package/dist/commands/push.js.map +1 -0
  49. package/dist/commands/remote-show.d.ts +6 -0
  50. package/dist/commands/remote-show.d.ts.map +1 -0
  51. package/dist/commands/remote-show.js +71 -0
  52. package/dist/commands/remote-show.js.map +1 -0
  53. package/dist/commands/search.d.ts +11 -0
  54. package/dist/commands/search.d.ts.map +1 -0
  55. package/dist/commands/search.js +47 -0
  56. package/dist/commands/search.js.map +1 -0
  57. package/dist/commands/serve.d.ts +9 -0
  58. package/dist/commands/serve.d.ts.map +1 -0
  59. package/dist/commands/serve.js +51 -0
  60. package/dist/commands/serve.js.map +1 -0
  61. package/dist/commands/set-remote.d.ts +9 -0
  62. package/dist/commands/set-remote.d.ts.map +1 -0
  63. package/dist/commands/set-remote.js +26 -0
  64. package/dist/commands/set-remote.js.map +1 -0
  65. package/dist/commands/status.d.ts +6 -0
  66. package/dist/commands/status.d.ts.map +1 -0
  67. package/dist/commands/status.js +54 -0
  68. package/dist/commands/status.js.map +1 -0
  69. package/dist/commands/unclaim.d.ts +9 -0
  70. package/dist/commands/unclaim.d.ts.map +1 -0
  71. package/dist/commands/unclaim.js +22 -0
  72. package/dist/commands/unclaim.js.map +1 -0
  73. package/dist/config.d.ts +19 -0
  74. package/dist/config.d.ts.map +1 -0
  75. package/dist/config.js +58 -0
  76. package/dist/config.js.map +1 -0
  77. package/dist/git-hooks.d.ts +6 -0
  78. package/dist/git-hooks.d.ts.map +1 -0
  79. package/dist/git-hooks.js +58 -0
  80. package/dist/git-hooks.js.map +1 -0
  81. package/package.json +24 -18
  82. package/.claude/settings.local.json +0 -41
  83. package/.contextgit/config.json +0 -10
  84. package/.contextgit/system-prompt.md +0 -4
  85. package/.github/workflows/contextgit-ci.yml +0 -40
  86. package/CLAUDE.md +0 -123
  87. package/CLAUDE.md.next +0 -65
  88. package/docs/ContextGit_ARCHITECTURE_v3.md +0 -1141
  89. package/docs/ContextGit_DELTA.md +0 -84
  90. package/docs/ContextGit_PHASE1_PLAN.md +0 -177
  91. package/docs/ContextGit_PHASE2_PLAN.md +0 -535
  92. package/docs/ContextGit_PRD_v4.md +0 -488
  93. package/docs/decisions.md +0 -370
  94. package/packages/api/package.json +0 -25
  95. package/packages/api/src/bootstrap.ts +0 -64
  96. package/packages/api/src/config.ts +0 -45
  97. package/packages/api/src/index.ts +0 -17
  98. package/packages/api/src/middleware/auth.test.ts +0 -83
  99. package/packages/api/src/middleware/auth.ts +0 -41
  100. package/packages/api/src/remote-store.test.ts +0 -301
  101. package/packages/api/src/router.ts +0 -121
  102. package/packages/api/src/server-config.ts +0 -34
  103. package/packages/api/src/server.ts +0 -38
  104. package/packages/api/src/store-router.ts +0 -241
  105. package/packages/api/tsconfig.json +0 -8
  106. package/packages/cli/package.json +0 -29
  107. package/packages/cli/src/bootstrap.ts +0 -68
  108. package/packages/cli/src/commands/branch.ts +0 -58
  109. package/packages/cli/src/commands/claim.ts +0 -58
  110. package/packages/cli/src/commands/commit.ts +0 -79
  111. package/packages/cli/src/commands/context.ts +0 -46
  112. package/packages/cli/src/commands/doctor.ts +0 -99
  113. package/packages/cli/src/commands/init.ts +0 -141
  114. package/packages/cli/src/commands/keygen.ts +0 -65
  115. package/packages/cli/src/commands/log.ts +0 -103
  116. package/packages/cli/src/commands/merge.ts +0 -36
  117. package/packages/cli/src/commands/pull.ts +0 -145
  118. package/packages/cli/src/commands/push.ts +0 -158
  119. package/packages/cli/src/commands/remote-show.ts +0 -87
  120. package/packages/cli/src/commands/search.ts +0 -54
  121. package/packages/cli/src/commands/serve.ts +0 -61
  122. package/packages/cli/src/commands/set-remote.ts +0 -30
  123. package/packages/cli/src/commands/status.ts +0 -62
  124. package/packages/cli/src/commands/unclaim.ts +0 -28
  125. package/packages/cli/src/config.ts +0 -64
  126. package/packages/cli/src/git-hooks.ts +0 -61
  127. package/packages/cli/tsconfig.json +0 -9
  128. package/packages/core/package.json +0 -28
  129. package/packages/core/src/embeddings.test.ts +0 -58
  130. package/packages/core/src/embeddings.ts +0 -75
  131. package/packages/core/src/engine.ts +0 -274
  132. package/packages/core/src/index.ts +0 -6
  133. package/packages/core/src/snapshot.ts +0 -82
  134. package/packages/core/src/summarizer.test.ts +0 -120
  135. package/packages/core/src/summarizer.ts +0 -113
  136. package/packages/core/src/threads.ts +0 -29
  137. package/packages/core/src/types.ts +0 -240
  138. package/packages/core/tsconfig.json +0 -9
  139. package/packages/mcp/package.json +0 -31
  140. package/packages/mcp/src/auto-snapshot.ts +0 -83
  141. package/packages/mcp/src/config.ts +0 -53
  142. package/packages/mcp/src/git-sync.ts +0 -94
  143. package/packages/mcp/src/index.ts +0 -19
  144. package/packages/mcp/src/server.ts +0 -377
  145. package/packages/mcp/tsconfig.json +0 -9
  146. package/packages/store/package.json +0 -30
  147. package/packages/store/src/branch-merge.test.ts +0 -127
  148. package/packages/store/src/engine-integration.test.ts +0 -93
  149. package/packages/store/src/index.ts +0 -3
  150. package/packages/store/src/interface.ts +0 -62
  151. package/packages/store/src/local/claims.test.ts +0 -190
  152. package/packages/store/src/local/index.ts +0 -380
  153. package/packages/store/src/local/local-store.test.ts +0 -164
  154. package/packages/store/src/local/migrations.ts +0 -99
  155. package/packages/store/src/local/queries.ts +0 -760
  156. package/packages/store/src/local/schema.ts +0 -157
  157. package/packages/store/src/remote/index.ts +0 -300
  158. package/packages/store/tsconfig.json +0 -9
  159. package/pnpm-workspace.yaml +0 -2
  160. package/scripts/build.sh +0 -28
  161. package/tsconfig.base.json +0 -14
  162. package/vitest.config.ts +0 -15
  163. /package/{packages/cli/bin → bin}/run.js +0 -0
@@ -1,377 +0,0 @@
1
- // server.ts — ContextGit MCP Server
2
- //
3
- // Exposes three tools to Claude and other MCP clients:
4
- // context_get — return a formatted project snapshot
5
- // context_commit — persist a context commit
6
- // context_search — full-text search over past commits
7
- //
8
- // Transport: stdio (launched by the MCP host, e.g. Claude Desktop / Claude Code)
9
- //
10
- // Initialization (per server process):
11
- // 1. Load .contextgit/config.json (search from CWD upward)
12
- // 2. Open LocalStore for projectId
13
- // 3. Detect current git branch via simple-git
14
- // 4. Resolve (or create) the context branch for that git branch
15
- // 5. Build ContextEngine, call engine.init()
16
-
17
- import os from 'os'
18
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
19
- import { z } from 'zod'
20
- import { simpleGit } from 'simple-git'
21
- import { ContextEngine, EmbeddingService, SnapshotFormatter } from '@contextgit/core'
22
- import { LocalStore, RemoteStore } from '@contextgit/store'
23
- import { loadConfig } from './config.js'
24
- import { captureGitMetadata } from './git-sync.js'
25
- import { AutoSnapshotManager } from './auto-snapshot.js'
26
- import type { ContextStore } from '@contextgit/store'
27
- import type { ContextGitConfig } from '@contextgit/core'
28
-
29
- // ─── Helpers ──────────────────────────────────────────────────────────────────
30
-
31
- async function detectGitBranch(): Promise<string> {
32
- try {
33
- const git = simpleGit(process.cwd())
34
- const result = await git.revparse(['--abbrev-ref', 'HEAD'])
35
- return result.trim()
36
- } catch {
37
- return 'main'
38
- }
39
- }
40
-
41
- /**
42
- * Resolve the ContextGit branch that tracks `gitBranch`.
43
- * Creates one if it doesn't exist yet.
44
- */
45
- async function resolveContextBranch(
46
- store: ContextStore,
47
- projectId: string,
48
- gitBranch: string,
49
- ): Promise<string> {
50
- const existing = await store.getBranchByGitName(projectId, gitBranch)
51
- if (existing) return existing.id
52
-
53
- const created = await store.createBranch({
54
- projectId,
55
- name: `Context: ${gitBranch}`,
56
- gitBranch,
57
- })
58
- return created.id
59
- }
60
-
61
- // ─── Server bootstrap ─────────────────────────────────────────────────────────
62
-
63
- interface ServerContext {
64
- engine: ContextEngine
65
- store: ContextStore
66
- projectId: string
67
- branchId: string
68
- config: ContextGitConfig
69
- }
70
-
71
- async function bootstrap(): Promise<ServerContext> {
72
- const config = loadConfig()
73
- const { projectId } = config
74
-
75
- const store: ContextStore =
76
- config.store && config.store !== 'local'
77
- ? new RemoteStore(config.store)
78
- : new LocalStore(projectId)
79
-
80
- // Ensure the project row exists before creating branches (FK constraint)
81
- const existing = await store.getProject(projectId)
82
- if (!existing) {
83
- await store.createProject({ id: projectId, name: config.project })
84
- }
85
-
86
- const gitBranch = await detectGitBranch()
87
- const branchId = await resolveContextBranch(store, projectId, gitBranch)
88
-
89
- const hostname = os.hostname()
90
- const agentId = `${hostname}-mcp-claude-code-interactive`
91
-
92
- const engine = new ContextEngine(
93
- store,
94
- agentId,
95
- config.agentRole ?? 'solo',
96
- 'claude-code',
97
- config.workflowType ?? 'interactive',
98
- { embeddingService: new EmbeddingService() },
99
- )
100
- await engine.init(projectId, branchId)
101
-
102
- return { engine, store, projectId, branchId, config }
103
- }
104
-
105
- // ─── Tool definitions ─────────────────────────────────────────────────────────
106
-
107
- export async function createServer(): Promise<McpServer> {
108
- const ctx = await bootstrap()
109
- const autoSnapshot = new AutoSnapshotManager(ctx.engine, {
110
- interval: ctx.config.snapshotInterval ?? 10,
111
- })
112
-
113
- const server = new McpServer({
114
- name: 'contextgit',
115
- version: '0.0.1',
116
- })
117
-
118
- // ── context_get ─────────────────────────────────────────────────────────────
119
- server.tool(
120
- 'context_get',
121
- 'Retrieve the current project snapshot. Call this at every session start to load project state.',
122
- {
123
- scope: z.enum(['global', 'branch']).default('global').describe(
124
- "'global' returns the full project summary + branch state. 'branch' scopes to the current branch.",
125
- ),
126
- format: z.enum(['agents-md', 'json', 'text']).default('agents-md').describe(
127
- 'Output format. agents-md is optimized for agent consumption.',
128
- ),
129
- agent_role: z.enum(['orchestrator','dev','test','review','background','ci','solo']).optional().describe(
130
- 'Filter recentCommits to this agent role only. Omit to return commits from all roles.',
131
- ),
132
- },
133
- async ({ format, agent_role }) => {
134
- await autoSnapshot.onToolCall('context_get')
135
- try {
136
- const snapshot = await ctx.store.getSessionSnapshot(
137
- ctx.projectId,
138
- ctx.branchId,
139
- agent_role ? { agentRole: agent_role } : undefined,
140
- )
141
- const text = new SnapshotFormatter().format(snapshot, format)
142
- return {
143
- content: [{ type: 'text', text }],
144
- }
145
- } catch (err) {
146
- const message = err instanceof Error ? err.message : String(err)
147
- return {
148
- content: [{ type: 'text', text: `Error retrieving snapshot: ${message}` }],
149
- isError: true,
150
- }
151
- }
152
- },
153
- )
154
-
155
- // ── context_commit ───────────────────────────────────────────────────────────
156
- server.tool(
157
- 'context_commit',
158
- 'Persist a context commit recording significant work. Call this after completing meaningful tasks or milestones.',
159
- {
160
- message: z.string().min(1).describe('Short summary of what was accomplished (1–2 sentences).'),
161
- content: z.string().min(1).describe('Detailed description of the work done, decisions made, and current state.'),
162
- open_threads: z.array(z.string()).optional().describe(
163
- 'New open questions or blockers to track (each max 200 chars).',
164
- ),
165
- close_thread_ids: z.array(z.string()).optional().describe(
166
- 'IDs of threads to close.',
167
- ),
168
- },
169
- async ({ message, content, open_threads, close_thread_ids }) => {
170
- await autoSnapshot.onToolCall('context_commit')
171
- try {
172
- const threads: { open?: string[]; close?: Array<{ id: string; note: string }> } = {}
173
- if (open_threads?.length) threads.open = open_threads
174
- if (close_thread_ids?.length) {
175
- threads.close = close_thread_ids.map(id => ({ id, note: 'Closed via context_commit' }))
176
- }
177
-
178
- const git = await captureGitMetadata(process.cwd())
179
- const commit = await ctx.engine.commit({
180
- message,
181
- content,
182
- gitCommitSha: git?.sha,
183
- ...(Object.keys(threads).length > 0 ? { threads } : {}),
184
- })
185
-
186
- return {
187
- content: [
188
- {
189
- type: 'text',
190
- text: `Commit recorded.\nID: ${commit.id}\nMessage: ${commit.message}`,
191
- },
192
- ],
193
- }
194
- } catch (err) {
195
- const message = err instanceof Error ? err.message : String(err)
196
- return {
197
- content: [{ type: 'text', text: `Error recording commit: ${message}` }],
198
- isError: true,
199
- }
200
- }
201
- },
202
- )
203
-
204
- // ── context_search ───────────────────────────────────────────────────────────
205
- server.tool(
206
- 'context_search',
207
- 'Search past context commits. Uses semantic + full-text search and merges results.',
208
- {
209
- query: z.string().min(1).describe('Search query — natural language or keywords.'),
210
- limit: z.number().int().min(1).max(20).default(5).describe('Maximum results to return.'),
211
- },
212
- async ({ query, limit }) => {
213
- await autoSnapshot.onToolCall('context_search')
214
- try {
215
- // Run semantic and full-text in parallel; merge by commit ID, dedup.
216
- const [semantic, fts] = await Promise.all([
217
- ctx.engine.semanticSearch(query, ctx.projectId, limit),
218
- ctx.store.fullTextSearch(query, ctx.projectId),
219
- ])
220
- const seen = new Set<string>()
221
- const merged = [...semantic, ...fts].filter(r => {
222
- if (seen.has(r.commit.id)) return false
223
- seen.add(r.commit.id)
224
- return true
225
- })
226
- const trimmed = merged.slice(0, limit)
227
-
228
- if (trimmed.length === 0) {
229
- return {
230
- content: [{ type: 'text', text: 'No results found.' }],
231
- }
232
- }
233
-
234
- const formatted = trimmed
235
- .map(
236
- (r, i) =>
237
- `[${i + 1}] ${r.commit.message}\n` +
238
- ` ID: ${r.commit.id} Score: ${r.score.toFixed(3)}\n` +
239
- ` ${r.commit.content.slice(0, 200)}${r.commit.content.length > 200 ? '…' : ''}`,
240
- )
241
- .join('\n\n')
242
-
243
- return {
244
- content: [{ type: 'text', text: formatted }],
245
- }
246
- } catch (err) {
247
- const message = err instanceof Error ? err.message : String(err)
248
- return {
249
- content: [{ type: 'text', text: `Error searching commits: ${message}` }],
250
- isError: true,
251
- }
252
- }
253
- },
254
- )
255
-
256
- // ── context_branch ───────────────────────────────────────────────────────────
257
- server.tool(
258
- 'context_branch',
259
- 'Create a new context branch tracking a git branch. Call this when starting work on a new git branch.',
260
- {
261
- git_branch: z.string().min(1).describe('The git branch name to track.'),
262
- name: z.string().optional().describe('Optional display name for the context branch.'),
263
- },
264
- async ({ git_branch, name }) => {
265
- await autoSnapshot.onToolCall('context_branch')
266
- try {
267
- const branch = await ctx.engine.branch(git_branch, name)
268
- return {
269
- content: [
270
- {
271
- type: 'text',
272
- text: `Branch created.\nID: ${branch.id}\nName: ${branch.name}`,
273
- },
274
- ],
275
- }
276
- } catch (err) {
277
- const message = err instanceof Error ? err.message : String(err)
278
- return {
279
- content: [{ type: 'text', text: `Error creating branch: ${message}` }],
280
- isError: true,
281
- }
282
- }
283
- },
284
- )
285
-
286
- // ── context_claim ────────────────────────────────────────────────────────────
287
- server.tool(
288
- 'context_claim',
289
- 'Claim a task to prevent other agents from picking it up simultaneously. Call this before starting any significant task.',
290
- {
291
- task: z.string().min(1).describe('Short description of the task being claimed (e.g. "build auth module").'),
292
- ttl_hours: z.number().positive().default(2).describe('Time-to-live in hours before the claim auto-expires. Default: 2.'),
293
- status: z.enum(['proposed', 'active']).default('proposed').describe("'proposed' for plan-mode claims; 'active' once approved and work begins."),
294
- },
295
- async ({ task, ttl_hours, status }) => {
296
- await autoSnapshot.onToolCall('context_claim')
297
- try {
298
- const claim = await ctx.store.claimTask(ctx.projectId, ctx.branchId, {
299
- task,
300
- agentId: `${os.hostname()}-mcp-claude-code-interactive`,
301
- role: ctx.config.agentRole ?? 'solo',
302
- status,
303
- ttl: Math.round(ttl_hours * 3_600_000),
304
- })
305
- return {
306
- content: [
307
- {
308
- type: 'text',
309
- text: `Claim recorded.\nID: ${claim.id}\nTask: ${claim.task}\nStatus: ${claim.status}\nTTL: ${ttl_hours}h`,
310
- },
311
- ],
312
- }
313
- } catch (err) {
314
- const message = err instanceof Error ? err.message : String(err)
315
- return {
316
- content: [{ type: 'text', text: `Error creating claim: ${message}` }],
317
- isError: true,
318
- }
319
- }
320
- },
321
- )
322
-
323
- // ── context_unclaim ──────────────────────────────────────────────────────────
324
- server.tool(
325
- 'context_unclaim',
326
- 'Release a previously claimed task. Use when abandoning a task without committing.',
327
- {
328
- claim_id: z.string().min(1).describe('ID of the claim to release.'),
329
- },
330
- async ({ claim_id }) => {
331
- await autoSnapshot.onToolCall('context_unclaim')
332
- try {
333
- await ctx.store.unclaimTask(claim_id)
334
- return {
335
- content: [{ type: 'text', text: `Claim released.\nID: ${claim_id}` }],
336
- }
337
- } catch (err) {
338
- const message = err instanceof Error ? err.message : String(err)
339
- return {
340
- content: [{ type: 'text', text: `Error releasing claim: ${message}` }],
341
- isError: true,
342
- }
343
- }
344
- },
345
- )
346
-
347
- // ── context_merge ────────────────────────────────────────────────────────────
348
- server.tool(
349
- 'context_merge',
350
- 'Merge a source context branch into the current branch. Call this after merging a git branch.',
351
- {
352
- source_branch_id: z.string().min(1).describe('ID of the context branch to merge in.'),
353
- },
354
- async ({ source_branch_id }) => {
355
- await autoSnapshot.onToolCall('context_merge')
356
- try {
357
- const commit = await ctx.engine.merge(source_branch_id)
358
- return {
359
- content: [
360
- {
361
- type: 'text',
362
- text: `Merge commit recorded.\nID: ${commit.id}`,
363
- },
364
- ],
365
- }
366
- } catch (err) {
367
- const message = err instanceof Error ? err.message : String(err)
368
- return {
369
- content: [{ type: 'text', text: `Error merging branch: ${message}` }],
370
- isError: true,
371
- }
372
- }
373
- },
374
- )
375
-
376
- return server
377
- }
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "rootDir": "./src",
5
- "outDir": "./dist"
6
- },
7
- "include": ["src/**/*"],
8
- "exclude": ["node_modules", "dist"]
9
- }
@@ -1,30 +0,0 @@
1
- {
2
- "name": "@contextgit/store",
3
- "version": "0.0.1",
4
- "type": "module",
5
- "main": "./dist/index.js",
6
- "types": "./dist/index.d.ts",
7
- "exports": {
8
- ".": {
9
- "import": "./dist/index.js",
10
- "types": "./dist/index.d.ts"
11
- }
12
- },
13
- "scripts": {
14
- "build": "tsc",
15
- "test": "vitest run",
16
- "typecheck": "tsc --noEmit"
17
- },
18
- "dependencies": {
19
- "@contextgit/core": "workspace:*",
20
- "better-sqlite3": "^9.4.0",
21
- "nanoid": "^5.0.0",
22
- "sqlite-vec": "^0.1.6"
23
- },
24
- "devDependencies": {
25
- "@types/better-sqlite3": "^7.6.0",
26
- "@types/node": "^20.0.0",
27
- "typescript": "^5.4.0",
28
- "vitest": "^1.6.0"
29
- }
30
- }
@@ -1,127 +0,0 @@
1
- // Days 12–13 integration test: engine.branch() + engine.merge()
2
- //
3
- // Scenario: main → commit + thread → branch('feature') → 2 commits + thread
4
- // → merge back to main → snapshot shows merged state + all threads.
5
-
6
- import { describe, it, expect, beforeEach, afterEach } from 'vitest'
7
- import { ContextEngine } from '@contextgit/core'
8
- import { LocalStore } from './local/index.js'
9
-
10
- describe('engine.branch() + engine.merge()', () => {
11
- let store: LocalStore
12
- let mainEngine: ContextEngine
13
- let featureEngine: ContextEngine
14
-
15
- beforeEach(() => {
16
- store = new LocalStore(':memory:')
17
- mainEngine = new ContextEngine(store, 'agent-main', 'orchestrator', 'claude-code', 'interactive')
18
- featureEngine = new ContextEngine(store, 'agent-feat', 'dev', 'claude-code', 'interactive')
19
- })
20
-
21
- afterEach(() => {
22
- store.close()
23
- })
24
-
25
- it('branch → 2 commits → merge → snapshot shows merged state and all threads', async () => {
26
- // 1. Setup: project + main branch
27
- const project = await store.createProject({ name: 'contextgit' })
28
- const mainBranch = await store.createBranch({ projectId: project.id, name: 'main', gitBranch: 'main' })
29
- await mainEngine.init(project.id, mainBranch.id)
30
-
31
- // 2. Commit on main with an open thread
32
- await mainEngine.commit({
33
- message: 'Initial main commit',
34
- content: 'Set up project foundation.',
35
- threads: { open: ['Choose DB migration strategy'] },
36
- })
37
-
38
- // 3. Branch off main
39
- const featureBranch = await mainEngine.branch('feature/auth', 'auth feature')
40
-
41
- expect(featureBranch.gitBranch).toBe('feature/auth')
42
- expect(featureBranch.name).toBe('auth feature')
43
- expect(featureBranch.parentBranchId).toBe(mainBranch.id)
44
- expect(featureBranch.status).toBe('active')
45
-
46
- // feature branch should have a branch-init commit with main's summary
47
- const featureCommits = await store.listCommits(featureBranch.id, { limit: 10, offset: 0 })
48
- expect(featureCommits).toHaveLength(1)
49
- expect(featureCommits[0].commitType).toBe('branch-init')
50
- expect(featureCommits[0].summary).toBeTruthy() // carried parent summary
51
-
52
- // 4. Two commits on feature branch (one with its own thread)
53
- await featureEngine.init(project.id, featureBranch.id)
54
-
55
- await featureEngine.commit({
56
- message: 'Add auth middleware',
57
- content: 'Implemented JWT middleware and route guards.',
58
- threads: { open: ['Token expiry policy TBD'] },
59
- })
60
-
61
- const lastFeatureCommit = await featureEngine.commit({
62
- message: 'Add login endpoint',
63
- content: 'POST /auth/login with bcrypt password verification.',
64
- })
65
- expect(lastFeatureCommit.summary).toContain('bcrypt')
66
-
67
- // 5. Merge feature → main
68
- const mergeCommit = await mainEngine.merge(featureBranch.id)
69
-
70
- expect(mergeCommit.commitType).toBe('merge')
71
- expect(mergeCommit.branchId).toBe(mainBranch.id)
72
- expect(mergeCommit.mergeSourceBranchId).toBe(featureBranch.id)
73
-
74
- // 6. Source branch should be marked merged
75
- const mergedBranch = await store.getBranch(featureBranch.id)
76
- expect(mergedBranch?.status).toBe('merged')
77
- expect(mergedBranch?.mergedAt).toBeTruthy()
78
-
79
- // 7. Snapshot on main should show merged state
80
- const snapshot = await mainEngine.context('global')
81
-
82
- expect(snapshot.branchName).toBe('main')
83
- // recent commits: merge commit is the newest
84
- expect(snapshot.recentCommits[0].commitType).toBe('merge')
85
- // open threads: both the main thread and the feature thread carried forward
86
- const threadDescriptions = snapshot.openThreads.map(t => t.description)
87
- expect(threadDescriptions).toContain('Choose DB migration strategy')
88
- expect(threadDescriptions).toContain('Token expiry policy TBD')
89
- })
90
-
91
- it('branch-init commit carries parent HEAD summary into new branch', async () => {
92
- const project = await store.createProject({ name: 'p' })
93
- const mainBranch = await store.createBranch({ projectId: project.id, name: 'main', gitBranch: 'main' })
94
- await mainEngine.init(project.id, mainBranch.id)
95
-
96
- await mainEngine.commit({ message: 'setup', content: 'foundation work done' })
97
-
98
- const feat = await mainEngine.branch('feature/x')
99
-
100
- // branch-init summary should match main's HEAD summary
101
- const mainHead = await store.getBranch(mainBranch.id)
102
- const mainHeadCommit = mainHead?.headCommitId
103
- ? await store.getCommit(mainHead.headCommitId)
104
- : null
105
-
106
- const featCommits = await store.listCommits(feat.id, { limit: 1, offset: 0 })
107
- expect(featCommits[0].summary).toBe(mainHeadCommit?.summary)
108
- })
109
-
110
- it('merge summary incorporates source branch content', async () => {
111
- const project = await store.createProject({ name: 'p' })
112
- const mainBranch = await store.createBranch({ projectId: project.id, name: 'main', gitBranch: 'main' })
113
- await mainEngine.init(project.id, mainBranch.id)
114
-
115
- await mainEngine.commit({ message: 'base', content: 'base content' })
116
-
117
- const feat = await mainEngine.branch('feature/y')
118
- await featureEngine.init(project.id, feat.id)
119
- await featureEngine.commit({ message: 'feature work', content: 'feature specific content' })
120
-
121
- const mergeCommit = await mainEngine.merge(feat.id)
122
-
123
- // merge commit summary should reference feature content
124
- expect(mergeCommit.summary).toBeTruthy()
125
- expect(mergeCommit.content).toContain('feature specific content')
126
- })
127
- })
@@ -1,93 +0,0 @@
1
- // Week 1 validation: ContextEngine + LocalStore end-to-end.
2
- //
3
- // Scenario: create project → 2 commits (one with open thread) →
4
- // context('global') → snapshot shows thread + both commits.
5
-
6
- import { describe, it, expect, beforeEach, afterEach } from 'vitest'
7
- import { ContextEngine } from '@contextgit/core'
8
- import { LocalStore } from './local/index.js'
9
-
10
- describe('ContextEngine + LocalStore integration', () => {
11
- let store: LocalStore
12
- let engine: ContextEngine
13
-
14
- beforeEach(() => {
15
- store = new LocalStore(':memory:')
16
- engine = new ContextEngine(store, 'agent-1', 'dev', 'claude-code', 'interactive')
17
- })
18
-
19
- afterEach(() => {
20
- store.close()
21
- })
22
-
23
- it('Week 1 validation: project → 2 commits → context("global") shows thread + both commits', async () => {
24
- // Setup: project + branch
25
- const project = await store.createProject({ name: 'contextgit', description: 'Persistent memory layer' })
26
- const branch = await store.createBranch({ projectId: project.id, name: 'main', gitBranch: 'main' })
27
-
28
- await engine.init(project.id, branch.id)
29
-
30
- // Commit 1 — opens a thread
31
- const c1 = await engine.commit({
32
- message: 'Set up monorepo structure',
33
- content: 'Created pnpm workspaces, tsconfig.base.json, core and store packages.',
34
- threads: { open: ['Database migration strategy not decided'] },
35
- })
36
- expect(c1.id).toBeTruthy()
37
- expect(c1.summary).toBeTruthy()
38
- expect(c1.agentRole).toBe('dev')
39
-
40
- // Commit 2 — no thread changes; summary should roll forward
41
- const c2 = await engine.commit({
42
- message: 'Add LocalStore implementation',
43
- content: 'Implemented LocalStore with better-sqlite3, schema, migrations, queries.',
44
- })
45
- expect(c2.id).toBeTruthy()
46
- // Rolling summary must include the new content
47
- expect(c2.summary).toContain('LocalStore')
48
-
49
- // context('global') → SessionSnapshot
50
- const snapshot = await engine.context('global')
51
-
52
- expect(snapshot.branchName).toBe('main')
53
- expect(snapshot.recentCommits).toHaveLength(2)
54
- expect(snapshot.recentCommits[0].message).toBe('Add LocalStore implementation')
55
- expect(snapshot.recentCommits[1].message).toBe('Set up monorepo structure')
56
-
57
- // Open thread from commit 1 must survive commit 2
58
- expect(snapshot.openThreads).toHaveLength(1)
59
- expect(snapshot.openThreads[0].description).toBe('Database migration strategy not decided')
60
- })
61
-
62
- it('summarizer rolls content into previous summary', async () => {
63
- const project = await store.createProject({ name: 'p' })
64
- const branch = await store.createBranch({ projectId: project.id, name: 'main', gitBranch: 'main' })
65
- await engine.init(project.id, branch.id)
66
-
67
- const c1 = await engine.commit({ message: 'first', content: 'alpha content' })
68
- const c2 = await engine.commit({ message: 'second', content: 'beta content' })
69
-
70
- // c2 summary must contain both pieces (budget large enough for test data)
71
- expect(c2.summary).toContain('alpha content')
72
- expect(c2.summary).toContain('beta content')
73
- // c1 summary should be just the first content
74
- expect(c1.summary).toBe('alpha content')
75
- })
76
-
77
- it('context("branch") returns the same snapshot as context("global")', async () => {
78
- const project = await store.createProject({ name: 'p' })
79
- const branch = await store.createBranch({ projectId: project.id, name: 'main', gitBranch: 'main' })
80
- await engine.init(project.id, branch.id)
81
- await engine.commit({ message: 'm', content: 'c' })
82
-
83
- const global = await engine.context('global')
84
- const branchCtx = await engine.context('branch')
85
- expect(branchCtx.branchName).toBe(global.branchName)
86
- expect(branchCtx.recentCommits).toHaveLength(global.recentCommits.length)
87
- })
88
-
89
- it('throws if context() called before init()', async () => {
90
- const uninitEngine = new ContextEngine(store, 'a', 'solo', 'cli', 'interactive')
91
- await expect(uninitEngine.context('global')).rejects.toThrow('not initialized')
92
- })
93
- })
@@ -1,3 +0,0 @@
1
- export type { ContextStore } from './interface.js'
2
- export { LocalStore } from './local/index.js'
3
- export { RemoteStore } from './remote/index.js'