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.
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 +13 -0
  10. package/dist/commands/claim.d.ts.map +1 -0
  11. package/dist/commands/claim.js +50 -0
  12. package/dist/commands/claim.js.map +1 -0
  13. package/dist/commands/commit.d.ts +14 -0
  14. package/dist/commands/commit.d.ts.map +1 -0
  15. package/dist/commands/commit.js +71 -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 +126 -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 +22 -19
  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,62 +0,0 @@
1
- import type {
2
- Agent,
3
- AgentInput,
4
- AgentRole,
5
- Branch,
6
- BranchInput,
7
- Claim,
8
- ClaimInput,
9
- Commit,
10
- CommitInput,
11
- Pagination,
12
- Project,
13
- ProjectInput,
14
- SearchResult,
15
- SessionSnapshot,
16
- SnapshotFormat,
17
- Thread,
18
- } from '@contextgit/core'
19
-
20
- export interface ContextStore {
21
- // Projects
22
- createProject(project: ProjectInput): Promise<Project>
23
- getProject(id: string): Promise<Project | null>
24
-
25
- // Branches
26
- createBranch(branch: BranchInput): Promise<Branch>
27
- getBranch(id: string): Promise<Branch | null>
28
- getBranchByGitName(projectId: string, gitBranch: string): Promise<Branch | null>
29
- listBranches(projectId: string): Promise<Branch[]>
30
- updateBranchHead(branchId: string, commitId: string): Promise<void>
31
- mergeBranch(sourceBranchId: string, targetBranchId: string, summary: string): Promise<Commit>
32
-
33
- // Commits
34
- createCommit(commit: CommitInput): Promise<Commit>
35
- getCommit(id: string): Promise<Commit | null>
36
- listCommits(branchId: string, pagination: Pagination): Promise<Commit[]>
37
-
38
- // Snapshots
39
- getSessionSnapshot(projectId: string, branchId: string, options?: { agentRole?: AgentRole }): Promise<SessionSnapshot>
40
- getFormattedSnapshot(projectId: string, branchId: string, format: SnapshotFormat): Promise<string>
41
-
42
- // Threads
43
- listOpenThreads(projectId: string): Promise<Thread[]>
44
- listOpenThreadsByBranch(branchId: string): Promise<Thread[]>
45
- syncThread(thread: Thread): Promise<Thread>
46
-
47
- // Search
48
- // vector is a 384-dim Float32Array produced by EmbeddingService in core.
49
- // Callers that cannot generate a vector should skip this and use fullTextSearch.
50
- indexEmbedding(commitId: string, vector: Float32Array): Promise<void>
51
- semanticSearch(vector: Float32Array, projectId: string, limit: number): Promise<SearchResult[]>
52
- fullTextSearch(query: string, projectId: string): Promise<SearchResult[]>
53
-
54
- // Agents
55
- upsertAgent(agent: AgentInput): Promise<Agent>
56
- listAgents(projectId: string): Promise<Agent[]>
57
-
58
- // Claims
59
- claimTask(projectId: string, branchId: string, input: ClaimInput): Promise<Claim>
60
- unclaimTask(claimId: string): Promise<void>
61
- listActiveClaims(projectId: string): Promise<Claim[]>
62
- }
@@ -1,190 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest'
2
- import { LocalStore } from './index.js'
3
-
4
- // Helpers to set up a minimal project + branch
5
- async function setupStore() {
6
- const store = new LocalStore(':memory:')
7
- const project = await store.createProject({ name: 'test-project' })
8
- const branch = await store.createBranch({
9
- projectId: project.id,
10
- name: 'main',
11
- gitBranch: 'main',
12
- })
13
- return { store, project, branch }
14
- }
15
-
16
- describe('claimTask', () => {
17
- afterEach(() => {
18
- // stores are :memory: — closed per test via store.close()
19
- })
20
-
21
- it('creates a claim with correct fields', async () => {
22
- const { store, project, branch } = await setupStore()
23
-
24
- const claim = await store.claimTask(project.id, branch.id, {
25
- task: 'build auth module',
26
- agentId: 'agent-1',
27
- role: 'dev',
28
- })
29
-
30
- expect(claim.id).toBeTruthy()
31
- expect(claim.projectId).toBe(project.id)
32
- expect(claim.branchId).toBe(branch.id)
33
- expect(claim.task).toBe('build auth module')
34
- expect(claim.agentId).toBe('agent-1')
35
- expect(claim.role).toBe('dev')
36
- expect(claim.status).toBe('proposed')
37
- expect(claim.ttl).toBe(7_200_000)
38
- expect(claim.claimedAt).toBeInstanceOf(Date)
39
- expect(claim.releasedAt).toBeUndefined()
40
-
41
- store.close()
42
- })
43
-
44
- it('respects custom status and ttl', async () => {
45
- const { store, project, branch } = await setupStore()
46
-
47
- const claim = await store.claimTask(project.id, branch.id, {
48
- task: 'write tests',
49
- agentId: 'agent-2',
50
- role: 'test',
51
- status: 'active',
52
- ttl: 3_600_000, // 1h
53
- })
54
-
55
- expect(claim.status).toBe('active')
56
- expect(claim.ttl).toBe(3_600_000)
57
-
58
- store.close()
59
- })
60
- })
61
-
62
- describe('listActiveClaims', () => {
63
- it('returns proposed and active claims', async () => {
64
- const { store, project, branch } = await setupStore()
65
-
66
- await store.claimTask(project.id, branch.id, { task: 'task-a', agentId: 'agent-1', role: 'dev', status: 'proposed' })
67
- await store.claimTask(project.id, branch.id, { task: 'task-b', agentId: 'agent-2', role: 'test', status: 'active' })
68
-
69
- const claims = await store.listActiveClaims(project.id)
70
- expect(claims).toHaveLength(2)
71
-
72
- store.close()
73
- })
74
-
75
- it('excludes released claims', async () => {
76
- const { store, project, branch } = await setupStore()
77
-
78
- const c1 = await store.claimTask(project.id, branch.id, { task: 'task-a', agentId: 'agent-1', role: 'dev' })
79
- await store.claimTask(project.id, branch.id, { task: 'task-b', agentId: 'agent-2', role: 'dev' })
80
- await store.unclaimTask(c1.id)
81
-
82
- const claims = await store.listActiveClaims(project.id)
83
- expect(claims).toHaveLength(1)
84
- expect(claims[0].task).toBe('task-b')
85
-
86
- store.close()
87
- })
88
-
89
- it('excludes TTL-expired claims', async () => {
90
- const { store, project, branch } = await setupStore()
91
-
92
- // Create a claim with 1ms TTL (already expired)
93
- await store.claimTask(project.id, branch.id, { task: 'expired', agentId: 'agent-1', role: 'dev', ttl: 1 })
94
- // Wait a tick to ensure the TTL has elapsed
95
- await new Promise((r) => setTimeout(r, 10))
96
- await store.claimTask(project.id, branch.id, { task: 'active', agentId: 'agent-2', role: 'dev', ttl: 7_200_000 })
97
-
98
- const claims = await store.listActiveClaims(project.id)
99
- expect(claims).toHaveLength(1)
100
- expect(claims[0].task).toBe('active')
101
-
102
- store.close()
103
- })
104
- })
105
-
106
- describe('unclaimTask', () => {
107
- it('sets status to released and sets releasedAt', async () => {
108
- const { store, project, branch } = await setupStore()
109
-
110
- const claim = await store.claimTask(project.id, branch.id, { task: 'do work', agentId: 'agent-1', role: 'solo' })
111
- await store.unclaimTask(claim.id)
112
-
113
- const active = await store.listActiveClaims(project.id)
114
- expect(active).toHaveLength(0)
115
-
116
- store.close()
117
- })
118
- })
119
-
120
- describe('createCommit auto-release', () => {
121
- it('releases this agent\'s claims on this branch after commit', async () => {
122
- const { store, project, branch } = await setupStore()
123
-
124
- await store.claimTask(project.id, branch.id, { task: 'my task', agentId: 'agent-1', role: 'dev' })
125
- // Second agent's claim should NOT be released
126
- await store.claimTask(project.id, branch.id, { task: 'other task', agentId: 'agent-2', role: 'dev' })
127
-
128
- await store.createCommit({
129
- branchId: branch.id,
130
- agentId: 'agent-1',
131
- agentRole: 'dev',
132
- tool: 'test',
133
- workflowType: 'interactive',
134
- message: 'done',
135
- content: 'finished',
136
- summary: 'finished',
137
- commitType: 'manual',
138
- })
139
-
140
- const remaining = await store.listActiveClaims(project.id)
141
- expect(remaining).toHaveLength(1)
142
- expect(remaining[0].agentId).toBe('agent-2')
143
-
144
- store.close()
145
- })
146
-
147
- it('does not release claims on other branches', async () => {
148
- const { store, project, branch } = await setupStore()
149
- const otherBranch = await store.createBranch({
150
- projectId: project.id,
151
- name: 'feature',
152
- gitBranch: 'feature/x',
153
- })
154
-
155
- // agent-1 has a claim on the other branch
156
- await store.claimTask(project.id, otherBranch.id, { task: 'other branch task', agentId: 'agent-1', role: 'dev' })
157
-
158
- await store.createCommit({
159
- branchId: branch.id,
160
- agentId: 'agent-1',
161
- agentRole: 'dev',
162
- tool: 'test',
163
- workflowType: 'interactive',
164
- message: 'commit on main',
165
- content: 'work',
166
- summary: 'work',
167
- commitType: 'manual',
168
- })
169
-
170
- const remaining = await store.listActiveClaims(project.id)
171
- expect(remaining).toHaveLength(1)
172
- expect(remaining[0].task).toBe('other branch task')
173
-
174
- store.close()
175
- })
176
- })
177
-
178
- describe('snapshot activeClaims', () => {
179
- it('includes active claims in session snapshot', async () => {
180
- const { store, project, branch } = await setupStore()
181
-
182
- await store.claimTask(project.id, branch.id, { task: 'build feature', agentId: 'agent-1', role: 'dev' })
183
-
184
- const snapshot = await store.getSessionSnapshot(project.id, branch.id)
185
- expect(snapshot.activeClaims).toHaveLength(1)
186
- expect(snapshot.activeClaims[0].task).toBe('build feature')
187
-
188
- store.close()
189
- })
190
- })
@@ -1,380 +0,0 @@
1
- // LocalStore — better-sqlite3 + sqlite-vec implementation of ContextStore.
2
- //
3
- // Key invariants:
4
- // • All SQLite calls are synchronous (better-sqlite3).
5
- // • The ContextStore interface returns Promises, so we wrap at the boundary
6
- // with Promise.resolve() / Promise.reject().
7
- // • IDs are nanoid() TEXT — never auto-increment integers.
8
- // • DB path: ~/.contextgit/projects/<projectId>.db
9
-
10
- import { homedir } from 'os'
11
- import { mkdirSync } from 'fs'
12
- import { join } from 'path'
13
- import { createRequire } from 'module'
14
- import Database from 'better-sqlite3'
15
- import { nanoid } from 'nanoid'
16
-
17
- const require = createRequire(import.meta.url)
18
- import type {
19
- Agent,
20
- AgentInput,
21
- Branch,
22
- BranchInput,
23
- Claim,
24
- ClaimInput,
25
- Commit,
26
- CommitInput,
27
- Pagination,
28
- Project,
29
- ProjectInput,
30
- SearchResult,
31
- SessionSnapshot,
32
- SnapshotFormat,
33
- Thread,
34
- } from '@contextgit/core'
35
- import { SnapshotFormatter } from '@contextgit/core'
36
- import type { ContextStore } from '../interface.js'
37
- import { runMigrations } from './migrations.js'
38
- import { Queries } from './queries.js'
39
-
40
- const snapshotFormatter = new SnapshotFormatter()
41
-
42
- // ─── LocalStore ───────────────────────────────────────────────────────────────
43
-
44
- export class LocalStore implements ContextStore {
45
- private readonly db: Database.Database
46
- private readonly q: Queries
47
-
48
- /**
49
- * @param projectId Used to derive the DB file path.
50
- * Pass ':memory:' for tests.
51
- */
52
- constructor(projectId: string) {
53
- let dbPath: string
54
- if (projectId === ':memory:') {
55
- dbPath = ':memory:'
56
- } else {
57
- const dir = join(homedir(), '.contextgit', 'projects')
58
- mkdirSync(dir, { recursive: true })
59
- dbPath = join(dir, `${projectId}.db`)
60
- }
61
-
62
- this.db = new Database(dbPath)
63
- this.db.pragma('journal_mode = WAL')
64
- this.db.pragma('busy_timeout = 5000')
65
- this.db.pragma('foreign_keys = ON')
66
-
67
- // Attempt to load sqlite-vec extension — graceful failure if not available
68
- try {
69
- const sqliteVec = require('sqlite-vec') as { load: (db: Database.Database) => void }
70
- sqliteVec.load(this.db)
71
- } catch {
72
- // sqlite-vec not installed or ABI mismatch — semantic search disabled
73
- }
74
-
75
- runMigrations(this.db)
76
- this.q = new Queries(this.db)
77
- }
78
-
79
- // ─── Projects ─────────────────────────────────────────────────────────────
80
-
81
- createProject(project: ProjectInput): Promise<Project> {
82
- try {
83
- return Promise.resolve(this.q.insertProject({ id: project.id ?? nanoid(), ...project }))
84
- } catch (e) {
85
- return Promise.reject(e)
86
- }
87
- }
88
-
89
- getProject(id: string): Promise<Project | null> {
90
- try {
91
- return Promise.resolve(this.q.getProject(id))
92
- } catch (e) {
93
- return Promise.reject(e)
94
- }
95
- }
96
-
97
- // ─── Branches ─────────────────────────────────────────────────────────────
98
-
99
- createBranch(branch: BranchInput): Promise<Branch> {
100
- try {
101
- const id = branch.id ?? nanoid()
102
- // Idempotent: if caller supplied an ID and it already exists, return existing
103
- if (branch.id) {
104
- const existing = this.q.getBranch(branch.id)
105
- if (existing) return Promise.resolve(existing)
106
- }
107
- return Promise.resolve(this.q.insertBranch({ ...branch, id }))
108
- } catch (e) {
109
- return Promise.reject(e)
110
- }
111
- }
112
-
113
- getBranch(id: string): Promise<Branch | null> {
114
- try {
115
- return Promise.resolve(this.q.getBranch(id))
116
- } catch (e) {
117
- return Promise.reject(e)
118
- }
119
- }
120
-
121
- getBranchByGitName(projectId: string, gitBranch: string): Promise<Branch | null> {
122
- try {
123
- return Promise.resolve(this.q.getBranchByGitName(projectId, gitBranch))
124
- } catch (e) {
125
- return Promise.reject(e)
126
- }
127
- }
128
-
129
- listBranches(projectId: string): Promise<Branch[]> {
130
- try {
131
- return Promise.resolve(this.q.listBranches(projectId))
132
- } catch (e) {
133
- return Promise.reject(e)
134
- }
135
- }
136
-
137
- updateBranchHead(branchId: string, commitId: string): Promise<void> {
138
- try {
139
- this.q.updateBranchHead(branchId, commitId)
140
- return Promise.resolve()
141
- } catch (e) {
142
- return Promise.reject(e)
143
- }
144
- }
145
-
146
- mergeBranch(sourceBranchId: string, targetBranchId: string, summary: string): Promise<Commit> {
147
- try {
148
- const result = this.db.transaction((): Commit => {
149
- const sourceBranch = this.q.getBranch(sourceBranchId)
150
- if (!sourceBranch) throw new Error(`Source branch not found: ${sourceBranchId}`)
151
- const targetBranch = this.q.getBranch(targetBranchId)
152
- if (!targetBranch) throw new Error(`Target branch not found: ${targetBranchId}`)
153
-
154
- const mergeCommitInput: CommitInput = {
155
- branchId: targetBranchId,
156
- parentId: targetBranch.headCommitId,
157
- agentId: 'system',
158
- agentRole: 'orchestrator',
159
- tool: 'contextgit',
160
- workflowType: 'interactive',
161
- message: `Merge ${sourceBranch.name} into ${targetBranch.name}`,
162
- content: summary,
163
- summary,
164
- commitType: 'merge',
165
- }
166
-
167
- const mergeCommit = this.q.insertCommit(
168
- nanoid(),
169
- mergeCommitInput,
170
- targetBranch.headCommitId ?? null,
171
- sourceBranchId,
172
- )
173
-
174
- // Update target branch HEAD
175
- this.q.updateBranchHead(targetBranchId, mergeCommit.id)
176
-
177
- // Mark source branch as merged
178
- this.q.markBranchMerged(sourceBranchId)
179
-
180
- // Carry open threads from source branch to target branch
181
- this.q.reassignOpenThreads(sourceBranchId, targetBranchId)
182
-
183
- return mergeCommit
184
- })()
185
-
186
- return Promise.resolve(result)
187
- } catch (e) {
188
- return Promise.reject(e)
189
- }
190
- }
191
-
192
- // ─── Commits ──────────────────────────────────────────────────────────────
193
-
194
- createCommit(input: CommitInput): Promise<Commit> {
195
- try {
196
- // Idempotent: if caller supplied an ID and it already exists, return existing
197
- if (input.id) {
198
- const existing = this.q.getCommit(input.id)
199
- if (existing) return Promise.resolve(existing)
200
- }
201
-
202
- const result = this.db.transaction((): Commit => {
203
- const branch = this.q.getBranch(input.branchId)
204
- if (!branch) throw new Error(`Branch not found: ${input.branchId}`)
205
-
206
- const commitId = input.id ?? nanoid()
207
- const parentId = input.parentId ?? branch.headCommitId ?? null
208
-
209
- const commit = this.q.insertCommit(commitId, input, parentId)
210
-
211
- // Handle thread operations
212
- if (input.threads?.open?.length) {
213
- for (const description of input.threads.open) {
214
- this.q.insertThread(
215
- nanoid(),
216
- description,
217
- branch.projectId,
218
- input.branchId,
219
- commitId,
220
- input.workflowType ?? null,
221
- )
222
- }
223
- }
224
-
225
- if (input.threads?.close?.length) {
226
- for (const { id, note } of input.threads.close) {
227
- this.q.closeThread(id, commitId, note)
228
- }
229
- }
230
-
231
- // Update branch HEAD
232
- this.q.updateBranchHead(input.branchId, commitId)
233
-
234
- // Update agent stats
235
- this.q.incrementAgentCommits(input.agentId)
236
-
237
- // Auto-release this agent's claims on this branch (branch-scoped)
238
- this.q.releaseClaimsByAgent(input.agentId, input.branchId)
239
-
240
- return commit
241
- })()
242
-
243
- return Promise.resolve(result)
244
- } catch (e) {
245
- return Promise.reject(e)
246
- }
247
- }
248
-
249
- getCommit(id: string): Promise<Commit | null> {
250
- try {
251
- return Promise.resolve(this.q.getCommit(id))
252
- } catch (e) {
253
- return Promise.reject(e)
254
- }
255
- }
256
-
257
- listCommits(branchId: string, pagination: Pagination): Promise<Commit[]> {
258
- try {
259
- return Promise.resolve(this.q.listCommits(branchId, pagination))
260
- } catch (e) {
261
- return Promise.reject(e)
262
- }
263
- }
264
-
265
- // ─── Snapshots ────────────────────────────────────────────────────────────
266
-
267
- getSessionSnapshot(projectId: string, branchId: string, options?: { agentRole?: import('@contextgit/core').AgentRole }): Promise<SessionSnapshot> {
268
- try {
269
- return Promise.resolve(this.q.getSessionSnapshot(projectId, branchId, options))
270
- } catch (e) {
271
- return Promise.reject(e)
272
- }
273
- }
274
-
275
- getFormattedSnapshot(projectId: string, branchId: string, format: SnapshotFormat): Promise<string> {
276
- try {
277
- const snapshot = this.q.getSessionSnapshot(projectId, branchId)
278
- return Promise.resolve(snapshotFormatter.format(snapshot, format))
279
- } catch (e) {
280
- return Promise.reject(e)
281
- }
282
- }
283
-
284
- // ─── Threads ──────────────────────────────────────────────────────────────
285
-
286
- listOpenThreads(projectId: string): Promise<Thread[]> {
287
- try {
288
- return Promise.resolve(this.q.listOpenThreads(projectId))
289
- } catch (e) {
290
- return Promise.reject(e)
291
- }
292
- }
293
-
294
- listOpenThreadsByBranch(branchId: string): Promise<Thread[]> {
295
- try {
296
- return Promise.resolve(this.q.listOpenThreadsByBranch(branchId))
297
- } catch (e) {
298
- return Promise.reject(e)
299
- }
300
- }
301
-
302
- syncThread(thread: Thread): Promise<Thread> {
303
- try {
304
- return Promise.resolve(this.q.syncThread(thread))
305
- } catch (e) {
306
- return Promise.reject(e)
307
- }
308
- }
309
-
310
- // ─── Search ───────────────────────────────────────────────────────────────
311
-
312
- indexEmbedding(commitId: string, vector: Float32Array): Promise<void> {
313
- this.q.insertEmbedding(this.db, commitId, vector)
314
- return Promise.resolve()
315
- }
316
-
317
- semanticSearch(vector: Float32Array, projectId: string, limit: number): Promise<SearchResult[]> {
318
- return Promise.resolve(this.q.semanticSearch(this.db, vector, projectId, limit))
319
- }
320
-
321
- fullTextSearch(query: string, projectId: string): Promise<SearchResult[]> {
322
- try {
323
- return Promise.resolve(this.q.fullTextSearch(this.db, query, projectId))
324
- } catch (e) {
325
- return Promise.reject(e)
326
- }
327
- }
328
-
329
- // ─── Agents ───────────────────────────────────────────────────────────────
330
-
331
- upsertAgent(agent: AgentInput): Promise<Agent> {
332
- try {
333
- return Promise.resolve(this.q.upsertAgent(agent))
334
- } catch (e) {
335
- return Promise.reject(e)
336
- }
337
- }
338
-
339
- listAgents(projectId: string): Promise<Agent[]> {
340
- try {
341
- return Promise.resolve(this.q.listAgents(projectId))
342
- } catch (e) {
343
- return Promise.reject(e)
344
- }
345
- }
346
-
347
- // ─── Claims ───────────────────────────────────────────────────────────────
348
-
349
- claimTask(projectId: string, branchId: string, input: ClaimInput): Promise<Claim> {
350
- try {
351
- return Promise.resolve(this.q.insertClaim(nanoid(), projectId, branchId, input))
352
- } catch (e) {
353
- return Promise.reject(e)
354
- }
355
- }
356
-
357
- unclaimTask(claimId: string): Promise<void> {
358
- try {
359
- this.q.updateClaimStatus(claimId, 'released', Date.now())
360
- return Promise.resolve()
361
- } catch (e) {
362
- return Promise.reject(e)
363
- }
364
- }
365
-
366
- listActiveClaims(projectId: string): Promise<Claim[]> {
367
- try {
368
- return Promise.resolve(this.q.listActiveClaims(projectId))
369
- } catch (e) {
370
- return Promise.reject(e)
371
- }
372
- }
373
-
374
- // ─── Lifecycle ────────────────────────────────────────────────────────────
375
-
376
- close(): void {
377
- this.db.close()
378
- }
379
- }
380
-