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.
- 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 +15 -0
- package/dist/commands/claim.d.ts.map +1 -0
- package/dist/commands/claim.js +60 -0
- package/dist/commands/claim.js.map +1 -0
- package/dist/commands/commit.d.ts +17 -0
- package/dist/commands/commit.d.ts.map +1 -0
- package/dist/commands/commit.js +83 -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 +184 -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 +24 -18
- 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,99 +0,0 @@
|
|
|
1
|
-
// doctor — diagnose the ContextGit installation and report issues.
|
|
2
|
-
|
|
3
|
-
import os from 'os'
|
|
4
|
-
import { join, resolve } from 'path'
|
|
5
|
-
import { existsSync, readFileSync } from 'fs'
|
|
6
|
-
import { Command } from '@oclif/core'
|
|
7
|
-
import { LocalStore } from '@contextgit/store'
|
|
8
|
-
import { findConfigPath } from '../config.js'
|
|
9
|
-
|
|
10
|
-
const SENTINEL = '# contextgit'
|
|
11
|
-
|
|
12
|
-
export default class DoctorCmd extends Command {
|
|
13
|
-
static description = 'Check ContextGit setup and diagnose issues'
|
|
14
|
-
|
|
15
|
-
async run(): Promise<void> {
|
|
16
|
-
this.log('ContextGit Doctor\n')
|
|
17
|
-
|
|
18
|
-
let passed = 0
|
|
19
|
-
let failed = 0
|
|
20
|
-
|
|
21
|
-
const check = (label: string, ok: boolean, hint?: string) => {
|
|
22
|
-
const icon = ok ? '✓' : '✗'
|
|
23
|
-
this.log(` [${icon}] ${label}`)
|
|
24
|
-
if (!ok && hint) this.log(` → ${hint}`)
|
|
25
|
-
ok ? passed++ : failed++
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// ── 1. Config file ────────────────────────────────────────────────────────
|
|
29
|
-
let configPath: string | null = null
|
|
30
|
-
let config: Record<string, unknown> | null = null
|
|
31
|
-
try {
|
|
32
|
-
configPath = findConfigPath(process.cwd())
|
|
33
|
-
config = JSON.parse(readFileSync(configPath, 'utf-8')) as Record<string, unknown>
|
|
34
|
-
check('Config file found and valid JSON', true)
|
|
35
|
-
} catch {
|
|
36
|
-
check('Config file found and valid JSON', false, 'Run: contextgit init')
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ── 2. DB reachable ───────────────────────────────────────────────────────
|
|
40
|
-
if (config?.projectId) {
|
|
41
|
-
try {
|
|
42
|
-
const store = new LocalStore(config.projectId as string)
|
|
43
|
-
await store.getProject(config.projectId as string)
|
|
44
|
-
check('Local DB reachable', true)
|
|
45
|
-
} catch {
|
|
46
|
-
check('Local DB reachable', false, 'DB file may be corrupted — try: contextgit init')
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
check('Local DB reachable', false, 'No config to check DB against')
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ── 3. Git hooks installed ────────────────────────────────────────────────
|
|
53
|
-
const gitHooksDir = resolve(process.cwd(), '.git', 'hooks')
|
|
54
|
-
const postCommitPath = join(gitHooksDir, 'post-commit')
|
|
55
|
-
const hooksInstalled = existsSync(postCommitPath) &&
|
|
56
|
-
readFileSync(postCommitPath, 'utf-8').includes(SENTINEL)
|
|
57
|
-
check(
|
|
58
|
-
'Git hooks installed',
|
|
59
|
-
hooksInstalled,
|
|
60
|
-
'Run: contextgit init --hooks',
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
// ── 4. API key configured (only relevant if remote is set) ────────────────
|
|
64
|
-
const remote = config?.remote as string | undefined
|
|
65
|
-
if (remote) {
|
|
66
|
-
const serverCfgPath = join(os.homedir(), '.contextgit', 'server.json')
|
|
67
|
-
let hasKey = false
|
|
68
|
-
try {
|
|
69
|
-
const cfg = JSON.parse(readFileSync(serverCfgPath, 'utf-8')) as Record<string, unknown>
|
|
70
|
-
hasKey = typeof cfg['keyHash'] === 'string' && cfg['keyHash'].length > 0
|
|
71
|
-
} catch { /* not configured */ }
|
|
72
|
-
check(
|
|
73
|
-
`API key configured (remote: ${remote})`,
|
|
74
|
-
hasKey,
|
|
75
|
-
'Run: contextgit keygen --save',
|
|
76
|
-
)
|
|
77
|
-
} else {
|
|
78
|
-
check('API key (no remote configured — skipped)', true)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// ── 5. MCP registered in ~/.claude.json ───────────────────────────────────
|
|
82
|
-
const claudeJsonPath = join(os.homedir(), '.claude.json')
|
|
83
|
-
let mcpRegistered = false
|
|
84
|
-
try {
|
|
85
|
-
const raw = readFileSync(claudeJsonPath, 'utf-8')
|
|
86
|
-
mcpRegistered = raw.includes('contextgit')
|
|
87
|
-
} catch { /* file missing */ }
|
|
88
|
-
check(
|
|
89
|
-
'MCP server registered in ~/.claude.json',
|
|
90
|
-
mcpRegistered,
|
|
91
|
-
'Add contextgit to mcpServers in ~/.claude.json',
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
// ── Summary ───────────────────────────────────────────────────────────────
|
|
95
|
-
this.log('')
|
|
96
|
-
this.log(`${passed} passed, ${failed} failed`)
|
|
97
|
-
if (failed > 0) process.exitCode = 1
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
// init — create .contextgit/config.json + project + branch in LocalStore.
|
|
2
|
-
|
|
3
|
-
import { Command, Flags } from '@oclif/core'
|
|
4
|
-
import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs'
|
|
5
|
-
import { join, basename } from 'path'
|
|
6
|
-
import { nanoid } from 'nanoid'
|
|
7
|
-
import { simpleGit } from 'simple-git'
|
|
8
|
-
import { LocalStore } from '@contextgit/store'
|
|
9
|
-
import type { ContextGitConfig } from '@contextgit/core'
|
|
10
|
-
import { installGitHooks } from '../git-hooks.js'
|
|
11
|
-
|
|
12
|
-
const SYSTEM_PROMPT_FRAGMENT = `\
|
|
13
|
-
You have access to ContextGit memory tools. At the start of every session, call
|
|
14
|
-
context_get with scope=global to load project state. After completing significant
|
|
15
|
-
work, call context_commit with a message describing what was done and any open
|
|
16
|
-
threads. Use context_branch before exploring risky changes.
|
|
17
|
-
`
|
|
18
|
-
|
|
19
|
-
export default class Init extends Command {
|
|
20
|
-
static description = 'Initialize ContextGit in this project'
|
|
21
|
-
|
|
22
|
-
static flags = {
|
|
23
|
-
name: Flags.string({
|
|
24
|
-
char: 'n',
|
|
25
|
-
description: 'Project name (defaults to current directory name)',
|
|
26
|
-
required: false,
|
|
27
|
-
}),
|
|
28
|
-
hooks: Flags.boolean({
|
|
29
|
-
description: 'Install git hooks to auto-capture context on every git commit',
|
|
30
|
-
default: false,
|
|
31
|
-
}),
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async run(): Promise<void> {
|
|
35
|
-
const { flags } = await this.parse(Init)
|
|
36
|
-
const cwd = process.cwd()
|
|
37
|
-
const configDir = join(cwd, '.contextgit')
|
|
38
|
-
const configPath = join(configDir, 'config.json')
|
|
39
|
-
const promptPath = join(configDir, 'system-prompt.md')
|
|
40
|
-
|
|
41
|
-
// ── Self-heal: config exists but DB may be empty ───────────────────────────
|
|
42
|
-
if (existsSync(configPath)) {
|
|
43
|
-
let existing: ContextGitConfig
|
|
44
|
-
try {
|
|
45
|
-
existing = JSON.parse(readFileSync(configPath, 'utf8')) as ContextGitConfig
|
|
46
|
-
} catch {
|
|
47
|
-
this.error('Found .contextgit/config.json but could not parse it. Delete it and re-run init.')
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const store = new LocalStore(existing.projectId)
|
|
51
|
-
const gitBranch = await detectGitBranch(cwd)
|
|
52
|
-
const branch = await store.getBranchByGitName(existing.projectId, gitBranch)
|
|
53
|
-
|
|
54
|
-
if (branch) {
|
|
55
|
-
this.log('ContextGit already initialized. Config found at .contextgit/config.json')
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// DB missing or empty — recreate project + branch
|
|
60
|
-
this.log('Config found but DB is empty — recreating project and branch in DB.')
|
|
61
|
-
await store.createProject({ id: existing.projectId, name: existing.project })
|
|
62
|
-
await store.createBranch({
|
|
63
|
-
projectId: existing.projectId,
|
|
64
|
-
name: `Context: ${gitBranch}`,
|
|
65
|
-
gitBranch,
|
|
66
|
-
})
|
|
67
|
-
writeSystemPrompt(promptPath)
|
|
68
|
-
this.log(`Recreated project "${existing.project}" (${existing.projectId}) for branch: ${gitBranch}`)
|
|
69
|
-
this.log(`System prompt: .contextgit/system-prompt.md`)
|
|
70
|
-
this.log(SYSTEM_PROMPT_FRAGMENT)
|
|
71
|
-
if (flags.hooks) {
|
|
72
|
-
installGitHooks(cwd)
|
|
73
|
-
this.log('Git hooks installed (.git/hooks/post-commit, post-checkout, post-merge)')
|
|
74
|
-
}
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ── Fresh init ─────────────────────────────────────────────────────────────
|
|
79
|
-
const projectName = flags.name ?? basename(cwd)
|
|
80
|
-
const projectId = nanoid()
|
|
81
|
-
|
|
82
|
-
// Open store (creates DB + runs migrations).
|
|
83
|
-
const store = new LocalStore(projectId)
|
|
84
|
-
await store.createProject({ id: projectId, name: projectName })
|
|
85
|
-
|
|
86
|
-
const gitBranch = await detectGitBranch(cwd)
|
|
87
|
-
|
|
88
|
-
await store.createBranch({
|
|
89
|
-
projectId,
|
|
90
|
-
name: `Context: ${gitBranch}`,
|
|
91
|
-
gitBranch,
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
// Write config
|
|
95
|
-
mkdirSync(configDir, { recursive: true })
|
|
96
|
-
const config: ContextGitConfig = {
|
|
97
|
-
project: projectName,
|
|
98
|
-
projectId,
|
|
99
|
-
store: 'local',
|
|
100
|
-
agentRole: 'solo',
|
|
101
|
-
workflowType: 'interactive',
|
|
102
|
-
autoSnapshot: false,
|
|
103
|
-
snapshotInterval: 10,
|
|
104
|
-
embeddingModel: 'local',
|
|
105
|
-
}
|
|
106
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n')
|
|
107
|
-
writeSystemPrompt(promptPath)
|
|
108
|
-
|
|
109
|
-
this.log(`Initialized ContextGit for project: ${projectName}`)
|
|
110
|
-
this.log(`Project ID: ${projectId}`)
|
|
111
|
-
this.log(`Branch: ${gitBranch}`)
|
|
112
|
-
this.log(`Config: .contextgit/config.json`)
|
|
113
|
-
this.log(`DB: ~/.contextgit/projects/${projectId}.db`)
|
|
114
|
-
this.log(``)
|
|
115
|
-
if (flags.hooks) {
|
|
116
|
-
installGitHooks(cwd)
|
|
117
|
-
this.log(`Git hooks installed (.git/hooks/post-commit, post-checkout, post-merge)`)
|
|
118
|
-
} else {
|
|
119
|
-
this.log(`Tip: run "contextgit init --hooks" to auto-capture context on every git commit`)
|
|
120
|
-
}
|
|
121
|
-
this.log(``)
|
|
122
|
-
this.log(`Add the following to your MCP system prompt (.contextgit/system-prompt.md):`)
|
|
123
|
-
this.log(``)
|
|
124
|
-
this.log(SYSTEM_PROMPT_FRAGMENT)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
129
|
-
|
|
130
|
-
async function detectGitBranch(cwd: string): Promise<string> {
|
|
131
|
-
try {
|
|
132
|
-
const git = simpleGit(cwd)
|
|
133
|
-
return (await git.revparse(['--abbrev-ref', 'HEAD'])).trim()
|
|
134
|
-
} catch {
|
|
135
|
-
return 'main'
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function writeSystemPrompt(promptPath: string): void {
|
|
140
|
-
writeFileSync(promptPath, SYSTEM_PROMPT_FRAGMENT)
|
|
141
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
// keygen — generate an API key and optionally save its hash to ~/.contextgit/server.json.
|
|
2
|
-
//
|
|
3
|
-
// The plaintext key is shown once and never written to disk. Only the SHA-256
|
|
4
|
-
// hash is persisted so the server can verify Bearer tokens without storing secrets.
|
|
5
|
-
|
|
6
|
-
import os from 'os'
|
|
7
|
-
import { join } from 'path'
|
|
8
|
-
import { createHash } from 'crypto'
|
|
9
|
-
import { mkdirSync, readFileSync, writeFileSync } from 'fs'
|
|
10
|
-
import { Command, Flags } from '@oclif/core'
|
|
11
|
-
import { nanoid } from 'nanoid'
|
|
12
|
-
|
|
13
|
-
const CONTEXTGIT_DIR = join(os.homedir(), '.contextgit')
|
|
14
|
-
const SERVER_CONFIG_PATH = join(CONTEXTGIT_DIR, 'server.json')
|
|
15
|
-
|
|
16
|
-
function sha256hex(input: string): string {
|
|
17
|
-
return createHash('sha256').update(input, 'utf8').digest('hex')
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function readServerConfig(): Record<string, unknown> {
|
|
21
|
-
try {
|
|
22
|
-
return JSON.parse(readFileSync(SERVER_CONFIG_PATH, 'utf-8')) as Record<string, unknown>
|
|
23
|
-
} catch {
|
|
24
|
-
return {}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function saveKeyHash(hash: string): void {
|
|
29
|
-
mkdirSync(CONTEXTGIT_DIR, { recursive: true })
|
|
30
|
-
const cfg = { ...readServerConfig(), keyHash: hash }
|
|
31
|
-
writeFileSync(SERVER_CONFIG_PATH, JSON.stringify(cfg, null, 2) + '\n', { mode: 0o600 })
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export default class KeygenCmd extends Command {
|
|
35
|
-
static description = 'Generate an API key for securing the ContextGit API server'
|
|
36
|
-
|
|
37
|
-
static flags = {
|
|
38
|
-
save: Flags.boolean({
|
|
39
|
-
description: 'Save the key hash to ~/.contextgit/server.json (key shown once, never stored)',
|
|
40
|
-
default: false,
|
|
41
|
-
}),
|
|
42
|
-
length: Flags.integer({
|
|
43
|
-
description: 'Key length (default 32)',
|
|
44
|
-
default: 32,
|
|
45
|
-
}),
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async run(): Promise<void> {
|
|
49
|
-
const { flags } = await this.parse(KeygenCmd)
|
|
50
|
-
|
|
51
|
-
const key = nanoid(flags.length)
|
|
52
|
-
|
|
53
|
-
this.log(`API Key: ${key}`)
|
|
54
|
-
this.log(`(Copy this now — it will not be stored anywhere in plaintext)`)
|
|
55
|
-
|
|
56
|
-
if (flags.save) {
|
|
57
|
-
saveKeyHash(sha256hex(key))
|
|
58
|
-
this.log(`\nKey hash saved to ~/.contextgit/server.json`)
|
|
59
|
-
this.log(`Set this key in the Authorization header when calling the API:`)
|
|
60
|
-
this.log(` Authorization: Bearer ${key}`)
|
|
61
|
-
} else {
|
|
62
|
-
this.log(`\nTo activate this key, re-run with --save`)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
// log — list context commits with formatting.
|
|
2
|
-
|
|
3
|
-
import { Command, Flags } from '@oclif/core'
|
|
4
|
-
import { simpleGit } from 'simple-git'
|
|
5
|
-
import { LocalStore } from '@contextgit/store'
|
|
6
|
-
import { loadConfig } from '../config.js'
|
|
7
|
-
|
|
8
|
-
export default class LogCmd extends Command {
|
|
9
|
-
static description = 'List context commits for the current branch'
|
|
10
|
-
|
|
11
|
-
static flags = {
|
|
12
|
-
limit: Flags.integer({
|
|
13
|
-
char: 'n',
|
|
14
|
-
description: 'Max commits to show',
|
|
15
|
-
default: 20,
|
|
16
|
-
}),
|
|
17
|
-
all: Flags.boolean({
|
|
18
|
-
char: 'a',
|
|
19
|
-
description: 'Show commits across all branches',
|
|
20
|
-
default: false,
|
|
21
|
-
}),
|
|
22
|
-
branch: Flags.string({
|
|
23
|
-
char: 'b',
|
|
24
|
-
description: 'Show commits for a specific git branch name',
|
|
25
|
-
}),
|
|
26
|
-
verbose: Flags.boolean({
|
|
27
|
-
char: 'v',
|
|
28
|
-
description: 'Show full commit content',
|
|
29
|
-
default: false,
|
|
30
|
-
}),
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async run(): Promise<void> {
|
|
34
|
-
const { flags } = await this.parse(LogCmd)
|
|
35
|
-
const config = loadConfig()
|
|
36
|
-
const store = new LocalStore(config.projectId)
|
|
37
|
-
const cwd = process.cwd()
|
|
38
|
-
|
|
39
|
-
let gitBranch = 'main'
|
|
40
|
-
try {
|
|
41
|
-
gitBranch = (await simpleGit(cwd).revparse(['--abbrev-ref', 'HEAD'])).trim()
|
|
42
|
-
} catch {
|
|
43
|
-
// fallback
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (flags.all) {
|
|
47
|
-
const branches = await store.listBranches(config.projectId)
|
|
48
|
-
if (branches.length === 0) {
|
|
49
|
-
this.log('No branches found. Run `contextgit init` first.')
|
|
50
|
-
return
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
for (const branch of branches) {
|
|
54
|
-
const commits = await store.listCommits(branch.id, { limit: flags.limit, offset: 0 })
|
|
55
|
-
if (commits.length === 0) continue
|
|
56
|
-
|
|
57
|
-
this.log(`\n── ${branch.name} (${branch.gitBranch}) ──`)
|
|
58
|
-
this.printCommits(commits, flags.verbose)
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
const targetGitBranch = flags.branch ?? gitBranch
|
|
62
|
-
const branch = await store.getBranchByGitName(config.projectId, targetGitBranch)
|
|
63
|
-
if (!branch) {
|
|
64
|
-
this.error(
|
|
65
|
-
`No context branch for git branch '${targetGitBranch}'. Run 'contextgit init' first.`,
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const commits = await store.listCommits(branch.id, { limit: flags.limit, offset: 0 })
|
|
70
|
-
if (commits.length === 0) {
|
|
71
|
-
this.log(`No commits on branch '${branch.name}'.`)
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
this.log(`Branch: ${branch.name} [${branch.gitBranch}]`)
|
|
76
|
-
this.log(`Showing ${commits.length} commit(s)\n`)
|
|
77
|
-
this.printCommits(commits, flags.verbose)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
private printCommits(
|
|
82
|
-
commits: import('@contextgit/core').Commit[],
|
|
83
|
-
verbose: boolean,
|
|
84
|
-
): void {
|
|
85
|
-
for (const c of commits) {
|
|
86
|
-
const ts = new Date(c.createdAt).toLocaleString()
|
|
87
|
-
const sha = c.gitCommitSha ? ` git:${c.gitCommitSha.slice(0, 7)}` : ''
|
|
88
|
-
const agent = c.agentId ? ` [${c.agentId}]` : ''
|
|
89
|
-
this.log(`commit ${c.id}`)
|
|
90
|
-
this.log(`Date: ${ts}${sha}${agent}`)
|
|
91
|
-
this.log(``)
|
|
92
|
-
this.log(` ${c.message}`)
|
|
93
|
-
if (verbose && c.content && c.content !== c.message) {
|
|
94
|
-
this.log(``)
|
|
95
|
-
const lines = c.content.split('\n')
|
|
96
|
-
for (const line of lines) {
|
|
97
|
-
this.log(` ${line}`)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
this.log(``)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// merge — merge a context branch into the current branch.
|
|
2
|
-
|
|
3
|
-
import { Command, Args, Flags } from '@oclif/core'
|
|
4
|
-
import { bootstrap } from '../bootstrap.js'
|
|
5
|
-
|
|
6
|
-
export default class MergeCmd extends Command {
|
|
7
|
-
static description = 'Merge a context branch into the current branch'
|
|
8
|
-
|
|
9
|
-
static args = {
|
|
10
|
-
sourceBranchId: Args.string({
|
|
11
|
-
description: 'ID of the source branch to merge from',
|
|
12
|
-
required: true,
|
|
13
|
-
}),
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
static flags = {
|
|
17
|
-
summary: Flags.string({
|
|
18
|
-
char: 's',
|
|
19
|
-
description: 'Summary of what was merged (defaults to a generic message)',
|
|
20
|
-
required: false,
|
|
21
|
-
}),
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async run(): Promise<void> {
|
|
25
|
-
const { args, flags } = await this.parse(MergeCmd)
|
|
26
|
-
const ctx = await bootstrap()
|
|
27
|
-
|
|
28
|
-
const summary = flags.summary ?? `Merged branch ${args.sourceBranchId} into current branch`
|
|
29
|
-
|
|
30
|
-
const mergeCommit = await ctx.store.mergeBranch(args.sourceBranchId, ctx.branchId, summary)
|
|
31
|
-
|
|
32
|
-
this.log(`Branch merged.`)
|
|
33
|
-
this.log(`Merge commit ID: ${mergeCommit.id}`)
|
|
34
|
-
this.log(`Summary: ${mergeCommit.summary}`)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
// pull — pull context commits from a remote ContextGit API server into local store.
|
|
2
|
-
//
|
|
3
|
-
// Requires `remote` in .contextgit/config.json.
|
|
4
|
-
// Algorithm:
|
|
5
|
-
// 1. Ensure project exists locally (by ID — already initialized)
|
|
6
|
-
// 2. For each remote branch: ensure branch exists locally
|
|
7
|
-
// 3. For each branch: collect remote commits not present locally, write them
|
|
8
|
-
|
|
9
|
-
import { Command, Flags } from '@oclif/core'
|
|
10
|
-
import { LocalStore, RemoteStore } from '@contextgit/store'
|
|
11
|
-
import type { Pagination } from '@contextgit/core'
|
|
12
|
-
import { loadConfig } from '../config.js'
|
|
13
|
-
|
|
14
|
-
const PAGE = 100
|
|
15
|
-
|
|
16
|
-
async function allCommits(
|
|
17
|
-
store: LocalStore | RemoteStore,
|
|
18
|
-
branchId: string,
|
|
19
|
-
): Promise<import('@contextgit/core').Commit[]> {
|
|
20
|
-
const acc: import('@contextgit/core').Commit[] = []
|
|
21
|
-
let offset = 0
|
|
22
|
-
const pg: Pagination = { limit: PAGE, offset }
|
|
23
|
-
while (true) {
|
|
24
|
-
pg.offset = offset
|
|
25
|
-
const page = await store.listCommits(branchId, pg)
|
|
26
|
-
acc.push(...page)
|
|
27
|
-
if (page.length < PAGE) break
|
|
28
|
-
offset += PAGE
|
|
29
|
-
}
|
|
30
|
-
return acc
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export default class PullCmd extends Command {
|
|
34
|
-
static description = 'Pull context commits from a remote ContextGit API'
|
|
35
|
-
|
|
36
|
-
static flags = {
|
|
37
|
-
remote: Flags.string({
|
|
38
|
-
char: 'r',
|
|
39
|
-
description: 'Remote API URL (overrides config.remote)',
|
|
40
|
-
required: false,
|
|
41
|
-
}),
|
|
42
|
-
'dry-run': Flags.boolean({
|
|
43
|
-
description: 'Show what would be pulled without writing locally',
|
|
44
|
-
default: false,
|
|
45
|
-
}),
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async run(): Promise<void> {
|
|
49
|
-
const { flags } = await this.parse(PullCmd)
|
|
50
|
-
const config = loadConfig()
|
|
51
|
-
|
|
52
|
-
const remoteUrl = flags.remote ?? config.remote
|
|
53
|
-
if (!remoteUrl) {
|
|
54
|
-
this.error(
|
|
55
|
-
'No remote configured. Add "remote": "<url>" to .contextgit/config.json or pass --remote.',
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const local = new LocalStore(config.projectId)
|
|
60
|
-
const remote = new RemoteStore(remoteUrl)
|
|
61
|
-
const dryRun = flags['dry-run']
|
|
62
|
-
|
|
63
|
-
// Verify project exists on remote
|
|
64
|
-
const remoteProject = await remote.getProject(config.projectId).catch(() => null)
|
|
65
|
-
if (!remoteProject) {
|
|
66
|
-
this.error(`Project ${config.projectId} not found on remote ${remoteUrl}. Push first.`)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// List all remote branches
|
|
70
|
-
const remoteBranches = await remote.listBranches(config.projectId)
|
|
71
|
-
let totalPulled = 0
|
|
72
|
-
|
|
73
|
-
for (const branch of remoteBranches) {
|
|
74
|
-
// Ensure branch exists locally
|
|
75
|
-
const localBranch = await local.getBranch(branch.id).catch(() => null)
|
|
76
|
-
if (!localBranch) {
|
|
77
|
-
if (!dryRun) {
|
|
78
|
-
await local.createBranch({
|
|
79
|
-
id: branch.id,
|
|
80
|
-
projectId: branch.projectId,
|
|
81
|
-
name: branch.name,
|
|
82
|
-
gitBranch: branch.gitBranch,
|
|
83
|
-
parentBranchId: branch.parentBranchId,
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
this.log(`[branch] ${dryRun ? 'would create' : 'created'}: ${branch.name}`)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Collect local commit IDs for this branch
|
|
90
|
-
const localCommits = await allCommits(local, branch.id)
|
|
91
|
-
const localCommitIds = new Set(localCommits.map(c => c.id))
|
|
92
|
-
|
|
93
|
-
// Pull missing commits from remote
|
|
94
|
-
const remoteCommits = await allCommits(remote, branch.id)
|
|
95
|
-
const missing = remoteCommits.filter(c => !localCommitIds.has(c.id))
|
|
96
|
-
|
|
97
|
-
if (missing.length === 0) {
|
|
98
|
-
this.log(`[branch] ${branch.name}: up to date (${remoteCommits.length} commits)`)
|
|
99
|
-
continue
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
this.log(`[branch] ${branch.name}: pulling ${missing.length} commit(s)…`)
|
|
103
|
-
|
|
104
|
-
for (const commit of missing) {
|
|
105
|
-
if (!dryRun) {
|
|
106
|
-
await local.createCommit({
|
|
107
|
-
id: commit.id,
|
|
108
|
-
branchId: commit.branchId,
|
|
109
|
-
parentId: commit.parentId,
|
|
110
|
-
agentId: commit.agentId,
|
|
111
|
-
agentRole: commit.agentRole,
|
|
112
|
-
tool: commit.tool,
|
|
113
|
-
workflowType: commit.workflowType,
|
|
114
|
-
loopIteration: commit.loopIteration,
|
|
115
|
-
ciRunId: commit.ciRunId,
|
|
116
|
-
pipelineName: commit.pipelineName,
|
|
117
|
-
message: commit.message,
|
|
118
|
-
content: commit.content,
|
|
119
|
-
summary: commit.summary,
|
|
120
|
-
commitType: commit.commitType,
|
|
121
|
-
gitCommitSha: commit.gitCommitSha,
|
|
122
|
-
})
|
|
123
|
-
}
|
|
124
|
-
totalPulled++
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Thread sync: pull open threads that are missing locally
|
|
129
|
-
const remoteThreads = await remote.listOpenThreads(config.projectId)
|
|
130
|
-
const localThreads = await local.listOpenThreads(config.projectId)
|
|
131
|
-
const localThreadIds = new Set(localThreads.map(t => t.id))
|
|
132
|
-
const missingThreads = remoteThreads.filter(t => !localThreadIds.has(t.id))
|
|
133
|
-
if (missingThreads.length > 0) {
|
|
134
|
-
this.log(`\n[threads] pulling ${missingThreads.length} open thread(s)…`)
|
|
135
|
-
for (const thread of missingThreads) {
|
|
136
|
-
if (!dryRun) {
|
|
137
|
-
await local.syncThread(thread)
|
|
138
|
-
}
|
|
139
|
-
this.log(` ${dryRun ? 'would pull' : 'pulled'}: ${thread.description}`)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
this.log(`\nDone. ${dryRun ? '(dry run) ' : ''}${totalPulled} commit(s), ${missingThreads.length} thread(s) pulled from ${remoteUrl}`)
|
|
144
|
-
}
|
|
145
|
-
}
|