-
Notifications
You must be signed in to change notification settings - Fork 0
feat: auto-detect agent/model and rework init prompts (GIT-73) #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
db98d0e
72bf364
033a0ee
861e1e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,6 +84,7 @@ fabric.properties | |
| node_modules/ | ||
| dist/ | ||
| *.tgz | ||
| releases/ | ||
|
|
||
| # Environment | ||
| .env | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,54 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
| #!/bin/bash | ||
| # Reinstall git-mem globally from local build | ||
| # Usage: ./scripts/reinstall-global.sh | ||
| # | ||
| # This script: | ||
| # 1. Builds the project and creates a .tgz package | ||
| # 2. Uninstalls existing global git-mem | ||
| # 3. Installs the new package globally | ||
| # 4. Verifies installation | ||
|
|
||
| cd "$(dirname "$0")/.." | ||
| set -e | ||
|
|
||
| echo "Building..." | ||
| npm run build | ||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" | ||
| RELEASES_DIR="$PROJECT_ROOT/releases" | ||
|
|
||
| echo "Uninstalling git-mem globally..." | ||
| echo "=== git-mem Local Reinstall ===" | ||
| echo "" | ||
|
|
||
| # Step 1: Package (builds + creates tgz) | ||
| echo "1. Building and packaging..." | ||
| cd "$PROJECT_ROOT" | ||
| npm run package | ||
|
|
||
| # Find the latest .tgz file | ||
| PACKAGE_FILE=$(ls -t "$RELEASES_DIR"/*.tgz 2>/dev/null | head -1) | ||
| if [ -z "$PACKAGE_FILE" ]; then | ||
| echo "Error: No .tgz file found in $RELEASES_DIR" | ||
| exit 1 | ||
| fi | ||
| echo " Package: $PACKAGE_FILE" | ||
|
|
||
| # Step 2: Uninstall existing | ||
| echo "" | ||
| echo "2. Uninstalling existing global git-mem..." | ||
| npm uninstall -g git-mem 2>/dev/null || true | ||
|
|
||
| echo "Installing git-mem globally from $(pwd)..." | ||
| npm install -g . | ||
| # Step 3: Install new package | ||
| echo "" | ||
| echo "3. Installing new package globally..." | ||
| npm install -g "$PACKAGE_FILE" | ||
|
|
||
| # Step 4: Verify installation | ||
| echo "" | ||
| echo "4. Verifying installation..." | ||
| INSTALLED_VERSION=$(git-mem --version 2>/dev/null || echo "unknown") | ||
| echo " Installed version: $INSTALLED_VERSION" | ||
| echo " git-mem: $(which git-mem)" | ||
| echo " git-mem-mcp: $(which git-mem-mcp)" | ||
|
|
||
| echo "" | ||
| echo "Installed:" | ||
| git-mem --version | ||
| which git-mem | ||
| which git-mem-mcp | ||
| echo "=== Done ===" | ||
| echo "" | ||
| echo "git-mem $INSTALLED_VERSION installed globally from local build." | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,9 +24,10 @@ import { createStderrProgressHandler } from './progress'; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface IInitCommandOptions { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yes?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commitCount?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hooks?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uninstallHooks?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| extract?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commitCount?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Pure helpers (exported for testing) ────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -110,12 +111,12 @@ export function ensureEnvPlaceholder(cwd: string): void { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Main command ───────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** Run unified project setup: hooks, MCP config, .gitignore, .env, and optional extract. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** Run unified project setup: hooks, MCP config, .gitignore, and .env. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function initCommand(options: IInitCommandOptions, logger?: ILogger): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const log = logger?.child({ command: 'init' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cwd = process.cwd(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log?.info('Command invoked', { yes: options.yes, commitCount: options.commitCount, hooks: options.hooks, uninstallHooks: options.uninstallHooks }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log?.info('Command invoked', { yes: options.yes, hooks: options.hooks, uninstallHooks: options.uninstallHooks }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Git hook uninstall (early exit) ───────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (options.uninstallHooks) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -129,36 +130,50 @@ export async function initCommand(options: IInitCommandOptions, logger?: ILogger | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Prompts (skipped with --yes) ─────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let commitCount = options.commitCount ? parseInt(options.commitCount, 10) : 30; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!Number.isFinite(commitCount) || commitCount <= 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`Invalid --commit-count value: "${options.commitCount}". Using default (30).`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commitCount = 30; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let claudeIntegration = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let runExtract = options.extract ?? false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let commitCount = options.commitCount ?? 10; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Validate commitCount (parseInt returns NaN for invalid input) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (Number.isNaN(commitCount) || commitCount < 1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commitCount = 10; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!options.yes) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await prompts([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'number', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: 'commitCount', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: 'How many commits to free?', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initial: commitCount, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'confirm', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: 'claudeIntegration', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: 'Integrate with Claude Code?', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initial: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'confirm', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: 'runExtract', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: 'Extract knowledge from commit history?', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initial: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: (prev) => prev ? 'select' : null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: 'commitCount', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: 'How many commits to extract?', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| choices: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { title: '10', value: 10 }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { title: '30', value: 30 }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { title: '50', value: 50 }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initial: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onCancel: () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('\nSetup cancelled.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.exit(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commitCount = response.commitCount ?? commitCount; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| claudeIntegration = response.claudeIntegration ?? true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runExtract = response.runExtract ?? false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commitCount = response.commitCount ?? 10; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Claude Code hooks ────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -209,21 +224,28 @@ export async function initCommand(options: IInitCommandOptions, logger?: ILogger | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ensureGitignoreEntries(cwd, ['.env', '.git-mem.json']); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('✓ Updated .gitignore'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── API key check & extract ──────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('\nChecking for ANTHROPIC_API_KEY in .env...\n'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── .env placeholder ────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ensureEnvPlaceholder(cwd); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('✓ Ensured ANTHROPIC_API_KEY placeholder in .env'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const apiKey = readEnvApiKey(cwd); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Extract from history ───────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (runExtract) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const apiKey = readEnvApiKey(cwd); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const enrich = !!apiKey; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (apiKey) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.env.ANTHROPIC_API_KEY = apiKey; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`Extracting knowledge from ${commitCount} commits with LLM enrichment...`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (enrich) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.env.ANTHROPIC_API_KEY = apiKey; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`\nExtracting knowledge from ${commitCount} commits with LLM enrichment...`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`\nExtracting knowledge from ${commitCount} commits (heuristic only)...`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+231
to
242
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider checking The current implementation only reads from ♻️ Proposed fix to check environment first // ── Extract from history ─────────────────────────────────────
if (runExtract) {
- const apiKey = readEnvApiKey(cwd);
+ const apiKey = process.env.ANTHROPIC_API_KEY || readEnvApiKey(cwd);
const enrich = !!apiKey;
if (enrich) {
- process.env.ANTHROPIC_API_KEY = apiKey;
+ if (!process.env.ANTHROPIC_API_KEY) {
+ process.env.ANTHROPIC_API_KEY = apiKey;
+ }
console.log(`\nExtracting knowledge from ${commitCount} commits with LLM enrichment...`);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const container = createContainer({ logger, scope: 'init', enrich: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const container = createContainer({ logger, scope: 'init', enrich }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { extractService } = container.cradle; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = await extractService.extract({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxCommits: commitCount, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enrich: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enrich, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onProgress: createStderrProgressHandler(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -233,10 +255,10 @@ export async function initCommand(options: IInitCommandOptions, logger?: ILogger | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Facts: ${result.factsExtracted} | ` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Duration: ${result.durationMs}ms`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ensureEnvPlaceholder(cwd); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('✓ Added ANTHROPIC_API_KEY= to .env'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('→ Add your key to .env, then run:'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(` git-mem extract --enrich --commit-count ${commitCount}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!enrich) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('\nFor a deeper AI summary of each commit, add your ANTHROPIC_API_KEY to .env and run:'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(' git-mem extract --enrich'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| /** | ||
| * Detect AI agent and model from environment variables. | ||
| * | ||
| * Agent detection chain: $GIT_MEM_AGENT > $CLAUDECODE (with version) > $CLAUDE_CODE | ||
| * Model detection chain: $GIT_MEM_MODEL > $ANTHROPIC_MODEL | ||
| */ | ||
|
|
||
| import { execFileSync } from 'child_process'; | ||
|
|
||
| /** | ||
| * Detect Claude Code agent string including version. | ||
| * Returns e.g. "Claude-Code/2.1.41" or "Claude-Code" if version unavailable. | ||
| */ | ||
| export function detectClaudeAgent(): string { | ||
| try { | ||
| // Unset CLAUDECODE so `claude --version` doesn't refuse to run inside a session | ||
| const version = execFileSync('claude', ['--version'], { | ||
| encoding: 'utf8', | ||
| env: { ...process.env, CLAUDECODE: '' }, | ||
| timeout: 3000, | ||
| stdio: ['pipe', 'pipe', 'pipe'], | ||
| }).trim().split(/\s+/)[0]; | ||
| if (version) return `Claude-Code/${version}`; | ||
| } catch { | ||
| // claude binary not found or timed out — fall back | ||
| } | ||
| return 'Claude-Code'; | ||
| } | ||
|
|
||
| /** | ||
| * Resolve the AI agent string from env vars. | ||
| * @param explicit Optional explicit value (from CLI flag or MCP param) | ||
| */ | ||
| export function resolveAgent(explicit?: string): string | undefined { | ||
| if (explicit) return explicit; | ||
| if (process.env.GIT_MEM_AGENT) return process.env.GIT_MEM_AGENT; | ||
| if (process.env.CLAUDECODE) return detectClaudeAgent(); | ||
| if (process.env.CLAUDE_CODE) return 'Claude-Code'; | ||
| return undefined; | ||
| } | ||
|
|
||
| /** | ||
| * Resolve the AI model string from env vars. | ||
| * @param explicit Optional explicit value (from CLI flag or MCP param) | ||
| */ | ||
| export function resolveModel(explicit?: string): string | undefined { | ||
| if (explicit) return explicit; | ||
| return process.env.GIT_MEM_MODEL || process.env.ANTHROPIC_MODEL || undefined; | ||
| } | ||
|
Comment on lines
+1
to
+49
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Minor:
lsparsing caveat acknowledged.The static analysis tool (SC2012) suggests using
findinstead oflsfor safer filename handling. However, sincenpm packproduces predictable filenames (git-mem-X.Y.Z.tgz), thels -tapproach is acceptable here. The error handling for missing packages is good.If you want stricter compliance, consider:
♻️ Optional alternative using find
📝 Committable suggestion
🧰 Tools
🪛 Shellcheck (0.11.0)
[info] 26-26: Use find instead of ls to better handle non-alphanumeric filenames.
(SC2012)
🤖 Prompt for AI Agents