contextgit 0.1.1 → 0.1.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 (37) hide show
  1. package/dist/commands/init.d.ts.map +1 -1
  2. package/dist/commands/init.js +53 -74
  3. package/dist/commands/init.js.map +1 -1
  4. package/dist/lib/init-helpers.d.ts +2 -1
  5. package/dist/lib/init-helpers.d.ts.map +1 -1
  6. package/dist/lib/init-helpers.js +9 -6
  7. package/dist/lib/init-helpers.js.map +1 -1
  8. package/dist/lib/init-helpers.test.js +67 -1
  9. package/dist/lib/init-helpers.test.js.map +1 -1
  10. package/node_modules/@contextgit/mcp/dist/auto-snapshot.d.ts +30 -0
  11. package/node_modules/@contextgit/mcp/dist/auto-snapshot.d.ts.map +1 -0
  12. package/node_modules/@contextgit/mcp/dist/auto-snapshot.js +68 -0
  13. package/node_modules/@contextgit/mcp/dist/auto-snapshot.js.map +1 -0
  14. package/node_modules/@contextgit/mcp/dist/config.d.ts +17 -0
  15. package/node_modules/@contextgit/mcp/dist/config.d.ts.map +1 -0
  16. package/node_modules/@contextgit/mcp/dist/config.js +48 -0
  17. package/node_modules/@contextgit/mcp/dist/config.js.map +1 -0
  18. package/node_modules/@contextgit/mcp/dist/git-sync.d.ts +15 -0
  19. package/node_modules/@contextgit/mcp/dist/git-sync.d.ts.map +1 -0
  20. package/node_modules/@contextgit/mcp/dist/git-sync.js +87 -0
  21. package/node_modules/@contextgit/mcp/dist/git-sync.js.map +1 -0
  22. package/node_modules/@contextgit/mcp/dist/index.d.ts +3 -0
  23. package/node_modules/@contextgit/mcp/dist/index.d.ts.map +1 -0
  24. package/node_modules/@contextgit/mcp/dist/index.js +17 -0
  25. package/node_modules/@contextgit/mcp/dist/index.js.map +1 -0
  26. package/node_modules/@contextgit/mcp/dist/server.d.ts +3 -0
  27. package/node_modules/@contextgit/mcp/dist/server.d.ts.map +1 -0
  28. package/node_modules/@contextgit/mcp/dist/server.js +377 -0
  29. package/node_modules/@contextgit/mcp/dist/server.js.map +1 -0
  30. package/node_modules/@contextgit/mcp/package.json +31 -0
  31. package/node_modules/@contextgit/mcp/src/auto-snapshot.ts +83 -0
  32. package/node_modules/@contextgit/mcp/src/config.ts +53 -0
  33. package/node_modules/@contextgit/mcp/src/git-sync.ts +94 -0
  34. package/node_modules/@contextgit/mcp/src/index.ts +19 -0
  35. package/node_modules/@contextgit/mcp/src/server.ts +568 -0
  36. package/node_modules/@contextgit/mcp/tsconfig.json +9 -0
  37. package/package.json +13 -10
@@ -0,0 +1,53 @@
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 & { configDir: string } {
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, configDir: dirname(configPath) }
53
+ }
@@ -0,0 +1,94 @@
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
+ }
@@ -0,0 +1,19 @@
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
+ })