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
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
// RollingSummarizer — Week 2: Claude Haiku with graceful string-truncation fallback.
|
|
2
|
-
// If the API key is absent or the API call fails, falls back to string truncation.
|
|
3
|
-
// The interface is stable; callers never know which implementation is running.
|
|
4
|
-
|
|
5
|
-
import Anthropic from '@anthropic-ai/sdk'
|
|
6
|
-
|
|
7
|
-
export interface SummarizerOptions {
|
|
8
|
-
/** Max characters for project-level summaries (~2000 tokens). */
|
|
9
|
-
maxProjectChars?: number
|
|
10
|
-
/** Max characters for branch-level summaries (~500 tokens). */
|
|
11
|
-
maxBranchChars?: number
|
|
12
|
-
/**
|
|
13
|
-
* Inject a pre-built Anthropic client (useful for tests).
|
|
14
|
-
* If omitted, one is created from ANTHROPIC_API_KEY.
|
|
15
|
-
* If ANTHROPIC_API_KEY is also absent, falls back to string truncation.
|
|
16
|
-
*/
|
|
17
|
-
client?: Anthropic
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export class RollingSummarizer {
|
|
21
|
-
private readonly maxProjectChars: number
|
|
22
|
-
private readonly maxBranchChars: number
|
|
23
|
-
private readonly client: Anthropic | null
|
|
24
|
-
|
|
25
|
-
constructor(options: SummarizerOptions = {}) {
|
|
26
|
-
this.maxProjectChars = options.maxProjectChars ?? 8000
|
|
27
|
-
this.maxBranchChars = options.maxBranchChars ?? 2000
|
|
28
|
-
|
|
29
|
-
if (options.client !== undefined) {
|
|
30
|
-
this.client = options.client
|
|
31
|
-
} else if (process.env['ANTHROPIC_API_KEY']) {
|
|
32
|
-
this.client = new Anthropic()
|
|
33
|
-
} else {
|
|
34
|
-
this.client = null
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Roll `content` into `previousSummary` and return a new summary that fits
|
|
40
|
-
* within the budget for the given scope.
|
|
41
|
-
*
|
|
42
|
-
* Week 2: uses Claude Haiku for intelligent compression.
|
|
43
|
-
* Fallback: dumb truncation (newest content appended, trimmed from the start
|
|
44
|
-
* so the most-recent work is always present).
|
|
45
|
-
*
|
|
46
|
-
* Never throws — summarizer failures are silently swallowed.
|
|
47
|
-
*/
|
|
48
|
-
async summarize(
|
|
49
|
-
content: string,
|
|
50
|
-
previousSummary: string,
|
|
51
|
-
budget: 'project' | 'branch',
|
|
52
|
-
): Promise<string> {
|
|
53
|
-
const max = budget === 'project' ? this.maxProjectChars : this.maxBranchChars
|
|
54
|
-
|
|
55
|
-
if (this.client !== null) {
|
|
56
|
-
try {
|
|
57
|
-
return await this._claudeSummarize(content, previousSummary, max)
|
|
58
|
-
} catch {
|
|
59
|
-
// Graceful fallback — never propagate summarizer failure to the caller.
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return this._truncateSummarize(content, previousSummary, max)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ─── Private ────────────────────────────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
private async _claudeSummarize(
|
|
69
|
-
content: string,
|
|
70
|
-
previousSummary: string,
|
|
71
|
-
maxChars: number,
|
|
72
|
-
): Promise<string> {
|
|
73
|
-
const userContent = previousSummary
|
|
74
|
-
? `Previous summary:\n${previousSummary}\n\nNew content to integrate:\n${content}`
|
|
75
|
-
: `Content to summarize:\n${content}`
|
|
76
|
-
|
|
77
|
-
const response = await this.client!.messages.create({
|
|
78
|
-
model: 'claude-haiku-4-5',
|
|
79
|
-
max_tokens: 1024,
|
|
80
|
-
system:
|
|
81
|
-
'You are a context summarizer for an AI coding agent. ' +
|
|
82
|
-
'Return only the summary text with no preamble or explanation.',
|
|
83
|
-
messages: [
|
|
84
|
-
{
|
|
85
|
-
role: 'user',
|
|
86
|
-
content:
|
|
87
|
-
userContent +
|
|
88
|
-
`\n\nCreate a concise rolling summary (max ${maxChars} characters) ` +
|
|
89
|
-
'that preserves the most important recent work and decisions. ' +
|
|
90
|
-
'Return only the summary text.',
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const text = response.content
|
|
96
|
-
.filter((b): b is Anthropic.TextBlock => b.type === 'text')
|
|
97
|
-
.map(b => b.text)
|
|
98
|
-
.join('')
|
|
99
|
-
|
|
100
|
-
return text.slice(0, maxChars)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
private _truncateSummarize(
|
|
104
|
-
content: string,
|
|
105
|
-
previousSummary: string,
|
|
106
|
-
max: number,
|
|
107
|
-
): string {
|
|
108
|
-
const combined = previousSummary ? `${previousSummary}\n\n${content}` : content
|
|
109
|
-
if (combined.length <= max) return combined
|
|
110
|
-
// Keep the tail so recent work is never lost.
|
|
111
|
-
return combined.slice(combined.length - max)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// ThreadManager — read-side helper for the open-thread immune-to-compression guarantee.
|
|
2
|
-
//
|
|
3
|
-
// Key invariant: open threads are stored in the `threads` table and NEVER
|
|
4
|
-
// passed to the summarizer. This file enforces that boundary by providing
|
|
5
|
-
// the only sanctioned way to query open threads.
|
|
6
|
-
//
|
|
7
|
-
// Write-side (opening / closing threads) happens via CommitInput.threads
|
|
8
|
-
// inside store.createCommit(), which is the right transactional boundary.
|
|
9
|
-
|
|
10
|
-
import type { Thread } from './types.js'
|
|
11
|
-
|
|
12
|
-
export interface ThreadReader {
|
|
13
|
-
listOpenThreads(projectId: string): Promise<Thread[]>
|
|
14
|
-
listOpenThreadsByBranch(branchId: string): Promise<Thread[]>
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export class ThreadManager {
|
|
18
|
-
constructor(private readonly store: ThreadReader) {}
|
|
19
|
-
|
|
20
|
-
/** All open threads for a project, across all branches. */
|
|
21
|
-
openForProject(projectId: string): Promise<Thread[]> {
|
|
22
|
-
return this.store.listOpenThreads(projectId)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Open threads scoped to a single branch. */
|
|
26
|
-
openForBranch(branchId: string): Promise<Thread[]> {
|
|
27
|
-
return this.store.listOpenThreadsByBranch(branchId)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// ============================================
|
|
4
|
-
// Primitive Types
|
|
5
|
-
// ============================================
|
|
6
|
-
|
|
7
|
-
export type AgentRole =
|
|
8
|
-
| 'orchestrator'
|
|
9
|
-
| 'dev'
|
|
10
|
-
| 'test'
|
|
11
|
-
| 'review'
|
|
12
|
-
| 'background'
|
|
13
|
-
| 'ci'
|
|
14
|
-
| 'solo'
|
|
15
|
-
|
|
16
|
-
export type WorkflowType =
|
|
17
|
-
| 'interactive'
|
|
18
|
-
| 'ralph-loop'
|
|
19
|
-
| 'ci'
|
|
20
|
-
| 'background'
|
|
21
|
-
| 'custom'
|
|
22
|
-
|
|
23
|
-
export type CommitType =
|
|
24
|
-
| 'manual'
|
|
25
|
-
| 'auto'
|
|
26
|
-
| 'merge'
|
|
27
|
-
| 'branch-init'
|
|
28
|
-
|
|
29
|
-
export type BranchStatus =
|
|
30
|
-
| 'active'
|
|
31
|
-
| 'merged'
|
|
32
|
-
| 'abandoned'
|
|
33
|
-
|
|
34
|
-
export type ClaimStatus =
|
|
35
|
-
| 'proposed'
|
|
36
|
-
| 'active'
|
|
37
|
-
| 'released'
|
|
38
|
-
|
|
39
|
-
export type SnapshotFormat =
|
|
40
|
-
| 'agents-md'
|
|
41
|
-
| 'json'
|
|
42
|
-
| 'text'
|
|
43
|
-
|
|
44
|
-
export type ContextScope =
|
|
45
|
-
| 'global'
|
|
46
|
-
| 'branch'
|
|
47
|
-
| 'search'
|
|
48
|
-
| 'commit'
|
|
49
|
-
| 'raw'
|
|
50
|
-
|
|
51
|
-
// ============================================
|
|
52
|
-
// Core Entities
|
|
53
|
-
// ============================================
|
|
54
|
-
|
|
55
|
-
export interface Project {
|
|
56
|
-
id: string
|
|
57
|
-
name: string
|
|
58
|
-
description?: string
|
|
59
|
-
githubUrl?: string
|
|
60
|
-
createdAt: Date
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export interface Branch {
|
|
64
|
-
id: string
|
|
65
|
-
projectId: string
|
|
66
|
-
name: string
|
|
67
|
-
gitBranch: string
|
|
68
|
-
githubPrUrl?: string
|
|
69
|
-
parentBranchId?: string
|
|
70
|
-
headCommitId?: string
|
|
71
|
-
status: BranchStatus
|
|
72
|
-
createdAt: Date
|
|
73
|
-
mergedAt?: Date
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface Commit {
|
|
77
|
-
id: string
|
|
78
|
-
branchId: string
|
|
79
|
-
parentId?: string
|
|
80
|
-
mergeSourceBranchId?: string
|
|
81
|
-
agentId: string
|
|
82
|
-
agentRole: AgentRole
|
|
83
|
-
tool: string
|
|
84
|
-
workflowType: WorkflowType
|
|
85
|
-
loopIteration?: number
|
|
86
|
-
ciRunId?: string
|
|
87
|
-
pipelineName?: string
|
|
88
|
-
message: string
|
|
89
|
-
content: string
|
|
90
|
-
summary: string
|
|
91
|
-
commitType: CommitType
|
|
92
|
-
gitCommitSha?: string
|
|
93
|
-
createdAt: Date
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface Thread {
|
|
97
|
-
id: string
|
|
98
|
-
projectId: string
|
|
99
|
-
branchId: string
|
|
100
|
-
description: string // max 200 chars
|
|
101
|
-
status: 'open' | 'closed'
|
|
102
|
-
workflowType?: WorkflowType
|
|
103
|
-
openedInCommit: string
|
|
104
|
-
closedInCommit?: string
|
|
105
|
-
closedNote?: string
|
|
106
|
-
createdAt: Date
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface Agent {
|
|
110
|
-
id: string
|
|
111
|
-
projectId: string
|
|
112
|
-
role: AgentRole
|
|
113
|
-
tool: string
|
|
114
|
-
workflowType: WorkflowType
|
|
115
|
-
displayName?: string
|
|
116
|
-
totalCommits: number
|
|
117
|
-
lastSeen: Date
|
|
118
|
-
createdAt: Date
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export interface Claim {
|
|
122
|
-
id: string
|
|
123
|
-
projectId: string
|
|
124
|
-
branchId: string
|
|
125
|
-
task: string
|
|
126
|
-
agentId: string
|
|
127
|
-
role: AgentRole
|
|
128
|
-
claimedAt: Date
|
|
129
|
-
status: ClaimStatus
|
|
130
|
-
ttl: number // ms, default 7_200_000 (2h)
|
|
131
|
-
releasedAt?: Date
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// ============================================
|
|
135
|
-
// Input Types (for creating entities)
|
|
136
|
-
// ============================================
|
|
137
|
-
|
|
138
|
-
export interface ProjectInput {
|
|
139
|
-
id?: string // caller-supplied ID; LocalStore generates nanoid() if omitted
|
|
140
|
-
name: string
|
|
141
|
-
description?: string
|
|
142
|
-
githubUrl?: string
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export interface BranchInput {
|
|
146
|
-
id?: string // caller-supplied ID; LocalStore generates nanoid() if omitted
|
|
147
|
-
projectId: string
|
|
148
|
-
name: string
|
|
149
|
-
gitBranch: string
|
|
150
|
-
parentBranchId?: string
|
|
151
|
-
githubPrUrl?: string
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export interface CommitInput {
|
|
155
|
-
id?: string // caller-supplied ID; LocalStore generates nanoid() if omitted
|
|
156
|
-
branchId: string
|
|
157
|
-
parentId?: string
|
|
158
|
-
agentId: string
|
|
159
|
-
agentRole: AgentRole
|
|
160
|
-
tool: string
|
|
161
|
-
workflowType: WorkflowType
|
|
162
|
-
loopIteration?: number
|
|
163
|
-
ciRunId?: string
|
|
164
|
-
pipelineName?: string
|
|
165
|
-
message: string
|
|
166
|
-
content: string
|
|
167
|
-
summary: string
|
|
168
|
-
commitType: CommitType
|
|
169
|
-
gitCommitSha?: string
|
|
170
|
-
threads?: {
|
|
171
|
-
open?: string[]
|
|
172
|
-
close?: Array<{ id: string; note: string }>
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export interface AgentInput {
|
|
177
|
-
id: string
|
|
178
|
-
projectId: string
|
|
179
|
-
role: AgentRole
|
|
180
|
-
tool: string
|
|
181
|
-
workflowType: WorkflowType
|
|
182
|
-
displayName?: string
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export interface ClaimInput {
|
|
186
|
-
task: string
|
|
187
|
-
agentId: string
|
|
188
|
-
role: AgentRole
|
|
189
|
-
status?: ClaimStatus // defaults to 'proposed'
|
|
190
|
-
ttl?: number // ms, defaults to 7_200_000 (2h)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// ============================================
|
|
194
|
-
// Session Snapshot
|
|
195
|
-
// ============================================
|
|
196
|
-
|
|
197
|
-
export interface SessionSnapshot {
|
|
198
|
-
projectSummary: string // max 2000 tokens
|
|
199
|
-
branchName: string
|
|
200
|
-
branchSummary: string // max 500 tokens
|
|
201
|
-
recentCommits: Commit[] // last 3
|
|
202
|
-
openThreads: Thread[]
|
|
203
|
-
activeClaims: Claim[] // non-released, non-TTL-expired claims
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// ============================================
|
|
207
|
-
// Search
|
|
208
|
-
// ============================================
|
|
209
|
-
|
|
210
|
-
export interface SearchResult {
|
|
211
|
-
commit: Commit
|
|
212
|
-
score: number
|
|
213
|
-
matchType: 'semantic' | 'fulltext'
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ============================================
|
|
217
|
-
// Config
|
|
218
|
-
// ============================================
|
|
219
|
-
|
|
220
|
-
export interface ContextGitConfig {
|
|
221
|
-
project: string
|
|
222
|
-
projectId: string
|
|
223
|
-
store: 'local' | string // 'local' or remote URL
|
|
224
|
-
remote?: string // remote ContextGit API URL for push/pull
|
|
225
|
-
agentRole: AgentRole
|
|
226
|
-
workflowType: WorkflowType
|
|
227
|
-
autoSnapshot: boolean
|
|
228
|
-
snapshotInterval: number
|
|
229
|
-
embeddingModel: 'local' | 'openai'
|
|
230
|
-
apiKey?: string
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// ============================================
|
|
234
|
-
// Pagination
|
|
235
|
-
// ============================================
|
|
236
|
-
|
|
237
|
-
export interface Pagination {
|
|
238
|
-
limit: number
|
|
239
|
-
offset: number
|
|
240
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@contextgit/mcp",
|
|
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
|
-
"bin": {
|
|
14
|
-
"contextgit-mcp": "./dist/index.js"
|
|
15
|
-
},
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "tsc",
|
|
18
|
-
"typecheck": "tsc --noEmit"
|
|
19
|
-
},
|
|
20
|
-
"dependencies": {
|
|
21
|
-
"@contextgit/core": "workspace:*",
|
|
22
|
-
"@contextgit/store": "workspace:*",
|
|
23
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
24
|
-
"simple-git": "^3.27.0",
|
|
25
|
-
"zod": "^3.23.0"
|
|
26
|
-
},
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"@types/node": "^20.0.0",
|
|
29
|
-
"typescript": "^5.4.0"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
// auto-snapshot.ts — AutoSnapshotManager
|
|
2
|
-
//
|
|
3
|
-
// Tracks tool calls made to the MCP server and fires an automatic context_commit
|
|
4
|
-
// after every N=10 non-commit calls. This ensures context is preserved even when
|
|
5
|
-
// the agent forgets to call context_commit manually.
|
|
6
|
-
//
|
|
7
|
-
// Context tool semantics:
|
|
8
|
-
// context_commit → resets the counter (manual commit = checkpoint reached)
|
|
9
|
-
// context_get → counted (each session-start snapshot counts toward the interval)
|
|
10
|
-
// context_search → counted
|
|
11
|
-
// any other tool → counted (future tools)
|
|
12
|
-
|
|
13
|
-
import type { ContextEngine } from '@contextgit/core'
|
|
14
|
-
|
|
15
|
-
export interface AutoSnapshotOptions {
|
|
16
|
-
/** Number of non-commit tool calls before an auto-commit fires. Default: 10. */
|
|
17
|
-
interval?: number
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export class AutoSnapshotManager {
|
|
21
|
-
private count = 0
|
|
22
|
-
private readonly interval: number
|
|
23
|
-
private readonly engine: ContextEngine
|
|
24
|
-
|
|
25
|
-
constructor(engine: ContextEngine, options: AutoSnapshotOptions = {}) {
|
|
26
|
-
this.engine = engine
|
|
27
|
-
this.interval = options.interval ?? 10
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Record a tool call by name.
|
|
32
|
-
*
|
|
33
|
-
* - `context_commit` resets the counter (manual commit; no auto-commit fired).
|
|
34
|
-
* - All other tool names increment the counter.
|
|
35
|
-
* - When the counter reaches `interval`, an auto-commit is fired and the
|
|
36
|
-
* counter resets to 0.
|
|
37
|
-
*
|
|
38
|
-
* Auto-commit failures are swallowed — the tool call is never blocked.
|
|
39
|
-
*
|
|
40
|
-
* @returns The new commit ID if an auto-commit was fired, otherwise undefined.
|
|
41
|
-
*/
|
|
42
|
-
async onToolCall(toolName: string): Promise<string | undefined> {
|
|
43
|
-
if (toolName === 'context_commit') {
|
|
44
|
-
this.count = 0
|
|
45
|
-
return undefined
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this.count++
|
|
49
|
-
|
|
50
|
-
if (this.count >= this.interval) {
|
|
51
|
-
this.count = 0
|
|
52
|
-
return this.fireAutoCommit()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return undefined
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Current tool-call count since the last commit (manual or auto). */
|
|
59
|
-
get toolCallCount(): number {
|
|
60
|
-
return this.count
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** Manually reset the counter (e.g. after an out-of-band commit). */
|
|
64
|
-
reset(): void {
|
|
65
|
-
this.count = 0
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ─── Private ──────────────────────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
private async fireAutoCommit(): Promise<string | undefined> {
|
|
71
|
-
try {
|
|
72
|
-
const commit = await this.engine.commit({
|
|
73
|
-
message: `Auto-snapshot after ${this.interval} tool calls`,
|
|
74
|
-
content: `Automatic context checkpoint triggered after ${this.interval} tool calls without a manual context_commit.`,
|
|
75
|
-
commitType: 'auto',
|
|
76
|
-
})
|
|
77
|
-
return commit.id
|
|
78
|
-
} catch {
|
|
79
|
-
// Never block a tool call due to auto-snapshot failure.
|
|
80
|
-
return undefined
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
@@ -1,53 +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
|
-
/**
|
|
16
|
-
* Search upward from `startDir` for `.contextgit/config.json`.
|
|
17
|
-
* Returns the first match found, or throws ConfigNotFoundError.
|
|
18
|
-
*/
|
|
19
|
-
export function findConfigPath(startDir: string = process.cwd()): string {
|
|
20
|
-
let current = startDir
|
|
21
|
-
while (true) {
|
|
22
|
-
const candidate = join(current, '.contextgit', 'config.json')
|
|
23
|
-
try {
|
|
24
|
-
readFileSync(candidate)
|
|
25
|
-
return candidate
|
|
26
|
-
} catch {
|
|
27
|
-
const parent = dirname(current)
|
|
28
|
-
if (parent === current) {
|
|
29
|
-
throw new ConfigNotFoundError(startDir)
|
|
30
|
-
}
|
|
31
|
-
current = parent
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Load and parse `.contextgit/config.json`.
|
|
38
|
-
* Throws ConfigNotFoundError if not found, or Error if JSON is invalid.
|
|
39
|
-
*/
|
|
40
|
-
export function loadConfig(startDir?: string): ContextGitConfig {
|
|
41
|
-
const configPath = findConfigPath(startDir)
|
|
42
|
-
const raw = readFileSync(configPath, 'utf-8')
|
|
43
|
-
const config = JSON.parse(raw) as ContextGitConfig
|
|
44
|
-
|
|
45
|
-
if (!config.projectId) {
|
|
46
|
-
throw new Error(`Invalid config at ${configPath}: missing required field 'projectId'`)
|
|
47
|
-
}
|
|
48
|
-
if (!config.project) {
|
|
49
|
-
throw new Error(`Invalid config at ${configPath}: missing required field 'project'`)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return config
|
|
53
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// git-sync.ts — git metadata capture and hook installation.
|
|
2
|
-
//
|
|
3
|
-
// captureGitMetadata: used by context_commit (MCP) and commit CLI command to
|
|
4
|
-
// auto-populate gitCommitSha on every context commit.
|
|
5
|
-
//
|
|
6
|
-
// installGitHooks: idempotent hook installer — writes post-commit,
|
|
7
|
-
// post-checkout, post-merge scripts into .git/hooks/.
|
|
8
|
-
|
|
9
|
-
import { writeFileSync, readFileSync, mkdirSync, existsSync, appendFileSync } from 'fs'
|
|
10
|
-
import { join, resolve } from 'path'
|
|
11
|
-
import { homedir } from 'os'
|
|
12
|
-
import { simpleGit } from 'simple-git'
|
|
13
|
-
|
|
14
|
-
const SENTINEL = '# contextgit'
|
|
15
|
-
const HOOKS_LOG = join(homedir(), '.contextgit', 'hooks.log')
|
|
16
|
-
|
|
17
|
-
// ─── captureGitMetadata ────────────────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Capture the current git commit SHA and branch name.
|
|
21
|
-
* Returns null on any error — must never block a context commit.
|
|
22
|
-
*/
|
|
23
|
-
export async function captureGitMetadata(
|
|
24
|
-
cwd: string,
|
|
25
|
-
): Promise<{ sha: string; branch: string } | null> {
|
|
26
|
-
try {
|
|
27
|
-
const git = simpleGit(cwd)
|
|
28
|
-
const [sha, branch] = await Promise.all([
|
|
29
|
-
git.revparse(['HEAD']),
|
|
30
|
-
git.revparse(['--abbrev-ref', 'HEAD']),
|
|
31
|
-
])
|
|
32
|
-
return { sha: sha.trim(), branch: branch.trim() }
|
|
33
|
-
} catch {
|
|
34
|
-
return null
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ─── installGitHooks ──────────────────────────────────────────────────────────
|
|
39
|
-
|
|
40
|
-
const HOOK_SCRIPTS: Record<string, string> = {
|
|
41
|
-
'post-commit': `#!/bin/sh
|
|
42
|
-
${SENTINEL}
|
|
43
|
-
contextgit commit -m "git: $(git log -1 --pretty=%s)" 2>>"${HOOKS_LOG}" || true
|
|
44
|
-
`,
|
|
45
|
-
'post-checkout': `#!/bin/sh
|
|
46
|
-
${SENTINEL}
|
|
47
|
-
contextgit context --quiet 2>>"${HOOKS_LOG}" || true
|
|
48
|
-
`,
|
|
49
|
-
'post-merge': `#!/bin/sh
|
|
50
|
-
${SENTINEL}
|
|
51
|
-
contextgit commit -m "Merged into $(git rev-parse --abbrev-ref HEAD)" 2>>"${HOOKS_LOG}" || true
|
|
52
|
-
`,
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Install contextgit git hooks into <projectRoot>/.git/hooks/.
|
|
57
|
-
* Idempotent: checks for the sentinel comment before appending.
|
|
58
|
-
* Hook failures are logged to ~/.contextgit/hooks.log — never to stderr.
|
|
59
|
-
*/
|
|
60
|
-
export async function installGitHooks(projectRoot: string): Promise<void> {
|
|
61
|
-
const hooksDir = join(resolve(projectRoot), '.git', 'hooks')
|
|
62
|
-
mkdirSync(hooksDir, { recursive: true })
|
|
63
|
-
|
|
64
|
-
for (const [hookName, script] of Object.entries(HOOK_SCRIPTS)) {
|
|
65
|
-
const hookPath = join(hooksDir, hookName)
|
|
66
|
-
|
|
67
|
-
if (existsSync(hookPath)) {
|
|
68
|
-
const existing = readFileSync(hookPath, 'utf-8')
|
|
69
|
-
if (existing.includes(SENTINEL)) continue // already installed
|
|
70
|
-
// Append to existing hook
|
|
71
|
-
writeFileSync(hookPath, existing.trimEnd() + '\n\n' + script)
|
|
72
|
-
} else {
|
|
73
|
-
writeFileSync(hookPath, script)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Make executable (chmod +x)
|
|
77
|
-
try {
|
|
78
|
-
const { chmodSync } = await import('fs')
|
|
79
|
-
chmodSync(hookPath, 0o755)
|
|
80
|
-
} catch {
|
|
81
|
-
logHookError(`chmod failed for ${hookPath}`)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function logHookError(msg: string): void {
|
|
87
|
-
try {
|
|
88
|
-
const dir = join(homedir(), '.contextgit')
|
|
89
|
-
mkdirSync(dir, { recursive: true })
|
|
90
|
-
appendFileSync(HOOKS_LOG, `[${new Date().toISOString()}] ${msg}\n`)
|
|
91
|
-
} catch {
|
|
92
|
-
// truly silent
|
|
93
|
-
}
|
|
94
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// index.ts — entry point for the ContextGit MCP server process.
|
|
3
|
-
// Started by the MCP host (Claude Desktop / Claude Code) via stdio.
|
|
4
|
-
|
|
5
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
6
|
-
import { createServer } from './server.js'
|
|
7
|
-
|
|
8
|
-
async function main(): Promise<void> {
|
|
9
|
-
const server = await createServer()
|
|
10
|
-
const transport = new StdioServerTransport()
|
|
11
|
-
await server.connect(transport)
|
|
12
|
-
// Server is now listening on stdin/stdout — process stays alive until the
|
|
13
|
-
// host closes the connection.
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
main().catch(err => {
|
|
17
|
-
console.error('[contextgit-mcp] Fatal error:', err)
|
|
18
|
-
process.exit(1)
|
|
19
|
-
})
|