diff --git a/.claude/commands/setup-statusline.md b/.claude/commands/setup-statusline.md index 7791e8ca80..c8dc2e2515 100644 --- a/.claude/commands/setup-statusline.md +++ b/.claude/commands/setup-statusline.md @@ -80,7 +80,7 @@ Output: Raw JSON status data ## Status File -Auto-build writes status to `.auto-claude-status` in your project root: +Auto-build writes status to `.aperant-status` in your project root: ```json { @@ -121,7 +121,7 @@ When active, you'll see these indicators: ## Troubleshooting ### Status not showing? -1. Check if `.auto-claude-status` exists in your project root +1. Check if `.aperant-status` exists in your project root 2. Verify the path to `statusline.py` is correct 3. Try running the command manually: `python auto-claude/statusline.py --format compact` diff --git a/.gitignore b/.gitignore index e41c5b3dfc..46ee37d061 100644 --- a/.gitignore +++ b/.gitignore @@ -54,13 +54,16 @@ lerna-debug.log* .worktrees/ # =========================== -# Auto Claude Generated +# Aperant Generated # =========================== +.aperant/ .auto-claude/ .planning/ .planning-archive/ .auto-build-security.json +.aperant-security.json .auto-claude-security.json +.aperant-status .auto-claude-status .claude_settings.json .update-metadata.json diff --git a/CLAUDE.md b/CLAUDE.md index f8808f8a94..9bc94d6afe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -82,10 +82,10 @@ Your context window will be automatically compacted as it approaches its limit, ### Resetting PR Review State -To fully clear all PR review data so reviews run fresh, delete/reset these three things in `.auto-claude/github/`: +To fully clear all PR review data so reviews run fresh, delete/reset these three things in `.aperant/github/`: -1. `rm .auto-claude/github/pr/logs_*.json` — review log files -2. `rm .auto-claude/github/pr/review_*.json` — review result files +1. `rm .aperant/github/pr/logs_*.json` — review log files +2. `rm .aperant/github/pr/review_*.json` — review result files 3. Reset `pr/index.json` to `{"reviews": [], "last_updated": null}` 4. Reset `bot_detection_state.json` to `{"reviewed_commits": {}}` — this is the gatekeeper; without clearing it, the bot detector skips already-seen commits @@ -217,7 +217,7 @@ const readTool = tool({ ### Spec Directory Structure -Each spec in `.auto-claude/specs/XXX-name/` contains: `spec.md`, `requirements.json`, `context.json`, `implementation_plan.json`, `qa_report.md`, `QA_FIX_REQUEST.md` +Each spec in `.aperant/specs/XXX-name/` contains: `spec.md`, `requirements.json`, `context.json`, `implementation_plan.json`, `qa_report.md`, `QA_FIX_REQUEST.md` ### Memory System (Graphiti) @@ -355,5 +355,5 @@ npm run dev # Development mode with HMR npm run dev:debug # Debug mode with verbose output npm run dev:mcp # Electron MCP server for AI debugging -# Project data: .auto-claude/specs/ (gitignored) +# Project data: .aperant/specs/ (gitignored) ``` diff --git a/apps/desktop/e2e/flows.e2e.ts b/apps/desktop/e2e/flows.e2e.ts index d10aa71ded..b88662c355 100644 --- a/apps/desktop/e2e/flows.e2e.ts +++ b/apps/desktop/e2e/flows.e2e.ts @@ -231,9 +231,9 @@ test.describe('E2E Flow Verification (Mock-based)', () => { const projectPath = TEST_PROJECT_DIR; expect(existsSync(projectPath)).toBe(true); - // Check for auto-claude directory detection - const autoBuildPath = path.join(projectPath, 'auto-claude'); - expect(existsSync(autoBuildPath)).toBe(true); + // Check for aperant directory detection + const aperantPath = path.join(projectPath, '.aperant'); + expect(existsSync(aperantPath)).toBe(true); cleanupTestEnvironment(); }); diff --git a/apps/desktop/prompts/coder.md b/apps/desktop/prompts/coder.md index 1c7db8e617..f34232ea34 100644 --- a/apps/desktop/prompts/coder.md +++ b/apps/desktop/prompts/coder.md @@ -12,7 +12,7 @@ You are continuing work on an autonomous development task. This is a **FRESH con environment at the start of each prompt in the "YOUR ENVIRONMENT" section. Pay close attention to: - **Working Directory**: This is your root - all paths are relative to here -- **Spec Location**: Where your spec files live (usually `./auto-claude/specs/{spec-name}/`) +- **Spec Location**: Where your spec files live (usually `./.aperant/specs/{spec-name}/`) - **Isolation Mode**: If present, you are in an isolated worktree (see below) **RULES:** @@ -57,9 +57,9 @@ You may see absolute paths like `/e/projects/myapp/prod/src/file.ts` in: ```bash # Verify you're still in the worktree pwd -# Should show: .../.auto-claude/worktrees/tasks/{spec-name}/ +# Should show: .../.aperant/worktrees/tasks/{spec-name}/ # Or (legacy): .../.worktrees/{spec-name}/ -# Or (PR review): .../.auto-claude/github/pr/worktrees/{pr-number}/ +# Or (PR review): .../.aperant/github/pr/worktrees/{pr-number}/ # NOT: /path/to/main/project ``` @@ -140,7 +140,7 @@ pwd && ls -la find . -name "implementation_plan.json" -type f 2>/dev/null | head -5 # 3. Set SPEC_DIR based on what you find (example - adjust path as needed) -SPEC_DIR="./auto-claude/specs/YOUR-SPEC-NAME" # Replace with actual path from step 2 +SPEC_DIR="./.aperant/specs/YOUR-SPEC-NAME" # Replace with actual path from step 2 # 4. Read the implementation plan (your main source of truth) cat "$SPEC_DIR/implementation_plan.json" @@ -791,7 +791,7 @@ The system **automatically scans for secrets** before every commit. If secrets a api_key = os.environ.get("API_KEY") ``` 3. **Update .env.example** - Add placeholder for the new variable -4. **Re-stage and retry** - `git add . ':!.auto-claude' && git commit ...` +4. **Re-stage and retry** - `git add . ':!.aperant' && git commit ...` **If it's a false positive:** - Add the file pattern to `.secretsignore` in the project root @@ -803,22 +803,22 @@ The system **automatically scans for secrets** before every commit. If secrets a # FIRST: Make sure you're in the working directory root (check YOUR ENVIRONMENT section at top) pwd # Should match your working directory -# Add all files EXCEPT .auto-claude directory (spec files should never be committed) -git add . ':!.auto-claude' +# Add all files EXCEPT .aperant directory (spec files should never be committed) +git add . ':!.aperant' # If git add fails with "pathspec did not match", you have a path problem: # 1. Run pwd to see where you are # 2. Run git status to see what git sees # 3. Adjust your paths accordingly -git commit -m "auto-claude: Complete [subtask-id] - [subtask description] +git commit -m "aperant: Complete [subtask-id] - [subtask description] - Files modified: [list] - Verification: [type] - passed - Phase progress: [X]/[Y] subtasks complete" ``` -**CRITICAL**: The `:!.auto-claude` pathspec exclusion ensures spec files are NEVER committed. +**CRITICAL**: The `:!.aperant` pathspec exclusion ensures spec files are NEVER committed. These are internal tracking files that must stay local. ### DO NOT Push to Remote @@ -851,7 +851,7 @@ Next phase (if applicable): [phase-name] === END SESSION N === ``` -**Note:** The `build-progress.txt` file is in `.auto-claude/specs/` which is gitignored. +**Note:** The `build-progress.txt` file is in `.aperant/specs/` which is gitignored. Do NOT try to commit it - the framework tracks progress automatically. --- @@ -881,7 +881,7 @@ All subtasks completed! Workflow type: [type] Total phases: [N] Total subtasks: [N] -Branch: auto-claude/[feature-name] +Branch: aperant/[feature-name] Ready for human review and merge. ``` diff --git a/apps/desktop/prompts/planner.md b/apps/desktop/prompts/planner.md index e5914ff8ef..26c4c73ffd 100644 --- a/apps/desktop/prompts/planner.md +++ b/apps/desktop/prompts/planner.md @@ -741,7 +741,7 @@ The following files are gitignored and should NOT be committed: - `init.sh` - tracked locally only - `build-progress.txt` - tracked locally only -These files live in `.auto-claude/specs/` which is gitignored. The orchestrator handles syncing them between worktrees and the main project. +These files live in `.aperant/specs/` which is gitignored. The orchestrator handles syncing them between worktrees and the main project. **Only code changes should be committed** - spec metadata stays local. diff --git a/apps/desktop/prompts/qa_fixer.md b/apps/desktop/prompts/qa_fixer.md index 8c94ccaa67..121c8eabaa 100644 --- a/apps/desktop/prompts/qa_fixer.md +++ b/apps/desktop/prompts/qa_fixer.md @@ -11,10 +11,10 @@ You are the **QA Fix Agent** in an autonomous development process. The QA Review ### NEVER edit qa_report.md The `qa_report.md` file belongs to the QA Reviewer. You must NEVER modify it. The reviewer writes the verdict; you implement fixes. If you change the report status (e.g., to "FIXES_APPLIED"), the orchestrator won't recognize it as a valid verdict and your fixes will be wasted. -### Fix in the PROJECT SOURCE, not in .auto-claude/specs/ -All your code changes, documentation additions, and new files must go into the **project source tree** (the actual codebase). Never create deliverable files inside `.auto-claude/specs/` — that directory contains gitignored metadata (spec, plan, QA report). The QA reviewer evaluates the project source, not spec artifacts. +### Fix in the PROJECT SOURCE, not in .aperant/specs/ +All your code changes, documentation additions, and new files must go into the **project source tree** (the actual codebase). Never create deliverable files inside `.aperant/specs/` — that directory contains gitignored metadata (spec, plan, QA report). The QA reviewer evaluates the project source, not spec artifacts. -**Example:** If QA says "missing route inventory document", create it in the project root (e.g., `docs/route-policy.md` or `ROUTE_POLICY.md`), NOT in `.auto-claude/specs/route_access_policy.md`. +**Example:** If QA says "missing route inventory document", create it in the project root (e.g., `docs/route-policy.md` or `ROUTE_POLICY.md`), NOT in `.aperant/specs/route_access_policy.md`. ### Fix CODE issues with CODE, not documentation If QA reports a missing test, write the test. If QA reports a code bug, fix the code. Don't write a markdown document explaining why the code is fine — write the code that makes it fine. @@ -204,7 +204,7 @@ Escaping the worktree causes: pwd # 2. Verify the target is within your worktree -# If pwd shows: /path/to/.auto-claude/worktrees/tasks/spec-name/ +# If pwd shows: /path/to/.aperant/worktrees/tasks/spec-name/ # Then: cd ./apps/desktop ✅ SAFE # But: cd /path/to/parent/project ❌ FORBIDDEN - ESCAPES ISOLATION @@ -338,8 +338,8 @@ ls -la [path-to-files] # Make sure the path is correct from your current locati # FIRST: Make sure you're in the working directory root pwd # Should match your working directory -# Add all files EXCEPT .auto-claude directory (spec files should never be committed) -git add . ':!.auto-claude' +# Add all files EXCEPT .aperant directory (spec files should never be committed) +git add . ':!.aperant' # If git add fails with "pathspec did not match", you have a path problem: # 1. Run pwd to see where you are @@ -360,7 +360,7 @@ Verified: QA Fix Session: [N]" ``` -**CRITICAL**: The `:!.auto-claude` pathspec exclusion ensures spec files are NEVER committed. +**CRITICAL**: The `:!.aperant` pathspec exclusion ensures spec files are NEVER committed. **NOTE**: Do NOT push to remote. All work stays local until user reviews and approves. @@ -490,7 +490,7 @@ npx prisma migrate dev --name [name] ### Write Deliverables to the Project, Not Spec Artifacts - All new files (docs, tests, code) go in the project source tree -- NEVER create deliverable files in `.auto-claude/specs/` — that directory is gitignored metadata +- NEVER create deliverable files in `.aperant/specs/` — that directory is gitignored metadata ### Git Configuration - NEVER MODIFY **CRITICAL**: You MUST NOT modify git user configuration. Never run: diff --git a/apps/desktop/prompts/qa_reviewer.md b/apps/desktop/prompts/qa_reviewer.md index 501b0dc0b5..947d554a02 100644 --- a/apps/desktop/prompts/qa_reviewer.md +++ b/apps/desktop/prompts/qa_reviewer.md @@ -480,7 +480,7 @@ cat > qa_report.md << 'EOF' [QA Report content] EOF -# Note: qa_report.md and implementation_plan.json are in .auto-claude/specs/ (gitignored) +# Note: qa_report.md and implementation_plan.json are in .aperant/specs/ (gitignored) # Do NOT commit them - the framework tracks QA status automatically # Only commit actual code changes to the project ``` @@ -517,7 +517,7 @@ Once fixes are complete: EOF -# Note: QA_FIX_REQUEST.md and implementation_plan.json are in .auto-claude/specs/ (gitignored) +# Note: QA_FIX_REQUEST.md and implementation_plan.json are in .aperant/specs/ (gitignored) # Do NOT commit them - the framework tracks QA status automatically # Only commit actual code fixes to the project ``` diff --git a/apps/desktop/src/__tests__/e2e/smoke.test.ts b/apps/desktop/src/__tests__/e2e/smoke.test.ts index bdee6480a9..4a12f01545 100644 --- a/apps/desktop/src/__tests__/e2e/smoke.test.ts +++ b/apps/desktop/src/__tests__/e2e/smoke.test.ts @@ -127,7 +127,7 @@ function setupTestDirs(): void { TEST_PROJECT_PATH = path.join(TEST_DIR, 'test-project'); mkdirSync(TEST_PROJECT_PATH, { recursive: true }); // Create a minimal project structure - mkdirSync(path.join(TEST_PROJECT_PATH, '.auto-claude'), { recursive: true }); + mkdirSync(path.join(TEST_PROJECT_PATH, '.aperant'), { recursive: true }); } // Cleanup test directories diff --git a/apps/desktop/src/__tests__/integration/rate-limit-subtask-recovery.test.ts b/apps/desktop/src/__tests__/integration/rate-limit-subtask-recovery.test.ts index a672a7ce44..5893773a25 100644 --- a/apps/desktop/src/__tests__/integration/rate-limit-subtask-recovery.test.ts +++ b/apps/desktop/src/__tests__/integration/rate-limit-subtask-recovery.test.ts @@ -23,7 +23,7 @@ let PLAN_PATH: string; // Setup test directories function setupTestDirs(): void { TEST_DIR = mkdtempSync(path.join(tmpdir(), 'rate-limit-recovery-test-')); - TEST_SPEC_DIR = path.join(TEST_DIR, '.auto-claude/specs/001-test-feature'); + TEST_SPEC_DIR = path.join(TEST_DIR, '.aperant/specs/001-test-feature'); PLAN_PATH = path.join(TEST_SPEC_DIR, 'implementation_plan.json'); mkdirSync(TEST_SPEC_DIR, { recursive: true }); } diff --git a/apps/desktop/src/__tests__/integration/task-lifecycle.test.ts b/apps/desktop/src/__tests__/integration/task-lifecycle.test.ts index be9b16cdfe..aa15c44348 100644 --- a/apps/desktop/src/__tests__/integration/task-lifecycle.test.ts +++ b/apps/desktop/src/__tests__/integration/task-lifecycle.test.ts @@ -94,7 +94,7 @@ function setupTestDirs(): void { // Create secure temp directory with random suffix TEST_DIR = mkdtempSync(path.join(tmpdir(), 'task-lifecycle-test-')); TEST_PROJECT_PATH = path.join(TEST_DIR, 'test-project'); - TEST_SPEC_DIR = path.join(TEST_PROJECT_PATH, '.auto-claude/specs/001-test-feature'); + TEST_SPEC_DIR = path.join(TEST_PROJECT_PATH, '.aperant/specs/001-test-feature'); mkdirSync(TEST_SPEC_DIR, { recursive: true }); } diff --git a/apps/desktop/src/__tests__/setup.ts b/apps/desktop/src/__tests__/setup.ts index 27f55fc68b..1766454750 100644 --- a/apps/desktop/src/__tests__/setup.ts +++ b/apps/desktop/src/__tests__/setup.ts @@ -48,7 +48,7 @@ if (typeof global.requestAnimationFrame === 'undefined') { } // Test data directory for isolated file operations -export const TEST_DATA_DIR = '/tmp/auto-claude-ui-tests'; +export const TEST_DATA_DIR = '/tmp/aperant-ui-tests'; // Create fresh test directory before each test beforeEach(() => { diff --git a/apps/desktop/src/main/__tests__/file-watcher.test.ts b/apps/desktop/src/main/__tests__/file-watcher.test.ts index 685e7e8950..677851ae40 100644 --- a/apps/desktop/src/main/__tests__/file-watcher.test.ts +++ b/apps/desktop/src/main/__tests__/file-watcher.test.ts @@ -74,7 +74,7 @@ describe('FileWatcher concurrency', () => { // ------------------------------------------------------------------------- describe('deduplication: second watch() with same specDir is a no-op', () => { it('should only create one FSWatcher when watch() is called twice with the same specDir while the first is still in-flight', async () => { - const specDir = '/project/.auto-claude/specs/001-task'; + const specDir = '/project/.aperant/specs/001-task'; const taskId = 'task-1'; // To create a real async gap we need an existing watcher whose close() is slow. @@ -111,8 +111,8 @@ describe('FileWatcher concurrency', () => { describe('supersession: watch() with different specDir replaces the in-flight call', () => { it('should let the second call win when the first is awaiting close()', async () => { const taskId = 'task-2'; - const specDir1 = path.join('/project', '.auto-claude', 'specs', '001-first'); - const specDir2 = path.join('/project', '.auto-claude', 'specs', '002-second'); + const specDir1 = path.join('/project', '.aperant', 'specs', '001-first'); + const specDir2 = path.join('/project', '.aperant', 'specs', '002-second'); // First call installs an existing watcher (simulate: the watcher for // specDir1 is already set up so the second watch() needs to close it). @@ -148,8 +148,8 @@ describe('FileWatcher concurrency', () => { it('first watch() bails when pendingWatches changes to a different specDir', async () => { const taskId = 'task-super'; - const specDir1 = path.join('/project', '.auto-claude', 'specs', 'super-first'); - const specDir2 = path.join('/project', '.auto-claude', 'specs', 'super-second'); + const specDir1 = path.join('/project', '.aperant', 'specs', 'super-first'); + const specDir2 = path.join('/project', '.aperant', 'specs', 'super-second'); // Make the first watcher's close() slow so we can interleave. let resolveFirstClose!: () => void; @@ -187,7 +187,7 @@ describe('FileWatcher concurrency', () => { describe('cancellation: unwatch() during in-flight watch() prevents watcher creation', () => { it('should not create a watcher when unwatch() is called before the async gap resolves', async () => { const taskId = 'task-3'; - const specDir = '/project/.auto-claude/specs/003-cancel'; + const specDir = '/project/.aperant/specs/003-cancel'; // There's no pre-existing watcher, so watch() won't call close(). But it // does go async (chokidar.watch is sync but we can test the cancellation @@ -207,7 +207,7 @@ describe('FileWatcher concurrency', () => { ); // Start a second watch() — it will await the slow close(). - const specDir2 = '/project/.auto-claude/specs/003-cancel-v2'; + const specDir2 = '/project/.aperant/specs/003-cancel-v2'; const watchPromise = fw.watch(taskId, specDir2); // While watch() is in-flight, call unwatch(). @@ -231,8 +231,8 @@ describe('FileWatcher concurrency', () => { it('should cancel pending watch() calls and clear pendingWatches', async () => { const taskId1 = 'task-4a'; const taskId2 = 'task-4b'; - const specDir1 = '/project/.auto-claude/specs/004a'; - const specDir2 = '/project/.auto-claude/specs/004b'; + const specDir1 = '/project/.aperant/specs/004a'; + const specDir2 = '/project/.aperant/specs/004b'; // Set up slow-close scenario for taskId1 (so watch() is in-flight). await fw.watch(taskId1, specDir1); @@ -243,7 +243,7 @@ describe('FileWatcher concurrency', () => { ); // Start a new watch for taskId1 with a different specDir — this is now in-flight. - const newSpecDir1 = '/project/.auto-claude/specs/004a-v2'; + const newSpecDir1 = '/project/.aperant/specs/004a-v2'; const watchPromise1 = fw.watch(taskId1, newSpecDir1); // Start a fresh watch for taskId2. @@ -262,7 +262,7 @@ describe('FileWatcher concurrency', () => { // pendingWatches should be cleared (we verify indirectly: a fresh // watch() call for taskId1 must succeed without treating it as a duplicate). - const specDirFresh = path.join('/project', '.auto-claude', 'specs', '004a-fresh'); + const specDirFresh = path.join('/project', '.aperant', 'specs', '004a-fresh'); await fw.watch(taskId1, specDirFresh); expect(fw.isWatching(taskId1)).toBe(true); expect(fw.getWatchedSpecDir(taskId1)).toBe(specDirFresh); @@ -275,7 +275,7 @@ describe('FileWatcher concurrency', () => { describe('getWatchedSpecDir()', () => { it('returns the specDir that was passed to watch()', async () => { const taskId = 'task-5'; - const specDir = path.join('/project', '.auto-claude', 'specs', '005-specdir'); + const specDir = path.join('/project', '.aperant', 'specs', '005-specdir'); await fw.watch(taskId, specDir); @@ -288,8 +288,8 @@ describe('FileWatcher concurrency', () => { it('returns updated specDir after re-watch with different specDir', async () => { const taskId = 'task-5b'; - const specDir1 = path.join('/project', '.auto-claude', 'specs', '005b-first'); - const specDir2 = path.join('/project', '.auto-claude', 'specs', '005b-second'); + const specDir1 = path.join('/project', '.aperant', 'specs', '005b-first'); + const specDir2 = path.join('/project', '.aperant', 'specs', '005b-second'); await fw.watch(taskId, specDir1); expect(fw.getWatchedSpecDir(taskId)).toBe(specDir1); diff --git a/apps/desktop/src/main/__tests__/insights-config.test.ts b/apps/desktop/src/main/__tests__/insights-config.test.ts index 20e9c48b01..dc77996330 100644 --- a/apps/desktop/src/main/__tests__/insights-config.test.ts +++ b/apps/desktop/src/main/__tests__/insights-config.test.ts @@ -45,7 +45,7 @@ describe('InsightsConfig', () => { it('should build process env with profile settings', async () => { const config = new InsightsConfig(); - vi.spyOn(config, 'loadAutoBuildEnv').mockReturnValue({ CUSTOM_ENV: '1' }); + vi.spyOn(config, 'loadAperantEnv').mockReturnValue({ CUSTOM_ENV: '1' }); const env = await config.getProcessEnv(); diff --git a/apps/desktop/src/main/__tests__/ipc-handlers.test.ts b/apps/desktop/src/main/__tests__/ipc-handlers.test.ts index 88ede24e20..2b9ee4179c 100644 --- a/apps/desktop/src/main/__tests__/ipc-handlers.test.ts +++ b/apps/desktop/src/main/__tests__/ipc-handlers.test.ts @@ -143,7 +143,7 @@ vi.mock("electron", () => { // Setup test project structure function setupTestProject(): void { mkdirSync(TEST_PROJECT_PATH, { recursive: true }); - mkdirSync(path.join(TEST_PROJECT_PATH, "auto-claude", "specs"), { recursive: true }); + mkdirSync(path.join(TEST_PROJECT_PATH, "aperant", "specs"), { recursive: true }); } // Cleanup test directories @@ -419,15 +419,15 @@ describe("IPC Handlers", { timeout: 30000 }, () => { () => mockMainWindow as never ); - // Create .auto-claude directory first (before adding project so it gets detected) - mkdirSync(path.join(TEST_PROJECT_PATH, ".auto-claude", "specs"), { recursive: true }); + // Create .aperant directory first (before adding project so it gets detected) + mkdirSync(path.join(TEST_PROJECT_PATH, ".aperant", "specs"), { recursive: true }); - // Add a project - it will detect .auto-claude + // Add a project - it will detect .aperant const addResult = await ipcMain.invokeHandler("project:add", {}, TEST_PROJECT_PATH); const projectId = (addResult as { data: { id: string } }).data.id; - // Create a spec directory with implementation plan in .auto-claude/specs - const specDir = path.join(TEST_PROJECT_PATH, ".auto-claude", "specs", "001-test-feature"); + // Create a spec directory with implementation plan in .aperant/specs + const specDir = path.join(TEST_PROJECT_PATH, ".aperant", "specs", "001-test-feature"); mkdirSync(specDir, { recursive: true }); writeFileSync( path.join(specDir, "implementation_plan.json"), @@ -489,8 +489,8 @@ describe("IPC Handlers", { timeout: 30000 }, () => { () => mockMainWindow as never ); - // Create .auto-claude directory first (before adding project so it gets detected) - mkdirSync(path.join(TEST_PROJECT_PATH, ".auto-claude", "specs"), { recursive: true }); + // Create .aperant directory first (before adding project so it gets detected) + mkdirSync(path.join(TEST_PROJECT_PATH, ".aperant", "specs"), { recursive: true }); // Add a project first const addResult = await ipcMain.invokeHandler("project:add", {}, TEST_PROJECT_PATH); @@ -630,7 +630,7 @@ describe("IPC Handlers", { timeout: 30000 }, () => { await ipcMain.invokeHandler("project:add", {}, TEST_PROJECT_PATH); // Create a spec/task directory with implementation_plan.json - const specDir = path.join(TEST_PROJECT_PATH, ".auto-claude", "specs", "task-1"); + const specDir = path.join(TEST_PROJECT_PATH, ".aperant", "specs", "task-1"); mkdirSync(specDir, { recursive: true }); writeFileSync( path.join(specDir, "implementation_plan.json"), diff --git a/apps/desktop/src/main/__tests__/project-migration.test.ts b/apps/desktop/src/main/__tests__/project-migration.test.ts new file mode 100644 index 0000000000..ce34f85190 --- /dev/null +++ b/apps/desktop/src/main/__tests__/project-migration.test.ts @@ -0,0 +1,172 @@ +/** + * Tests for needsMigration() and migrateProject() in project-initializer.ts + */ + +import { describe, test, expect, vi, beforeEach } from 'vitest'; + +// ---- fs mock ---- +const mockExistingPaths = new Set(); +const mockFiles = new Map(); + +vi.mock('fs', () => { + const existsSync = vi.fn((p: string) => mockExistingPaths.has(p)); + + const renameSync = vi.fn((oldPath: string, newPath: string) => { + // Simulate rename: remove old, add new + mockExistingPaths.delete(oldPath); + mockExistingPaths.add(newPath); + }); + + const readFileSync = vi.fn((filePath: string, _encoding?: string): string => { + const content = mockFiles.get(filePath); + if (content === undefined) { + const err = new Error(`ENOENT: no such file or directory, open '${filePath}'`) as NodeJS.ErrnoException; + err.code = 'ENOENT'; + throw err; + } + return content; + }); + + const writeFileSync = vi.fn((filePath: string, content: string) => { + mockFiles.set(filePath, content); + }); + + const appendFileSync = vi.fn((filePath: string, content: string) => { + const existing = mockFiles.get(filePath) ?? ''; + mockFiles.set(filePath, existing + content); + }); + + const mkdirSync = vi.fn(); + + return { + default: { existsSync, renameSync, readFileSync, writeFileSync, appendFileSync, mkdirSync }, + existsSync, + renameSync, + readFileSync, + writeFileSync, + appendFileSync, + mkdirSync, + }; +}); + +// ---- stub heavy transitive deps ---- +vi.mock('child_process', () => ({ + execFileSync: vi.fn(() => ''), +})); + +vi.mock('../cli-tool-manager', () => ({ + getToolPath: vi.fn(() => 'git'), +})); + +// ---- import after mocks ---- +import { needsMigration, migrateProject } from '../project-initializer'; +import * as fs from 'fs'; +import * as path from 'path'; + +const PROJECT = '/test/project'; +const OLD_PATH = path.join(PROJECT, '.auto-claude'); +const NEW_PATH = path.join(PROJECT, '.aperant'); +const GITIGNORE = path.join(PROJECT, '.gitignore'); + +beforeEach(() => { + vi.clearAllMocks(); + mockExistingPaths.clear(); + mockFiles.clear(); +}); + +// ──────────────────────────────────────────────────────────── +// needsMigration() +// ──────────────────────────────────────────────────────────── + +describe('needsMigration', () => { + test('returns true when .auto-claude exists and .aperant does not', () => { + mockExistingPaths.add(OLD_PATH); + expect(needsMigration(PROJECT)).toBe(true); + }); + + test('returns false when .aperant already exists', () => { + mockExistingPaths.add(OLD_PATH); + mockExistingPaths.add(NEW_PATH); + expect(needsMigration(PROJECT)).toBe(false); + }); + + test('returns false when neither exists', () => { + expect(needsMigration(PROJECT)).toBe(false); + }); + + test('returns false when both exist', () => { + mockExistingPaths.add(OLD_PATH); + mockExistingPaths.add(NEW_PATH); + expect(needsMigration(PROJECT)).toBe(false); + }); +}); + +// ──────────────────────────────────────────────────────────── +// migrateProject() +// ──────────────────────────────────────────────────────────── + +describe('migrateProject', () => { + test('successfully renames .auto-claude to .aperant', () => { + mockExistingPaths.add(OLD_PATH); + + const result = migrateProject(PROJECT); + + expect(result.success).toBe(true); + expect(fs.renameSync).toHaveBeenCalledWith(OLD_PATH, NEW_PATH); + }); + + test('returns error when .auto-claude does not exist', () => { + // OLD_PATH not in mockExistingPaths + + const result = migrateProject(PROJECT); + + expect(result.success).toBe(false); + expect(result.error).toMatch(/No \.auto-claude directory/i); + expect(fs.renameSync).not.toHaveBeenCalled(); + }); + + test('returns error when .aperant already exists', () => { + mockExistingPaths.add(OLD_PATH); + mockExistingPaths.add(NEW_PATH); + + const result = migrateProject(PROJECT); + + expect(result.success).toBe(false); + expect(result.error).toMatch(/\.aperant directory already exists/i); + expect(fs.renameSync).not.toHaveBeenCalled(); + }); + + test('updates .gitignore entries during migration', () => { + mockExistingPaths.add(OLD_PATH); + mockFiles.set(GITIGNORE, '.auto-claude/\n.auto-claude-security.json\n.auto-claude-status\n'); + + const result = migrateProject(PROJECT); + + expect(result.success).toBe(true); + expect(fs.writeFileSync).toHaveBeenCalled(); + // Find the call that wrote to the gitignore + const gitignoreWrite = (fs.writeFileSync as ReturnType).mock.calls.find( + (call: unknown[]) => call[0] === GITIGNORE + ); + expect(gitignoreWrite).toBeDefined(); + const writtenContent = gitignoreWrite![1] as string; + expect(writtenContent).toContain('.aperant/'); + expect(writtenContent).not.toContain('.auto-claude/'); + }); + + test('handles .gitignore not existing gracefully', () => { + mockExistingPaths.add(OLD_PATH); + // mockFiles has no GITIGNORE entry → readFileSync throws ENOENT + + // Should not throw; the catch block swallows the .gitignore read error, + // then ensureGitignoreEntries creates the file via writeFileSync + const result = migrateProject(PROJECT); + expect(result.success).toBe(true); + // ensureGitignoreEntries creates a new .gitignore with .aperant/ + const written = (fs.writeFileSync as ReturnType).mock.calls.find( + (call: unknown[]) => call[0] === GITIGNORE + ); + expect(written).toBeDefined(); + expect(written![1] as string).toContain('.aperant/'); + }); +}); diff --git a/apps/desktop/src/main/__tests__/project-store.test.ts b/apps/desktop/src/main/__tests__/project-store.test.ts index 9273f6186d..9785f84857 100644 --- a/apps/desktop/src/main/__tests__/project-store.test.ts +++ b/apps/desktop/src/main/__tests__/project-store.test.ts @@ -88,25 +88,25 @@ describe('ProjectStore', () => { expect(project1.id).toBe(project2.id); }); - it('should detect auto-claude directory if present', async () => { - // Create .auto-claude directory (the data directory, not source code) - mkdirSync(path.join(TEST_PROJECT_PATH, '.auto-claude'), { recursive: true }); + it('should detect aperant directory if present', async () => { + // Create .aperant directory (the data directory, not source code) + mkdirSync(path.join(TEST_PROJECT_PATH, '.aperant'), { recursive: true }); const { ProjectStore } = await import('../project-store'); const store = new ProjectStore(); const project = store.addProject(TEST_PROJECT_PATH); - expect(project.autoBuildPath).toBe('.auto-claude'); + expect(project.aperantPath).toBe('.aperant'); }); - it('should set empty autoBuildPath if not present', async () => { + it('should set empty aperantPath if not present', async () => { const { ProjectStore } = await import('../project-store'); const store = new ProjectStore(); const project = store.addProject(TEST_PROJECT_PATH); - expect(project.autoBuildPath).toBe(''); + expect(project.aperantPath).toBe(''); }); it('should persist project to disk', async () => { @@ -284,8 +284,8 @@ describe('ProjectStore', () => { }); it('should read tasks from filesystem correctly', async () => { - // Create spec directory structure in .auto-claude (the data directory) - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '001-test-feature'); + // Create spec directory structure in .aperant (the data directory) + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '001-test-feature'); mkdirSync(specsDir, { recursive: true }); const plan = { @@ -332,7 +332,7 @@ describe('ProjectStore', () => { }); it('should determine status as backlog when no subtasks completed', async () => { - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '002-pending'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '002-pending'); mkdirSync(specsDir, { recursive: true }); const plan = { @@ -372,7 +372,7 @@ describe('ProjectStore', () => { }); it('should determine status as ai_review when all subtasks completed', async () => { - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '003-complete'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '003-complete'); mkdirSync(specsDir, { recursive: true }); const plan = { @@ -412,7 +412,7 @@ describe('ProjectStore', () => { }); it('should determine status as human_review when plan status is human_review', async () => { - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '004-rejected'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '004-rejected'); mkdirSync(specsDir, { recursive: true }); const plan = { @@ -452,7 +452,7 @@ describe('ProjectStore', () => { }); it('should determine reviewReason from plan when status is human_review', async () => { - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '005-approved'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '005-approved'); mkdirSync(specsDir, { recursive: true }); const plan = { @@ -494,7 +494,7 @@ describe('ProjectStore', () => { it('should determine status as done when plan status is explicitly done', async () => { // User explicitly marking task as done via drag-and-drop sets status to done - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '006-done'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '006-done'); mkdirSync(specsDir, { recursive: true }); const plan = { @@ -533,7 +533,7 @@ describe('ProjectStore', () => { }); it('should prefer original task description from requirements.json over plan description', async () => { - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '007-description-priority'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '007-description-priority'); mkdirSync(specsDir, { recursive: true }); const aiDescription = 'AI-generated implementation plan description'; @@ -587,7 +587,7 @@ describe('ProjectStore', () => { id: 'test-id-123', name: 'Preexisting Project', path: '/test/path', - autoBuildPath: '', + aperantPath: '', settings: { model: 'sonnet', memoryBackend: 'memory', @@ -633,7 +633,7 @@ describe('ProjectStore', () => { describe('archiveTasks - multi-location handling', () => { it('should archive task from main specs directory only', async () => { // Create spec directory in main location only - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '001-test-task'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '001-test-task'); mkdirSync(specsDir, { recursive: true }); const plan = { @@ -666,18 +666,18 @@ describe('ProjectStore', () => { it('should archive task from BOTH main and worktree locations', async () => { // Create spec directory in main location - const mainSpecsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '002-multi-location'); + const mainSpecsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '002-multi-location'); mkdirSync(mainSpecsDir, { recursive: true }); // Create spec directory in worktree location - // Worktree path: .auto-claude/worktrees/tasks//.auto-claude/specs/ + // Worktree path: .aperant/worktrees/tasks//.aperant/specs/ const worktreeDir = path.join( TEST_PROJECT_PATH, - '.auto-claude', + '.aperant', 'worktrees', 'tasks', 'my-worktree', - '.auto-claude', + '.aperant', 'specs', '002-multi-location' ); @@ -724,11 +724,11 @@ describe('ProjectStore', () => { // Create spec directory ONLY in worktree location (not in main) const worktreeDir = path.join( TEST_PROJECT_PATH, - '.auto-claude', + '.aperant', 'worktrees', 'tasks', 'only-worktree', - '.auto-claude', + '.aperant', 'specs', '003-worktree-only' ); @@ -765,8 +765,8 @@ describe('ProjectStore', () => { const { ProjectStore } = await import('../project-store'); const store = new ProjectStore(); - // Create .auto-claude directory so project is recognized - mkdirSync(path.join(TEST_PROJECT_PATH, '.auto-claude'), { recursive: true }); + // Create .aperant directory so project is recognized + mkdirSync(path.join(TEST_PROJECT_PATH, '.aperant'), { recursive: true }); const project = store.addProject(TEST_PROJECT_PATH); // Task doesn't exist anywhere @@ -778,7 +778,7 @@ describe('ProjectStore', () => { it('should reject path traversal attempts in taskId', async () => { // Create a valid spec dir - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', 'valid-task'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', 'valid-task'); mkdirSync(specsDir, { recursive: true }); const plan = { feature: 'Test', phases: [] }; @@ -820,16 +820,16 @@ describe('ProjectStore', () => { describe('unarchiveTasks - multi-location handling', () => { it('should unarchive task from BOTH main and worktree locations', async () => { // Create archived task in both locations - const mainSpecsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '004-unarchive-test'); + const mainSpecsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '004-unarchive-test'); mkdirSync(mainSpecsDir, { recursive: true }); const worktreeDir = path.join( TEST_PROJECT_PATH, - '.auto-claude', + '.aperant', 'worktrees', 'tasks', 'unarchive-worktree', - '.auto-claude', + '.aperant', 'specs', '004-unarchive-test' ); @@ -874,7 +874,7 @@ describe('ProjectStore', () => { describe('cache invalidation', () => { it('should invalidate cache after archiveTasks', async () => { - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '005-cache-test'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '005-cache-test'); mkdirSync(specsDir, { recursive: true }); const plan = { @@ -915,7 +915,7 @@ describe('ProjectStore', () => { }); it('should return fresh data after invalidateTasksCache is called', async () => { - const specsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '006-invalidate-test'); + const specsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '006-invalidate-test'); mkdirSync(specsDir, { recursive: true }); const plan = { @@ -959,16 +959,16 @@ describe('ProjectStore', () => { describe('getTasks - worktree deduplication', () => { it('should not duplicate tasks that exist in both main and worktree', async () => { // Create same task in both main and worktree - const mainSpecsDir = path.join(TEST_PROJECT_PATH, '.auto-claude', 'specs', '007-dedupe-test'); + const mainSpecsDir = path.join(TEST_PROJECT_PATH, '.aperant', 'specs', '007-dedupe-test'); mkdirSync(mainSpecsDir, { recursive: true }); const worktreeDir = path.join( TEST_PROJECT_PATH, - '.auto-claude', + '.aperant', 'worktrees', 'tasks', 'dedupe-worktree', - '.auto-claude', + '.aperant', 'specs', '007-dedupe-test' ); diff --git a/apps/desktop/src/main/agent/agent-manager.ts b/apps/desktop/src/main/agent/agent-manager.ts index 21a538490c..2ded6f51ac 100644 --- a/apps/desktop/src/main/agent/agent-manager.ts +++ b/apps/desktop/src/main/agent/agent-manager.ts @@ -15,7 +15,7 @@ import { } from './types'; import type { IdeationConfig } from '../../shared/types'; import { resetStuckSubtasks } from '../ipc-handlers/task/plan-file-utils'; -import { AUTO_BUILD_PATHS, getSpecsDir } from '../../shared/constants'; +import { APERANT_PATHS, getSpecsDir } from '../../shared/constants'; import { projectStore } from '../project-store'; import { resolveAuth, resolveAuthFromQueue } from '../ai/auth/resolver'; import { resolveModelId } from '../ai/config/phase-config'; @@ -112,10 +112,10 @@ export class AgentManager extends EventEmitter { } /** - * Configure paths for Python and auto-claude source + * Configure paths for Python and aperant source */ - configure(pythonPath?: string, autoBuildSourcePath?: string): void { - this.processManager.configure(pythonPath, autoBuildSourcePath); + configure(pythonPath?: string, aperantSourcePath?: string): void { + this.processManager.configure(pythonPath, aperantSourcePath); } /** @@ -216,11 +216,11 @@ export class AgentManager extends EventEmitter { // Scan each project for stuck subtasks for (const project of projects) { - if (!project.autoBuildPath) { + if (!project.aperantPath) { continue; // Skip projects that haven't been initialized yet } - const specsDir = path.join(project.path, getSpecsDir(project.autoBuildPath)); + const specsDir = path.join(project.path, getSpecsDir(project.aperantPath)); // Check if specs directory exists if (!existsSync(specsDir)) { @@ -235,7 +235,7 @@ export class AgentManager extends EventEmitter { // Process each spec directory for (const specDirName of specDirs) { - const planPath = path.join(specsDir, specDirName, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specsDir, specDirName, APERANT_PATHS.IMPLEMENTATION_PLAN); // Check if implementation_plan.json exists if (!existsSync(planPath)) { @@ -336,7 +336,7 @@ export class AgentManager extends EventEmitter { // Reset stuck subtasks if restarting an existing spec creation task if (specDir) { - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); console.log('[AgentManager] Resetting stuck subtasks before spec creation restart:', planPath); try { const { success, resetCount } = await resetStuckSubtasks(planPath); @@ -375,7 +375,7 @@ export class AgentManager extends EventEmitter { const resolved = await this.resolveAuthFromProviderQueue(specModelId, preferredProvider); // Build the serializable session config for the worker - const resolvedSpecDir = specDir ?? path.join(projectPath, '.auto-claude', 'specs', taskId); + const resolvedSpecDir = specDir ?? path.join(projectPath, '.aperant', 'specs', taskId); const sessionConfig: SerializableSessionConfig = { agentType: 'spec_orchestrator' as const, systemPrompt, @@ -456,7 +456,7 @@ export class AgentManager extends EventEmitter { // Resolve the spec directory from specId const project = projectStore.getProjects().find((p) => p.id === projectId || p.path === projectPath); - const specsBaseDir = getSpecsDir(project?.autoBuildPath); + const specsBaseDir = getSpecsDir(project?.aperantPath); const specDir = path.join(projectPath, specsBaseDir, specId); // Load model configuration from task_metadata.json if available @@ -483,7 +483,7 @@ export class AgentManager extends EventEmitter { baseBranch, options.useLocalBranch ?? false, project?.settings?.pushNewBranches !== false, - project?.autoBuildPath, + project?.aperantPath, ); worktreePath = result.worktreePath; // Spec dir in the worktree (spec files were copied by createOrGetWorktree) @@ -578,7 +578,7 @@ export class AgentManager extends EventEmitter { // Resolve the spec directory from specId const project = projectStore.getProjects().find((p) => p.id === projectId || p.path === projectPath); - const specsBaseDir = getSpecsDir(project?.autoBuildPath); + const specsBaseDir = getSpecsDir(project?.aperantPath); const specDir = path.join(projectPath, specsBaseDir, specId); // Load model configuration from task_metadata.json if available @@ -820,8 +820,8 @@ export class AgentManager extends EventEmitter { // Reset stuck subtasks before restart to avoid picking up stale in-progress states if (context.specId || context.specDir) { const planPath = context.specDir - ? path.join(context.specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN) - : path.join(context.projectPath, AUTO_BUILD_PATHS.SPECS_DIR, context.specId, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + ? path.join(context.specDir, APERANT_PATHS.IMPLEMENTATION_PLAN) + : path.join(context.projectPath, APERANT_PATHS.SPECS_DIR, context.specId, APERANT_PATHS.IMPLEMENTATION_PLAN); console.log('[AgentManager] Resetting stuck subtasks before restart:', planPath); try { diff --git a/apps/desktop/src/main/agent/agent-process.test.ts b/apps/desktop/src/main/agent/agent-process.test.ts index e4622914dc..eafdd44800 100644 --- a/apps/desktop/src/main/agent/agent-process.test.ts +++ b/apps/desktop/src/main/agent/agent-process.test.ts @@ -48,7 +48,7 @@ vi.mock('child_process', async (importOriginal) => { // Mock project-initializer to avoid child_process.execSync issues vi.mock('../project-initializer', () => ({ - getAutoBuildPath: vi.fn(() => '/fake/auto-build'), + getAperantPath: vi.fn(() => '/fake/aperant'), isInitialized: vi.fn(() => true), initializeProject: vi.fn(), getProjectStorePath: vi.fn(() => '/fake/store/path') @@ -126,7 +126,7 @@ vi.mock('../env-utils', () => ({ getAugmentedEnv: vi.fn(() => ({ ...process.env })) })); -// Mock fs.existsSync for getAutoBuildSourcePath path validation +// Mock fs.existsSync for getAperantSourcePath path validation vi.mock('fs', async (importOriginal) => { const actual = await importOriginal(); return { @@ -135,10 +135,10 @@ vi.mock('fs', async (importOriginal) => { // Normalize path separators for cross-platform compatibility // path.join() uses backslashes on Windows, so we normalize to forward slashes const normalizedPath = inputPath.replace(/\\/g, '/'); - // Return true for the fake auto-build path and its expected files - if (normalizedPath === '/fake/auto-build' || - normalizedPath === '/fake/auto-build/runners' || - normalizedPath === '/fake/auto-build/runners/spec_runner.py') { + // Return true for the fake aperant path and its expected files + if (normalizedPath === '/fake/aperant' || + normalizedPath === '/fake/aperant/runners' || + normalizedPath === '/fake/aperant/runners/spec_runner.py') { return true; } return false; diff --git a/apps/desktop/src/main/agent/agent-process.ts b/apps/desktop/src/main/agent/agent-process.ts index d3f114211f..d359bc2026 100644 --- a/apps/desktop/src/main/agent/agent-process.ts +++ b/apps/desktop/src/main/agent/agent-process.ts @@ -102,7 +102,7 @@ export class AgentProcessManager { private state: AgentState; private events: AgentEvents; private emitter: EventEmitter; - private autoBuildSourcePath: string = ''; + private aperantSourcePath: string = ''; constructor(state: AgentState, events: AgentEvents, emitter: EventEmitter) { this.state = state; @@ -110,14 +110,14 @@ export class AgentProcessManager { this.emitter = emitter; } - configure(_pythonPath?: string, autoBuildSourcePath?: string): void { - if (autoBuildSourcePath) { - this.autoBuildSourcePath = autoBuildSourcePath; + configure(_pythonPath?: string, aperantSourcePath?: string): void { + if (aperantSourcePath) { + this.aperantSourcePath = aperantSourcePath; } } - getAutoBuildSourcePath(): string { - return this.autoBuildSourcePath; + getAperantSourcePath(): string { + return this.aperantSourcePath; } /** @@ -493,31 +493,31 @@ export class AgentProcessManager { } /** - * Load environment variables from project's .auto-claude/.env file + * Load environment variables from project's .aperant/.env file * This contains frontend-configured settings like memory configuration */ private loadProjectEnv(projectPath: string): Record { - // Find project by path to get autoBuildPath + // Find project by path to get aperantPath const projects = projectStore.getProjects(); const project = projects.find((p) => p.path === projectPath); - if (!project?.autoBuildPath) { + if (!project?.aperantPath) { return {}; } - const envPath = path.join(projectPath, project.autoBuildPath, '.env'); + const envPath = path.join(projectPath, project.aperantPath, '.env'); return this.parseEnvFile(envPath); } /** - * Load environment variables from auto-claude .env file + * Load environment variables from aperant .env file */ - loadAutoBuildEnv(): Record { - if (!this.autoBuildSourcePath) { + loadAperantEnv(): Record { + if (!this.aperantSourcePath) { return {}; } - const envPath = path.join(this.autoBuildSourcePath, '.env'); + const envPath = path.join(this.aperantSourcePath, '.env'); return this.parseEnvFile(envPath); } @@ -1036,13 +1036,13 @@ export class AgentProcessManager { * Priority (later sources override earlier): * 1. App-wide memory settings from settings.json (NEW - enables memory from onboarding) * 2. Auto-build source .env (prompts directory) - default values - * 3. Project's .auto-claude/.env - Frontend-configured settings (memory, integrations) + * 3. Project's .aperant/.env - Frontend-configured settings (memory, integrations) * 4. Project settings (useClaudeMd) - Runtime overrides */ getCombinedEnv(projectPath: string): Record { - const autoBuildEnv = this.loadAutoBuildEnv(); + const aperantEnv = this.loadAperantEnv(); const projectFileEnv = this.loadProjectEnv(projectPath); const projectSettingsEnv = this.getProjectEnvVars(projectPath); - return { ...autoBuildEnv, ...projectFileEnv, ...projectSettingsEnv }; + return { ...aperantEnv, ...projectFileEnv, ...projectSettingsEnv }; } } diff --git a/apps/desktop/src/main/agent/agent-queue.ts b/apps/desktop/src/main/agent/agent-queue.ts index aada34a53f..816689ec4d 100644 --- a/apps/desktop/src/main/agent/agent-queue.ts +++ b/apps/desktop/src/main/agent/agent-queue.ts @@ -6,7 +6,7 @@ import type { AgentEvents } from './agent-events'; import { AgentProcessManager } from './agent-process'; import { RoadmapConfig } from './types'; import type { IdeationConfig, Idea } from '../../shared/types'; -import { AUTO_BUILD_PATHS } from '../../shared/constants'; +import { APERANT_PATHS } from '../../shared/constants'; import { detectRateLimit, createSDKRateLimitInfo } from '../rate-limit-detector'; import { debugLog, debugError } from '../../shared/utils/debug-logger'; import { transformIdeaFromSnakeCase, transformSessionFromSnakeCase } from '../ipc-handlers/ideation/transformers'; @@ -83,8 +83,8 @@ export class AgentQueueManager { isRunning: boolean ): Promise { try { - const roadmapDir = path.join(projectPath, AUTO_BUILD_PATHS.ROADMAP_DIR); - const progressPath = path.join(roadmapDir, AUTO_BUILD_PATHS.GENERATION_PROGRESS); + const roadmapDir = path.join(projectPath, APERANT_PATHS.ROADMAP_DIR); + const progressPath = path.join(roadmapDir, APERANT_PATHS.GENERATION_PROGRESS); // Ensure roadmap directory exists if (!existsSync(roadmapDir)) { @@ -120,8 +120,8 @@ export class AgentQueueManager { try { const progressPath = path.join( projectPath, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.GENERATION_PROGRESS + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.GENERATION_PROGRESS ); if (existsSync(progressPath)) { @@ -225,7 +225,7 @@ export class AgentQueueManager { // which handles both dev (apps/desktop/prompts/) and production (resourcesPath/prompts/) const promptsDir = resolvePromptsDir(); - const outputDir = path.join(projectPath, '.auto-claude', 'ideation'); + const outputDir = path.join(projectPath, '.aperant', 'ideation'); // Emit initial progress this.emitter.emit('ideation-progress', projectId, { @@ -469,7 +469,7 @@ export class AgentQueueManager { this.clearRoadmapProgress(projectPath); // Load and emit the complete roadmap - const roadmapFilePath = path.join(projectPath, '.auto-claude', 'roadmap', 'roadmap.json'); + const roadmapFilePath = path.join(projectPath, '.aperant', 'roadmap', 'roadmap.json'); if (existsSync(roadmapFilePath)) { try { const content = await fsPromises.readFile(roadmapFilePath, 'utf-8'); diff --git a/apps/desktop/src/main/ai/auth/codex-oauth.ts b/apps/desktop/src/main/ai/auth/codex-oauth.ts index 70d4564ee0..1dbf3276af 100644 --- a/apps/desktop/src/main/ai/auth/codex-oauth.ts +++ b/apps/desktop/src/main/ai/auth/codex-oauth.ts @@ -196,7 +196,7 @@ export async function startCodexOAuthFlow(): Promise { authUrl.searchParams.set('state', state); authUrl.searchParams.set('code_challenge', codeChallenge); authUrl.searchParams.set('code_challenge_method', 'S256'); - authUrl.searchParams.set('originator', 'auto-claude'); + authUrl.searchParams.set('originator', 'aperant'); authUrl.searchParams.set('codex_cli_simplified_flow', 'true'); debugLog('Built authorization URL', { url: authUrl.toString() }); diff --git a/apps/desktop/src/main/ai/client/__tests__/factory.test.ts b/apps/desktop/src/main/ai/client/__tests__/factory.test.ts index 608dd4a96d..cc9fe01f8f 100644 --- a/apps/desktop/src/main/ai/client/__tests__/factory.test.ts +++ b/apps/desktop/src/main/ai/client/__tests__/factory.test.ts @@ -82,7 +82,7 @@ const FAKE_MODEL = { type: 'language-model', modelId: 'mock-model-id' }; const baseToolContext = { cwd: '/project', projectDir: '/project', - specDir: '/project/.auto-claude/specs/001', + specDir: '/project/.aperant/specs/001', securityProfile: 'standard' as const, } as unknown as ToolContext; diff --git a/apps/desktop/src/main/ai/config/__tests__/agent-configs.test.ts b/apps/desktop/src/main/ai/config/__tests__/agent-configs.test.ts index 7a189a811a..2c4a183ad8 100644 --- a/apps/desktop/src/main/ai/config/__tests__/agent-configs.test.ts +++ b/apps/desktop/src/main/ai/config/__tests__/agent-configs.test.ts @@ -87,11 +87,11 @@ describe('AGENT_CONFIGS', () => { expect(config.thinkingDefault).toBe('low'); }); - it('should configure planner with memory and auto-claude MCP', () => { + it('should configure planner with memory and aperant MCP', () => { const config = AGENT_CONFIGS.planner; expect(config.mcpServers).toContain('context7'); expect(config.mcpServers).toContain('memory'); - expect(config.mcpServers).toContain('auto-claude'); + expect(config.mcpServers).toContain('aperant'); expect(config.mcpServersOptional).toContain('linear'); expect(config.thinkingDefault).toBe('high'); }); @@ -197,7 +197,7 @@ describe('mapMcpServerName', () => { expect(mapMcpServerName('graphiti')).toBe('memory'); expect(mapMcpServerName('graphiti-memory')).toBe('memory'); expect(mapMcpServerName('linear')).toBe('linear'); - expect(mapMcpServerName('auto-claude')).toBe('auto-claude'); + expect(mapMcpServerName('aperant')).toBe('aperant'); }); it('should return null for unknown names', () => { @@ -291,12 +291,12 @@ describe('getRequiredMcpServers', () => { expect(servers).toContain('context7'); }); - it('should support per-agent MCP removals but never remove auto-claude', () => { + it('should support per-agent MCP removals but never remove aperant', () => { const servers = getRequiredMcpServers('coder', { memoryEnabled: true, - agentMcpRemove: 'auto-claude,memory', + agentMcpRemove: 'aperant,memory', }); - expect(servers).toContain('auto-claude'); + expect(servers).toContain('aperant'); expect(servers).not.toContain('memory'); }); }); diff --git a/apps/desktop/src/main/ai/config/agent-configs.ts b/apps/desktop/src/main/ai/config/agent-configs.ts index 0fe5aae9f1..1449c9ff38 100644 --- a/apps/desktop/src/main/ai/config/agent-configs.ts +++ b/apps/desktop/src/main/ai/config/agent-configs.ts @@ -36,15 +36,15 @@ const ALL_BUILTIN_TOOLS = [...BASE_READ_TOOLS, ...BASE_WRITE_TOOLS, ...WEB_TOOLS const SPEC_TOOLS = [...BASE_READ_TOOLS, 'Write', ...WEB_TOOLS] as const; // ============================================================================= -// Auto-Claude MCP Tools (Custom build management) +// Aperant MCP Tools (Custom build management) // ============================================================================= -const TOOL_UPDATE_SUBTASK_STATUS = 'mcp__auto-claude__update_subtask_status'; -const TOOL_GET_BUILD_PROGRESS = 'mcp__auto-claude__get_build_progress'; -const TOOL_RECORD_DISCOVERY = 'mcp__auto-claude__record_discovery'; -const TOOL_RECORD_GOTCHA = 'mcp__auto-claude__record_gotcha'; -const TOOL_GET_SESSION_CONTEXT = 'mcp__auto-claude__get_session_context'; -const TOOL_UPDATE_QA_STATUS = 'mcp__auto-claude__update_qa_status'; +const TOOL_UPDATE_SUBTASK_STATUS = 'mcp__aperant__update_subtask_status'; +const TOOL_GET_BUILD_PROGRESS = 'mcp__aperant__get_build_progress'; +const TOOL_RECORD_DISCOVERY = 'mcp__aperant__record_discovery'; +const TOOL_RECORD_GOTCHA = 'mcp__aperant__record_gotcha'; +const TOOL_GET_SESSION_CONTEXT = 'mcp__aperant__get_session_context'; +const TOOL_UPDATE_QA_STATUS = 'mcp__aperant__update_qa_status'; // ============================================================================= // External MCP Tools @@ -246,7 +246,7 @@ export const AGENT_CONFIGS: Record = { */ build_orchestrator: { tools: [...ALL_BUILTIN_TOOLS, 'SpawnSubagent'], - mcpServers: ['context7', 'memory', 'auto-claude'], + mcpServers: ['context7', 'memory', 'aperant'], mcpServersOptional: ['linear'], autoClaudeTools: [ TOOL_GET_BUILD_PROGRESS, @@ -263,7 +263,7 @@ export const AGENT_CONFIGS: Record = { // ═══════════════════════════════════════════════════════════════════════ planner: { tools: [...ALL_BUILTIN_TOOLS], - mcpServers: ['context7', 'memory', 'auto-claude'], + mcpServers: ['context7', 'memory', 'aperant'], mcpServersOptional: ['linear'], autoClaudeTools: [ TOOL_GET_BUILD_PROGRESS, @@ -274,7 +274,7 @@ export const AGENT_CONFIGS: Record = { }, coder: { tools: [...ALL_BUILTIN_TOOLS], - mcpServers: ['context7', 'memory', 'auto-claude'], + mcpServers: ['context7', 'memory', 'aperant'], mcpServersOptional: ['linear'], autoClaudeTools: [ TOOL_UPDATE_SUBTASK_STATUS, @@ -291,7 +291,7 @@ export const AGENT_CONFIGS: Record = { // ═══════════════════════════════════════════════════════════════════════ qa_reviewer: { tools: [...ALL_BUILTIN_TOOLS], - mcpServers: ['context7', 'memory', 'auto-claude', 'browser'], + mcpServers: ['context7', 'memory', 'aperant', 'browser'], mcpServersOptional: ['linear'], autoClaudeTools: [ TOOL_GET_BUILD_PROGRESS, @@ -302,7 +302,7 @@ export const AGENT_CONFIGS: Record = { }, qa_fixer: { tools: [...ALL_BUILTIN_TOOLS], - mcpServers: ['context7', 'memory', 'auto-claude', 'browser'], + mcpServers: ['context7', 'memory', 'aperant', 'browser'], mcpServersOptional: ['linear'], autoClaudeTools: [ TOOL_UPDATE_SUBTASK_STATUS, @@ -482,7 +482,7 @@ const MCP_SERVER_NAME_MAP: Record = { linear: 'linear', electron: 'electron', puppeteer: 'puppeteer', - 'auto-claude': 'auto-claude', + 'aperant': 'aperant', }; /** @@ -593,11 +593,11 @@ export function getRequiredMcpServers( } } - // Apply per-agent MCP removals (never remove auto-claude) + // Apply per-agent MCP removals (never remove aperant) if (options.agentMcpRemove) { for (const name of options.agentMcpRemove.split(',')) { const mapped = mapMcpServerName(name.trim(), options.customServerIds); - if (mapped && mapped !== 'auto-claude') { + if (mapped && mapped !== 'aperant') { const idx = servers.indexOf(mapped); if (idx !== -1) servers.splice(idx, 1); } diff --git a/apps/desktop/src/main/ai/context/builder.ts b/apps/desktop/src/main/ai/context/builder.ts index 41b97c32b7..61ea03d3fe 100644 --- a/apps/desktop/src/main/ai/context/builder.ts +++ b/apps/desktop/src/main/ai/context/builder.ts @@ -33,7 +33,7 @@ import type { // --------------------------------------------------------------------------- function loadProjectIndex(projectDir: string): ProjectIndex { - const indexFile = path.join(projectDir, '.auto-claude', 'project_index.json'); + const indexFile = path.join(projectDir, '.aperant', 'project_index.json'); if (fs.existsSync(indexFile)) { try { return JSON.parse(fs.readFileSync(indexFile, 'utf8')) as ProjectIndex; diff --git a/apps/desktop/src/main/ai/context/search.ts b/apps/desktop/src/main/ai/context/search.ts index b5ca39819c..18103a4f4b 100644 --- a/apps/desktop/src/main/ai/context/search.ts +++ b/apps/desktop/src/main/ai/context/search.ts @@ -14,8 +14,8 @@ import type { FileMatch } from './types.js'; /** Directories that should never be searched. */ const SKIP_DIRS = new Set([ 'node_modules', '.git', '__pycache__', '.venv', 'venv', 'dist', 'build', - '.next', '.nuxt', 'target', 'vendor', '.idea', '.vscode', 'auto-claude', - '.auto-claude', '.pytest_cache', '.mypy_cache', 'coverage', '.turbo', '.cache', + '.next', '.nuxt', 'target', 'vendor', '.idea', '.vscode', + '.aperant', '.pytest_cache', '.mypy_cache', 'coverage', '.turbo', '.cache', 'out', ]); diff --git a/apps/desktop/src/main/ai/context/types.ts b/apps/desktop/src/main/ai/context/types.ts index d47dca30d4..a18b7ae60c 100644 --- a/apps/desktop/src/main/ai/context/types.ts +++ b/apps/desktop/src/main/ai/context/types.ts @@ -55,7 +55,7 @@ export interface ServiceInfo { key_directories?: Record; } -/** Shape of .auto-claude/project_index.json */ +/** Shape of .aperant/project_index.json */ export interface ProjectIndex { services?: Record; [key: string]: unknown; diff --git a/apps/desktop/src/main/ai/mcp/__tests__/client.test.ts b/apps/desktop/src/main/ai/mcp/__tests__/client.test.ts index 151350feb9..9d1931ea67 100644 --- a/apps/desktop/src/main/ai/mcp/__tests__/client.test.ts +++ b/apps/desktop/src/main/ai/mcp/__tests__/client.test.ts @@ -195,10 +195,10 @@ describe('createMcpClientsForAgent', () => { }); it('creates clients for each resolved server config', async () => { - mockGetRequiredMcpServers.mockReturnValueOnce(['context7', 'auto-claude']); + mockGetRequiredMcpServers.mockReturnValueOnce(['context7', 'aperant']); mockResolveMcpServers.mockReturnValueOnce([ { ...stdioConfig, id: 'context7' }, - { ...stdioConfig, id: 'auto-claude' }, + { ...stdioConfig, id: 'aperant' }, ]); // Two separate mock instances for the two servers mockCreateMCPClient @@ -209,7 +209,7 @@ describe('createMcpClientsForAgent', () => { expect(clients).toHaveLength(2); expect(clients[0].serverId).toBe('context7'); - expect(clients[1].serverId).toBe('auto-claude'); + expect(clients[1].serverId).toBe('aperant'); }); it('skips failed connections without throwing', async () => { diff --git a/apps/desktop/src/main/ai/mcp/__tests__/registry.test.ts b/apps/desktop/src/main/ai/mcp/__tests__/registry.test.ts index 2d8c5f8d8f..a9797345e7 100644 --- a/apps/desktop/src/main/ai/mcp/__tests__/registry.test.ts +++ b/apps/desktop/src/main/ai/mcp/__tests__/registry.test.ts @@ -104,23 +104,23 @@ describe('getMcpServerConfig', () => { }); }); - describe('auto-claude', () => { - it('returns auto-claude config with empty specDir as default', () => { - const config = getMcpServerConfig('auto-claude', {}); + describe('aperant', () => { + it('returns aperant config with empty specDir as default', () => { + const config = getMcpServerConfig('aperant', {}); expect(config).not.toBeNull(); - expect(config?.id).toBe('auto-claude'); + expect(config?.id).toBe('aperant'); }); it('injects SPEC_DIR into transport env', () => { - const config = getMcpServerConfig('auto-claude', { specDir: '/project/.auto-claude/specs/001-feature' }); + const config = getMcpServerConfig('aperant', { specDir: '/project/.aperant/specs/001-feature' }); expect(config?.transport.type).toBe('stdio'); if (config?.transport.type === 'stdio') { - expect(config.transport.env?.SPEC_DIR).toBe('/project/.auto-claude/specs/001-feature'); + expect(config.transport.env?.SPEC_DIR).toBe('/project/.aperant/specs/001-feature'); } }); it('uses node command', () => { - const config = getMcpServerConfig('auto-claude', {}); + const config = getMcpServerConfig('aperant', {}); if (config?.transport.type === 'stdio') { expect(config.transport.command).toBe('node'); } @@ -174,9 +174,9 @@ describe('resolveMcpServers', () => { expect(configs[0].id).toBe('memory'); }); - it('passes specDir through to auto-claude config', () => { - const specDir = '/my-project/.auto-claude/specs/042-auth'; - const configs = resolveMcpServers(['auto-claude'], { specDir }); + it('passes specDir through to aperant config', () => { + const specDir = '/my-project/.aperant/specs/042-auth'; + const configs = resolveMcpServers(['aperant'], { specDir }); expect(configs).toHaveLength(1); if (configs[0].transport.type === 'stdio') { expect(configs[0].transport.env?.SPEC_DIR).toBe(specDir); diff --git a/apps/desktop/src/main/ai/mcp/registry.ts b/apps/desktop/src/main/ai/mcp/registry.ts index 54892f8d1d..eb6b36801a 100644 --- a/apps/desktop/src/main/ai/mcp/registry.ts +++ b/apps/desktop/src/main/ai/mcp/registry.ts @@ -100,19 +100,19 @@ const PUPPETEER_SERVER: McpServerConfig = { }; /** - * Auto-Claude MCP server - custom build management tools. + * Aperant MCP server - custom build management tools. * Used by planner, coder, and QA agents for build progress tracking. */ -function createAutoClaudeServer(specDir: string): McpServerConfig { +function createAperantServer(specDir: string): McpServerConfig { return { - id: 'auto-claude', + id: 'aperant', name: 'Aperant', description: 'Build management tools (progress tracking, session context)', enabledByDefault: true, transport: { type: 'stdio', command: 'node', - args: ['auto-claude-mcp-server.js'], + args: ['aperant-mcp-server.js'], env: { SPEC_DIR: specDir }, }, }; @@ -124,7 +124,7 @@ function createAutoClaudeServer(specDir: string): McpServerConfig { /** Options for resolving MCP server configurations */ export interface McpRegistryOptions { - /** Spec directory for auto-claude MCP server */ + /** Spec directory for aperant MCP server */ specDir?: string; /** Memory MCP server URL (if enabled) */ memoryMcpUrl?: string; @@ -175,9 +175,9 @@ export function getMcpServerConfig( case 'puppeteer': return PUPPETEER_SERVER; - case 'auto-claude': { + case 'aperant': { const specDir = options.specDir ?? ''; - return createAutoClaudeServer(specDir); + return createAperantServer(specDir); } default: diff --git a/apps/desktop/src/main/ai/mcp/types.ts b/apps/desktop/src/main/ai/mcp/types.ts index c0cefbd46b..858e306695 100644 --- a/apps/desktop/src/main/ai/mcp/types.ts +++ b/apps/desktop/src/main/ai/mcp/types.ts @@ -49,7 +49,7 @@ export type McpServerId = | 'memory' | 'electron' | 'puppeteer' - | 'auto-claude'; + | 'aperant'; /** Configuration for a single MCP server */ export interface McpServerConfig { diff --git a/apps/desktop/src/main/ai/memory/graph/incremental-indexer.ts b/apps/desktop/src/main/ai/memory/graph/incremental-indexer.ts index fa4f06963e..4de2896cec 100644 --- a/apps/desktop/src/main/ai/memory/graph/incremental-indexer.ts +++ b/apps/desktop/src/main/ai/memory/graph/incremental-indexer.ts @@ -46,7 +46,7 @@ export class IncrementalIndexer { ignored: [ '**/node_modules/**', '**/.git/**', - '**/.auto-claude/**', + '**/.aperant/**', '**/dist/**', '**/build/**', '**/.next/**', @@ -313,7 +313,7 @@ export class IncrementalIndexer { private collectSupportedFiles(dir: string, extensions: string[]): string[] { const files: string[] = []; const IGNORED_DIRS = new Set([ - 'node_modules', '.git', '.auto-claude', 'dist', 'build', + 'node_modules', '.git', '.aperant', 'dist', 'build', '.next', '__pycache__', 'target', '.venv', ]); diff --git a/apps/desktop/src/main/ai/merge/file-evolution.ts b/apps/desktop/src/main/ai/merge/file-evolution.ts index 2d868f812c..2f2e07f87d 100644 --- a/apps/desktop/src/main/ai/merge/file-evolution.ts +++ b/apps/desktop/src/main/ai/merge/file-evolution.ts @@ -7,7 +7,7 @@ * * Manages: * - Baseline capture when worktrees are created - * - File content snapshots in .auto-claude/baselines/ + * - File content snapshots in .aperant/baselines/ * - Task modification tracking with semantic analysis * - Persistence of evolution data */ @@ -198,7 +198,7 @@ export class FileEvolutionTracker { storageDir?: string, semanticAnalyzer?: SemanticAnalyzer, ) { - const resolvedStorageDir = storageDir ?? path.join(projectDir, '.auto-claude'); + const resolvedStorageDir = storageDir ?? path.join(projectDir, '.aperant'); this.storage = new EvolutionStorage(projectDir, resolvedStorageDir); this.analyzer = semanticAnalyzer ?? new SemanticAnalyzer(); this.evolutions = this.storage.loadEvolutions(); diff --git a/apps/desktop/src/main/ai/merge/orchestrator.ts b/apps/desktop/src/main/ai/merge/orchestrator.ts index 02ac252f15..10c52e60d3 100644 --- a/apps/desktop/src/main/ai/merge/orchestrator.ts +++ b/apps/desktop/src/main/ai/merge/orchestrator.ts @@ -110,8 +110,8 @@ function getFileFromBranch( function findWorktree(projectDir: string, taskId: string): string | undefined { // Common worktree locations const candidates = [ - path.join(projectDir, '.auto-claude', 'worktrees', taskId), - path.join(projectDir, '.auto-claude', 'worktrees', 'tasks', taskId), + path.join(projectDir, '.aperant', 'worktrees', taskId), + path.join(projectDir, '.aperant', 'worktrees', 'tasks', taskId), ]; for (const c of candidates) { if (fs.existsSync(c)) return c; @@ -254,7 +254,7 @@ export class MergeOrchestrator { dryRun?: boolean; }) { this.projectDir = path.resolve(options.projectDir); - this.storageDir = options.storageDir ?? path.join(this.projectDir, '.auto-claude'); + this.storageDir = options.storageDir ?? path.join(this.projectDir, '.aperant'); this.enableAi = options.enableAi ?? true; this.dryRun = options.dryRun ?? false; this.aiResolver = options.aiResolver; diff --git a/apps/desktop/src/main/ai/merge/timeline-tracker.ts b/apps/desktop/src/main/ai/merge/timeline-tracker.ts index 8e06abeb86..32a1849e1f 100644 --- a/apps/desktop/src/main/ai/merge/timeline-tracker.ts +++ b/apps/desktop/src/main/ai/merge/timeline-tracker.ts @@ -313,7 +313,7 @@ function getCommitInfo(commitHash: string, cwd: string): Record function getWorktreeFileContent(taskId: string, filePath: string, projectDir: string): string { // Try common worktree locations - const worktreePath = path.join(projectDir, '.auto-claude', 'worktrees', taskId, filePath); + const worktreePath = path.join(projectDir, '.aperant', 'worktrees', taskId, filePath); if (fs.existsSync(worktreePath)) { try { return fs.readFileSync(worktreePath, 'utf8'); @@ -369,7 +369,7 @@ export class FileTimelineTracker { constructor(projectPath: string, storagePath?: string) { this.projectPath = path.resolve(projectPath); - const resolvedStoragePath = storagePath ?? path.join(this.projectPath, '.auto-claude'); + const resolvedStoragePath = storagePath ?? path.join(this.projectPath, '.aperant'); this.persistence = new TimelinePersistence(resolvedStoragePath); this.timelines = this.persistence.loadAllTimelines(); } diff --git a/apps/desktop/src/main/ai/orchestration/__tests__/qa-loop.test.ts b/apps/desktop/src/main/ai/orchestration/__tests__/qa-loop.test.ts index c30ff044ac..1cb0b39fb9 100644 --- a/apps/desktop/src/main/ai/orchestration/__tests__/qa-loop.test.ts +++ b/apps/desktop/src/main/ai/orchestration/__tests__/qa-loop.test.ts @@ -49,7 +49,7 @@ import type { SessionResult } from '../../session/types'; // Helpers // --------------------------------------------------------------------------- -const SPEC_DIR = '/project/.auto-claude/specs/001-feature'; +const SPEC_DIR = '/project/.aperant/specs/001-feature'; const PROJECT_DIR = '/project'; function completedPlan(qaStatus?: 'approved' | 'rejected' | 'unknown') { diff --git a/apps/desktop/src/main/ai/orchestration/__tests__/qa-reports.test.ts b/apps/desktop/src/main/ai/orchestration/__tests__/qa-reports.test.ts index 078b8f219a..bbb279c522 100644 --- a/apps/desktop/src/main/ai/orchestration/__tests__/qa-reports.test.ts +++ b/apps/desktop/src/main/ai/orchestration/__tests__/qa-reports.test.ts @@ -207,7 +207,7 @@ describe('generateEscalationReport', () => { // --------------------------------------------------------------------------- describe('generateManualTestPlan', () => { - const SPEC_DIR = '/project/.auto-claude/specs/001-feature'; + const SPEC_DIR = '/project/.aperant/specs/001-feature'; const PROJECT_DIR = '/project'; beforeEach(() => { diff --git a/apps/desktop/src/main/ai/orchestration/__tests__/recovery-manager.test.ts b/apps/desktop/src/main/ai/orchestration/__tests__/recovery-manager.test.ts index ba123685f5..f1e7390b39 100644 --- a/apps/desktop/src/main/ai/orchestration/__tests__/recovery-manager.test.ts +++ b/apps/desktop/src/main/ai/orchestration/__tests__/recovery-manager.test.ts @@ -33,7 +33,7 @@ import type { BuildCheckpoint, FailureType } from '../recovery-manager'; // --------------------------------------------------------------------------- const PROJECT_DIR = path.join(path.sep, 'project'); -const SPEC_DIR = path.join(PROJECT_DIR, '.auto-claude', 'specs', '001-feature'); +const SPEC_DIR = path.join(PROJECT_DIR, '.aperant', 'specs', '001-feature'); const MEMORY_DIR = path.join(SPEC_DIR, 'memory'); const ATTEMPT_HISTORY_PATH = path.join(MEMORY_DIR, 'attempt_history.json'); diff --git a/apps/desktop/src/main/ai/orchestration/build-orchestrator.ts b/apps/desktop/src/main/ai/orchestration/build-orchestrator.ts index d04dea9393..a6d70abda6 100644 --- a/apps/desktop/src/main/ai/orchestration/build-orchestrator.ts +++ b/apps/desktop/src/main/ai/orchestration/build-orchestrator.ts @@ -77,7 +77,7 @@ const PHASE_CONFIG_MAP: Record = { /** Configuration for the build orchestrator */ export interface BuildOrchestratorConfig { - /** Spec directory path (e.g., .auto-claude/specs/001-feature/) */ + /** Spec directory path (e.g., .aperant/specs/001-feature/) */ specDir: string; /** Project root directory */ projectDir: string; diff --git a/apps/desktop/src/main/ai/project/analyzer.ts b/apps/desktop/src/main/ai/project/analyzer.ts index dcbab70533..9abaf03fc1 100644 --- a/apps/desktop/src/main/ai/project/analyzer.ts +++ b/apps/desktop/src/main/ai/project/analyzer.ts @@ -40,8 +40,8 @@ import type { // Constants // --------------------------------------------------------------------------- -const PROFILE_FILENAME = '.auto-claude-security.json'; -const CUSTOM_ALLOWLIST_FILENAME = '.auto-claude-allowlist'; +const PROFILE_FILENAME = '.aperant-security.json'; +const CUSTOM_ALLOWLIST_FILENAME = '.aperant-allowlist'; const HASH_FILES = [ 'package.json', diff --git a/apps/desktop/src/main/ai/project/project-indexer.ts b/apps/desktop/src/main/ai/project/project-indexer.ts index 2ed5dd9ca8..fce0a9e29f 100644 --- a/apps/desktop/src/main/ai/project/project-indexer.ts +++ b/apps/desktop/src/main/ai/project/project-indexer.ts @@ -35,7 +35,7 @@ const SKIP_DIRS = new Set([ '.nuxt', 'target', 'vendor', - '.auto-claude', + '.aperant', 'coverage', '.nyc_output', ]); diff --git a/apps/desktop/src/main/ai/prompts/prompt-loader.ts b/apps/desktop/src/main/ai/prompts/prompt-loader.ts index 6ad1ff34fe..a2beb56a57 100644 --- a/apps/desktop/src/main/ai/prompts/prompt-loader.ts +++ b/apps/desktop/src/main/ai/prompts/prompt-loader.ts @@ -402,10 +402,10 @@ function validateBranchName(branch: string | null | undefined): string | null { // ============================================================================= /** - * Load project_index.json from the project's .auto-claude directory. + * Load project_index.json from the project's .aperant directory. */ export function loadProjectIndex(projectDir: string): Record { - const indexPath = join(projectDir, '.auto-claude', 'project_index.json'); + const indexPath = join(projectDir, '.aperant', 'project_index.json'); if (!existsSync(indexPath)) return {}; try { return JSON.parse(readFileSync(indexPath, 'utf-8')) as Record; diff --git a/apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts b/apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts index 0e7663c061..ce261dc9cd 100644 --- a/apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts +++ b/apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts @@ -28,8 +28,8 @@ import type { /** Patterns to detect worktree isolation */ const WORKTREE_PATH_PATTERNS = [ - /[/\\]\.auto-claude[/\\]worktrees[/\\]tasks[/\\]/, - /[/\\]\.auto-claude[/\\]github[/\\]pr[/\\]worktrees[/\\]/, + /[/\\]\.aperant[/\\]worktrees[/\\]tasks[/\\]/, + /[/\\]\.aperant[/\\]github[/\\]pr[/\\]worktrees[/\\]/, /[/\\]\.worktrees[/\\]/, ]; @@ -106,7 +106,7 @@ function getRelativeSpecPath(specDir: string, projectDir: string): string { // Fallback: just use the spec dir name const parts = resolvedSpec.split(/[/\\]/); - return `./auto-claude/specs/${parts[parts.length - 1]}`; + return `./aperant/specs/${parts[parts.length - 1]}`; } /** @@ -331,7 +331,7 @@ export async function generateSubtaskPrompt(config: SubtaskPromptConfig): Promis `5. **Commit your changes:**\n` + ` \`\`\`bash\n` + ` git add .\n` + - ` git commit -m "auto-claude: ${subtask.id} - ${subtask.description.slice(0, 50)}"\n` + + ` git commit -m "aperant: ${subtask.id} - ${subtask.description.slice(0, 50)}"\n` + ` \`\`\`\n` + `6. **Update the plan** - set this subtask's status to "completed" in implementation_plan.json\n\n` + `## Quality Checklist\n\n` + diff --git a/apps/desktop/src/main/ai/runners/__tests__/commit-message.test.ts b/apps/desktop/src/main/ai/runners/__tests__/commit-message.test.ts index 82107644dc..02d989da65 100644 --- a/apps/desktop/src/main/ai/runners/__tests__/commit-message.test.ts +++ b/apps/desktop/src/main/ai/runners/__tests__/commit-message.test.ts @@ -161,7 +161,7 @@ describe('generateCommitMessage', () => { // --------------------------------------------------------------------------- it('reads spec.md for title when spec directory exists', async () => { - // Spec directory at .auto-claude/specs/001-add-feature + // Spec directory at .aperant/specs/001-add-feature mockExistsSync.mockImplementation((p: string) => { const normalized = p.replace(/\\/g, '/'); if (normalized.includes('specs/001-add-feature')) return true; diff --git a/apps/desktop/src/main/ai/runners/__tests__/ideation.test.ts b/apps/desktop/src/main/ai/runners/__tests__/ideation.test.ts index 85dedd277e..e0b2bc97c3 100644 --- a/apps/desktop/src/main/ai/runners/__tests__/ideation.test.ts +++ b/apps/desktop/src/main/ai/runners/__tests__/ideation.test.ts @@ -71,7 +71,7 @@ function makeStream(parts: Array>) { function baseConfig(overrides: Partial = {}): IdeationConfig { return { projectDir: '/project', - outputDir: '/project/.auto-claude/ideation', + outputDir: '/project/.aperant/ideation', promptsDir: '/app/prompts', ideationType: 'code_improvements', ...overrides, @@ -285,7 +285,7 @@ describe('runIdeation', () => { mockStreamText.mockReturnValue(makeStream([])); await runIdeation( - baseConfig({ projectDir: '/my/project', outputDir: '/my/project/.auto-claude/ideation' }), + baseConfig({ projectDir: '/my/project', outputDir: '/my/project/.aperant/ideation' }), ); // The system prompt passed to streamText should contain the project dir diff --git a/apps/desktop/src/main/ai/runners/__tests__/roadmap.test.ts b/apps/desktop/src/main/ai/runners/__tests__/roadmap.test.ts index 16721617d6..2009b13566 100644 --- a/apps/desktop/src/main/ai/runners/__tests__/roadmap.test.ts +++ b/apps/desktop/src/main/ai/runners/__tests__/roadmap.test.ts @@ -124,7 +124,7 @@ const VALID_ROADMAP_JSON = JSON.stringify({ function baseConfig(overrides: Partial = {}): RoadmapConfig { return { projectDir: '/project', - outputDir: '/project/.auto-claude/roadmap', + outputDir: '/project/.aperant/roadmap', ...overrides, }; } @@ -277,7 +277,7 @@ describe('runRoadmapGeneration', () => { }); mockStreamText.mockReturnValue(makeStream([])); - await runRoadmapGeneration(baseConfig({ outputDir: '/project/.auto-claude/roadmap' })); + await runRoadmapGeneration(baseConfig({ outputDir: '/project/.aperant/roadmap' })); expect(mockMkdirSync).toHaveBeenCalledWith( expect.stringContaining('roadmap'), @@ -388,7 +388,7 @@ describe('runRoadmapGeneration', () => { // mkdirSync should have been called with the default path expect(mockMkdirSync).toHaveBeenCalledWith( - expect.stringContaining('.auto-claude'), + expect.stringContaining('.aperant'), expect.anything(), ); }); diff --git a/apps/desktop/src/main/ai/runners/commit-message.ts b/apps/desktop/src/main/ai/runners/commit-message.ts index 1d20dd2222..c25241b0ea 100644 --- a/apps/desktop/src/main/ai/runners/commit-message.ts +++ b/apps/desktop/src/main/ai/runners/commit-message.ts @@ -236,10 +236,7 @@ export async function generateCommitMessage( } = config; // Find spec directory - let specDir = join(projectDir, '.auto-claude', 'specs', specName); - if (!existsSync(specDir)) { - specDir = join(projectDir, 'auto-claude', 'specs', specName); - } + const specDir = join(projectDir, '.aperant', 'specs', specName); // Get context from spec files const specContext = existsSync(specDir) ? getSpecContext(specDir) : { diff --git a/apps/desktop/src/main/ai/runners/github/pr-creator.ts b/apps/desktop/src/main/ai/runners/github/pr-creator.ts index e42dbb2870..ee411f4903 100644 --- a/apps/desktop/src/main/ai/runners/github/pr-creator.ts +++ b/apps/desktop/src/main/ai/runners/github/pr-creator.ts @@ -141,7 +141,7 @@ function gatherPRContext( * Extract a brief summary from the spec file for fallback PR body. */ function extractSpecSummary(projectDir: string, specId: string): string { - const specFile = join(projectDir, '.auto-claude', 'specs', specId, 'spec.md'); + const specFile = join(projectDir, '.aperant', 'specs', specId, 'spec.md'); if (!existsSync(specFile)) { return `Implements ${specId}`; } diff --git a/apps/desktop/src/main/ai/runners/ideation.ts b/apps/desktop/src/main/ai/runners/ideation.ts index 4b75fe4612..cfc3180cdc 100644 --- a/apps/desktop/src/main/ai/runners/ideation.ts +++ b/apps/desktop/src/main/ai/runners/ideation.ts @@ -160,7 +160,7 @@ export async function runIdeation( const toolContext: ToolContext = { cwd: projectDir, projectDir, - specDir: join(projectDir, '.auto-claude', 'specs'), + specDir: join(projectDir, '.aperant', 'specs'), securityProfile: null as unknown as SecurityProfile, abortSignal, }; diff --git a/apps/desktop/src/main/ai/runners/insights.ts b/apps/desktop/src/main/ai/runners/insights.ts index d4da7daa67..d5ef967986 100644 --- a/apps/desktop/src/main/ai/runners/insights.ts +++ b/apps/desktop/src/main/ai/runners/insights.ts @@ -99,7 +99,7 @@ function loadProjectContext(projectDir: string): string { const contextParts: string[] = []; // Load project index if available - const indexPath = join(projectDir, '.auto-claude', 'project_index.json'); + const indexPath = join(projectDir, '.aperant', 'project_index.json'); if (existsSync(indexPath)) { const index = safeParseJson>(readFileSync(indexPath, 'utf-8')); if (index) { @@ -116,7 +116,7 @@ function loadProjectContext(projectDir: string): string { } // Load roadmap if available - const roadmapPath = join(projectDir, '.auto-claude', 'roadmap', 'roadmap.json'); + const roadmapPath = join(projectDir, '.aperant', 'roadmap', 'roadmap.json'); if (existsSync(roadmapPath)) { const roadmap = safeParseJson>(readFileSync(roadmapPath, 'utf-8')); if (roadmap) { @@ -132,7 +132,7 @@ function loadProjectContext(projectDir: string): string { } // Load existing tasks - const tasksPath = join(projectDir, '.auto-claude', 'specs'); + const tasksPath = join(projectDir, '.aperant', 'specs'); if (existsSync(tasksPath)) { try { const taskDirs = readdirSync(tasksPath, { withFileTypes: true }) @@ -246,7 +246,7 @@ export async function runInsightsQuery( const toolContext: ToolContext = { cwd: projectDir, projectDir, - specDir: join(projectDir, '.auto-claude', 'specs'), + specDir: join(projectDir, '.aperant', 'specs'), securityProfile: null as unknown as SecurityProfile, abortSignal, }; diff --git a/apps/desktop/src/main/ai/runners/roadmap.ts b/apps/desktop/src/main/ai/runners/roadmap.ts index b589af4c70..3b475ac3fb 100644 --- a/apps/desktop/src/main/ai/runners/roadmap.ts +++ b/apps/desktop/src/main/ai/runners/roadmap.ts @@ -39,7 +39,7 @@ const MAX_STEPS_PER_PHASE = 30; export interface RoadmapConfig { /** Project directory path */ projectDir: string; - /** Output directory for roadmap files (defaults to .auto-claude/roadmap/) */ + /** Output directory for roadmap files (defaults to .aperant/roadmap/) */ outputDir?: string; /** Model shorthand (defaults to 'sonnet') */ modelShorthand?: ModelShorthand; @@ -443,7 +443,7 @@ export async function runRoadmapGeneration( abortSignal, } = config; - const outputDir = config.outputDir ?? join(projectDir, '.auto-claude', 'roadmap'); + const outputDir = config.outputDir ?? join(projectDir, '.aperant', 'roadmap'); // Ensure output directory exists if (!existsSync(outputDir)) { @@ -454,7 +454,7 @@ export async function runRoadmapGeneration( const toolContext: ToolContext = { cwd: projectDir, projectDir, - specDir: join(projectDir, '.auto-claude', 'specs'), + specDir: join(projectDir, '.aperant', 'specs'), securityProfile: null as unknown as SecurityProfile, abortSignal, }; diff --git a/apps/desktop/src/main/ai/schema/structured-output.ts b/apps/desktop/src/main/ai/schema/structured-output.ts index 638a35cbdf..89c8c613a9 100644 --- a/apps/desktop/src/main/ai/schema/structured-output.ts +++ b/apps/desktop/src/main/ai/schema/structured-output.ts @@ -160,7 +160,7 @@ export async function validateAndNormalizeJsonFile( if (result.valid && result.data) { // Write back the coerced data so downstream consumers get canonical field names. // Use a secure temp file + atomic rename to avoid TOCTOU races on the target path. - const tempDir = await mkdtemp(join(tmpdir(), 'auto-claude-normalize-')); + const tempDir = await mkdtemp(join(tmpdir(), 'aperant-normalize-')); const tempFile = join(tempDir, 'output.json'); try { await writeFile(tempFile, JSON.stringify(result.data, null, 2)); @@ -338,7 +338,7 @@ export async function repairJsonWithLLM( const coerced = schema.safeParse(result.output); if (coerced.success) { // Use a secure temp file + atomic rename to avoid TOCTOU races - const tempDir = await mkdtemp(join(tmpdir(), 'auto-claude-repair-')); + const tempDir = await mkdtemp(join(tmpdir(), 'aperant-repair-')); const tempFile = join(tempDir, 'output.json'); try { await writeFile(tempFile, JSON.stringify(coerced.data, null, 2)); diff --git a/apps/desktop/src/main/ai/security/security-profile.ts b/apps/desktop/src/main/ai/security/security-profile.ts index 041a35d54e..49704b73ee 100644 --- a/apps/desktop/src/main/ai/security/security-profile.ts +++ b/apps/desktop/src/main/ai/security/security-profile.ts @@ -2,7 +2,7 @@ * Security Profile Management * ============================ * - * Loads and caches project security profiles from .auto-claude/ config. + * Loads and caches project security profiles from .aperant/ config. * Provides SecurityProfile instances consumed by bash-validator.ts. * * NOTE: With the denylist security model, SecurityProfile command sets are no @@ -23,8 +23,8 @@ import type { SecurityProfile } from './bash-validator'; // Constants // --------------------------------------------------------------------------- -const PROFILE_FILENAME = '.auto-claude-security.json'; -const ALLOWLIST_FILENAME = '.auto-claude-allowlist'; +const PROFILE_FILENAME = '.aperant-security.json'; +const ALLOWLIST_FILENAME = '.aperant-allowlist'; // --------------------------------------------------------------------------- // Cache state diff --git a/apps/desktop/src/main/ai/security/validators/process-validators.ts b/apps/desktop/src/main/ai/security/validators/process-validators.ts index 5c3a91e532..53fcb0d43a 100644 --- a/apps/desktop/src/main/ai/security/validators/process-validators.ts +++ b/apps/desktop/src/main/ai/security/validators/process-validators.ts @@ -86,7 +86,7 @@ const BLOCKED_PROCESS_NAMES = new Set([ // -- Self-protection (don't let the agent kill its own host) -- 'electron', 'Electron', - 'auto-claude', + 'aperant', 'Aperant', ]); diff --git a/apps/desktop/src/main/ai/session/__tests__/progress-tracker.test.ts b/apps/desktop/src/main/ai/session/__tests__/progress-tracker.test.ts index 84ea0e51cb..c5ad7e5a98 100644 --- a/apps/desktop/src/main/ai/session/__tests__/progress-tracker.test.ts +++ b/apps/desktop/src/main/ai/session/__tests__/progress-tracker.test.ts @@ -33,7 +33,7 @@ describe('ProgressTracker', () => { type: 'tool-call', toolName: 'Write', toolCallId: 'c1', - args: { file_path: '/project/.auto-claude/specs/001/implementation_plan.json' }, + args: { file_path: '/project/.aperant/specs/001/implementation_plan.json' }, }); expect(result).not.toBeNull(); diff --git a/apps/desktop/src/main/ai/tools/__tests__/registry.test.ts b/apps/desktop/src/main/ai/tools/__tests__/registry.test.ts index ba20621cca..eb3d8adb11 100644 --- a/apps/desktop/src/main/ai/tools/__tests__/registry.test.ts +++ b/apps/desktop/src/main/ai/tools/__tests__/registry.test.ts @@ -251,12 +251,12 @@ describe('getRequiredMcpServers (registry)', () => { expect(servers).toContain('context7'); }); - it('should support per-agent MCP REMOVE overrides but protect auto-claude', () => { + it('should support per-agent MCP REMOVE overrides but protect aperant', () => { const servers = getRequiredMcpServers('coder', { memoryEnabled: true, - mcpConfig: { AGENT_MCP_coder_REMOVE: 'auto-claude,memory' }, + mcpConfig: { AGENT_MCP_coder_REMOVE: 'aperant,memory' }, }); - expect(servers).toContain('auto-claude'); + expect(servers).toContain('aperant'); expect(servers).not.toContain('memory'); }); }); diff --git a/apps/desktop/src/main/ai/tools/auto-claude/get-build-progress.ts b/apps/desktop/src/main/ai/tools/aperant/get-build-progress.ts similarity index 95% rename from apps/desktop/src/main/ai/tools/auto-claude/get-build-progress.ts rename to apps/desktop/src/main/ai/tools/aperant/get-build-progress.ts index b4e45c643c..8bd5990c61 100644 --- a/apps/desktop/src/main/ai/tools/auto-claude/get-build-progress.ts +++ b/apps/desktop/src/main/ai/tools/aperant/get-build-progress.ts @@ -3,9 +3,9 @@ * ======================= * * Reports current build progress from implementation_plan.json. - * See apps/desktop/src/main/ai/tools/auto-claude/get-build-progress.ts for the TypeScript implementation. + * See apps/desktop/src/main/ai/tools/aperant/get-build-progress.ts for the TypeScript implementation. * - * Tool name: mcp__auto-claude__get_build_progress + * Tool name: mcp__aperant__get_build_progress */ import * as fs from 'node:fs'; @@ -50,7 +50,7 @@ interface ImplementationPlan { export const getBuildProgressTool = Tool.define({ metadata: { - name: 'mcp__auto-claude__get_build_progress', + name: 'mcp__aperant__get_build_progress', description: 'Get the current build progress including completed subtasks, pending subtasks, and next subtask to work on.', permission: ToolPermission.ReadOnly, diff --git a/apps/desktop/src/main/ai/tools/auto-claude/get-session-context.ts b/apps/desktop/src/main/ai/tools/aperant/get-session-context.ts similarity index 94% rename from apps/desktop/src/main/ai/tools/auto-claude/get-session-context.ts rename to apps/desktop/src/main/ai/tools/aperant/get-session-context.ts index b6f5ed44f9..3b1c97bb23 100644 --- a/apps/desktop/src/main/ai/tools/auto-claude/get-session-context.ts +++ b/apps/desktop/src/main/ai/tools/aperant/get-session-context.ts @@ -7,9 +7,9 @@ * - memory/gotchas.md → gotchas & pitfalls * - memory/patterns.md → code patterns * - * See apps/desktop/src/main/ai/tools/auto-claude/get-session-context.ts for the TypeScript implementation. + * See apps/desktop/src/main/ai/tools/aperant/get-session-context.ts for the TypeScript implementation. * - * Tool name: mcp__auto-claude__get_session_context + * Tool name: mcp__aperant__get_session_context */ import * as fs from 'node:fs'; @@ -40,7 +40,7 @@ interface CodebaseMap { export const getSessionContextTool = Tool.define({ metadata: { - name: 'mcp__auto-claude__get_session_context', + name: 'mcp__aperant__get_session_context', description: 'Get context from previous sessions including codebase discoveries, gotchas, and patterns. Call this at the start of a session to pick up where the last session left off.', permission: ToolPermission.ReadOnly, diff --git a/apps/desktop/src/main/ai/tools/auto-claude/index.ts b/apps/desktop/src/main/ai/tools/aperant/index.ts similarity index 74% rename from apps/desktop/src/main/ai/tools/auto-claude/index.ts rename to apps/desktop/src/main/ai/tools/aperant/index.ts index 9a82f4052b..3a9a817db0 100644 --- a/apps/desktop/src/main/ai/tools/auto-claude/index.ts +++ b/apps/desktop/src/main/ai/tools/aperant/index.ts @@ -1,11 +1,11 @@ /** - * Auto-Claude Custom Tools - * ======================== + * Aperant Custom Tools + * ==================== * - * Barrel export for all auto-claude builtin tools. + * Barrel export for all aperant builtin tools. * These replace the Python tools_pkg/tools/* implementations. * - * Tool names follow the mcp__auto-claude__* convention to match the + * Tool names follow the mcp__aperant__* convention to match the * TOOL_* constants in registry.ts and AGENT_CONFIGS autoClaudeTools arrays. */ diff --git a/apps/desktop/src/main/ai/tools/auto-claude/record-discovery.ts b/apps/desktop/src/main/ai/tools/aperant/record-discovery.ts similarity index 93% rename from apps/desktop/src/main/ai/tools/auto-claude/record-discovery.ts rename to apps/desktop/src/main/ai/tools/aperant/record-discovery.ts index dedefbaae6..c3c1513401 100644 --- a/apps/desktop/src/main/ai/tools/auto-claude/record-discovery.ts +++ b/apps/desktop/src/main/ai/tools/aperant/record-discovery.ts @@ -3,9 +3,9 @@ * ===================== * * Records a codebase discovery to session memory (codebase_map.json). - * See apps/desktop/src/main/ai/tools/auto-claude/record-discovery.ts for the TypeScript implementation. + * See apps/desktop/src/main/ai/tools/aperant/record-discovery.ts for the TypeScript implementation. * - * Tool name: mcp__auto-claude__record_discovery + * Tool name: mcp__aperant__record_discovery */ import * as fs from 'node:fs'; @@ -44,7 +44,7 @@ interface CodebaseMap { export const recordDiscoveryTool = Tool.define({ metadata: { - name: 'mcp__auto-claude__record_discovery', + name: 'mcp__aperant__record_discovery', description: 'Record a codebase discovery to session memory. Use this when you learn something important about the codebase structure or behavior.', permission: ToolPermission.Auto, diff --git a/apps/desktop/src/main/ai/tools/auto-claude/record-gotcha.ts b/apps/desktop/src/main/ai/tools/aperant/record-gotcha.ts similarity index 93% rename from apps/desktop/src/main/ai/tools/auto-claude/record-gotcha.ts rename to apps/desktop/src/main/ai/tools/aperant/record-gotcha.ts index a274389635..7e481570b8 100644 --- a/apps/desktop/src/main/ai/tools/auto-claude/record-gotcha.ts +++ b/apps/desktop/src/main/ai/tools/aperant/record-gotcha.ts @@ -3,9 +3,9 @@ * ================== * * Records a gotcha or pitfall to specDir/memory/gotchas.md. - * See apps/desktop/src/main/ai/tools/auto-claude/record-gotcha.ts for the TypeScript implementation. + * See apps/desktop/src/main/ai/tools/aperant/record-gotcha.ts for the TypeScript implementation. * - * Tool name: mcp__auto-claude__record_gotcha + * Tool name: mcp__aperant__record_gotcha */ import * as fs from 'node:fs'; @@ -33,7 +33,7 @@ const inputSchema = z.object({ export const recordGotchaTool = Tool.define({ metadata: { - name: 'mcp__auto-claude__record_gotcha', + name: 'mcp__aperant__record_gotcha', description: 'Record a gotcha or pitfall to avoid. Use this when you encounter something that future sessions should know about to avoid repeating mistakes.', permission: ToolPermission.Auto, diff --git a/apps/desktop/src/main/ai/tools/auto-claude/update-qa-status.ts b/apps/desktop/src/main/ai/tools/aperant/update-qa-status.ts similarity index 95% rename from apps/desktop/src/main/ai/tools/auto-claude/update-qa-status.ts rename to apps/desktop/src/main/ai/tools/aperant/update-qa-status.ts index 2ed296a9fe..c55045c100 100644 --- a/apps/desktop/src/main/ai/tools/auto-claude/update-qa-status.ts +++ b/apps/desktop/src/main/ai/tools/aperant/update-qa-status.ts @@ -3,9 +3,9 @@ * ===================== * * Updates the QA sign-off status in implementation_plan.json. - * See apps/desktop/src/main/ai/tools/auto-claude/update-qa-status.ts for the TypeScript implementation. + * See apps/desktop/src/main/ai/tools/aperant/update-qa-status.ts for the TypeScript implementation. * - * Tool name: mcp__auto-claude__update_qa_status + * Tool name: mcp__aperant__update_qa_status * * IMPORTANT: Do NOT write plan["status"] or plan["planStatus"] here. * The frontend XState task state machine owns status transitions. @@ -68,7 +68,7 @@ interface ImplementationPlan { export const updateQaStatusTool = Tool.define({ metadata: { - name: 'mcp__auto-claude__update_qa_status', + name: 'mcp__aperant__update_qa_status', description: 'Update the QA sign-off status in implementation_plan.json. Use this after completing a QA review to record the outcome.', permission: ToolPermission.Auto, diff --git a/apps/desktop/src/main/ai/tools/auto-claude/update-subtask-status.ts b/apps/desktop/src/main/ai/tools/aperant/update-subtask-status.ts similarity index 94% rename from apps/desktop/src/main/ai/tools/auto-claude/update-subtask-status.ts rename to apps/desktop/src/main/ai/tools/aperant/update-subtask-status.ts index 209275d1cf..e3c441dc25 100644 --- a/apps/desktop/src/main/ai/tools/auto-claude/update-subtask-status.ts +++ b/apps/desktop/src/main/ai/tools/aperant/update-subtask-status.ts @@ -3,9 +3,9 @@ * ========================== * * Updates the status of a subtask in implementation_plan.json. - * See apps/desktop/src/main/ai/tools/auto-claude/update-subtask-status.ts for the TypeScript implementation. + * See apps/desktop/src/main/ai/tools/aperant/update-subtask-status.ts for the TypeScript implementation. * - * Tool name: mcp__auto-claude__update_subtask_status + * Tool name: mcp__aperant__update_subtask_status */ import * as fs from 'node:fs'; @@ -82,7 +82,7 @@ function updateSubtaskInPlan( export const updateSubtaskStatusTool = Tool.define({ metadata: { - name: 'mcp__auto-claude__update_subtask_status', + name: 'mcp__aperant__update_subtask_status', description: 'Update the status of a subtask in implementation_plan.json. Use this when completing or starting a subtask.', permission: ToolPermission.Auto, diff --git a/apps/desktop/src/main/ai/tools/providers/fetch-browse.ts b/apps/desktop/src/main/ai/tools/providers/fetch-browse.ts index 22739a0579..389e9c52c1 100644 --- a/apps/desktop/src/main/ai/tools/providers/fetch-browse.ts +++ b/apps/desktop/src/main/ai/tools/providers/fetch-browse.ts @@ -24,7 +24,7 @@ export class FetchBrowseProvider implements BrowseProvider { const response = await fetch(url, { signal: controller.signal, headers: { - 'User-Agent': 'AutoClaude/1.0', + 'User-Agent': 'Aperant/1.0', Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', }, }); diff --git a/apps/desktop/src/main/ai/tools/registry.ts b/apps/desktop/src/main/ai/tools/registry.ts index d38372f55b..6bb4b3f8f0 100644 --- a/apps/desktop/src/main/ai/tools/registry.ts +++ b/apps/desktop/src/main/ai/tools/registry.ts @@ -45,12 +45,12 @@ export { export const BASE_READ_TOOLS = ['Read', 'Glob', 'Grep'] as const; export const BASE_WRITE_TOOLS = ['Write', 'Edit', 'Bash'] as const; export const WEB_TOOLS = ['WebFetch', 'WebSearch'] as const; -export const TOOL_UPDATE_SUBTASK_STATUS = 'mcp__auto-claude__update_subtask_status'; -export const TOOL_GET_BUILD_PROGRESS = 'mcp__auto-claude__get_build_progress'; -export const TOOL_RECORD_DISCOVERY = 'mcp__auto-claude__record_discovery'; -export const TOOL_RECORD_GOTCHA = 'mcp__auto-claude__record_gotcha'; -export const TOOL_GET_SESSION_CONTEXT = 'mcp__auto-claude__get_session_context'; -export const TOOL_UPDATE_QA_STATUS = 'mcp__auto-claude__update_qa_status'; +export const TOOL_UPDATE_SUBTASK_STATUS = 'mcp__aperant__update_subtask_status'; +export const TOOL_GET_BUILD_PROGRESS = 'mcp__aperant__get_build_progress'; +export const TOOL_RECORD_DISCOVERY = 'mcp__aperant__record_discovery'; +export const TOOL_RECORD_GOTCHA = 'mcp__aperant__record_gotcha'; +export const TOOL_GET_SESSION_CONTEXT = 'mcp__aperant__get_session_context'; +export const TOOL_UPDATE_QA_STATUS = 'mcp__aperant__update_qa_status'; // ============================================================================= // MCP Config for dynamic server resolution @@ -221,7 +221,7 @@ export function getRequiredMcpServers( const removals = removeValue.split(',').map((s) => s.trim()).filter(Boolean); for (const server of removals) { const mapped = mapMcpServerName(server, customServerIds); - if (mapped && mapped !== 'auto-claude') { + if (mapped && mapped !== 'aperant') { servers = servers.filter((s) => s !== mapped); } } diff --git a/apps/desktop/src/main/ai/tools/truncation.ts b/apps/desktop/src/main/ai/tools/truncation.ts index 447908f19b..1155c1ea39 100644 --- a/apps/desktop/src/main/ai/tools/truncation.ts +++ b/apps/desktop/src/main/ai/tools/truncation.ts @@ -44,7 +44,7 @@ export interface TruncationResult { * * @param output - The raw tool output string * @param toolName - Name of the tool (for spillover filename) - * @param projectDir - Project directory (spillover written to .auto-claude/tool-output/) + * @param projectDir - Project directory (spillover written to .aperant/tool-output/) * @param maxBytes - Override max bytes limit (default: MAX_BYTES) * @returns TruncationResult with potentially truncated content */ @@ -67,7 +67,7 @@ export function truncateToolOutput( } // Exceeds limits — spill to disk - const spilloverDir = path.join(projectDir, '.auto-claude', 'tool-output'); + const spilloverDir = path.join(projectDir, '.aperant', 'tool-output'); try { fs.mkdirSync(spilloverDir, { recursive: true }); } catch { diff --git a/apps/desktop/src/main/ai/tools/types.ts b/apps/desktop/src/main/ai/tools/types.ts index 9ee673ccc2..683eb1d503 100644 --- a/apps/desktop/src/main/ai/tools/types.ts +++ b/apps/desktop/src/main/ai/tools/types.ts @@ -23,7 +23,7 @@ export interface ToolContext { cwd: string; /** Root directory of the project being worked on */ projectDir: string; - /** Spec directory for the current task (e.g., .auto-claude/specs/001-feature/) */ + /** Spec directory for the current task (e.g., .aperant/specs/001-feature/) */ specDir: string; /** Security profile governing command allowlists */ securityProfile: SecurityProfile; diff --git a/apps/desktop/src/main/ai/worktree/worktree-manager.ts b/apps/desktop/src/main/ai/worktree/worktree-manager.ts index d5deac4ab9..55a5addf3d 100644 --- a/apps/desktop/src/main/ai/worktree/worktree-manager.ts +++ b/apps/desktop/src/main/ai/worktree/worktree-manager.ts @@ -7,9 +7,9 @@ * * Creates and manages git worktrees for autonomous task execution. * Each task runs in an isolated worktree at: - * {projectPath}/.auto-claude/worktrees/tasks/{specId}/ + * {projectPath}/.aperant/worktrees/tasks/{specId}/ * on branch: - * auto-claude/{specId} + * aperant/{specId} * * The function is idempotent — calling it repeatedly with the same specId * returns the existing worktree without error. @@ -78,7 +78,7 @@ export interface WorktreeResult { * the remote ref (preserves gitignored files) * @param pushNewBranches If true, push the branch to origin and set upstream * tracking after worktree creation. Defaults to true. - * @param autoBuildPath Optional custom data directory (e.g. ".auto-claude"). + * @param aperantPath Optional custom data directory (e.g. ".aperant"). * Passed to getSpecsDir() for spec-copy logic. */ export async function createOrGetWorktree( @@ -87,10 +87,10 @@ export async function createOrGetWorktree( baseBranch = 'main', useLocalBranch = false, pushNewBranches = true, - autoBuildPath?: string, + aperantPath?: string, ): Promise { - const worktreePath = join(projectPath, '.auto-claude/worktrees/tasks', specId); - const branchName = `auto-claude/${specId}`; + const worktreePath = join(projectPath, '.aperant/worktrees/tasks', specId); + const branchName = `aperant/${specId}`; // ------------------------------------------------------------------ // Step 1: Prune stale worktree references from git's internal records @@ -239,11 +239,11 @@ export async function createOrGetWorktree( // ------------------------------------------------------------------ // Step 7: Copy spec directory into the worktree // - // .auto-claude/specs/ is gitignored, so it is NOT present in the + // .aperant/specs/ is gitignored, so it is NOT present in the // newly-created worktree checkout. Copy it from the main project so // that agents can read spec.md, implementation_plan.json, etc. // ------------------------------------------------------------------ - const specsRelDir = getSpecsDir(autoBuildPath); // e.g. ".auto-claude/specs" + const specsRelDir = getSpecsDir(aperantPath); // e.g. ".aperant/specs" const sourceSpecDir = join(projectPath, specsRelDir, specId); const destSpecDir = join(worktreePath, specsRelDir, specId); diff --git a/apps/desktop/src/main/changelog/__tests__/changelog-service.integration.test.ts b/apps/desktop/src/main/changelog/__tests__/changelog-service.integration.test.ts index 1a6757637f..17bdcb3b7e 100644 --- a/apps/desktop/src/main/changelog/__tests__/changelog-service.integration.test.ts +++ b/apps/desktop/src/main/changelog/__tests__/changelog-service.integration.test.ts @@ -35,7 +35,7 @@ describe('ChangelogService - Task Filtering Integration', () => { // Create temporary test directory testDir = mkdtempSync(path.join(tmpdir(), 'changelog-test-')); projectPath = path.join(testDir, 'test-project'); - specsDir = path.join(projectPath, '.auto-claude', 'specs'); + specsDir = path.join(projectPath, '.aperant', 'specs'); // Create project structure mkdirSync(projectPath, { recursive: true }); diff --git a/apps/desktop/src/main/changelog/changelog-service.ts b/apps/desktop/src/main/changelog/changelog-service.ts index 0ba31698e6..b463852483 100644 --- a/apps/desktop/src/main/changelog/changelog-service.ts +++ b/apps/desktop/src/main/changelog/changelog-service.ts @@ -7,7 +7,7 @@ import { app } from 'electron'; // ESM-compatible __dirname const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -import { AUTO_BUILD_PATHS, DEFAULT_CHANGELOG_PATH } from '../../shared/constants'; +import { APERANT_PATHS, DEFAULT_CHANGELOG_PATH } from '../../shared/constants'; import { getToolPath, getToolInfo } from '../cli-tool-manager'; import type { ChangelogTask, @@ -40,7 +40,7 @@ import { */ export class ChangelogService extends EventEmitter { private claudePath: string; - private autoBuildSourcePath: string = ''; + private aperantSourcePath: string = ''; private debugEnabled: boolean | null = null; private generator: ChangelogGenerator | null = null; private versionSuggester: VersionSuggester | null = null; @@ -54,7 +54,7 @@ export class ChangelogService extends EventEmitter { /** * Check if debug mode is enabled - * Checks DEBUG from auto-claude/.env and DEBUG from process.env + * Checks DEBUG from aperant/.env and DEBUG from process.env */ private isDebugEnabled(): boolean { // Cache the result after first check @@ -71,14 +71,14 @@ export class ChangelogService extends EventEmitter { return true; } - // Check auto-claude .env file - const env = this.loadAutoBuildEnv(); + // Check aperant .env file + const env = this.loadAperantEnv(); this.debugEnabled = env.DEBUG === 'true' || env.DEBUG === '1'; return this.debugEnabled; } /** - * Debug logging - only logs when DEBUG=true in auto-claude/.env or DEBUG is set + * Debug logging - only logs when DEBUG=true in aperant/.env or DEBUG is set */ private debug(...args: unknown[]): void { if (this.isDebugEnabled()) { @@ -86,18 +86,18 @@ export class ChangelogService extends EventEmitter { } } - configure(_pythonPath?: string, autoBuildSourcePath?: string): void { - if (autoBuildSourcePath) { - this.autoBuildSourcePath = autoBuildSourcePath; + configure(_pythonPath?: string, aperantSourcePath?: string): void { + if (aperantSourcePath) { + this.aperantSourcePath = aperantSourcePath; } } /** - * Get the auto-claude source path (detects automatically if not configured) + * Get the aperant source path (detects automatically if not configured) */ - private getAutoBuildSourcePath(): string | null { - if (this.autoBuildSourcePath && existsSync(this.autoBuildSourcePath)) { - return this.autoBuildSourcePath; + private getAperantSourcePath(): string | null { + if (this.aperantSourcePath && existsSync(this.aperantSourcePath)) { + return this.aperantSourcePath; } const possiblePaths = [ @@ -116,13 +116,13 @@ export class ChangelogService extends EventEmitter { } /** - * Load environment variables from auto-claude .env file + * Load environment variables from aperant .env file */ - private loadAutoBuildEnv(): Record { - const autoBuildSource = this.getAutoBuildSourcePath(); - if (!autoBuildSource) return {}; + private loadAperantEnv(): Record { + const aperantSource = this.getAperantSourcePath(); + if (!aperantSource) return {}; - const envPath = path.join(autoBuildSource, '.env'); + const envPath = path.join(aperantSource, '.env'); if (!existsSync(envPath)) return {}; try { @@ -156,13 +156,13 @@ export class ChangelogService extends EventEmitter { /** * Ensure prerequisites are met for changelog generation - * Validates auto-build source path and Claude CLI availability + * Validates aperant source path and Claude CLI availability * Returns the resolved Claude CLI path to ensure we use the freshly validated path */ - private ensurePrerequisites(): { autoBuildSource: string; claudePath: string } { - const autoBuildSource = this.getAutoBuildSourcePath(); - if (!autoBuildSource) { - throw new Error('Auto-build source path not found'); + private ensurePrerequisites(): { aperantSource: string; claudePath: string } { + const aperantSource = this.getAperantSourcePath(); + if (!aperantSource) { + throw new Error('Aperant source path not found'); } const claudeInfo = getToolInfo('claude'); @@ -173,7 +173,7 @@ export class ChangelogService extends EventEmitter { // Update cached path with freshly resolved value this.claudePath = claudeInfo.path; - return { autoBuildSource, claudePath: claudeInfo.path }; + return { aperantSource, claudePath: claudeInfo.path }; } /** @@ -181,15 +181,15 @@ export class ChangelogService extends EventEmitter { */ private getGenerator(): ChangelogGenerator { if (!this.generator) { - const { autoBuildSource, claudePath } = this.ensurePrerequisites(); + const { aperantSource, claudePath } = this.ensurePrerequisites(); - const autoBuildEnv = this.loadAutoBuildEnv(); + const aperantEnv = this.loadAperantEnv(); this.generator = new ChangelogGenerator( '', claudePath, - autoBuildSource, - autoBuildEnv, + aperantSource, + aperantEnv, this.isDebugEnabled() ); @@ -219,12 +219,12 @@ export class ChangelogService extends EventEmitter { */ private getVersionSuggester(): VersionSuggester { if (!this.versionSuggester) { - const { autoBuildSource, claudePath } = this.ensurePrerequisites(); + const { aperantSource, claudePath } = this.ensurePrerequisites(); this.versionSuggester = new VersionSuggester( '', claudePath, - autoBuildSource, + aperantSource, this.isDebugEnabled() ); } @@ -240,13 +240,13 @@ export class ChangelogService extends EventEmitter { * Get completed tasks from a project */ getCompletedTasks(projectPath: string, tasks: Task[], specsBaseDir?: string): ChangelogTask[] { - const specsDir = path.join(projectPath, specsBaseDir || AUTO_BUILD_PATHS.SPECS_DIR); + const specsDir = path.join(projectPath, specsBaseDir || APERANT_PATHS.SPECS_DIR); return tasks .filter(task => isCompletedTask(task.status, task.reviewReason) && !task.metadata?.archivedAt) .map(task => { const specDir = path.join(specsDir, task.specId); - const hasSpecs = existsSync(specDir) && existsSync(path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE)); + const hasSpecs = existsSync(specDir) && existsSync(path.join(specDir, APERANT_PATHS.SPEC_FILE)); return { id: task.id, @@ -264,7 +264,7 @@ export class ChangelogService extends EventEmitter { * Load spec files for given tasks */ async loadTaskSpecs(projectPath: string, taskIds: string[], tasks: Task[], specsBaseDir?: string): Promise { - const specsDir = path.join(projectPath, specsBaseDir || AUTO_BUILD_PATHS.SPECS_DIR); + const specsDir = path.join(projectPath, specsBaseDir || APERANT_PATHS.SPECS_DIR); this.debug('loadTaskSpecs called', { projectPath, specsDir, taskCount: taskIds.length }); const results: TaskSpecContent[] = []; @@ -286,26 +286,26 @@ export class ChangelogService extends EventEmitter { try { // Load spec.md - const specPath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specPath = path.join(specDir, APERANT_PATHS.SPEC_FILE); if (existsSync(specPath)) { content.spec = readFileSync(specPath, 'utf-8'); this.debug('Loaded spec.md', { specId: task.specId, length: content.spec.length }); } // Load requirements.json - const requirementsPath = path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS); + const requirementsPath = path.join(specDir, APERANT_PATHS.REQUIREMENTS); if (existsSync(requirementsPath)) { content.requirements = JSON.parse(readFileSync(requirementsPath, 'utf-8')); } // Load qa_report.md - const qaReportPath = path.join(specDir, AUTO_BUILD_PATHS.QA_REPORT); + const qaReportPath = path.join(specDir, APERANT_PATHS.QA_REPORT); if (existsSync(qaReportPath)) { content.qaReport = readFileSync(qaReportPath, 'utf-8'); } // Load implementation_plan.json - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); if (existsSync(planPath)) { content.implementationPlan = JSON.parse(readFileSync(planPath, 'utf-8')) as ImplementationPlan; } diff --git a/apps/desktop/src/main/changelog/generator.ts b/apps/desktop/src/main/changelog/generator.ts index 1cd613235d..c8668b40de 100644 --- a/apps/desktop/src/main/changelog/generator.ts +++ b/apps/desktop/src/main/changelog/generator.ts @@ -28,8 +28,8 @@ export class ChangelogGenerator extends EventEmitter { constructor( private pythonPath: string, private claudePath: string, - private autoBuildSourcePath: string, - private autoBuildEnv: Record, + private aperantSourcePath: string, + private aperantEnv: Record, debugEnabled: boolean ) { super(); @@ -146,7 +146,7 @@ export class ChangelogGenerator extends EventEmitter { // Use python3/python as fallback command (Python subprocess path removed in Vercel AI SDK migration) const pythonCommand = this.pythonPath || 'python3'; const childProcess = spawn(pythonCommand, ['-c', script], { - cwd: this.autoBuildSourcePath, + cwd: this.aperantSourcePath, env: spawnEnv }); @@ -306,7 +306,7 @@ export class ChangelogGenerator extends EventEmitter { const spawnEnv: Record = { ...augmentedEnv, - ...this.autoBuildEnv, + ...this.aperantEnv, ...profileEnv, // Include active Claude profile config // Ensure critical env vars are set for claude CLI // Use USERPROFILE on Windows, HOME on Unix diff --git a/apps/desktop/src/main/changelog/types.ts b/apps/desktop/src/main/changelog/types.ts index b89fb89807..07b7e756f4 100644 --- a/apps/desktop/src/main/changelog/types.ts +++ b/apps/desktop/src/main/changelog/types.ts @@ -6,7 +6,7 @@ export interface ChangelogConfig { pythonPath: string; claudePath: string; - autoBuildSourcePath: string; + aperantSourcePath: string; } export interface PromptBuildOptions { diff --git a/apps/desktop/src/main/changelog/version-suggester.ts b/apps/desktop/src/main/changelog/version-suggester.ts index 87a6f5ad43..54864531a7 100644 --- a/apps/desktop/src/main/changelog/version-suggester.ts +++ b/apps/desktop/src/main/changelog/version-suggester.ts @@ -22,7 +22,7 @@ export class VersionSuggester { constructor( private pythonPath: string, private claudePath: string, - private autoBuildSourcePath: string, + private aperantSourcePath: string, debugEnabled: boolean ) { this.debugEnabled = debugEnabled; @@ -57,7 +57,7 @@ export class VersionSuggester { // Use python3/python as fallback command (Python subprocess path removed in Vercel AI SDK migration) const pythonCommand = this.pythonPath || 'python3'; const childProcess = spawn(pythonCommand, ['-c', script], { - cwd: this.autoBuildSourcePath, + cwd: this.aperantSourcePath, env: spawnEnv }); diff --git a/apps/desktop/src/main/claude-code-settings/index.ts b/apps/desktop/src/main/claude-code-settings/index.ts index 8bacfb2f67..1a321a8d9a 100644 --- a/apps/desktop/src/main/claude-code-settings/index.ts +++ b/apps/desktop/src/main/claude-code-settings/index.ts @@ -2,7 +2,7 @@ * Claude Code CLI Settings Module * * Reads and merges Claude Code CLI settings files from the user's system. - * These settings are separate from Auto Claude's own settings (settings-utils.ts). + * These settings are separate from Aperant's own settings (settings-utils.ts). * * Usage: * import { getClaudeCodeEnv, readAllSettings } from './claude-code-settings'; diff --git a/apps/desktop/src/main/config-paths.ts b/apps/desktop/src/main/config-paths.ts index 839da5b8fa..a882061e9f 100644 --- a/apps/desktop/src/main/config-paths.ts +++ b/apps/desktop/src/main/config-paths.ts @@ -16,9 +16,10 @@ import * as path from 'path'; import * as os from 'os'; +import { existsSync } from 'fs'; import { isLinux } from './platform'; -const APP_NAME = 'auto-claude'; +const APP_NAME = 'aperant'; /** * Get the XDG config home directory @@ -70,10 +71,13 @@ export function getAppCacheDir(): string { /** * Get the memories storage directory - * This is where graph databases are stored (previously ~/.auto-claude/memories) + * This is where graph databases are stored (previously ~/.aperant/memories) */ export function getMemoriesDir(): string { - // For compatibility, we still support the legacy path + // New path under ~/.aperant/memories + const newPath = path.join(os.homedir(), '.aperant', 'memories'); + + // Legacy path from before the rebrand const legacyPath = path.join(os.homedir(), '.auto-claude', 'memories'); // On Linux with XDG variables set (AppImage, Flatpak, Snap), use XDG path @@ -81,8 +85,17 @@ export function getMemoriesDir(): string { return path.join(getXdgDataHome(), APP_NAME, 'memories'); } - // Default to legacy path for backwards compatibility - return legacyPath; + // Use new path if it exists, otherwise fall back to legacy + if (existsSync(newPath)) { + return newPath; + } + + if (existsSync(legacyPath)) { + return legacyPath; + } + + // Default to new path for fresh installs + return newPath; } /** diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts index 2ae1cc8c1b..e5eb0d3eb9 100644 --- a/apps/desktop/src/main/index.ts +++ b/apps/desktop/src/main/index.ts @@ -444,17 +444,17 @@ app.whenReady().then(() => { // Initialize agent manager agentManager = new AgentManager(); - // Load settings and configure agent manager with Python and auto-claude paths + // Load settings and configure agent manager with Python and aperant paths // Uses EAFP pattern (try/catch) instead of LBYL (existsSync) to avoid TOCTOU race conditions const settingsPath = join(app.getPath('userData'), 'settings.json'); try { const settings = JSON.parse(readFileSync(settingsPath, 'utf-8')); - // Validate and migrate autoBuildPath - must contain planner.md (prompts directory) + // Validate and migrate aperantPath - must contain planner.md (prompts directory) // Uses EAFP pattern (try/catch with accessSync) instead of existsSync to avoid TOCTOU race conditions - let validAutoBuildPath = settings.autoBuildPath; - if (validAutoBuildPath) { - const plannerMdPath = join(validAutoBuildPath, 'planner.md'); + let validAperantPath = settings.aperantPath; + if (validAperantPath) { + const plannerMdPath = join(validAperantPath, 'planner.md'); let plannerExists = false; try { accessSync(plannerMdPath); @@ -465,12 +465,12 @@ app.whenReady().then(() => { if (!plannerExists) { // Migration: Try to fix stale paths from old project structure - // Old structure: /path/to/project/auto-claude or apps/backend + // Old structure: /path/to/project/aperant or apps/backend // New structure: /path/to/project/apps/desktop/prompts let migrated = false; const possibleCorrections = [ - join(validAutoBuildPath.replace(/[/\\]auto-claude[/\\]*$/, ''), 'apps', 'desktop', 'prompts'), - join(validAutoBuildPath.replace(/[/\\]backend[/\\]*$/, ''), 'desktop', 'prompts'), + join(validAperantPath.replace(/[/\\]auto-claude[/\\]*$/, ''), 'apps', 'desktop', 'prompts'), + join(validAperantPath.replace(/[/\\]backend[/\\]*$/, ''), 'desktop', 'prompts'), ]; for (const correctedPath of possibleCorrections) { const correctedPlannerPath = join(correctedPath, 'planner.md'); @@ -483,31 +483,31 @@ app.whenReady().then(() => { } if (correctedPathExists) { - console.log('[main] Migrating autoBuildPath from old structure:', validAutoBuildPath, '->', correctedPath); - settings.autoBuildPath = correctedPath; - validAutoBuildPath = correctedPath; + console.log('[main] Migrating aperantPath from old structure:', validAperantPath, '->', correctedPath); + settings.aperantPath = correctedPath; + validAperantPath = correctedPath; migrated = true; // Save the corrected setting - we're the only process modifying settings at startup try { writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8'); - console.log('[main] Successfully saved migrated autoBuildPath to settings'); + console.log('[main] Successfully saved migrated aperantPath to settings'); } catch (writeError) { - console.warn('[main] Failed to save migrated autoBuildPath:', writeError); + console.warn('[main] Failed to save migrated aperantPath:', writeError); } break; } } if (!migrated) { - console.warn('[main] Configured autoBuildPath is invalid (missing planner.md), will use auto-detection:', validAutoBuildPath); - validAutoBuildPath = undefined; // Let auto-detection find the correct path + console.warn('[main] Configured aperantPath is invalid (missing planner.md), will use auto-detection:', validAperantPath); + validAperantPath = undefined; // Let auto-detection find the correct path // Clear the stale setting so this warning doesn't repeat every startup try { - delete settings.autoBuildPath; + delete settings.aperantPath; writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8'); - console.log('[main] Cleared stale autoBuildPath from settings'); + console.log('[main] Cleared stale aperantPath from settings'); } catch { // Non-critical - warning will just repeat next startup } @@ -515,12 +515,12 @@ app.whenReady().then(() => { } } - if (settings.pythonPath || validAutoBuildPath) { + if (settings.pythonPath || validAperantPath) { console.warn('[main] Configuring AgentManager with settings:', { pythonPath: settings.pythonPath, - autoBuildPath: validAutoBuildPath + aperantPath: validAperantPath }); - agentManager.configure(settings.pythonPath, validAutoBuildPath); + agentManager.configure(settings.pythonPath, validAperantPath); } } catch (error: unknown) { // ENOENT means no settings file yet - that's fine, use defaults diff --git a/apps/desktop/src/main/insights-service.ts b/apps/desktop/src/main/insights-service.ts index 0668673464..e5d078a3ad 100644 --- a/apps/desktop/src/main/insights-service.ts +++ b/apps/desktop/src/main/insights-service.ts @@ -56,10 +56,10 @@ export class InsightsService extends EventEmitter { } /** - * Configure paths for Python and auto-claude source + * Configure paths for Python and aperant source */ - configure(pythonPath?: string, autoBuildSourcePath?: string): void { - this.config.configure(pythonPath, autoBuildSourcePath); + configure(pythonPath?: string, aperantSourcePath?: string): void { + this.config.configure(pythonPath, aperantSourcePath); } /** diff --git a/apps/desktop/src/main/insights/README.md b/apps/desktop/src/main/insights/README.md index b9f68a7f85..8ecaba9ab9 100644 --- a/apps/desktop/src/main/insights/README.md +++ b/apps/desktop/src/main/insights/README.md @@ -18,9 +18,9 @@ insights-service.ts (186 lines) ## Module Responsibilities ### InsightsConfig (`config.ts`) -- Manages Python and auto-claude source path configuration -- Detects auto-claude installation automatically -- Loads environment variables from auto-claude .env file +- Manages Python and aperant source path configuration +- Detects aperant installation automatically +- Loads environment variables from aperant .env file - Provides complete process environment with profile support ### InsightsPaths (`paths.ts`) @@ -58,7 +58,7 @@ import { InsightsService } from './insights-service'; const service = new InsightsService(); // Configure paths -service.configure(pythonPath, autoBuildSourcePath); +service.configure(pythonPath, aperantSourcePath); // Load session const session = service.loadSession(projectId, projectPath); diff --git a/apps/desktop/src/main/insights/REFACTORING_NOTES.md b/apps/desktop/src/main/insights/REFACTORING_NOTES.md index f3b30079ad..5ef9cfeb61 100644 --- a/apps/desktop/src/main/insights/REFACTORING_NOTES.md +++ b/apps/desktop/src/main/insights/REFACTORING_NOTES.md @@ -83,7 +83,7 @@ No migration needed! The refactoring is transparent to consumers: // This code continues to work exactly as before import { insightsService } from '../insights-service'; -insightsService.configure(pythonPath, autoBuildSourcePath); +insightsService.configure(pythonPath, aperantSourcePath); const session = insightsService.loadSession(projectId, projectPath); await insightsService.sendMessage(projectId, projectPath, message); ``` diff --git a/apps/desktop/src/main/insights/config.ts b/apps/desktop/src/main/insights/config.ts index 9262406353..871282ab31 100644 --- a/apps/desktop/src/main/insights/config.ts +++ b/apps/desktop/src/main/insights/config.ts @@ -12,25 +12,25 @@ import { getEffectiveSourcePath } from '../updater/path-resolver'; * Handles path detection and environment variable loading */ export class InsightsConfig { - private autoBuildSourcePath: string = ''; + private aperantSourcePath: string = ''; - configure(_pythonPath?: string, autoBuildSourcePath?: string): void { - if (autoBuildSourcePath) { - this.autoBuildSourcePath = autoBuildSourcePath; + configure(_pythonPath?: string, aperantSourcePath?: string): void { + if (aperantSourcePath) { + this.aperantSourcePath = aperantSourcePath; } } /** - * Get the auto-claude source path (detects automatically if not configured) + * Get the aperant source path (detects automatically if not configured) * Uses getEffectiveSourcePath() which handles userData override for user-updated backend */ - getAutoBuildSourcePath(): string | null { - if (this.autoBuildSourcePath && existsSync(this.autoBuildSourcePath)) { - return this.autoBuildSourcePath; + getAperantSourcePath(): string | null { + if (this.aperantSourcePath && existsSync(this.aperantSourcePath)) { + return this.aperantSourcePath; } // Use shared path resolver which handles: - // 1. User settings (autoBuildPath) + // 1. User settings (aperantPath) // 2. userData override (backend-source) for user-updated backend // 3. Bundled backend (process.resourcesPath/backend) // 4. Development paths @@ -43,13 +43,13 @@ export class InsightsConfig { } /** - * Load environment variables from auto-claude .env file + * Load environment variables from aperant .env file */ - loadAutoBuildEnv(): Record { - const autoBuildSource = this.getAutoBuildSourcePath(); - if (!autoBuildSource) return {}; + loadAperantEnv(): Record { + const aperantSource = this.getAperantSourcePath(); + if (!aperantSource) return {}; - const envPath = path.join(autoBuildSource, '.env'); + const envPath = path.join(aperantSource, '.env'); if (!existsSync(envPath)) return {}; try { @@ -83,10 +83,10 @@ export class InsightsConfig { /** * Get complete environment for process execution - * Includes system env, auto-claude env, and active Claude profile + * Includes system env, aperant env, and active Claude profile */ async getProcessEnv(): Promise> { - const autoBuildEnv = this.loadAutoBuildEnv(); + const aperantEnv = this.loadAperantEnv(); // Get best available Claude profile environment (automatically handles rate limits) const profileResult = getBestAvailableProfileEnv(); const profileEnv = profileResult.env; @@ -99,7 +99,7 @@ export class InsightsConfig { return { ...augmentedEnv, - ...autoBuildEnv, + ...aperantEnv, ...oauthModeClearVars, ...profileEnv, ...apiProfileEnv, diff --git a/apps/desktop/src/main/insights/paths.ts b/apps/desktop/src/main/insights/paths.ts index 4433990a53..fcea3a50d9 100644 --- a/apps/desktop/src/main/insights/paths.ts +++ b/apps/desktop/src/main/insights/paths.ts @@ -1,6 +1,6 @@ import path from 'path'; -const INSIGHTS_DIR = '.auto-claude/insights'; +const INSIGHTS_DIR = '.aperant/insights'; const SESSIONS_DIR = 'sessions'; const CURRENT_SESSION_FILE = 'current_session.json'; diff --git a/apps/desktop/src/main/ipc-handlers/README.md b/apps/desktop/src/main/ipc-handlers/README.md index 6ae86748bb..d0a2a037e3 100644 --- a/apps/desktop/src/main/ipc-handlers/README.md +++ b/apps/desktop/src/main/ipc-handlers/README.md @@ -1,6 +1,6 @@ # IPC Handlers - Modular Architecture -This directory contains the refactored IPC (Inter-Process Communication) handlers for Auto Claude UI, organized into domain-specific modules for better maintainability and code organization. +This directory contains the refactored IPC (Inter-Process Communication) handlers for Aperant UI, organized into domain-specific modules for better maintainability and code organization. ## Overview @@ -16,9 +16,9 @@ Handles project lifecycle and Python environment management: - `PROJECT_REMOVE` - Remove project - `PROJECT_LIST` - List all projects - `PROJECT_UPDATE_SETTINGS` - Update project settings -- `PROJECT_INITIALIZE` - Initialize .auto-claude directory +- `PROJECT_INITIALIZE` - Initialize .aperant directory - `PROJECT_CHECK_VERSION` - Check initialization status -- `project:has-local-source` - Check if project has local auto-claude source +- `project:has-local-source` - Check if project has local aperant source - Python environment initialization and status events #### `task-handlers.ts` (52KB) - Largest module @@ -243,7 +243,7 @@ Each module may depend on: - **Services**: AgentManager, TerminalManager, ChangelogService, etc. - **Stores**: projectStore - **Utilities**: fileWatcher, titleGenerator -- **Constants**: IPC_CHANNELS, AUTO_BUILD_PATHS, getSpecsDir +- **Constants**: IPC_CHANNELS, APERANT_PATHS, getSpecsDir - **Types**: Extensive TypeScript types from shared/types All dependencies are explicitly imported at the module level. diff --git a/apps/desktop/src/main/ipc-handlers/agent-events-handlers.ts b/apps/desktop/src/main/ipc-handlers/agent-events-handlers.ts index 14adb7edb2..1ed9e465b8 100644 --- a/apps/desktop/src/main/ipc-handlers/agent-events-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/agent-events-handlers.ts @@ -2,7 +2,7 @@ import type { BrowserWindow } from "electron"; import path from "path"; import { existsSync, readFileSync } from "fs"; import { safeParseJson } from "../utils/json-repair"; -import { IPC_CHANNELS, AUTO_BUILD_PATHS, getSpecsDir } from "../../shared/constants"; +import { IPC_CHANNELS, APERANT_PATHS, getSpecsDir } from "../../shared/constants"; import type { SDKRateLimitInfo, AuthFailureInfo, @@ -164,8 +164,8 @@ export function registerAgenteventsHandlers( if (exitTask && exitProject) { const worktreePath = findTaskWorktree(exitProject.path, exitTask.specId); if (worktreePath) { - const specsBaseDir = getSpecsDir(exitProject.autoBuildPath); - const worktreePlanPath = path.join(worktreePath, specsBaseDir, exitTask.specId, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const specsBaseDir = getSpecsDir(exitProject.aperantPath); + const worktreePlanPath = path.join(worktreePath, specsBaseDir, exitTask.specId, APERANT_PATHS.IMPLEMENTATION_PLAN); try { const content = readFileSync(worktreePlanPath, 'utf-8'); const parsed = safeParseJson(content); @@ -205,9 +205,9 @@ export function registerAgenteventsHandlers( if (code === 0) { const { task: specTask, project: specProject } = findTaskAndProject(taskId, projectId); if (specTask && specProject) { - const specsBaseDir = getSpecsDir(specProject.autoBuildPath); + const specsBaseDir = getSpecsDir(specProject.aperantPath); const specDir = path.join(specProject.path, specsBaseDir, specTask.specId); - const specFilePath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specFilePath = path.join(specDir, APERANT_PATHS.SPEC_FILE); if (existsSync(specFilePath)) { console.warn(`[Task ${taskId}] Spec created successfully — starting task execution`); // Re-watch the spec directory for the build phase @@ -290,12 +290,12 @@ export function registerAgenteventsHandlers( const worktreePath = findTaskWorktree(project.path, task.specId); if (worktreePath) { - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const worktreePlanPath = path.join( worktreePath, specsBaseDir, task.specId, - AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN + APERANT_PATHS.IMPLEMENTATION_PLAN ); if (existsSync(worktreePlanPath)) { persistPlanLastEventSync(worktreePlanPath, event); @@ -329,11 +329,11 @@ export function registerAgenteventsHandlers( // Also persist to worktree if task has one const worktreePath = findTaskWorktree(project.path, task.specId); if (worktreePath) { - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const worktreeSpecDir = path.join(worktreePath, specsBaseDir, task.specId); const worktreePlanPath = path.join( worktreeSpecDir, - AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN + APERANT_PATHS.IMPLEMENTATION_PLAN ); if (existsSync(worktreePlanPath)) { persistPlanPhaseSync(worktreePlanPath, progress.phase, project.id); @@ -426,12 +426,12 @@ export function registerAgenteventsHandlers( // Also re-stamp worktree copy if it exists const worktreePath = findTaskWorktree(project.path, task.specId); if (worktreePath) { - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const worktreePlanPath = path.join( worktreePath, specsBaseDir, task.specId, - AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN + APERANT_PATHS.IMPLEMENTATION_PLAN ); if (existsSync(worktreePlanPath)) { persistPlanStatusAndReasonSync(worktreePlanPath, status, reviewReason, project.id, currentXState, phase); diff --git a/apps/desktop/src/main/ipc-handlers/changelog-handlers.ts b/apps/desktop/src/main/ipc-handlers/changelog-handlers.ts index b336bfc9c7..d3560109a6 100644 --- a/apps/desktop/src/main/ipc-handlers/changelog-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/changelog-handlers.ts @@ -113,7 +113,7 @@ export function registerChangelogHandlers( const tasks = rendererTasks || projectStore.getTasks(projectId); // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const doneTasks = changelogService.getCompletedTasks(project.path, tasks, specsBaseDir); return { success: true, data: doneTasks }; @@ -131,7 +131,7 @@ export function registerChangelogHandlers( const tasks = projectStore.getTasks(projectId); // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specs = await changelogService.loadTaskSpecs(project.path, taskIds, tasks, specsBaseDir); return { success: true, data: specs }; @@ -164,7 +164,7 @@ export function registerChangelogHandlers( let specs: TaskSpecContent[] = []; if (request.sourceMode === 'tasks' && request.taskIds && request.taskIds.length > 0) { const tasks = projectStore.getTasks(request.projectId); - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); specs = await changelogService.loadTaskSpecs(project.path, request.taskIds, tasks, specsBaseDir); } @@ -282,7 +282,7 @@ export function registerChangelogHandlers( // Load specs for selected tasks to analyze change types const tasks = projectStore.getTasks(projectId); - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specs = await changelogService.loadTaskSpecs(project.path, taskIds, tasks, specsBaseDir); // Analyze specs and suggest version diff --git a/apps/desktop/src/main/ipc-handlers/changelog-handlers.ts.bk b/apps/desktop/src/main/ipc-handlers/changelog-handlers.ts.bk index 21bc287422..016faec22b 100644 --- a/apps/desktop/src/main/ipc-handlers/changelog-handlers.ts.bk +++ b/apps/desktop/src/main/ipc-handlers/changelog-handlers.ts.bk @@ -44,7 +44,7 @@ export function registerChangelogHandlers( const tasks = rendererTasks || projectStore.getTasks(projectId); // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const doneTasks = changelogService.getCompletedTasks(project.path, tasks, specsBaseDir); return { success: true, data: doneTasks }; @@ -62,7 +62,7 @@ export function registerChangelogHandlers( const tasks = projectStore.getTasks(projectId); // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specs = await changelogService.loadTaskSpecs(project.path, taskIds, tasks, specsBaseDir); return { success: true, data: specs }; @@ -89,7 +89,7 @@ export function registerChangelogHandlers( let specs: import('../shared/types').TaskSpecContent[] = []; if (request.sourceMode === 'tasks' && request.taskIds && request.taskIds.length > 0) { const tasks = projectStore.getTasks(request.projectId); - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); specs = await changelogService.loadTaskSpecs(project.path, request.taskIds, tasks, specsBaseDir); } @@ -146,7 +146,7 @@ export function registerChangelogHandlers( // Load specs for selected tasks to analyze change types const tasks = projectStore.getTasks(projectId); - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specs = await changelogService.loadTaskSpecs(project.path, taskIds, tasks, specsBaseDir); // Analyze specs and suggest version diff --git a/apps/desktop/src/main/ipc-handlers/claude-code-handlers.ts b/apps/desktop/src/main/ipc-handlers/claude-code-handlers.ts index c26f3fcc93..0454ff8e83 100644 --- a/apps/desktop/src/main/ipc-handlers/claude-code-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/claude-code-handlers.ts @@ -1372,7 +1372,7 @@ export function registerClaudeCodeHandlers(): void { // If authenticated, update the profile with metadata from credentials // NOTE: We intentionally do NOT store the OAuth token in the profile. - // Storing the token causes AutoClaude to use a stale cached token instead of + // Storing the token causes Aperant to use a stale cached token instead of // letting Claude CLI read fresh tokens from Keychain (which auto-refreshes). // By only storing metadata, we ensure getProfileEnv() uses CLAUDE_CONFIG_DIR, // which allows Claude CLI's working token refresh mechanism to be used. diff --git a/apps/desktop/src/main/ipc-handlers/context/README.md b/apps/desktop/src/main/ipc-handlers/context/README.md index 19de7f4ad6..fae0d46f1c 100644 --- a/apps/desktop/src/main/ipc-handlers/context/README.md +++ b/apps/desktop/src/main/ipc-handlers/context/README.md @@ -1,6 +1,6 @@ # Context Handlers Module -This directory contains the refactored context-related IPC handlers for the Auto Claude UI application. The handlers manage project context, memory systems (both file-based and Graphiti/LadybugDB), and project index operations. +This directory contains the refactored context-related IPC handlers for the Aperant UI application. The handlers manage project context, memory systems (both file-based and Graphiti/LadybugDB), and project index operations. ## Architecture @@ -12,9 +12,9 @@ The module is organized into focused, single-responsibility files: Shared utility functions for environment configuration and parsing. **Exports:** -- `getAutoBuildSourcePath()` - Get auto-build source path from settings +- `getAperantSourcePath()` - Get aperant source path from settings - `parseEnvFile(content)` - Parse .env file content into key-value pairs -- `loadProjectEnvVars(projectPath, autoBuildPath)` - Load project-specific environment variables +- `loadProjectEnvVars(projectPath, aperantPath)` - Load project-specific environment variables - `loadGlobalSettings()` - Load global application settings - `isGraphitiEnabled(projectEnvVars)` - Check if Graphiti memory system is enabled - `hasOpenAIKey(projectEnvVars, globalSettings)` - Check if OpenAI API key is available @@ -29,8 +29,8 @@ Shared utility functions for environment configuration and parsing. Handlers for checking Graphiti/memory system configuration status. **Exports:** -- `loadGraphitiStateFromSpecs(projectPath, autoBuildPath)` - Load Graphiti state from most recent spec -- `buildMemoryStatus(projectPath, autoBuildPath, memoryState)` - Build memory status from environment +- `loadGraphitiStateFromSpecs(projectPath, aperantPath)` - Load Graphiti state from most recent spec +- `buildMemoryStatus(projectPath, aperantPath, memoryState)` - Build memory status from environment - `registerMemoryStatusHandlers(getMainWindow)` - Register IPC handlers **IPC Channels:** @@ -136,7 +136,7 @@ test('parseEnvFile handles quotes correctly', () => { import { buildMemoryStatus } from './memory-status-handlers'; test('buildMemoryStatus returns correct status', () => { - const status = buildMemoryStatus('/path/to/project', 'auto-claude'); + const status = buildMemoryStatus('/path/to/project', 'aperant'); expect(status).toHaveProperty('enabled'); expect(status).toHaveProperty('available'); }); @@ -152,7 +152,7 @@ test('buildMemoryStatus returns correct status', () => { ## Related Documentation -- [Project Memory System](../../../../auto-claude/memory.py) -- [Graphiti Memory Integration](../../../../auto-claude/graphiti_memory.py) +- [Project Memory System](../../../../aperant/memory.py) +- [Graphiti Memory Integration](../../../../aperant/graphiti_memory.py) - [LadybugDB Integration](../../ladybug-service.ts) - [IPC Channels](../../../shared/constants.ts) diff --git a/apps/desktop/src/main/ipc-handlers/context/project-context-handlers.ts b/apps/desktop/src/main/ipc-handlers/context/project-context-handlers.ts index ef4d826644..91d409db4f 100644 --- a/apps/desktop/src/main/ipc-handlers/context/project-context-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/context/project-context-handlers.ts @@ -2,7 +2,7 @@ import { ipcMain } from 'electron'; import type { BrowserWindow } from 'electron'; import path from 'path'; import { existsSync, readFileSync } from 'fs'; -import { IPC_CHANNELS, AUTO_BUILD_PATHS } from '../../../shared/constants'; +import { IPC_CHANNELS, APERANT_PATHS } from '../../../shared/constants'; import type { IPCResult, ProjectContextData, @@ -47,7 +47,7 @@ function toRendererMemory(m: Memory): RendererMemory { * Load project index from file */ function loadProjectIndex(projectPath: string): ProjectIndex | null { - const indexPath = path.join(projectPath, AUTO_BUILD_PATHS.PROJECT_INDEX); + const indexPath = path.join(projectPath, APERANT_PATHS.PROJECT_INDEX); if (!existsSync(indexPath)) { return null; } @@ -137,7 +137,7 @@ export function registerProjectContextHandlers( } try { - const indexOutputPath = path.join(project.path, AUTO_BUILD_PATHS.PROJECT_INDEX); + const indexOutputPath = path.join(project.path, APERANT_PATHS.PROJECT_INDEX); // Run the TypeScript project indexer (replaces Python subprocess) const projectIndex = runProjectIndexer(project.path, indexOutputPath); diff --git a/apps/desktop/src/main/ipc-handlers/context/utils.ts b/apps/desktop/src/main/ipc-handlers/context/utils.ts index 41e94ecdbf..a1d2f3a743 100644 --- a/apps/desktop/src/main/ipc-handlers/context/utils.ts +++ b/apps/desktop/src/main/ipc-handlers/context/utils.ts @@ -7,22 +7,22 @@ export interface EnvironmentVars { } export interface GlobalSettings { - autoBuildPath?: string; + aperantPath?: string; globalOpenAIApiKey?: string; } const settingsPath = path.join(app.getPath('userData'), 'settings.json'); /** - * Get the auto-build source path from settings + * Get the aperant source path from settings */ -export function getAutoBuildSourcePath(): string | null { +export function getAperantSourcePath(): string | null { if (existsSync(settingsPath)) { try { const content = readFileSync(settingsPath, 'utf-8'); const settings = JSON.parse(content); - if (settings.autoBuildPath && existsSync(settings.autoBuildPath)) { - return settings.autoBuildPath; + if (settings.aperantPath && existsSync(settings.aperantPath)) { + return settings.aperantPath; } } catch { // Fall through to null @@ -63,12 +63,12 @@ export function parseEnvFile(envContent: string): EnvironmentVars { /** * Load environment variables from project .env file */ -export function loadProjectEnvVars(projectPath: string, autoBuildPath?: string): EnvironmentVars { - if (!autoBuildPath) { +export function loadProjectEnvVars(projectPath: string, aperantPath?: string): EnvironmentVars { + if (!aperantPath) { return {}; } - const projectEnvPath = path.join(projectPath, autoBuildPath, '.env'); + const projectEnvPath = path.join(projectPath, aperantPath, '.env'); if (!existsSync(projectEnvPath)) { return {}; } @@ -218,7 +218,7 @@ export interface MemoryDatabaseDetails { export function getMemoryDatabaseDetails(projectEnvVars: EnvironmentVars): MemoryDatabaseDetails { const dbPath = projectEnvVars['GRAPHITI_DB_PATH'] || process.env.GRAPHITI_DB_PATH || - require('path').join(require('os').homedir(), '.auto-claude', 'memories'); + require('path').join(require('os').homedir(), '.aperant', 'memories'); const database = projectEnvVars['GRAPHITI_DATABASE'] || process.env.GRAPHITI_DATABASE || diff --git a/apps/desktop/src/main/ipc-handlers/env-handlers.ts b/apps/desktop/src/main/ipc-handlers/env-handlers.ts index 7f7e5c3aeb..3e29e26859 100644 --- a/apps/desktop/src/main/ipc-handlers/env-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/env-handlers.ts @@ -48,8 +48,8 @@ export function registerEnvHandlers( const existingVars = existingContent ? parseEnvFile(existingContent) : {}; // Update with new values - if (config.autoBuildModel !== undefined) { - existingVars['AUTO_BUILD_MODEL'] = config.autoBuildModel; + if (config.aperantModel !== undefined) { + existingVars['APERANT_MODEL'] = config.aperantModel; } if (config.linearApiKey !== undefined) { existingVars['LINEAR_API_KEY'] = config.linearApiKey; @@ -183,11 +183,11 @@ export function registerEnvHandlers( } // Generate content with sections - const content = `# Auto Claude Framework Environment Variables -# Managed by Auto Claude UI + const content = `# Aperant Framework Environment Variables +# Managed by Aperant UI # Model override (OPTIONAL) -${existingVars['AUTO_BUILD_MODEL'] ? `AUTO_BUILD_MODEL=${existingVars['AUTO_BUILD_MODEL']}` : '# AUTO_BUILD_MODEL=claude-opus-4-6'} +${existingVars['APERANT_MODEL'] ? `APERANT_MODEL=${existingVars['APERANT_MODEL']}` : '# APERANT_MODEL=claude-opus-4-6'} # ============================================================================= # LINEAR INTEGRATION (OPTIONAL) @@ -217,7 +217,7 @@ ${envLine(existingVars, GITLAB_ENV_KEYS.AUTO_SYNC, 'false')} # GIT/WORKTREE SETTINGS (OPTIONAL) # ============================================================================= # Default base branch for worktree creation -# If not set, Auto Claude will auto-detect main/master, or fall back to current branch +# If not set, Aperant will auto-detect main/master, or fall back to current branch ${existingVars['DEFAULT_BRANCH'] ? `DEFAULT_BRANCH=${existingVars['DEFAULT_BRANCH']}` : '# DEFAULT_BRANCH=main'} # ============================================================================= @@ -288,7 +288,7 @@ ${existingVars['OLLAMA_EMBEDDING_DIM'] ? `OLLAMA_EMBEDDING_DIM=${existingVars['O # LadybugDB Database (embedded - no Docker required) ${existingVars['GRAPHITI_DATABASE'] ? `GRAPHITI_DATABASE=${existingVars['GRAPHITI_DATABASE']}` : '# GRAPHITI_DATABASE=auto_claude_memory'} -${existingVars['GRAPHITI_DB_PATH'] ? `GRAPHITI_DB_PATH=${existingVars['GRAPHITI_DB_PATH']}` : '# GRAPHITI_DB_PATH=~/.auto-claude/memories'} +${existingVars['GRAPHITI_DB_PATH'] ? `GRAPHITI_DB_PATH=${existingVars['GRAPHITI_DB_PATH']}` : '# GRAPHITI_DB_PATH=~/.aperant/memories'} `; return content; @@ -302,11 +302,11 @@ ${existingVars['GRAPHITI_DB_PATH'] ? `GRAPHITI_DB_PATH=${existingVars['GRAPHITI_ return { success: false, error: 'Project not found' }; } - if (!project.autoBuildPath) { + if (!project.aperantPath) { return { success: false, error: 'Project not initialized' }; } - const envPath = path.join(project.path, project.autoBuildPath, '.env'); + const envPath = path.join(project.path, project.aperantPath, '.env'); // Load global settings for fallbacks let globalSettings: AppSettings = { ...DEFAULT_APP_SETTINGS }; @@ -340,8 +340,8 @@ ${existingVars['GRAPHITI_DB_PATH'] ? `GRAPHITI_DB_PATH=${existingVars['GRAPHITI_ } } - if (vars['AUTO_BUILD_MODEL']) { - config.autoBuildModel = vars['AUTO_BUILD_MODEL']; + if (vars['APERANT_MODEL']) { + config.aperantModel = vars['APERANT_MODEL']; } if (vars['LINEAR_API_KEY']) { @@ -494,11 +494,11 @@ ${existingVars['GRAPHITI_DB_PATH'] ? `GRAPHITI_DB_PATH=${existingVars['GRAPHITI_ return { success: false, error: 'Project not found' }; } - if (!project.autoBuildPath) { + if (!project.aperantPath) { return { success: false, error: 'Project not initialized' }; } - const envPath = path.join(project.path, project.autoBuildPath, '.env'); + const envPath = path.join(project.path, project.aperantPath, '.env'); try { // Read existing content if file exists (atomic read, no TOCTOU) diff --git a/apps/desktop/src/main/ipc-handlers/github/__tests__/runner-env-handlers.test.ts b/apps/desktop/src/main/ipc-handlers/github/__tests__/runner-env-handlers.test.ts index c100a227e5..287cfcd544 100644 --- a/apps/desktop/src/main/ipc-handlers/github/__tests__/runner-env-handlers.test.ts +++ b/apps/desktop/src/main/ipc-handlers/github/__tests__/runner-env-handlers.test.ts @@ -242,7 +242,7 @@ function createProject(): Project { id: 'project-1', name: 'Test Project', path: projectPath, - autoBuildPath: '.auto-claude', + aperantPath: '.aperant', settings: { model: 'default', memoryBackend: 'file', diff --git a/apps/desktop/src/main/ipc-handlers/github/autofix-handlers.ts b/apps/desktop/src/main/ipc-handlers/github/autofix-handlers.ts index f4476b7e13..b370f1c579 100644 --- a/apps/desktop/src/main/ipc-handlers/github/autofix-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/github/autofix-handlers.ts @@ -28,7 +28,7 @@ import type { ModelShorthand, ThinkingLevel } from '../../ai/config/types'; const { debug: debugLog } = createContextLogger('GitHub AutoFix'); /** - * Auto-fix configuration stored in .auto-claude/github/config.json + * Auto-fix configuration stored in .aperant/github/config.json */ export interface AutoFixConfig { enabled: boolean; @@ -99,7 +99,7 @@ export interface BatchProgress { * Get the GitHub directory for a project */ function getGitHubDir(project: Project): string { - return path.join(project.path, '.auto-claude', 'github'); + return path.join(project.path, '.aperant', 'github'); } /** diff --git a/apps/desktop/src/main/ipc-handlers/github/pr-handlers.ts b/apps/desktop/src/main/ipc-handlers/github/pr-handlers.ts index 1ff9f8e074..9cec98da0b 100644 --- a/apps/desktop/src/main/ipc-handlers/github/pr-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/github/pr-handlers.ts @@ -822,7 +822,7 @@ async function performCIWaitCheck( * Get the GitHub directory for a project */ function getGitHubDir(project: Project): string { - return path.join(project.path, ".auto-claude", "github"); + return path.join(project.path, ".aperant", "github"); } /** @@ -1081,7 +1081,7 @@ function createEmptyPRLogs(prNumber: number, repo: string, isFollowup: boolean): /** * Get PR logs file path * - * Logs are stored at `.auto-claude/github/pr/logs_${prNumber}.json` within the project directory. + * Logs are stored at `.aperant/github/pr/logs_${prNumber}.json` within the project directory. * This provides persistent storage for streaming log data during PR reviews. */ function getPRLogsPath(project: Project, prNumber: number): string { @@ -1163,7 +1163,7 @@ function addLogEntry(logs: PRLogs, entry: PRLogEntry): boolean { * This class implements a hybrid push/pull approach for real-time log streaming: * * 1. **File-Based Storage**: Logs are saved to disk every 3 entries (saveInterval) - * - Location: .auto-claude/github/pr/logs_${prNumber}.json + * - Location: .aperant/github/pr/logs_${prNumber}.json * - Format: JSON with phase status and log entries * * 2. **Push-Based Updates**: Emits IPC events (GITHUB_PR_LOGS_UPDATED) after each save @@ -1281,7 +1281,7 @@ class PRLogCollector { * Two-step update mechanism: * -------------------------- * 1. **File Write**: Persists logs to disk via savePRLogs() - * - Creates/updates .auto-claude/github/pr/logs_${prNumber}.json + * - Creates/updates .aperant/github/pr/logs_${prNumber}.json * - Updates the `updated_at` timestamp * * 2. **IPC Push Event**: Sends GITHUB_PR_LOGS_UPDATED to renderer @@ -2508,7 +2508,7 @@ export function registerPRHandlers(getMainWindow: () => BrowserWindow | null): v } // Use temp file to avoid shell escaping issues - const tmpFile = join(project.path, ".auto-claude", "tmp_comment_body.txt"); + const tmpFile = join(project.path, ".aperant", "tmp_comment_body.txt"); try { writeFileSync(tmpFile, body, "utf-8"); // Use execFileSync with arguments array to prevent command injection @@ -2733,7 +2733,7 @@ export function registerPRHandlers(getMainWindow: () => BrowserWindow | null): v const result = await withProjectOrNull(projectId, async (project) => { // Check if review exists and has reviewed_commit_sha - const githubDir = path.join(project.path, ".auto-claude", "github"); + const githubDir = path.join(project.path, ".aperant", "github"); const reviewPath = path.join(githubDir, "pr", `review_${prNumber}.json`); let review: PRReviewResult; diff --git a/apps/desktop/src/main/ipc-handlers/github/spec-utils.ts b/apps/desktop/src/main/ipc-handlers/github/spec-utils.ts index 46bbb368e9..19f41a5d8f 100644 --- a/apps/desktop/src/main/ipc-handlers/github/spec-utils.ts +++ b/apps/desktop/src/main/ipc-handlers/github/spec-utils.ts @@ -4,7 +4,7 @@ import path from 'path'; import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'; -import { AUTO_BUILD_PATHS, getSpecsDir } from '../../../shared/constants'; +import { APERANT_PATHS, getSpecsDir } from '../../../shared/constants'; import type { Project, TaskMetadata } from '../../../shared/types'; import { withSpecNumberLock } from '../../utils/spec-number-lock'; import { debugLog } from './utils/logger'; @@ -101,7 +101,7 @@ export async function createSpecForIssue( labels: string[] = [], baseBranch?: string ): Promise { - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (!existsSync(specsDir)) { @@ -117,7 +117,7 @@ export async function createSpecForIssue( // Use coordinated spec numbering with lock to prevent collisions return await withSpecNumberLock(project.path, async (lock) => { // Get next spec number from global scan (main + all worktrees) - const specNumber = lock.getNextSpecNumber(project.autoBuildPath); + const specNumber = lock.getNextSpecNumber(project.aperantPath); const slugifiedTitle = slugifyTitle(safeTitle); const specId = `${String(specNumber).padStart(3, '0')}-${slugifiedTitle}`; @@ -139,7 +139,7 @@ export async function createSpecForIssue( }; // lgtm[js/http-to-file-access] - specDir is controlled, slugifiedTitle sanitizes input writeFileSync( - path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), + path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2), 'utf-8' ); @@ -151,7 +151,7 @@ export async function createSpecForIssue( }; // lgtm[js/http-to-file-access] - specDir is controlled, slugifiedTitle sanitizes input writeFileSync( - path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS), + path.join(specDir, APERANT_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2), 'utf-8' ); @@ -234,7 +234,7 @@ Please analyze this issue and provide: * Used to immediately update the plan file so the frontend shows the correct status */ export function updateImplementationPlanStatus(specDir: string, status: string): void { - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); try { const content = readFileSync(planPath, 'utf-8'); diff --git a/apps/desktop/src/main/ipc-handlers/github/triage-handlers.ts b/apps/desktop/src/main/ipc-handlers/github/triage-handlers.ts index 93f4209a05..93e1ebf356 100644 --- a/apps/desktop/src/main/ipc-handlers/github/triage-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/github/triage-handlers.ts @@ -92,7 +92,7 @@ export interface TriageProgress { * Get the GitHub directory for a project */ function getGitHubDir(project: Project): string { - return path.join(project.path, '.auto-claude', 'github'); + return path.join(project.path, '.aperant', 'github'); } /** diff --git a/apps/desktop/src/main/ipc-handlers/github/utils.ts b/apps/desktop/src/main/ipc-handlers/github/utils.ts index 1aadb7b7da..d2db7ca8e2 100644 --- a/apps/desktop/src/main/ipc-handlers/github/utils.ts +++ b/apps/desktop/src/main/ipc-handlers/github/utils.ts @@ -197,8 +197,8 @@ export async function getGitHubTokenForSubprocess(): Promise { * Falls back to gh CLI token if GITHUB_TOKEN not in .env */ export function getGitHubConfig(project: Project): GitHubConfig | null { - if (!project.autoBuildPath) return null; - const envPath = path.join(project.path, project.autoBuildPath, '.env'); + if (!project.aperantPath) return null; + const envPath = path.join(project.path, project.aperantPath, '.env'); if (!existsSync(envPath)) return null; try { diff --git a/apps/desktop/src/main/ipc-handlers/gitlab/autofix-handlers.ts b/apps/desktop/src/main/ipc-handlers/gitlab/autofix-handlers.ts index 87b8edf00e..abef008cbf 100644 --- a/apps/desktop/src/main/ipc-handlers/gitlab/autofix-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/gitlab/autofix-handlers.ts @@ -68,7 +68,7 @@ function validatePathWithinProject(projectPath: string, resolvedPath: string): v * Get the GitLab directory for a project */ function getGitLabDir(project: Project): string { - const gitlabDir = path.join(project.path, '.auto-claude', 'gitlab'); + const gitlabDir = path.join(project.path, '.aperant', 'gitlab'); validatePathWithinProject(project.path, gitlabDir); return gitlabDir; } diff --git a/apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts b/apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts index 8ade48cd05..0e361db60f 100644 --- a/apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts @@ -58,7 +58,7 @@ function getReviewKey(projectId: string, mrIid: number): string { * Get the GitLab directory for a project */ function getGitLabDir(project: Project): string { - return path.join(project.path, '.auto-claude', 'gitlab'); + return path.join(project.path, '.aperant', 'gitlab'); } async function waitForRebaseCompletion( @@ -774,7 +774,7 @@ export function registerMRReviewHandlers( debugLog('checkNewCommits handler called', { projectId, mrIid }); const result = await withProjectOrNull(projectId, async (project) => { - const gitlabDir = path.join(project.path, '.auto-claude', 'gitlab'); + const gitlabDir = path.join(project.path, '.aperant', 'gitlab'); const reviewPath = path.join(gitlabDir, 'mr', `review_${mrIid}.json`); if (!fs.existsSync(reviewPath)) { diff --git a/apps/desktop/src/main/ipc-handlers/gitlab/spec-utils.ts b/apps/desktop/src/main/ipc-handlers/gitlab/spec-utils.ts index f501e476fc..45652132b3 100644 --- a/apps/desktop/src/main/ipc-handlers/gitlab/spec-utils.ts +++ b/apps/desktop/src/main/ipc-handlers/gitlab/spec-utils.ts @@ -388,7 +388,7 @@ export async function createSpecForIssue( const safeProject = sanitizeText(config.project, 200); const safeInstanceUrl = sanitizeInstanceUrl(config.instanceUrl); - const specsDir = path.join(project.path, project.autoBuildPath, 'specs'); + const specsDir = path.join(project.path, project.aperantPath, 'specs'); // Ensure specs directory exists await mkdir(specsDir, { recursive: true }); diff --git a/apps/desktop/src/main/ipc-handlers/gitlab/triage-handlers.ts b/apps/desktop/src/main/ipc-handlers/gitlab/triage-handlers.ts index c99593e832..d40ed4be71 100644 --- a/apps/desktop/src/main/ipc-handlers/gitlab/triage-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/gitlab/triage-handlers.ts @@ -95,7 +95,7 @@ function sanitizeTriageResult(result: GitLabTriageResult): { * Get the GitLab directory for a project */ function getGitLabDir(project: Project): string { - return path.join(project.path, '.auto-claude', 'gitlab'); + return path.join(project.path, '.aperant', 'gitlab'); } /** diff --git a/apps/desktop/src/main/ipc-handlers/gitlab/utils.ts b/apps/desktop/src/main/ipc-handlers/gitlab/utils.ts index 6083ea25a9..28a53b45b4 100644 --- a/apps/desktop/src/main/ipc-handlers/gitlab/utils.ts +++ b/apps/desktop/src/main/ipc-handlers/gitlab/utils.ts @@ -143,8 +143,8 @@ async function fileExists(filePath: string): Promise { * Returns null if GitLab is explicitly disabled via GITLAB_ENABLED=false */ export async function getGitLabConfig(project: Project): Promise { - if (!project.autoBuildPath) return null; - const envPath = path.join(project.path, project.autoBuildPath, '.env'); + if (!project.aperantPath) return null; + const envPath = path.join(project.path, project.aperantPath, '.env'); if (!(await fileExists(envPath))) return null; try { diff --git a/apps/desktop/src/main/ipc-handlers/ideation/idea-manager.ts b/apps/desktop/src/main/ipc-handlers/ideation/idea-manager.ts index 5782343b27..330caf3a41 100644 --- a/apps/desktop/src/main/ipc-handlers/ideation/idea-manager.ts +++ b/apps/desktop/src/main/ipc-handlers/ideation/idea-manager.ts @@ -4,7 +4,7 @@ import path from 'path'; import type { IpcMainInvokeEvent } from 'electron'; -import { AUTO_BUILD_PATHS } from '../../../shared/constants'; +import { APERANT_PATHS } from '../../../shared/constants'; import type { IPCResult, IdeationStatus } from '../../../shared/types'; import { projectStore } from '../../project-store'; import { readIdeationFile, writeIdeationFile, updateIdeationTimestamp } from './file-utils'; @@ -25,8 +25,8 @@ export async function updateIdeaStatus( const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); const ideation = readIdeationFile(ideationPath); @@ -69,8 +69,8 @@ export async function dismissIdea( const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); const ideation = readIdeationFile(ideationPath); @@ -112,8 +112,8 @@ export async function dismissAllIdeas( const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); const ideation = readIdeationFile(ideationPath); @@ -158,8 +158,8 @@ export async function archiveIdea( const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); const ideation = readIdeationFile(ideationPath); @@ -201,8 +201,8 @@ export async function deleteIdea( const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); const ideation = readIdeationFile(ideationPath); @@ -244,8 +244,8 @@ export async function deleteMultipleIdeas( const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); const ideation = readIdeationFile(ideationPath); diff --git a/apps/desktop/src/main/ipc-handlers/ideation/session-manager.ts b/apps/desktop/src/main/ipc-handlers/ideation/session-manager.ts index 72299dbe1d..06f191135e 100644 --- a/apps/desktop/src/main/ipc-handlers/ideation/session-manager.ts +++ b/apps/desktop/src/main/ipc-handlers/ideation/session-manager.ts @@ -4,7 +4,7 @@ import path from 'path'; import type { IpcMainInvokeEvent } from 'electron'; -import { AUTO_BUILD_PATHS } from '../../../shared/constants'; +import { APERANT_PATHS } from '../../../shared/constants'; import type { IPCResult, IdeationSession } from '../../../shared/types'; import { projectStore } from '../../project-store'; import { transformIdeaFromSnakeCase } from './transformers'; @@ -24,8 +24,8 @@ export async function getIdeationSession( const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); const rawIdeation = readIdeationFile(ideationPath); diff --git a/apps/desktop/src/main/ipc-handlers/ideation/task-converter.ts b/apps/desktop/src/main/ipc-handlers/ideation/task-converter.ts index 075a1e4877..04d8b198d9 100644 --- a/apps/desktop/src/main/ipc-handlers/ideation/task-converter.ts +++ b/apps/desktop/src/main/ipc-handlers/ideation/task-converter.ts @@ -5,7 +5,7 @@ import path from 'path'; import { existsSync, mkdirSync, writeFileSync } from 'fs'; import type { IpcMainInvokeEvent } from 'electron'; -import { AUTO_BUILD_PATHS, getSpecsDir } from '../../../shared/constants'; +import { APERANT_PATHS, getSpecsDir } from '../../../shared/constants'; import type { IPCResult, Task, @@ -154,7 +154,7 @@ function createSpecFiles( spec_file: 'spec.md' }; writeFileSync( - path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), + path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(initialPlan, null, 2), 'utf-8' ); @@ -173,7 +173,7 @@ ${idea.rationale} --- *This spec was created from ideation and is pending detailed specification.* `; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE), specContent, 'utf-8'); + writeFileSync(path.join(specDir, APERANT_PATHS.SPEC_FILE), specContent, 'utf-8'); } /** @@ -191,8 +191,8 @@ export async function convertIdeaToTask( const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); // Quick check that ideation file exists (actual read happens inside lock) @@ -201,7 +201,7 @@ export async function convertIdeaToTask( } // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Ensure specs directory exists @@ -236,7 +236,7 @@ export async function convertIdeaToTask( } // Get next spec number from global scan (main + all worktrees) - const nextNum = lock.getNextSpecNumber(project.autoBuildPath); + const nextNum = lock.getNextSpecNumber(project.aperantPath); const slugifiedTitle = slugifyTitle(idea.title); const specId = `${String(nextNum).padStart(3, '0')}-${slugifiedTitle}`; const specDir = path.join(specsDir, specId); diff --git a/apps/desktop/src/main/ipc-handlers/insights-handlers.ts b/apps/desktop/src/main/ipc-handlers/insights-handlers.ts index 55a4f73e60..745b965471 100644 --- a/apps/desktop/src/main/ipc-handlers/insights-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/insights-handlers.ts @@ -5,7 +5,7 @@ import { existsSync, readdirSync, mkdirSync, writeFileSync } from "fs"; import { IPC_CHANNELS, getSpecsDir, - AUTO_BUILD_PATHS, + APERANT_PATHS, } from "../../shared/constants"; import type { IPCResult, @@ -132,14 +132,14 @@ export function registerInsightsHandlers(getMainWindow: () => BrowserWindow | nu return { success: false, error: "Project not found" }; } - if (!project.autoBuildPath) { + if (!project.aperantPath) { return { success: false, error: "Aperant not initialized for this project" }; } try { // Generate a unique spec ID based on existing specs // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Find next available spec number @@ -190,7 +190,7 @@ export function registerInsightsHandlers(getMainWindow: () => BrowserWindow | nu phases: [], }; - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); writeFileSync(planPath, JSON.stringify(implementationPlan, null, 2), 'utf-8'); // Save task metadata diff --git a/apps/desktop/src/main/ipc-handlers/linear-handlers.ts b/apps/desktop/src/main/ipc-handlers/linear-handlers.ts index 3e2061d709..181757e2ed 100644 --- a/apps/desktop/src/main/ipc-handlers/linear-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/linear-handlers.ts @@ -1,6 +1,6 @@ import { ipcMain } from 'electron'; import type { BrowserWindow } from 'electron'; -import { IPC_CHANNELS, getSpecsDir, AUTO_BUILD_PATHS } from '../../shared/constants'; +import { IPC_CHANNELS, getSpecsDir, APERANT_PATHS } from '../../shared/constants'; import type { IPCResult, LinearIssue, LinearTeam, LinearProject, LinearImportResult, LinearSyncStatus, Project, TaskMetadata } from '../../shared/types'; import path from 'path'; import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'fs'; @@ -26,8 +26,8 @@ export function registerLinearHandlers( * Helper to get Linear API key from project env */ const getLinearApiKey = (project: Project): string | null => { - if (!project.autoBuildPath) return null; - const envPath = path.join(project.path, project.autoBuildPath, '.env'); + if (!project.aperantPath) return null; + const envPath = path.join(project.path, project.aperantPath, '.env'); if (!existsSync(envPath)) return null; try { @@ -438,7 +438,7 @@ export function registerLinearHandlers( const errors: string[] = []; // Set up specs directory - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (!existsSync(specsDir)) { mkdirSync(specsDir, { recursive: true }); @@ -508,7 +508,7 @@ ${safeDescription || 'No description provided.'} phases: [] }; // lgtm[js/http-to-file-access] - specDir is controlled, Linear data sanitized - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2), 'utf-8'); + writeFileSync(path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2), 'utf-8'); // Create requirements.json const requirements = { @@ -516,7 +516,7 @@ ${safeDescription || 'No description provided.'} workflow_type: 'feature' }; // lgtm[js/http-to-file-access] - specDir is controlled, Linear data sanitized - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2), 'utf-8'); + writeFileSync(path.join(specDir, APERANT_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2), 'utf-8'); // Build metadata const metadata: TaskMetadata = { diff --git a/apps/desktop/src/main/ipc-handlers/mcp-handlers.ts b/apps/desktop/src/main/ipc-handlers/mcp-handlers.ts index 2a9d82420e..e19815b783 100644 --- a/apps/desktop/src/main/ipc-handlers/mcp-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/mcp-handlers.ts @@ -302,7 +302,7 @@ async function testHttpConnection(server: CustomMcpServer, startTime: number): P protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { - name: 'auto-claude-health-check', + name: 'aperant-health-check', version: '1.0.0', }, }, @@ -466,7 +466,7 @@ async function testCommandConnection(server: CustomMcpServer, startTime: number) protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { - name: 'auto-claude-health-check', + name: 'aperant-health-check', version: '1.0.0', }, }, diff --git a/apps/desktop/src/main/ipc-handlers/memory-handlers.ts b/apps/desktop/src/main/ipc-handlers/memory-handlers.ts index ec74869987..dea8b8824c 100644 --- a/apps/desktop/src/main/ipc-handlers/memory-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/memory-handlers.ts @@ -54,7 +54,7 @@ interface OllamaEmbeddingModel { /** * Recommended Embedding Model Card - * Pre-curated models suitable for Auto Claude memory system + * Pre-curated models suitable for Aperant memory system */ interface OllamaRecommendedModel { name: string; // Model identifier diff --git a/apps/desktop/src/main/ipc-handlers/project-handlers.ts b/apps/desktop/src/main/ipc-handlers/project-handlers.ts index e5567c1792..6582be60c8 100644 --- a/apps/desktop/src/main/ipc-handlers/project-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/project-handlers.ts @@ -7,7 +7,7 @@ import type { ProjectSettings, IPCResult, InitializationResult, - AutoBuildVersionInfo, + AperantVersionInfo, GitStatus, GitBranchDetail } from '../../shared/types'; @@ -17,7 +17,9 @@ import { isInitialized, hasLocalSource, checkGitStatus, - initializeGit + initializeGit, + needsMigration, + migrateProject } from '../project-initializer'; import { getToolPath } from '../cli-tool-manager'; import type { BrowserWindow } from 'electron'; @@ -274,11 +276,11 @@ export function registerProjectHandlers( ipcMain.handle( IPC_CHANNELS.PROJECT_LIST, async (): Promise> => { - // Validate that .auto-claude folders still exist for all projects - // If a folder was deleted, reset autoBuildPath so UI prompts for reinitialization + // Validate that .aperant folders still exist for all projects + // If a folder was deleted, reset aperantPath so UI prompts for reinitialization const resetIds = projectStore.validateProjects(); if (resetIds.length > 0) { - console.warn('[IPC] PROJECT_LIST: Detected missing .auto-claude folders for', resetIds.length, 'project(s)'); + console.warn('[IPC] PROJECT_LIST: Detected missing .aperant folders for', resetIds.length, 'project(s)'); } const projects = projectStore.getProjects(); @@ -310,7 +312,6 @@ export function registerProjectHandlers( IPC_CHANNELS.TAB_STATE_GET, async (): Promise> => { const tabState = projectStore.getTabState(); - console.log('[IPC] TAB_STATE_GET returning:', tabState); return { success: true, data: tabState }; } ); @@ -321,7 +322,6 @@ export function registerProjectHandlers( _, tabState: { openProjectIds: string[]; activeProjectId: string | null; tabOrder: string[] } ): Promise => { - console.log('[IPC] TAB_STATE_SAVE called with:', tabState); projectStore.saveTabState(tabState); return { success: true }; } @@ -381,8 +381,8 @@ export function registerProjectHandlers( const result = initializeProject(project.path); if (result.success) { - // Update project's autoBuildPath - projectStore.updateAutoBuildPath(projectId, '.auto-claude'); + // Update project's aperant path + projectStore.updateAperantPath(projectId, '.aperant'); } return { success: result.success, data: result, error: result.error }; @@ -396,10 +396,10 @@ export function registerProjectHandlers( ); // PROJECT_CHECK_VERSION now just checks if project is initialized - // Version tracking for .auto-claude is removed since it only contains data + // Version tracking for .aperant is removed since it only contains data ipcMain.handle( IPC_CHANNELS.PROJECT_CHECK_VERSION, - async (_, projectId: string): Promise> => { + async (_, projectId: string): Promise> => { try { const project = projectStore.getProject(projectId); if (!project) { @@ -410,7 +410,7 @@ export function registerProjectHandlers( success: true, data: { isInitialized: isInitialized(project.path), - updateAvailable: false // No updates for .auto-claude - it's just data + updateAvailable: false // No updates for .aperant - it's just data } }; } catch (error) { @@ -422,7 +422,7 @@ export function registerProjectHandlers( } ); - // Check if project has local auto-claude source (is dev project) + // Check if project has local aperant source (is dev project) ipcMain.handle( 'project:has-local-source', async (_, projectId: string): Promise> => { @@ -441,6 +441,45 @@ export function registerProjectHandlers( } ); + // ============================================ + // Migration Operations + // ============================================ + + // Check if project needs migration from .auto-claude to .aperant + ipcMain.handle( + IPC_CHANNELS.PROJECT_NEEDS_MIGRATION, + async (_, projectId: string): Promise => { + try { + const project = projectStore.getProject(projectId); + if (!project) return false; + return needsMigration(project.path); + } catch { + return false; + } + } + ); + + // Migrate project from .auto-claude to .aperant + ipcMain.handle( + IPC_CHANNELS.PROJECT_MIGRATE, + async (_, projectId: string): Promise> => { + try { + const project = projectStore.getProject(projectId); + if (!project) return { success: false, error: 'Project not found' }; + const result = migrateProject(project.path); + if (result.success) { + projectStore.updateAperantPath(projectId, '.aperant'); + } + return { success: result.success, data: result, error: result.error }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } + } + ); + // ============================================ // Git Operations // ============================================ diff --git a/apps/desktop/src/main/ipc-handlers/roadmap-handlers.ts b/apps/desktop/src/main/ipc-handlers/roadmap-handlers.ts index 3c17026a3c..0c86ae8cfd 100644 --- a/apps/desktop/src/main/ipc-handlers/roadmap-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/roadmap-handlers.ts @@ -2,7 +2,7 @@ import { ipcMain } from "electron"; import type { BrowserWindow } from "electron"; import { IPC_CHANNELS, - AUTO_BUILD_PATHS, + APERANT_PATHS, getSpecsDir, } from "../../shared/constants"; import type { @@ -54,8 +54,8 @@ export function registerRoadmapHandlers( const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); if (!existsSync(roadmapPath)) { @@ -69,8 +69,8 @@ export function registerRoadmapHandlers( // Load competitor analysis if available (competitor_analysis.json) const competitorAnalysisPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.COMPETITOR_ANALYSIS + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.COMPETITOR_ANALYSIS ); let competitorAnalysis: CompetitorAnalysis | undefined; if (existsSync(competitorAnalysisPath)) { @@ -351,8 +351,8 @@ export function registerRoadmapHandlers( const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); try { @@ -420,8 +420,8 @@ export function registerRoadmapHandlers( const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); try { @@ -474,8 +474,8 @@ export function registerRoadmapHandlers( const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); try { @@ -513,7 +513,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join("\n" `; // Generate proper spec directory (like task creation) - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Ensure specs directory exists @@ -561,7 +561,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join("\n" phases: [], }; await writeFileWithRetry( - path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), + path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2), { encoding: 'utf-8' } ); @@ -572,13 +572,13 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join("\n" workflow_type: "feature", }; await writeFileWithRetry( - path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS), + path.join(specDir, APERANT_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2), { encoding: 'utf-8' } ); // Create spec.md (required by backend spec creation process) - await writeFileWithRetry(path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE), taskDescription, { encoding: 'utf-8' }); + await writeFileWithRetry(path.join(specDir, APERANT_PATHS.SPEC_FILE), taskDescription, { encoding: 'utf-8' }); // Build metadata const metadata: TaskMetadata = { @@ -644,8 +644,8 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join("\n" return { success: false, error: "Project not found" }; } - const roadmapDir = path.join(project.path, AUTO_BUILD_PATHS.ROADMAP_DIR); - const progressPath = path.join(roadmapDir, AUTO_BUILD_PATHS.GENERATION_PROGRESS); + const roadmapDir = path.join(project.path, APERANT_PATHS.ROADMAP_DIR); + const progressPath = path.join(roadmapDir, APERANT_PATHS.GENERATION_PROGRESS); try { // Ensure roadmap directory exists @@ -693,8 +693,8 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join("\n" const progressPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.GENERATION_PROGRESS + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.GENERATION_PROGRESS ); if (!existsSync(progressPath)) { @@ -746,8 +746,8 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join("\n" const progressPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.GENERATION_PROGRESS + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.GENERATION_PROGRESS ); try { @@ -782,10 +782,10 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join("\n" return { success: false, error: "Project not found" }; } - const roadmapDir = path.join(project.path, AUTO_BUILD_PATHS.ROADMAP_DIR); + const roadmapDir = path.join(project.path, APERANT_PATHS.ROADMAP_DIR); const competitorAnalysisPath = path.join( roadmapDir, - AUTO_BUILD_PATHS.COMPETITOR_ANALYSIS + APERANT_PATHS.COMPETITOR_ANALYSIS ); try { @@ -863,7 +863,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join("\n" if (manualCompetitors.length > 0) { const manualCompetitorsPath = path.join( roadmapDir, - AUTO_BUILD_PATHS.MANUAL_COMPETITORS + APERANT_PATHS.MANUAL_COMPETITORS ); const manualSerialized = { competitors: manualCompetitors.map((c) => ({ diff --git a/apps/desktop/src/main/ipc-handlers/sections/context-roadmap-section.txt b/apps/desktop/src/main/ipc-handlers/sections/context-roadmap-section.txt index f919768bab..1e95c57b95 100644 --- a/apps/desktop/src/main/ipc-handlers/sections/context-roadmap-section.txt +++ b/apps/desktop/src/main/ipc-handlers/sections/context-roadmap-section.txt @@ -12,8 +12,8 @@ const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); if (!existsSync(roadmapPath)) { @@ -160,8 +160,8 @@ const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); if (!existsSync(roadmapPath)) { @@ -208,8 +208,8 @@ const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); if (!existsSync(roadmapPath)) { @@ -242,7 +242,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' `; // Generate proper spec directory (like task creation) - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Ensure specs directory exists @@ -289,14 +289,14 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' status: 'pending', phases: [] }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); // Create requirements.json const requirements = { task_description: taskDescription, workflow_type: 'feature' }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); // Build metadata const metadata: TaskMetadata = { @@ -381,7 +381,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' try { // Load project index let projectIndex: ProjectIndex | null = null; - const indexPath = path.join(project.path, AUTO_BUILD_PATHS.PROJECT_INDEX); + const indexPath = path.join(project.path, APERANT_PATHS.PROJECT_INDEX); if (existsSync(indexPath)) { const content = readFileSync(indexPath, 'utf-8'); projectIndex = JSON.parse(content); @@ -396,7 +396,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' }; // Check for graphiti state in specs - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (existsSync(specsDir)) { const specDirs = readdirSync(specsDir) @@ -408,7 +408,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' .reverse(); for (const specDir of specDirs) { - const statePath = path.join(specsDir, specDir, AUTO_BUILD_PATHS.GRAPHITI_STATE); + const statePath = path.join(specsDir, specDir, APERANT_PATHS.GRAPHITI_STATE); if (existsSync(statePath)) { const stateContent = readFileSync(statePath, 'utf-8'); memoryState = JSON.parse(stateContent); @@ -432,8 +432,8 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' if (!memoryState) { // Load project .env file and global settings to check for Graphiti config let projectEnvVars: Record = {}; - if (project.autoBuildPath) { - const projectEnvPath = path.join(project.path, project.autoBuildPath, '.env'); + if (project.aperantPath) { + const projectEnvPath = path.join(project.path, project.aperantPath, '.env'); if (existsSync(projectEnvPath)) { try { const envContent = readFileSync(projectEnvPath, 'utf-8'); @@ -607,17 +607,17 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' try { // Run the analyzer script to regenerate project_index.json - const autoBuildSource = getAutoBuildSourcePath(); + const aperantSource = getAperantSourcePath(); - if (!autoBuildSource) { + if (!aperantSource) { return { success: false, error: 'Auto-build source path not configured' }; } - const analyzerPath = path.join(autoBuildSource, 'analyzer.py'); - const indexOutputPath = path.join(project.path, AUTO_BUILD_PATHS.PROJECT_INDEX); + const analyzerPath = path.join(aperantSource, 'analyzer.py'); + const indexOutputPath = path.join(project.path, APERANT_PATHS.PROJECT_INDEX); // Run analyzer await new Promise((resolve, reject) => { @@ -668,8 +668,8 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' // Load project .env file to check for Graphiti config let projectEnvVars: Record = {}; - if (project.autoBuildPath) { - const projectEnvPath = path.join(project.path, project.autoBuildPath, '.env'); + if (project.aperantPath) { + const projectEnvPath = path.join(project.path, project.aperantPath, '.env'); if (existsSync(projectEnvPath)) { try { const envContent = readFileSync(projectEnvPath, 'utf-8'); @@ -771,7 +771,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' const queryLower = query.toLowerCase(); // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (existsSync(specsDir)) { const allSpecDirs = readdirSync(specsDir) @@ -822,7 +822,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' const memories: MemoryEpisode[] = []; // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (existsSync(specsDir)) { diff --git a/apps/desktop/src/main/ipc-handlers/sections/context_extracted.txt b/apps/desktop/src/main/ipc-handlers/sections/context_extracted.txt index a7e747afb0..da2c6e4fa9 100644 --- a/apps/desktop/src/main/ipc-handlers/sections/context_extracted.txt +++ b/apps/desktop/src/main/ipc-handlers/sections/context_extracted.txt @@ -13,7 +13,7 @@ try { // Load project index let projectIndex: ProjectIndex | null = null; - const indexPath = path.join(project.path, AUTO_BUILD_PATHS.PROJECT_INDEX); + const indexPath = path.join(project.path, APERANT_PATHS.PROJECT_INDEX); if (existsSync(indexPath)) { const content = readFileSync(indexPath, 'utf-8'); projectIndex = JSON.parse(content); @@ -28,7 +28,7 @@ }; // Check for graphiti state in specs - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (existsSync(specsDir)) { const specDirs = readdirSync(specsDir) @@ -40,7 +40,7 @@ .reverse(); for (const specDir of specDirs) { - const statePath = path.join(specsDir, specDir, AUTO_BUILD_PATHS.GRAPHITI_STATE); + const statePath = path.join(specsDir, specDir, APERANT_PATHS.GRAPHITI_STATE); if (existsSync(statePath)) { const stateContent = readFileSync(statePath, 'utf-8'); memoryState = JSON.parse(stateContent); @@ -64,8 +64,8 @@ if (!memoryState) { // Load project .env file and global settings to check for Graphiti config let projectEnvVars: Record = {}; - if (project.autoBuildPath) { - const projectEnvPath = path.join(project.path, project.autoBuildPath, '.env'); + if (project.aperantPath) { + const projectEnvPath = path.join(project.path, project.aperantPath, '.env'); if (existsSync(projectEnvPath)) { try { const envContent = readFileSync(projectEnvPath, 'utf-8'); @@ -239,17 +239,17 @@ try { // Run the analyzer script to regenerate project_index.json - const autoBuildSource = getAutoBuildSourcePath(); + const aperantSource = getAperantSourcePath(); - if (!autoBuildSource) { + if (!aperantSource) { return { success: false, error: 'Auto-build source path not configured' }; } - const analyzerPath = path.join(autoBuildSource, 'analyzer.py'); - const indexOutputPath = path.join(project.path, AUTO_BUILD_PATHS.PROJECT_INDEX); + const analyzerPath = path.join(aperantSource, 'analyzer.py'); + const indexOutputPath = path.join(project.path, APERANT_PATHS.PROJECT_INDEX); // Run analyzer await new Promise((resolve, reject) => { @@ -300,8 +300,8 @@ // Load project .env file to check for Graphiti config let projectEnvVars: Record = {}; - if (project.autoBuildPath) { - const projectEnvPath = path.join(project.path, project.autoBuildPath, '.env'); + if (project.aperantPath) { + const projectEnvPath = path.join(project.path, project.aperantPath, '.env'); if (existsSync(projectEnvPath)) { try { const envContent = readFileSync(projectEnvPath, 'utf-8'); @@ -403,7 +403,7 @@ const queryLower = query.toLowerCase(); // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (existsSync(specsDir)) { const allSpecDirs = readdirSync(specsDir) @@ -454,7 +454,7 @@ const memories: MemoryEpisode[] = []; // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (existsSync(specsDir)) { diff --git a/apps/desktop/src/main/ipc-handlers/sections/ideation-insights-section.txt b/apps/desktop/src/main/ipc-handlers/sections/ideation-insights-section.txt index f4b0bfbe40..01e30f9cf1 100644 --- a/apps/desktop/src/main/ipc-handlers/sections/ideation-insights-section.txt +++ b/apps/desktop/src/main/ipc-handlers/sections/ideation-insights-section.txt @@ -100,8 +100,8 @@ const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); if (!existsSync(ideationPath)) { @@ -235,8 +235,8 @@ const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); if (!existsSync(ideationPath)) { @@ -284,8 +284,8 @@ const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); if (!existsSync(ideationPath)) { @@ -327,8 +327,8 @@ const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); if (!existsSync(ideationPath)) { @@ -370,8 +370,8 @@ const ideationPath = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, - AUTO_BUILD_PATHS.IDEATION_FILE + APERANT_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_FILE ); if (!existsSync(ideationPath)) { @@ -390,7 +390,7 @@ // Generate spec ID by finding next available number // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Ensure specs directory exists @@ -472,7 +472,7 @@ spec_file: 'spec.md' }; writeFileSync( - path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), + path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(initialPlan, null, 2) ); @@ -490,7 +490,7 @@ ${idea.rationale} --- *This spec was created from ideation and is pending detailed specification.* `; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE), specContent); + writeFileSync(path.join(specDir, APERANT_PATHS.SPEC_FILE), specContent); // Update idea with converted status idea.status = 'converted'; @@ -631,7 +631,7 @@ ${idea.rationale} if (project) { const typeFile = path.join( project.path, - AUTO_BUILD_PATHS.IDEATION_DIR, + APERANT_PATHS.IDEATION_DIR, `${ideationType}_ideas.json` ); if (existsSync(typeFile)) { @@ -679,7 +679,7 @@ ${idea.rationale} const tasks = rendererTasks || projectStore.getTasks(projectId); // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const doneTasks = changelogService.getCompletedTasks(project.path, tasks, specsBaseDir); return { success: true, data: doneTasks }; @@ -697,7 +697,7 @@ ${idea.rationale} const tasks = projectStore.getTasks(projectId); // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specs = await changelogService.loadTaskSpecs(project.path, taskIds, tasks, specsBaseDir); return { success: true, data: specs }; @@ -724,7 +724,7 @@ ${idea.rationale} let specs: import('../shared/types').TaskSpecContent[] = []; if (request.sourceMode === 'tasks' && request.taskIds && request.taskIds.length > 0) { const tasks = projectStore.getTasks(request.projectId); - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); specs = await changelogService.loadTaskSpecs(project.path, request.taskIds, tasks, specsBaseDir); } @@ -781,7 +781,7 @@ ${idea.rationale} // Load specs for selected tasks to analyze change types const tasks = projectStore.getTasks(projectId); - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specs = await changelogService.loadTaskSpecs(project.path, taskIds, tasks, specsBaseDir); // Analyze specs and suggest version @@ -957,11 +957,11 @@ ${idea.rationale} // Ensure Python environment is ready before sending message if (!pythonEnvManager.isEnvReady()) { - const autoBuildSource = getAutoBuildSourcePath(); - if (autoBuildSource) { - const status = await pythonEnvManager.initialize(autoBuildSource); + const aperantSource = getAperantSourcePath(); + if (aperantSource) { + const status = await pythonEnvManager.initialize(aperantSource); if (status.ready && status.pythonPath) { - configureServicesWithPython(status.pythonPath, autoBuildSource); + configureServicesWithPython(status.pythonPath, aperantSource); } else { const mainWindow = getMainWindow(); if (mainWindow) { @@ -1007,14 +1007,14 @@ ${idea.rationale} return { success: false, error: 'Project not found' }; } - if (!project.autoBuildPath) { - return { success: false, error: 'Auto Claude not initialized for this project' }; + if (!project.aperantPath) { + return { success: false, error: 'Aperant not initialized for this project' }; } try { // Generate a unique spec ID based on existing specs // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Find next available spec number @@ -1065,7 +1065,7 @@ ${idea.rationale} phases: [] }; - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); writeFileSync(planPath, JSON.stringify(implementationPlan, null, 2)); // Save task metadata diff --git a/apps/desktop/src/main/ipc-handlers/sections/integration-section.txt b/apps/desktop/src/main/ipc-handlers/sections/integration-section.txt index 5b2378bada..450f49a40b 100644 --- a/apps/desktop/src/main/ipc-handlers/sections/integration-section.txt +++ b/apps/desktop/src/main/ipc-handlers/sections/integration-section.txt @@ -43,8 +43,8 @@ if (config.claudeOAuthToken !== undefined) { existingVars['CLAUDE_CODE_OAUTH_TOKEN'] = config.claudeOAuthToken; } - if (config.autoBuildModel !== undefined) { - existingVars['AUTO_BUILD_MODEL'] = config.autoBuildModel; + if (config.aperantModel !== undefined) { + existingVars['APERANT_MODEL'] = config.aperantModel; } if (config.linearApiKey !== undefined) { existingVars['LINEAR_API_KEY'] = config.linearApiKey; @@ -91,14 +91,14 @@ } // Generate content with sections - let content = `# Auto Claude Framework Environment Variables -# Managed by Auto Claude UI + let content = `# Aperant Framework Environment Variables +# Managed by Aperant UI # Claude Code OAuth Token (REQUIRED) CLAUDE_CODE_OAUTH_TOKEN=${existingVars['CLAUDE_CODE_OAUTH_TOKEN'] || ''} # Model override (OPTIONAL) -${existingVars['AUTO_BUILD_MODEL'] ? `AUTO_BUILD_MODEL=${existingVars['AUTO_BUILD_MODEL']}` : '# AUTO_BUILD_MODEL=claude-opus-4-6'} +${existingVars['APERANT_MODEL'] ? `APERANT_MODEL=${existingVars['APERANT_MODEL']}` : '# APERANT_MODEL=claude-opus-4-6'} # ============================================================================= # LINEAR INTEGRATION (OPTIONAL) @@ -142,11 +142,11 @@ ${existingVars['GRAPHITI_DATABASE'] ? `GRAPHITI_DATABASE=${existingVars['GRAPHIT return { success: false, error: 'Project not found' }; } - if (!project.autoBuildPath) { + if (!project.aperantPath) { return { success: false, error: 'Project not initialized' }; } - const envPath = path.join(project.path, project.autoBuildPath, '.env'); + const envPath = path.join(project.path, project.aperantPath, '.env'); // Load global settings for fallbacks let globalSettings: AppSettings = { ...DEFAULT_APP_SETTINGS }; @@ -192,8 +192,8 @@ ${existingVars['GRAPHITI_DATABASE'] ? `GRAPHITI_DATABASE=${existingVars['GRAPHIT config.claudeTokenIsGlobal = true; } - if (vars['AUTO_BUILD_MODEL']) { - config.autoBuildModel = vars['AUTO_BUILD_MODEL']; + if (vars['APERANT_MODEL']) { + config.aperantModel = vars['APERANT_MODEL']; } if (vars['LINEAR_API_KEY']) { @@ -264,11 +264,11 @@ ${existingVars['GRAPHITI_DATABASE'] ? `GRAPHITI_DATABASE=${existingVars['GRAPHIT return { success: false, error: 'Project not found' }; } - if (!project.autoBuildPath) { + if (!project.aperantPath) { return { success: false, error: 'Project not initialized' }; } - const envPath = path.join(project.path, project.autoBuildPath, '.env'); + const envPath = path.join(project.path, project.aperantPath, '.env'); try { // Read existing content if file exists @@ -435,8 +435,8 @@ ${existingVars['GRAPHITI_DATABASE'] ? `GRAPHITI_DATABASE=${existingVars['GRAPHIT * Helper to get Linear API key from project env */ const getLinearApiKey = (project: Project): string | null => { - if (!project.autoBuildPath) return null; - const envPath = path.join(project.path, project.autoBuildPath, '.env'); + if (!project.aperantPath) return null; + const envPath = path.join(project.path, project.aperantPath, '.env'); if (!existsSync(envPath)) return null; try { @@ -824,7 +824,7 @@ ${existingVars['GRAPHITI_DATABASE'] ? `GRAPHITI_DATABASE=${existingVars['GRAPHIT const errors: string[] = []; // Set up specs directory - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (!existsSync(specsDir)) { mkdirSync(specsDir, { recursive: true }); @@ -884,14 +884,14 @@ ${issue.description || 'No description provided.'} status: 'pending', phases: [] }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); // Create requirements.json const requirements = { task_description: description, workflow_type: 'feature' }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); // Build metadata const metadata: TaskMetadata = { @@ -939,8 +939,8 @@ ${issue.description || 'No description provided.'} * Helper to get GitHub config from project env */ const getGitHubConfig = (project: Project): { token: string; repo: string } | null => { - if (!project.autoBuildPath) return null; - const envPath = path.join(project.path, project.autoBuildPath, '.env'); + if (!project.aperantPath) return null; + const envPath = path.join(project.path, project.aperantPath, '.env'); if (!existsSync(envPath)) return null; try { @@ -1333,7 +1333,7 @@ Please analyze this issue and provide: 5. Acceptance criteria for resolving this issue`; // Create proper spec directory - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (!existsSync(specsDir)) { mkdirSync(specsDir, { recursive: true }); @@ -1376,14 +1376,14 @@ Please analyze this issue and provide: status: 'pending', phases: [] }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); // Create requirements.json const requirements = { task_description: taskDescription, workflow_type: 'feature' }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); // Build metadata const metadata: TaskMetadata = { @@ -1473,7 +1473,7 @@ Please analyze this issue and provide: const tasks: Task[] = []; // Set up specs directory - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); if (!existsSync(specsDir)) { mkdirSync(specsDir, { recursive: true }); @@ -1540,14 +1540,14 @@ ${issue.body || 'No description provided.'} status: 'pending', phases: [] }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); // Create requirements.json const requirements = { task_description: description, workflow_type: 'feature' }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); // Build metadata const metadata: TaskMetadata = { diff --git a/apps/desktop/src/main/ipc-handlers/sections/roadmap_extracted.txt b/apps/desktop/src/main/ipc-handlers/sections/roadmap_extracted.txt index ba527ce82b..0459ed2ab6 100644 --- a/apps/desktop/src/main/ipc-handlers/sections/roadmap_extracted.txt +++ b/apps/desktop/src/main/ipc-handlers/sections/roadmap_extracted.txt @@ -12,8 +12,8 @@ const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); if (!existsSync(roadmapPath)) { @@ -160,8 +160,8 @@ const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); if (!existsSync(roadmapPath)) { @@ -208,8 +208,8 @@ const roadmapPath = path.join( project.path, - AUTO_BUILD_PATHS.ROADMAP_DIR, - AUTO_BUILD_PATHS.ROADMAP_FILE + APERANT_PATHS.ROADMAP_DIR, + APERANT_PATHS.ROADMAP_FILE ); if (!existsSync(roadmapPath)) { @@ -242,7 +242,7 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' `; // Generate proper spec directory (like task creation) - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Ensure specs directory exists @@ -289,14 +289,14 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' status: 'pending', phases: [] }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), JSON.stringify(implementationPlan, null, 2)); // Create requirements.json const requirements = { task_description: taskDescription, workflow_type: 'feature' }; - writeFileSync(path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); + writeFileSync(path.join(specDir, APERANT_PATHS.REQUIREMENTS), JSON.stringify(requirements, null, 2)); // Build metadata const metadata: TaskMetadata = { diff --git a/apps/desktop/src/main/ipc-handlers/sections/task-section.txt b/apps/desktop/src/main/ipc-handlers/sections/task-section.txt index d077902de8..ba8fdf32c5 100644 --- a/apps/desktop/src/main/ipc-handlers/sections/task-section.txt +++ b/apps/desktop/src/main/ipc-handlers/sections/task-section.txt @@ -51,7 +51,7 @@ // Generate a unique spec ID based on existing specs // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Find next available spec number @@ -137,7 +137,7 @@ phases: [] }; - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); writeFileSync(planPath, JSON.stringify(implementationPlan, null, 2)); // Save task metadata if provided @@ -161,7 +161,7 @@ })); } - const requirementsPath = path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS); + const requirementsPath = path.join(specDir, APERANT_PATHS.REQUIREMENTS); writeFileSync(requirementsPath, JSON.stringify(requirements, null, 2)); // Create the task object @@ -213,7 +213,7 @@ } // Delete the spec directory - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join(project.path, specsBaseDir, task.specId); try { @@ -258,8 +258,8 @@ return { success: false, error: 'Task not found' }; } - const autoBuildDir = project.autoBuildPath || '.auto-claude'; - const specDir = path.join(project.path, autoBuildDir, 'specs', task.specId); + const aperantDir = project.aperantPath || '.aperant'; + const specDir = path.join(project.path, aperantDir, 'specs', task.specId); if (!existsSync(specDir)) { return { success: false, error: 'Spec directory not found' }; @@ -291,7 +291,7 @@ } // Update implementation_plan.json - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); if (existsSync(planPath)) { try { const planContent = readFileSync(planPath, 'utf-8'); @@ -312,7 +312,7 @@ } // Update spec.md if it exists - const specPath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specPath = path.join(specDir, APERANT_PATHS.SPEC_FILE); if (existsSync(specPath)) { try { let specContent = readFileSync(specPath, 'utf-8'); @@ -395,7 +395,7 @@ console.log('[TASK_START] Found task:', task.specId, 'status:', task.status, 'subtasks:', task.subtasks.length); // Start file watcher for this task - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join( project.path, specsBaseDir, @@ -404,7 +404,7 @@ fileWatcher.watch(taskId, specDir); // Check if spec.md exists (indicates spec creation was already done or in progress) - const specFilePath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specFilePath = path.join(specDir, APERANT_PATHS.SPEC_FILE); const hasSpec = existsSync(specFilePath); // Check if this task needs spec creation first (no spec file = not yet created) @@ -523,7 +523,7 @@ } // Check if dev mode is enabled for this project - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join( project.path, specsBaseDir, @@ -532,7 +532,7 @@ if (approved) { // Write approval to QA report - const qaReportPath = path.join(specDir, AUTO_BUILD_PATHS.QA_REPORT); + const qaReportPath = path.join(specDir, APERANT_PATHS.QA_REPORT); writeFileSync( qaReportPath, `# QA Review\n\nStatus: APPROVED\n\nReviewed at: ${new Date().toISOString()}\n` @@ -597,7 +597,7 @@ } // Get the spec directory - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join( project.path, specsBaseDir, @@ -605,7 +605,7 @@ ); // Update implementation_plan.json if it exists - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); try { if (existsSync(planPath)) { @@ -656,7 +656,7 @@ fileWatcher.watch(taskId, specDir); // Check if spec.md exists - const specFilePath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specFilePath = path.join(specDir, APERANT_PATHS.SPEC_FILE); const hasSpec = existsSync(specFilePath); const needsSpecCreation = !hasSpec; const needsImplementation = hasSpec && task.subtasks.length === 0; @@ -775,16 +775,16 @@ } // Get the spec directory - const autoBuildDir = project.autoBuildPath || '.auto-claude'; + const aperantDir = project.aperantPath || '.aperant'; const specDir = path.join( project.path, - autoBuildDir, + aperantDir, 'specs', task.specId ); // Update implementation_plan.json - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); try { // Read the plan to analyze subtask progress @@ -894,7 +894,7 @@ const workers = useParallel ? project.settings.maxWorkers : 1; // Start file watcher for this task - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDirForWatcher = path.join(project.path, specsBaseDir, task.specId); fileWatcher.watch(taskId, specDirForWatcher); @@ -1184,14 +1184,14 @@ try { // Ensure Python environment is ready if (!pythonEnvManager.isEnvReady()) { - const autoBuildSource = getEffectiveSourcePath(); - if (autoBuildSource) { - const status = await pythonEnvManager.initialize(autoBuildSource); + const aperantSource = getEffectiveSourcePath(); + if (aperantSource) { + const status = await pythonEnvManager.initialize(aperantSource); if (!status.ready) { return { success: false, error: `Python environment not ready: ${status.error || 'Unknown error'}` }; } } else { - return { success: false, error: 'Python environment not ready and Auto Claude source not found' }; + return { success: false, error: 'Python environment not ready and Aperant source not found' }; } } @@ -1203,11 +1203,11 @@ // Use run.py --merge to handle the merge const sourcePath = getEffectiveSourcePath(); if (!sourcePath) { - return { success: false, error: 'Auto Claude source not found' }; + return { success: false, error: 'Aperant source not found' }; } const runScript = path.join(sourcePath, 'run.py'); - const specDir = path.join(project.path, project.autoBuildPath || '.auto-claude', 'specs', task.specId); + const specDir = path.join(project.path, project.aperantPath || '.aperant', 'specs', task.specId); if (!existsSync(specDir)) { return { success: false, error: 'Spec directory not found' }; @@ -1249,7 +1249,7 @@ mergeProcess.on('close', (code: number) => { if (code === 0) { // Persist the status change to implementation_plan.json - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); try { if (existsSync(planPath)) { const planContent = readFileSync(planPath, 'utf-8'); diff --git a/apps/desktop/src/main/ipc-handlers/sections/task_extracted.txt b/apps/desktop/src/main/ipc-handlers/sections/task_extracted.txt index d077902de8..ba8fdf32c5 100644 --- a/apps/desktop/src/main/ipc-handlers/sections/task_extracted.txt +++ b/apps/desktop/src/main/ipc-handlers/sections/task_extracted.txt @@ -51,7 +51,7 @@ // Generate a unique spec ID based on existing specs // Get specs directory path - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Find next available spec number @@ -137,7 +137,7 @@ phases: [] }; - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); writeFileSync(planPath, JSON.stringify(implementationPlan, null, 2)); // Save task metadata if provided @@ -161,7 +161,7 @@ })); } - const requirementsPath = path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS); + const requirementsPath = path.join(specDir, APERANT_PATHS.REQUIREMENTS); writeFileSync(requirementsPath, JSON.stringify(requirements, null, 2)); // Create the task object @@ -213,7 +213,7 @@ } // Delete the spec directory - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join(project.path, specsBaseDir, task.specId); try { @@ -258,8 +258,8 @@ return { success: false, error: 'Task not found' }; } - const autoBuildDir = project.autoBuildPath || '.auto-claude'; - const specDir = path.join(project.path, autoBuildDir, 'specs', task.specId); + const aperantDir = project.aperantPath || '.aperant'; + const specDir = path.join(project.path, aperantDir, 'specs', task.specId); if (!existsSync(specDir)) { return { success: false, error: 'Spec directory not found' }; @@ -291,7 +291,7 @@ } // Update implementation_plan.json - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); if (existsSync(planPath)) { try { const planContent = readFileSync(planPath, 'utf-8'); @@ -312,7 +312,7 @@ } // Update spec.md if it exists - const specPath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specPath = path.join(specDir, APERANT_PATHS.SPEC_FILE); if (existsSync(specPath)) { try { let specContent = readFileSync(specPath, 'utf-8'); @@ -395,7 +395,7 @@ console.log('[TASK_START] Found task:', task.specId, 'status:', task.status, 'subtasks:', task.subtasks.length); // Start file watcher for this task - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join( project.path, specsBaseDir, @@ -404,7 +404,7 @@ fileWatcher.watch(taskId, specDir); // Check if spec.md exists (indicates spec creation was already done or in progress) - const specFilePath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specFilePath = path.join(specDir, APERANT_PATHS.SPEC_FILE); const hasSpec = existsSync(specFilePath); // Check if this task needs spec creation first (no spec file = not yet created) @@ -523,7 +523,7 @@ } // Check if dev mode is enabled for this project - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join( project.path, specsBaseDir, @@ -532,7 +532,7 @@ if (approved) { // Write approval to QA report - const qaReportPath = path.join(specDir, AUTO_BUILD_PATHS.QA_REPORT); + const qaReportPath = path.join(specDir, APERANT_PATHS.QA_REPORT); writeFileSync( qaReportPath, `# QA Review\n\nStatus: APPROVED\n\nReviewed at: ${new Date().toISOString()}\n` @@ -597,7 +597,7 @@ } // Get the spec directory - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join( project.path, specsBaseDir, @@ -605,7 +605,7 @@ ); // Update implementation_plan.json if it exists - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); try { if (existsSync(planPath)) { @@ -656,7 +656,7 @@ fileWatcher.watch(taskId, specDir); // Check if spec.md exists - const specFilePath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specFilePath = path.join(specDir, APERANT_PATHS.SPEC_FILE); const hasSpec = existsSync(specFilePath); const needsSpecCreation = !hasSpec; const needsImplementation = hasSpec && task.subtasks.length === 0; @@ -775,16 +775,16 @@ } // Get the spec directory - const autoBuildDir = project.autoBuildPath || '.auto-claude'; + const aperantDir = project.aperantPath || '.aperant'; const specDir = path.join( project.path, - autoBuildDir, + aperantDir, 'specs', task.specId ); // Update implementation_plan.json - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); try { // Read the plan to analyze subtask progress @@ -894,7 +894,7 @@ const workers = useParallel ? project.settings.maxWorkers : 1; // Start file watcher for this task - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDirForWatcher = path.join(project.path, specsBaseDir, task.specId); fileWatcher.watch(taskId, specDirForWatcher); @@ -1184,14 +1184,14 @@ try { // Ensure Python environment is ready if (!pythonEnvManager.isEnvReady()) { - const autoBuildSource = getEffectiveSourcePath(); - if (autoBuildSource) { - const status = await pythonEnvManager.initialize(autoBuildSource); + const aperantSource = getEffectiveSourcePath(); + if (aperantSource) { + const status = await pythonEnvManager.initialize(aperantSource); if (!status.ready) { return { success: false, error: `Python environment not ready: ${status.error || 'Unknown error'}` }; } } else { - return { success: false, error: 'Python environment not ready and Auto Claude source not found' }; + return { success: false, error: 'Python environment not ready and Aperant source not found' }; } } @@ -1203,11 +1203,11 @@ // Use run.py --merge to handle the merge const sourcePath = getEffectiveSourcePath(); if (!sourcePath) { - return { success: false, error: 'Auto Claude source not found' }; + return { success: false, error: 'Aperant source not found' }; } const runScript = path.join(sourcePath, 'run.py'); - const specDir = path.join(project.path, project.autoBuildPath || '.auto-claude', 'specs', task.specId); + const specDir = path.join(project.path, project.aperantPath || '.aperant', 'specs', task.specId); if (!existsSync(specDir)) { return { success: false, error: 'Spec directory not found' }; @@ -1249,7 +1249,7 @@ mergeProcess.on('close', (code: number) => { if (code === 0) { // Persist the status change to implementation_plan.json - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); try { if (existsSync(planPath)) { const planContent = readFileSync(planPath, 'utf-8'); diff --git a/apps/desktop/src/main/ipc-handlers/settings-handlers.ts b/apps/desktop/src/main/ipc-handlers/settings-handlers.ts index d5a57fb09b..c5aeff2680 100644 --- a/apps/desktop/src/main/ipc-handlers/settings-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/settings-handlers.ts @@ -204,11 +204,11 @@ async function migrateToProviderAccounts(settings: AppSettings): Promise<{ chang } /** - * Auto-detect the auto-claude prompts path relative to the app location. + * Auto-detect the aperant prompts path relative to the app location. * Works across platforms (macOS, Windows, Linux) in both dev and production modes. * Prompts live in apps/desktop/prompts/ (dev) or extraResources/prompts (prod). */ -const detectAutoBuildSourcePath = (): string | null => { +const detectAperantSourcePath = (): string | null => { const possiblePaths: string[] = []; // Development mode paths @@ -241,12 +241,12 @@ const detectAutoBuildSourcePath = (): string | null => { const debug = process.env.DEBUG === '1' || process.env.DEBUG === 'true'; if (debug) { - console.warn('[detectAutoBuildSourcePath] Platform:', process.platform); - console.warn('[detectAutoBuildSourcePath] Is dev:', is.dev); - console.warn('[detectAutoBuildSourcePath] __dirname:', __dirname); - console.warn('[detectAutoBuildSourcePath] app.getAppPath():', app.getAppPath()); - console.warn('[detectAutoBuildSourcePath] process.cwd():', process.cwd()); - console.warn('[detectAutoBuildSourcePath] Checking paths:', possiblePaths); + console.warn('[detectAperantSourcePath] Platform:', process.platform); + console.warn('[detectAperantSourcePath] Is dev:', is.dev); + console.warn('[detectAperantSourcePath] __dirname:', __dirname); + console.warn('[detectAperantSourcePath] app.getAppPath():', app.getAppPath()); + console.warn('[detectAperantSourcePath] process.cwd():', process.cwd()); + console.warn('[detectAperantSourcePath] Checking paths:', possiblePaths); } for (const p of possiblePaths) { @@ -255,17 +255,17 @@ const detectAutoBuildSourcePath = (): string | null => { const exists = existsSync(p) && existsSync(markerPath); if (debug) { - console.warn(`[detectAutoBuildSourcePath] Checking ${p}: ${exists ? '✓ FOUND' : '✗ not found'}`); + console.warn(`[detectAperantSourcePath] Checking ${p}: ${exists ? '✓ FOUND' : '✗ not found'}`); } if (exists) { - console.warn(`[detectAutoBuildSourcePath] Auto-detected prompts path: ${p}`); + console.warn(`[detectAperantSourcePath] Auto-detected prompts path: ${p}`); return p; } } - console.warn('[detectAutoBuildSourcePath] Could not auto-detect Aperant prompts path. Please configure manually in settings.'); - console.warn('[detectAutoBuildSourcePath] Set DEBUG=1 environment variable for detailed path checking.'); + console.warn('[detectAperantSourcePath] Could not auto-detect Aperant prompts path. Please configure manually in settings.'); + console.warn('[detectAperantSourcePath] Set DEBUG=1 environment variable for detailed path checking.'); return null; }; @@ -377,7 +377,7 @@ export function registerSettingsHandlers( // Fixes issue where Windows paths persisted on macOS (and vice versa) // when settings were synced/transferred between platforms // See: https://github.com/AndyMik90/Auto-Claude/issues/XXX - const pathFields = ['pythonPath', 'gitPath', 'githubCLIPath', 'gitlabCLIPath', 'claudePath', 'autoBuildPath'] as const; + const pathFields = ['pythonPath', 'gitPath', 'githubCLIPath', 'gitlabCLIPath', 'claudePath', 'aperantPath'] as const; for (const field of pathFields) { const pathValue = settings[field]; if (pathValue && isPathFromWrongPlatform(pathValue)) { @@ -389,11 +389,11 @@ export function registerSettingsHandlers( } } - // If no manual autoBuildPath is set, try to auto-detect - if (!settings.autoBuildPath) { - const detectedPath = detectAutoBuildSourcePath(); + // If no manual aperantPath is set, try to auto-detect + if (!settings.aperantPath) { + const detectedPath = detectAperantSourcePath(); if (detectedPath) { - settings.autoBuildPath = detectedPath; + settings.aperantPath = detectedPath; } } @@ -450,8 +450,8 @@ export function registerSettingsHandlers( writeFileSync(settingsPath, JSON.stringify(newSettings, null, 2), 'utf-8'); // Apply Python path if changed - if (settings.pythonPath || settings.autoBuildPath) { - agentManager.configure(settings.pythonPath, settings.autoBuildPath); + if (settings.pythonPath || settings.aperantPath) { + agentManager.configure(settings.pythonPath, settings.aperantPath); } // Configure CLI tools if any paths changed diff --git a/apps/desktop/src/main/ipc-handlers/task/README.md b/apps/desktop/src/main/ipc-handlers/task/README.md index c3669374bf..6361dece2d 100644 --- a/apps/desktop/src/main/ipc-handlers/task/README.md +++ b/apps/desktop/src/main/ipc-handlers/task/README.md @@ -131,7 +131,7 @@ registerTaskHandlers(agentManager, pythonEnvManager, getMainWindow); - `../../task-log-service` - Log service - `../../title-generator` - AI title generation - `../../python-env-manager` - Python environment -- `../../auto-claude-updater` - Source paths +- `../../aperant-updater` - Source paths - `../../rate-limit-detector` - Profile environment ## Architecture Notes @@ -139,7 +139,7 @@ registerTaskHandlers(agentManager, pythonEnvManager, getMainWindow); ### Worktree Architecture Each task spec has its own isolated worktree at `.worktrees/{spec-name}/`: - Enables safe parallel development -- Each spec has dedicated branch: `auto-claude/{spec-name}` +- Each spec has dedicated branch: `aperant/{spec-name}` - Branches stay local until user explicitly pushes - User reviews in worktree before merging to main diff --git a/apps/desktop/src/main/ipc-handlers/task/__tests__/logs-integration.test.ts b/apps/desktop/src/main/ipc-handlers/task/__tests__/logs-integration.test.ts index b2298de5ab..9d4a56037f 100644 --- a/apps/desktop/src/main/ipc-handlers/task/__tests__/logs-integration.test.ts +++ b/apps/desktop/src/main/ipc-handlers/task/__tests__/logs-integration.test.ts @@ -101,7 +101,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProject = { id: 'project-123', path: '/absolute/path/to/project', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; const mockLogs: TaskLogs = { @@ -158,9 +158,9 @@ describe('Task Logs Integration (IPC → Service → State)', () => { expect(result.data).toEqual(mockLogs); expect(projectStore.getProject).toHaveBeenCalledWith('project-123'); expect(taskLogService.loadLogs).toHaveBeenCalledWith( - path.join('/absolute/path/to/project', '.auto-claude/specs', '001-test-task'), + path.join('/absolute/path/to/project', '.aperant/specs', '001-test-task'), '/absolute/path/to/project', - '.auto-claude/specs', + '.aperant/specs', '001-test-task' ); }); @@ -173,7 +173,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProject = { id: 'project-123', path: './relative/path', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; const mockLogs: TaskLogs = { @@ -228,7 +228,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProject = { id: 'project-123', path: '/absolute/path/to/project', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; (projectStore.getProject as Mock).mockReturnValue(mockProject); @@ -250,7 +250,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProject = { id: 'project-123', path: '/absolute/path/to/project', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; (projectStore.getProject as Mock).mockReturnValue(mockProject); @@ -274,7 +274,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProject = { id: 'project-123', path: '/absolute/path/to/project', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; (projectStore.getProject as Mock).mockReturnValue(mockProject); @@ -298,7 +298,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProject = { id: 'project-123', path: '/absolute/path/to/project', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; (projectStore.getProject as Mock).mockReturnValue(mockProject); @@ -310,9 +310,9 @@ describe('Task Logs Integration (IPC → Service → State)', () => { expect(result.success).toBe(true); expect(taskLogService.startWatching).toHaveBeenCalledWith( '001-test-task', - path.join('/absolute/path/to/project', '.auto-claude/specs', '001-test-task'), + path.join('/absolute/path/to/project', '.aperant/specs', '001-test-task'), '/absolute/path/to/project', - '.auto-claude/specs' + '.aperant/specs' ); }); @@ -343,7 +343,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProject = { id: 'project-123', path: '/absolute/path/to/project', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; (projectStore.getProject as Mock).mockReturnValue(mockProject); @@ -355,9 +355,9 @@ describe('Task Logs Integration (IPC → Service → State)', () => { expect(result.success).toBe(true); expect(taskLogService.startWatching).toHaveBeenCalledWith( 'nonexistent-spec', - path.join('/absolute/path/to/project', '.auto-claude/specs', 'nonexistent-spec'), + path.join('/absolute/path/to/project', '.aperant/specs', 'nonexistent-spec'), '/absolute/path/to/project', - '.auto-claude/specs' + '.aperant/specs' ); }); @@ -369,7 +369,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProject = { id: 'project-123', path: '/absolute/path/to/project', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; (projectStore.getProject as Mock).mockReturnValue(mockProject); @@ -422,7 +422,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProjectRelative = { id: 'project-123', path: './my-project', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; (projectStore.getProject as Mock).mockReturnValue(mockProjectRelative); @@ -463,7 +463,7 @@ describe('Task Logs Integration (IPC → Service → State)', () => { const mockProject = { id: 'project-123', path: '/absolute/path/to/project', - autoBuildPath: '.auto-claude' + aperantPath: '.aperant' }; (projectStore.getProject as Mock).mockReturnValue(mockProject); diff --git a/apps/desktop/src/main/ipc-handlers/task/__tests__/worktree-branch-validation.test.ts b/apps/desktop/src/main/ipc-handlers/task/__tests__/worktree-branch-validation.test.ts index a7d318bd12..bcf4330a99 100644 --- a/apps/desktop/src/main/ipc-handlers/task/__tests__/worktree-branch-validation.test.ts +++ b/apps/desktop/src/main/ipc-handlers/task/__tests__/worktree-branch-validation.test.ts @@ -12,10 +12,10 @@ import { describe, expect, it } from 'vitest'; import { GIT_BRANCH_REGEX, validateWorktreeBranch } from '../worktree-handlers'; describe('GIT_BRANCH_REGEX', () => { - it('should accept valid auto-claude branch names', () => { - expect(GIT_BRANCH_REGEX.test('auto-claude/my-feature')).toBe(true); - expect(GIT_BRANCH_REGEX.test('auto-claude/123-fix-bug')).toBe(true); - expect(GIT_BRANCH_REGEX.test('auto-claude/feature_with_underscore')).toBe(true); + it('should accept valid aperant branch names', () => { + expect(GIT_BRANCH_REGEX.test('aperant/my-feature')).toBe(true); + expect(GIT_BRANCH_REGEX.test('aperant/123-fix-bug')).toBe(true); + expect(GIT_BRANCH_REGEX.test('aperant/feature_with_underscore')).toBe(true); }); it('should accept valid feature branch names', () => { @@ -46,28 +46,28 @@ describe('GIT_BRANCH_REGEX', () => { }); describe('validateWorktreeBranch', () => { - const expectedBranch = 'auto-claude/my-feature-123'; + const expectedBranch = 'aperant/my-feature-123'; describe('exact match scenarios', () => { it('should use detected branch when it matches expected exactly', () => { - const result = validateWorktreeBranch('auto-claude/my-feature-123', expectedBranch); - expect(result.branchToDelete).toBe('auto-claude/my-feature-123'); + const result = validateWorktreeBranch('aperant/my-feature-123', expectedBranch); + expect(result.branchToDelete).toBe('aperant/my-feature-123'); expect(result.usedFallback).toBe(false); expect(result.reason).toBe('exact_match'); }); }); describe('pattern match scenarios', () => { - it('should allow other auto-claude branches (specId renamed)', () => { - const result = validateWorktreeBranch('auto-claude/renamed-feature', expectedBranch); - expect(result.branchToDelete).toBe('auto-claude/renamed-feature'); + it('should allow other aperant branches (specId renamed)', () => { + const result = validateWorktreeBranch('aperant/renamed-feature', expectedBranch); + expect(result.branchToDelete).toBe('aperant/renamed-feature'); expect(result.usedFallback).toBe(false); expect(result.reason).toBe('pattern_match'); }); - it('should allow auto-claude branches with different formats', () => { - const result = validateWorktreeBranch('auto-claude/001-task', expectedBranch); - expect(result.branchToDelete).toBe('auto-claude/001-task'); + it('should allow aperant branches with different formats', () => { + const result = validateWorktreeBranch('aperant/001-task', expectedBranch); + expect(result.branchToDelete).toBe('aperant/001-task'); expect(result.usedFallback).toBe(false); expect(result.reason).toBe('pattern_match'); }); @@ -142,17 +142,17 @@ describe('validateWorktreeBranch', () => { expect(result.reason).toBe('invalid_pattern'); }); - it('should handle branch that starts with auto-claude but is malformed', () => { - // "auto-claude" without a slash should still be rejected - const result = validateWorktreeBranch('auto-claude', expectedBranch); + it('should handle branch that starts with aperant but is malformed', () => { + // "aperant" without a slash should still be rejected + const result = validateWorktreeBranch('aperant', expectedBranch); expect(result.branchToDelete).toBe(expectedBranch); expect(result.usedFallback).toBe(true); expect(result.reason).toBe('invalid_pattern'); }); - it('should reject auto-claude/ with no suffix (invalid branch name)', () => { - // "auto-claude/" alone is not a valid branch name - needs actual specId - const result = validateWorktreeBranch('auto-claude/', expectedBranch); + it('should reject aperant/ with no suffix (invalid branch name)', () => { + // "aperant/" alone is not a valid branch name - needs actual specId + const result = validateWorktreeBranch('aperant/', expectedBranch); expect(result.branchToDelete).toBe(expectedBranch); expect(result.usedFallback).toBe(true); expect(result.reason).toBe('invalid_pattern'); diff --git a/apps/desktop/src/main/ipc-handlers/task/crud-handlers.ts b/apps/desktop/src/main/ipc-handlers/task/crud-handlers.ts index 76561d2b1c..d662ccf8b3 100644 --- a/apps/desktop/src/main/ipc-handlers/task/crud-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/task/crud-handlers.ts @@ -1,5 +1,5 @@ import { ipcMain, nativeImage } from 'electron'; -import { IPC_CHANNELS, AUTO_BUILD_PATHS, getSpecsDir, VALID_THINKING_LEVELS, sanitizeThinkingLevel } from '../../../shared/constants'; +import { IPC_CHANNELS, APERANT_PATHS, getSpecsDir, VALID_THINKING_LEVELS, sanitizeThinkingLevel } from '../../../shared/constants'; import type { IPCResult, Task, TaskMetadata, TaskOutcome } from '../../../shared/types'; import path from 'path'; import { execFileSync } from 'child_process'; @@ -113,7 +113,7 @@ async function updateLinkedRoadmapFeature( specId: string, taskOutcome: TaskOutcome ): Promise { - const roadmapFile = path.join(projectPath, AUTO_BUILD_PATHS.ROADMAP_DIR, AUTO_BUILD_PATHS.ROADMAP_FILE); + const roadmapFile = path.join(projectPath, APERANT_PATHS.ROADMAP_DIR, APERANT_PATHS.ROADMAP_FILE); await updateRoadmapFeatureOutcome(roadmapFile, [specId], taskOutcome, '[TASK_CRUD]'); } @@ -172,7 +172,7 @@ export function registerTaskCRUDHandlers(agentManager: AgentManager): void { } // Generate a unique spec ID based on existing specs - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specsDir = path.join(project.path, specsBaseDir); // Find next available spec number @@ -282,7 +282,7 @@ export function registerTaskCRUDHandlers(agentManager: AgentManager): void { phases: [] }; - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); writeFileSync(planPath, JSON.stringify(implementationPlan, null, 2), 'utf-8'); // Save task metadata if provided (sanitize thinking levels before writing) @@ -308,7 +308,7 @@ export function registerTaskCRUDHandlers(agentManager: AgentManager): void { })); } - const requirementsPath = path.join(specDir, AUTO_BUILD_PATHS.REQUIREMENTS); + const requirementsPath = path.join(specDir, APERANT_PATHS.REQUIREMENTS); writeFileSync(requirementsPath, JSON.stringify(requirements, null, 2), 'utf-8'); // Create the task object @@ -391,7 +391,7 @@ export function registerTaskCRUDHandlers(agentManager: AgentManager): void { // Find ALL locations where this task exists (main + any remaining worktree dirs) // Following the archiveTasks() pattern from project-store.ts - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specPaths = findAllSpecPaths(project.path, specsBaseDir, task.specId); // If spec directory doesn't exist anywhere, return success (already removed) @@ -462,8 +462,8 @@ export function registerTaskCRUDHandlers(agentManager: AgentManager): void { return { success: false, error: 'Task not found' }; } - const autoBuildDir = project.autoBuildPath || '.auto-claude'; - const specDir = path.join(project.path, autoBuildDir, 'specs', task.specId); + const aperantDir = project.aperantPath || '.aperant'; + const specDir = path.join(project.path, aperantDir, 'specs', task.specId); if (!existsSync(specDir)) { return { success: false, error: 'Spec directory not found' }; @@ -478,7 +478,7 @@ export function registerTaskCRUDHandlers(agentManager: AgentManager): void { } // Update implementation_plan.json - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); try { const planContent = readFileSync(planPath, 'utf-8'); const plan = JSON.parse(planContent); @@ -500,7 +500,7 @@ export function registerTaskCRUDHandlers(agentManager: AgentManager): void { } // Update spec.md if it exists - const specPath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specPath = path.join(specDir, APERANT_PATHS.SPEC_FILE); try { let specContent = readFileSync(specPath, 'utf-8'); @@ -674,10 +674,10 @@ export function registerTaskCRUDHandlers(agentManager: AgentManager): void { console.error(`[IPC] TASK_LOAD_IMAGE_THUMBNAIL: Unknown project: "${projectPath}"`); return { success: false, error: 'Unknown project' }; } - const autoBuildPath = project.autoBuildPath || '.auto-claude'; + const aperantPath = project.aperantPath || '.aperant'; // Build full path to the image - const specsDir = getSpecsDir(autoBuildPath); + const specsDir = getSpecsDir(aperantPath); const fullImagePath = path.join(projectPath, specsDir, specId, imagePath); // Validate path to prevent path traversal attacks diff --git a/apps/desktop/src/main/ipc-handlers/task/execution-handlers.ts b/apps/desktop/src/main/ipc-handlers/task/execution-handlers.ts index d3d62eb2cd..41d2e6702d 100644 --- a/apps/desktop/src/main/ipc-handlers/task/execution-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/task/execution-handlers.ts @@ -1,5 +1,5 @@ import { ipcMain, BrowserWindow } from 'electron'; -import { IPC_CHANNELS, AUTO_BUILD_PATHS, getSpecsDir } from '../../../shared/constants'; +import { IPC_CHANNELS, APERANT_PATHS, getSpecsDir } from '../../../shared/constants'; import type { IPCResult, TaskStartOptions, TaskStatus, ImageAttachment } from '../../../shared/types'; import path from 'path'; import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; @@ -170,7 +170,7 @@ export function registerTaskExecutionHandlers( ? (projectStore.getProject(task.projectId) ?? foundProject) : foundProject; - // Check git status - Auto Claude requires git for worktree-based builds + // Check git status - Aperant requires git for worktree-based builds const gitStatus = checkGitStatus(project.path); if (!gitStatus.isGitRepo) { console.warn('[TASK_START] Project is not a git repository:', project.path); @@ -211,13 +211,13 @@ export function registerTaskExecutionHandlers( // Check if implementation_plan.json has valid subtasks BEFORE XState handling. // This is more reliable than task.subtasks.length which may not be loaded yet. - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join( project.path, specsBaseDir, task.specId ); - const planFilePath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planFilePath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); let planHasSubtasks = false; const planContent = safeReadFileSync(planFilePath); if (planContent) { @@ -291,7 +291,7 @@ export function registerTaskExecutionHandlers( // Check if spec.md exists (indicates spec creation was already done or in progress) // Check main project path for spec file (spec is created before worktree) - const specFilePath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specFilePath = path.join(specDir, APERANT_PATHS.SPEC_FILE); const hasSpec = existsSync(specFilePath); // Check if this task needs spec creation first (no spec file = not yet created) @@ -409,7 +409,7 @@ export function registerTaskExecutionHandlers( } // Check if dev mode is enabled for this project - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join( project.path, specsBaseDir, @@ -423,7 +423,7 @@ export function registerTaskExecutionHandlers( if (approved) { // Write approval to QA report - const qaReportPath = path.join(specDir, AUTO_BUILD_PATHS.QA_REPORT); + const qaReportPath = path.join(specDir, APERANT_PATHS.QA_REPORT); try { writeFileSync( qaReportPath, @@ -468,15 +468,15 @@ export function registerTaskExecutionHandlers( } // Step 3: Clean untracked files that came from the merge - // IMPORTANT: Exclude .auto-claude directory to preserve specs and worktree data - const cleanResult = spawnSync(getToolPath('git'), ['clean', '-fd', '-e', '.auto-claude'], { + // IMPORTANT: Exclude .aperant directory to preserve specs and worktree data + const cleanResult = spawnSync(getToolPath('git'), ['clean', '-fd', '-e', '.aperant'], { cwd: project.path, encoding: 'utf-8', stdio: 'pipe', env: getIsolatedGitEnv() }); if (cleanResult.status === 0) { - console.log('[TASK_REVIEW] Cleaned untracked files in main (excluding .auto-claude)'); + console.log('[TASK_REVIEW] Cleaned untracked files in main (excluding .aperant)'); } console.log('[TASK_REVIEW] Main branch restored to pre-merge state'); @@ -681,13 +681,13 @@ export function registerTaskExecutionHandlers( // Validate status transition - 'human_review' requires actual work to have been done // This prevents tasks from being incorrectly marked as ready for review when execution failed if (status === 'human_review') { - const specsBaseDirForValidation = getSpecsDir(project.autoBuildPath); + const specsBaseDirForValidation = getSpecsDir(project.aperantPath); const specDirForValidation = path.join( project.path, specsBaseDirForValidation, task.specId ); - const specFilePath = path.join(specDirForValidation, AUTO_BUILD_PATHS.SPEC_FILE); + const specFilePath = path.join(specDirForValidation, APERANT_PATHS.SPEC_FILE); // Check if spec.md exists and has meaningful content (at least 100 chars) const MIN_SPEC_CONTENT_LENGTH = 100; @@ -710,7 +710,7 @@ export function registerTaskExecutionHandlers( } // Get the spec directory and plan path using shared utility - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join(project.path, specsBaseDir, task.specId); const planPath = getPlanPath(project, task); @@ -802,11 +802,11 @@ export function registerTaskExecutionHandlers( }); // Check if spec.md exists - const specFilePath = path.join(specDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specFilePath = path.join(specDir, APERANT_PATHS.SPEC_FILE); const hasSpec = existsSync(specFilePath); const needsSpecCreation = !hasSpec; // FIX (#1562): Check actual plan file for subtasks, not just task.subtasks.length - const updatePlanFilePath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const updatePlanFilePath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); let updatePlanHasSubtasks = false; const updatePlanContent = safeReadFileSync(updatePlanFilePath); if (updatePlanContent) { @@ -914,7 +914,7 @@ export function registerTaskExecutionHandlers( } // Get the spec directory - use task.specsPath if available (handles worktree vs main) - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = task.specsPath || path.join( project.path, specsBaseDir, @@ -1006,28 +1006,28 @@ export function registerTaskExecutionHandlers( // If we write to main project but task is in worktree, the worktree's old status takes precedence on refresh. const specDir = task.specsPath || path.join( project.path, - getSpecsDir(project.autoBuildPath), + getSpecsDir(project.aperantPath), task.specId ); // Update implementation_plan.json - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); console.log(`[Recovery] Writing to plan file at: ${planPath} (task location: ${task.location || 'main'})`); // Also update the OTHER location if task exists in both main and worktree // This ensures consistency regardless of which version getTasks() prefers - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const mainSpecDir = path.join(project.path, specsBaseDir, task.specId); const worktreePath = findTaskWorktree(project.path, task.specId); const worktreeSpecDir = worktreePath ? path.join(worktreePath, specsBaseDir, task.specId) : null; // Collect all plan file paths that need updating const planPathsToUpdate: string[] = [planPath]; - if (mainSpecDir !== specDir && existsSync(path.join(mainSpecDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN))) { - planPathsToUpdate.push(path.join(mainSpecDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN)); + if (mainSpecDir !== specDir && existsSync(path.join(mainSpecDir, APERANT_PATHS.IMPLEMENTATION_PLAN))) { + planPathsToUpdate.push(path.join(mainSpecDir, APERANT_PATHS.IMPLEMENTATION_PLAN)); } - if (worktreeSpecDir && worktreeSpecDir !== specDir && existsSync(path.join(worktreeSpecDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN))) { - planPathsToUpdate.push(path.join(worktreeSpecDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN)); + if (worktreeSpecDir && worktreeSpecDir !== specDir && existsSync(path.join(worktreeSpecDir, APERANT_PATHS.IMPLEMENTATION_PLAN))) { + planPathsToUpdate.push(path.join(worktreeSpecDir, APERANT_PATHS.IMPLEMENTATION_PLAN)); } console.log(`[Recovery] Will update ${planPathsToUpdate.length} plan file(s):`, planPathsToUpdate); @@ -1327,7 +1327,7 @@ export function registerTaskExecutionHandlers( // Check if spec.md exists to determine whether to run spec creation or task execution // Check main project path for spec file (spec is created before worktree) // mainSpecDir is declared earlier in the handler scope - const specFilePath = path.join(mainSpecDir, AUTO_BUILD_PATHS.SPEC_FILE); + const specFilePath = path.join(mainSpecDir, APERANT_PATHS.SPEC_FILE); const hasSpec = existsSync(specFilePath); const needsSpecCreation = !hasSpec; diff --git a/apps/desktop/src/main/ipc-handlers/task/logs-handlers.ts b/apps/desktop/src/main/ipc-handlers/task/logs-handlers.ts index b02c25c83b..fe128f0eb6 100644 --- a/apps/desktop/src/main/ipc-handlers/task/logs-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/task/logs-handlers.ts @@ -33,7 +33,7 @@ export function registerTaskLogsHandlers(getMainWindow: () => BrowserWindow | nu // Defense-in-depth: project.path is normally absolute from ProjectStore, // but we guard here against edge cases (e.g., manually edited store file) const absoluteProjectPath = ensureAbsolutePath(project.path); - const specsRelPath = getSpecsDir(project.autoBuildPath); + const specsRelPath = getSpecsDir(project.aperantPath); const specDir = path.join(absoluteProjectPath, specsRelPath, specId); debugLog('[TASK_LOGS_GET] Path resolution:', { @@ -87,7 +87,7 @@ export function registerTaskLogsHandlers(getMainWindow: () => BrowserWindow | nu } const absoluteProjectPath = ensureAbsolutePath(project.path); - const specsRelPath = getSpecsDir(project.autoBuildPath); + const specsRelPath = getSpecsDir(project.aperantPath); const specDir = path.join(absoluteProjectPath, specsRelPath, specId); debugLog('[TASK_LOGS_WATCH] Starting watch:', { diff --git a/apps/desktop/src/main/ipc-handlers/task/plan-file-utils.ts b/apps/desktop/src/main/ipc-handlers/task/plan-file-utils.ts index 55f3031f6c..9909b65a24 100644 --- a/apps/desktop/src/main/ipc-handlers/task/plan-file-utils.ts +++ b/apps/desktop/src/main/ipc-handlers/task/plan-file-utils.ts @@ -19,7 +19,7 @@ import path from 'path'; import { readFileSync, mkdirSync } from 'fs'; -import { AUTO_BUILD_PATHS, getSpecsDir } from '../../../shared/constants'; +import { APERANT_PATHS, getSpecsDir } from '../../../shared/constants'; import type { TaskStatus, Project, Task } from '../../../shared/types'; import { projectStore } from '../../project-store'; import type { TaskEventPayload } from '../../agent/task-event-schema'; @@ -69,9 +69,9 @@ function isFileNotFoundError(err: unknown): boolean { * Get the plan file path for a task */ export function getPlanPath(project: Project, task: Task): string { - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join(project.path, specsBaseDir, task.specId); - return path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + return path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); } /** diff --git a/apps/desktop/src/main/ipc-handlers/task/worktree-handlers.ts b/apps/desktop/src/main/ipc-handlers/task/worktree-handlers.ts index 3ff3ac25c5..68623319f3 100644 --- a/apps/desktop/src/main/ipc-handlers/task/worktree-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/task/worktree-handlers.ts @@ -1,5 +1,5 @@ import { ipcMain, BrowserWindow, shell, app } from 'electron'; -import { IPC_CHANNELS, AUTO_BUILD_PATHS, DEFAULT_APP_SETTINGS, DEFAULT_FEATURE_MODELS, DEFAULT_FEATURE_THINKING, MODEL_ID_MAP, THINKING_BUDGET_MAP, getSpecsDir } from '../../../shared/constants'; +import { IPC_CHANNELS, APERANT_PATHS, DEFAULT_APP_SETTINGS, DEFAULT_FEATURE_MODELS, DEFAULT_FEATURE_THINKING, MODEL_ID_MAP, THINKING_BUDGET_MAP, getSpecsDir } from '../../../shared/constants'; import type { IPCResult, WorktreeStatus, WorktreeDiff, WorktreeDiffFile, WorktreeMergeResult, WorktreeDiscardResult, WorktreeListResult, WorktreeListItem, WorktreeCreatePROptions, WorktreeCreatePRResult, SupportedIDE, SupportedTerminal, SupportedCLI, AppSettings } from '../../../shared/types'; import path from 'path'; import { minimatch } from 'minimatch'; @@ -33,9 +33,9 @@ export const GIT_BRANCH_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9._/-]*[a-zA-Z0-9]$|^[a-zA /** * Validates a detected branch name and returns the safe branch to delete. * - * Why `auto-claude/` prefix is considered safe: - * - All task worktrees use branches named `auto-claude/{specId}` - * - This pattern is controlled by Auto-Claude, not user input + * Why `aperant/` prefix is considered safe: + * - All task worktrees use branches named `aperant/{specId}` + * - This pattern is controlled by Aperant, not user input * - If detected branch matches this pattern, it's a valid task branch * - If it doesn't match (e.g., `main`, `develop`, `feature/xxx`), it's likely * the main project's branch being incorrectly detected from a corrupted worktree @@ -66,9 +66,9 @@ export function validateWorktreeBranch( }; } - // Matches auto-claude pattern with valid specId (not just "auto-claude/") + // Matches aperant pattern with valid specId (not just "aperant/") // The specId must be non-empty for this to be a valid task branch - if (detectedBranch.startsWith('auto-claude/') && detectedBranch.length > 'auto-claude/'.length) { + if (detectedBranch.startsWith('aperant/') && detectedBranch.length > 'aperant/'.length) { return { branchToDelete: detectedBranch, usedFallback: false, @@ -1480,7 +1480,7 @@ function getEffectiveBaseBranch(projectPath: string, specId: string, projectMain } // 1. Try task metadata baseBranch - const specDir = path.join(projectPath, '.auto-claude', 'specs', specId); + const specDir = path.join(projectPath, '.aperant', 'specs', specId); const taskBaseBranch = getTaskBaseBranch(specDir); if (taskBaseBranch) { return taskBaseBranch; @@ -1603,7 +1603,7 @@ async function updateTaskStatusAfterPRCreation( specDir: string, worktreePath: string | null, prUrl: string, - autoBuildPath: string | undefined, + aperantPath: string | undefined, specId: string, debug: (...args: unknown[]) => void ): Promise { @@ -1614,7 +1614,7 @@ async function updateTaskStatusAfterPRCreation( worktreeMetadata: false }; - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); const metadataPath = path.join(specDir, 'task_metadata.json'); // Await status persistence to ensure completion before resolving @@ -1633,8 +1633,8 @@ async function updateTaskStatusAfterPRCreation( // Also persist to WORKTREE location (worktree takes priority when loading tasks) // This ensures the status persists after refresh since getTasks() prefers worktree version if (worktreePath) { - const specsBaseDir = getSpecsDir(autoBuildPath); - const worktreePlanPath = path.join(worktreePath, specsBaseDir, specId, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const specsBaseDir = getSpecsDir(aperantPath); + const worktreePlanPath = path.join(worktreePath, specsBaseDir, specId, APERANT_PATHS.IMPLEMENTATION_PLAN); const worktreeMetadataPath = path.join(worktreePath, specsBaseDir, specId, 'task_metadata.json'); try { @@ -1755,7 +1755,7 @@ export function registerWorktreeHandlers( ): void { /** * Get the worktree status for a task - * Per-spec architecture: Each spec has its own worktree at .auto-claude/worktrees/tasks/{spec-name}/ + * Per-spec architecture: Each spec has its own worktree at .aperant/worktrees/tasks/{spec-name}/ */ ipcMain.handle( IPC_CHANNELS.TASK_WORKTREE_STATUS, @@ -1766,7 +1766,7 @@ export function registerWorktreeHandlers( return { success: false, error: 'Task not found' }; } - // Find worktree at .auto-claude/worktrees/tasks/{spec-name}/ + // Find worktree at .aperant/worktrees/tasks/{spec-name}/ const worktreePath = findTaskWorktree(project.path, task.specId); if (!worktreePath) { @@ -1871,7 +1871,7 @@ export function registerWorktreeHandlers( /** * Get the diff for a task's worktree - * Per-spec architecture: Each spec has its own worktree at .auto-claude/worktrees/tasks/{spec-name}/ + * Per-spec architecture: Each spec has its own worktree at .aperant/worktrees/tasks/{spec-name}/ */ ipcMain.handle( IPC_CHANNELS.TASK_WORKTREE_DIFF, @@ -1882,7 +1882,7 @@ export function registerWorktreeHandlers( return { success: false, error: 'Task not found' }; } - // Find worktree at .auto-claude/worktrees/tasks/{spec-name}/ + // Find worktree at .aperant/worktrees/tasks/{spec-name}/ const worktreePath = findTaskWorktree(project.path, task.specId); if (!worktreePath) { @@ -1987,7 +1987,7 @@ export function registerWorktreeHandlers( debug('Found task:', task.specId, 'project:', project.path); - const specDir = path.join(project.path, project.autoBuildPath || '.auto-claude', 'specs', task.specId); + const specDir = path.join(project.path, project.aperantPath || '.aperant', 'specs', task.specId); const worktreePath = findTaskWorktree(project.path, task.specId); // Auto-fix any misconfigured bare repo before merge operation @@ -2031,7 +2031,7 @@ export function registerWorktreeHandlers( const aiResolverFn = createMergeResolverFn(modelShorthand, 'low'); // Create the merge orchestrator - const storageDir = path.join(project.path, project.autoBuildPath || '.auto-claude'); + const storageDir = path.join(project.path, project.aperantPath || '.aperant'); const orchestrator = new MergeOrchestrator({ projectDir: project.path, storageDir, @@ -2126,7 +2126,7 @@ export function registerWorktreeHandlers( if (!hasActualStagedChanges) { // Check if worktree branch was already merged (merge commit exists) - const specBranch = `auto-claude/${task.specId}`; + const specBranch = `aperant/${task.specId}`; try { // Check if current branch contains all commits from spec branch // git merge-base --is-ancestor returns exit code 0 if true, 1 if false @@ -2265,12 +2265,12 @@ export function registerWorktreeHandlers( // OPTIMIZATION: Use async I/O and parallel updates to prevent UI blocking // NOTE: The worktree has the same directory structure as main project const planPaths: { path: string; isMain: boolean }[] = [ - { path: path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), isMain: true }, + { path: path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN), isMain: true }, ]; // Add worktree plan path if worktree exists if (worktreePath) { - const worktreeSpecDir = path.join(worktreePath, project.autoBuildPath || '.auto-claude', 'specs', task.specId); - planPaths.push({ path: path.join(worktreeSpecDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN), isMain: false }); + const worktreeSpecDir = path.join(worktreePath, project.aperantPath || '.aperant', 'specs', task.specId); + planPaths.push({ path: path.join(worktreeSpecDir, APERANT_PATHS.IMPLEMENTATION_PLAN), isMain: false }); } const { promises: fsPromises } = require('fs'); @@ -2433,7 +2433,7 @@ export function registerWorktreeHandlers( // 1. Task metadata baseBranch (explicit task-level override) // 2. Project settings mainBranch (project-level default) // 3. Default to 'main' - const specDir = path.join(project.path, project.autoBuildPath || '.auto-claude', 'specs', task.specId); + const specDir = path.join(project.path, project.aperantPath || '.aperant', 'specs', task.specId); const taskBaseBranch = getTaskBaseBranch(specDir); const projectMainBranch = project.settings?.mainBranch; const effectiveBaseBranch = taskBaseBranch || projectMainBranch || 'main'; @@ -2442,7 +2442,7 @@ export function registerWorktreeHandlers( // Run preview using the TypeScript MergeOrchestrator in dry-run mode // (no AI resolver needed for preview — only conflict detection and analysis) - const storageDir = path.join(project.path, project.autoBuildPath || '.auto-claude'); + const storageDir = path.join(project.path, project.aperantPath || '.aperant'); const orchestrator = new MergeOrchestrator({ projectDir: project.path, storageDir, @@ -2515,7 +2515,7 @@ export function registerWorktreeHandlers( /** * Discard the worktree changes - * Per-spec architecture: Each spec has its own worktree at .auto-claude/worktrees/tasks/{spec-name}/ + * Per-spec architecture: Each spec has its own worktree at .aperant/worktrees/tasks/{spec-name}/ * * Note: Uses the shared cleanupWorktree utility which handles Windows-specific issues * where `git worktree remove --force` fails when the directory contains untracked files. @@ -2530,7 +2530,7 @@ export function registerWorktreeHandlers( return { success: false, error: 'Task not found' }; } - // Find worktree at .auto-claude/worktrees/tasks/{spec-name}/ + // Find worktree at .aperant/worktrees/tasks/{spec-name}/ const worktreePath = findTaskWorktree(project.path, task.specId); if (!worktreePath) { @@ -2622,7 +2622,7 @@ export function registerWorktreeHandlers( return { success: false, error: 'Project path is invalid' }; } - // Find worktree at .auto-claude/worktrees/tasks/{spec-name}/ + // Find worktree at .aperant/worktrees/tasks/{spec-name}/ const worktreePath = findTaskWorktree(project.path, specName); if (!worktreePath) { @@ -2670,7 +2670,7 @@ export function registerWorktreeHandlers( /** * List all spec worktrees for a project - * Per-spec architecture: Each spec has its own worktree at .auto-claude/worktrees/tasks/{spec-name}/ + * Per-spec architecture: Each spec has its own worktree at .aperant/worktrees/tasks/{spec-name}/ */ ipcMain.handle( IPC_CHANNELS.TASK_LIST_WORKTREES, @@ -2699,7 +2699,7 @@ export function registerWorktreeHandlers( // Used for orphan detection - worktrees without a matching task are orphaned const tasks = projectStore.getTasks(projectId); // Track if task lookup was successful (empty array with existing specs dir = lookup failed) - const mainSpecsDir = path.join(project.path, '.auto-claude', 'specs'); + const mainSpecsDir = path.join(project.path, '.aperant', 'specs'); const taskLookupSuccessful = tasks.length > 0 || !existsSync(mainSpecsDir); // Helper to process a single worktree entry (async) @@ -2909,9 +2909,9 @@ export function registerWorktreeHandlers( return { success: false, error: 'Task not found' }; } - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const specDir = path.join(project.path, specsBaseDir, task.specId); - const planPath = path.join(specDir, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const planPath = path.join(specDir, APERANT_PATHS.IMPLEMENTATION_PLAN); // Use EAFP pattern (try/catch) instead of LBYL (existsSync check) to avoid TOCTOU race conditions const { promises: fsPromises } = require('fs'); @@ -2941,7 +2941,7 @@ export function registerWorktreeHandlers( // Also update worktree plan if it exists const worktreePath = findTaskWorktree(project.path, task.specId); if (worktreePath) { - const worktreePlanPath = path.join(worktreePath, specsBaseDir, task.specId, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); + const worktreePlanPath = path.join(worktreePath, specsBaseDir, task.specId, APERANT_PATHS.IMPLEMENTATION_PLAN); try { const worktreePlanContent = await fsPromises.readFile(worktreePlanPath, 'utf-8'); const worktreePlan = JSON.parse(worktreePlanContent); @@ -2997,7 +2997,7 @@ export function registerWorktreeHandlers( debug('Found task:', task.specId, 'project:', project.path); - const specDir = path.join(project.path, project.autoBuildPath || '.auto-claude', 'specs', task.specId); + const specDir = path.join(project.path, project.aperantPath || '.aperant', 'specs', task.specId); // Use EAFP pattern - try to read specDir and catch ENOENT try { @@ -3034,8 +3034,8 @@ export function registerWorktreeHandlers( // Determine base branch and branch name const taskBaseBranch = getTaskBaseBranch(specDir); const baseBranch = options?.targetBranch || taskBaseBranch || 'main'; - const branchName = `auto-claude/${task.specId}`; - const prTitle = options?.title || `auto-claude: ${task.specId}`; + const branchName = `aperant/${task.specId}`; + const prTitle = options?.title || `aperant: ${task.specId}`; if (taskBaseBranch) { debug('Using stored base branch:', taskBaseBranch); @@ -3068,14 +3068,14 @@ export function registerWorktreeHandlers( specDir, worktreePath, result.prUrl, - project.autoBuildPath, + project.aperantPath, task.specId, debug ); // Update linked roadmap feature if (project.path && task.specId) { - const roadmapFile = path.join(project.path, AUTO_BUILD_PATHS.ROADMAP_DIR, AUTO_BUILD_PATHS.ROADMAP_FILE); + const roadmapFile = path.join(project.path, APERANT_PATHS.ROADMAP_DIR, APERANT_PATHS.ROADMAP_FILE); updateRoadmapFeatureOutcome(roadmapFile, [task.specId], 'completed', '[PR_CREATE]').catch((err) => { debug('Failed to update roadmap feature after PR creation:', err); }); diff --git a/apps/desktop/src/main/ipc-handlers/terminal/worktree-handlers.ts b/apps/desktop/src/main/ipc-handlers/terminal/worktree-handlers.ts index 5f6be07519..115a9a44a8 100644 --- a/apps/desktop/src/main/ipc-handlers/terminal/worktree-handlers.ts +++ b/apps/desktop/src/main/ipc-handlers/terminal/worktree-handlers.ts @@ -195,7 +195,7 @@ function getDefaultBranch(projectPath: string): string { return project.settings.mainBranch; } - const envPath = path.join(projectPath, '.auto-claude', '.env'); + const envPath = path.join(projectPath, '.aperant', '.env'); if (existsSync(envPath)) { try { const content = readFileSync(envPath, 'utf-8'); @@ -291,7 +291,7 @@ const DEFAULT_STRATEGY_MAP: Record { // Validate projectPath against registered projects @@ -1061,9 +1061,9 @@ async function listOtherWorktrees(projectPath: string): Promise p.path === absolutePath); if (existing) { - // Validate that .auto-claude folder still exists for existing project - // If manually deleted, reset autoBuildPath so UI prompts for reinitialization - if (existing.autoBuildPath && !isInitialized(existing.path)) { - console.warn(`[ProjectStore] .auto-claude folder was deleted for project "${existing.name}" - resetting autoBuildPath`); - existing.autoBuildPath = ''; + // Validate that .aperant folder still exists for existing project + // If manually deleted, reset aperantPath so UI prompts for reinitialization + if (existing.aperantPath && !isInitialized(existing.path)) { + console.warn(`[ProjectStore] .aperant folder was deleted for project "${existing.name}" - resetting aperantPath`); + existing.aperantPath = ''; existing.updatedAt = new Date(); this.save(); } @@ -111,14 +111,14 @@ export class ProjectStore { // Derive name from path if not provided const projectName = name || path.basename(absolutePath); - // Determine auto-claude path (supports both 'auto-claude' and '.auto-claude') - const autoBuildPath = getAutoBuildPath(absolutePath) || ''; + // Determine aperant path + const aperantPath = getAperantPath(absolutePath) || ''; const project: Project = { id: uuidv4(), name: projectName, path: absolutePath, // Store absolute path - autoBuildPath, + aperantPath: aperantPath, settings: { ...DEFAULT_PROJECT_SETTINGS }, createdAt: new Date(), updatedAt: new Date() @@ -131,12 +131,12 @@ export class ProjectStore { } /** - * Update project's autoBuildPath after initialization + * Update project's aperantPath after initialization */ - updateAutoBuildPath(projectId: string, autoBuildPath: string): Project | undefined { + updateAperantPath(projectId: string, aperantPath: string): Project | undefined { const project = this.data.projects.find((p) => p.id === projectId); if (project) { - project.autoBuildPath = autoBuildPath; + project.aperantPath = aperantPath; project.updatedAt = new Date(); this.save(); } @@ -213,19 +213,19 @@ export class ProjectStore { } /** - * Validate all projects to ensure their .auto-claude folders still exist. - * If a project has autoBuildPath set but the folder was deleted, - * reset autoBuildPath to empty string so the UI prompts for reinitialization. + * Validate all projects to ensure their .aperant folders still exist. + * If a project has aperantPath set but the folder was deleted, + * reset aperantPath to empty string so the UI prompts for reinitialization. * - * @returns Array of project IDs that were reset due to missing .auto-claude folder + * @returns Array of project IDs that were reset due to missing .aperant folder */ validateProjects(): string[] { const resetProjectIds: string[] = []; let hasChanges = false; for (const project of this.data.projects) { - // Skip projects that aren't initialized (autoBuildPath is empty) - if (!project.autoBuildPath) { + // Skip projects that aren't initialized (aperantPath is empty) + if (!project.aperantPath) { continue; } @@ -235,10 +235,10 @@ export class ProjectStore { continue; // Don't reset - let user handle this case } - // Check if .auto-claude folder still exists + // Check if .aperant folder still exists if (!isInitialized(project.path)) { - console.warn(`[ProjectStore] .auto-claude folder missing for project "${project.name}" at ${project.path}`); - project.autoBuildPath = ''; + console.warn(`[ProjectStore] .aperant folder missing for project "${project.name}" at ${project.path}`); + project.aperantPath = ''; project.updatedAt = new Date(); resetProjectIds.push(project.id); hasChanges = true; @@ -247,7 +247,7 @@ export class ProjectStore { if (hasChanges) { this.save(); - console.warn(`[ProjectStore] Reset ${resetProjectIds.length} project(s) due to missing .auto-claude folder`); + console.warn(`[ProjectStore] Reset ${resetProjectIds.length} project(s) due to missing .aperant folder`); } return resetProjectIds; @@ -295,7 +295,7 @@ export class ProjectStore { } const allTasks: Task[] = []; - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); // 1. Scan main project specs directory (source of truth for task existence) const mainSpecsDir = path.join(project.path, specsBaseDir); @@ -420,8 +420,8 @@ export class ProjectStore { try { const specPath = path.join(specsDir, dir.name); - const planPath = path.join(specPath, AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN); - const specFilePath = path.join(specPath, AUTO_BUILD_PATHS.SPEC_FILE); + const planPath = path.join(specPath, APERANT_PATHS.IMPLEMENTATION_PLAN); + const specFilePath = path.join(specPath, APERANT_PATHS.SPEC_FILE); // Try to read implementation plan let plan: ImplementationPlan | null = null; @@ -448,7 +448,7 @@ export class ProjectStore { } let description = ''; - const requirementsPath = path.join(specPath, AUTO_BUILD_PATHS.REQUIREMENTS); + const requirementsPath = path.join(specPath, APERANT_PATHS.REQUIREMENTS); // PRIORITY 1: Read original user task description from requirements.json if (existsSync(requirementsPath)) { try { @@ -779,7 +779,7 @@ export class ProjectStore { return false; } - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const archivedAt = new Date().toISOString(); let hasErrors = false; @@ -836,7 +836,7 @@ export class ProjectStore { * Update roadmap features linked to archived tasks */ private updateRoadmapForArchivedTasks(project: Project, taskIds: string[]): void { - const roadmapFile = path.join(project.path, AUTO_BUILD_PATHS.ROADMAP_DIR, AUTO_BUILD_PATHS.ROADMAP_FILE); + const roadmapFile = path.join(project.path, APERANT_PATHS.ROADMAP_DIR, APERANT_PATHS.ROADMAP_FILE); updateRoadmapFeatureOutcome(roadmapFile, taskIds, 'archived', '[ProjectStore]').catch((err) => { console.warn('[ProjectStore] Failed to update roadmap for archived tasks:', err); }); @@ -854,7 +854,7 @@ export class ProjectStore { return false; } - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); let hasErrors = false; for (const taskId of taskIds) { @@ -895,7 +895,7 @@ export class ProjectStore { } // Revert linked roadmap features from 'archived' back to 'in_progress' - const roadmapFile = path.join(project.path, AUTO_BUILD_PATHS.ROADMAP_DIR, AUTO_BUILD_PATHS.ROADMAP_FILE); + const roadmapFile = path.join(project.path, APERANT_PATHS.ROADMAP_DIR, APERANT_PATHS.ROADMAP_FILE); revertRoadmapFeatureOutcome(roadmapFile, taskIds, '[ProjectStore]').catch((err) => { console.warn('[ProjectStore] Failed to revert roadmap for unarchived tasks:', err); }); diff --git a/apps/desktop/src/main/release-service.ts b/apps/desktop/src/main/release-service.ts index e7203cb8ee..ac406c1056 100644 --- a/apps/desktop/src/main/release-service.ts +++ b/apps/desktop/src/main/release-service.ts @@ -344,7 +344,7 @@ export class ReleaseService extends EventEmitter { tasks: Task[] ): Promise { const unmerged: UnmergedWorktreeInfo[] = []; - const worktreesDir = path.join(projectPath, '.auto-claude', 'worktrees', 'tasks'); + const worktreesDir = path.join(projectPath, '.aperant', 'worktrees', 'tasks'); if (!existsSync(worktreesDir)) { return []; @@ -505,7 +505,7 @@ export class ReleaseService extends EventEmitter { message: 'Stashing current changes...' }); - execFileSync(getToolPath('git'), ['stash', 'push', '-m', 'auto-claude-release-temp'], { + execFileSync(getToolPath('git'), ['stash', 'push', '-m', 'aperant-release-temp'], { cwd: projectPath, encoding: 'utf-8' }); diff --git a/apps/desktop/src/main/sentry.ts b/apps/desktop/src/main/sentry.ts index 476933cd3f..1049ac985a 100644 --- a/apps/desktop/src/main/sentry.ts +++ b/apps/desktop/src/main/sentry.ts @@ -123,7 +123,7 @@ export function initSentryMain(): void { Sentry.init({ dsn: cachedDsn, environment: app.isPackaged ? 'production' : 'development', - release: `auto-claude@${app.getVersion()}`, + release: `aperant@${app.getVersion()}`, beforeSend(event: Sentry.ErrorEvent) { if (!sentryEnabledState) { diff --git a/apps/desktop/src/main/services/profile/profile-manager.test.ts b/apps/desktop/src/main/services/profile/profile-manager.test.ts index e2e336588b..d7d881d19d 100644 --- a/apps/desktop/src/main/services/profile/profile-manager.test.ts +++ b/apps/desktop/src/main/services/profile/profile-manager.test.ts @@ -119,15 +119,15 @@ describe('profile-manager', () => { expect(result).toEqual(mockData); }); - it('should use auto-claude directory for profiles.json path', async () => { + it('should use aperant directory for profiles.json path', async () => { fsMocks.readFile.mockRejectedValue(new Error('ENOENT')); await loadProfilesFile(); - // Verify the file path includes auto-claude + // Verify the file path includes aperant const readFileCalls = fsMocks.readFile.mock.calls; const filePath = readFileCalls[0]?.[0]; - expect(filePath).toContain('auto-claude'); + expect(filePath).toContain('aperant'); expect(filePath).toContain('profiles.json'); }); }); @@ -147,7 +147,7 @@ describe('profile-manager', () => { const filePath = writeFileCall?.[0]; const content = writeFileCall?.[1]; - expect(filePath).toContain('auto-claude'); + expect(filePath).toContain('aperant'); expect(filePath).toContain('profiles.json'); expect(content).toBe(JSON.stringify(mockData, null, 2)); }); diff --git a/apps/desktop/src/main/services/profile/profile-manager.ts b/apps/desktop/src/main/services/profile/profile-manager.ts index dcb75d286f..0d8492c309 100644 --- a/apps/desktop/src/main/services/profile/profile-manager.ts +++ b/apps/desktop/src/main/services/profile/profile-manager.ts @@ -1,12 +1,12 @@ /** * Profile Manager - File I/O for API profiles * - * Handles loading and saving profiles.json from the auto-claude directory. + * Handles loading and saving profiles.json from the aperant directory. * Provides graceful handling for missing or corrupted files. * Uses file locking to prevent race conditions in concurrent operations. */ -import { promises as fs } from 'fs'; +import { promises as fs, existsSync, mkdirSync, copyFileSync } from 'fs'; import path from 'path'; import { app } from 'electron'; // @ts-expect-error - no types available for proper-lockfile @@ -14,11 +14,20 @@ import * as lockfile from 'proper-lockfile'; import type { APIProfile, ProfilesFile } from '@shared/types/profile'; /** - * Get the path to profiles.json in the auto-claude directory + * Get the path to profiles.json in the aperant directory */ export function getProfilesFilePath(): string { const userDataPath = app.getPath('userData'); - return path.join(userDataPath, 'auto-claude', 'profiles.json'); + const newPath = path.join(userDataPath, 'aperant', 'profiles.json'); + const oldPath = path.join(userDataPath, 'auto-claude', 'profiles.json'); + + // Migrate from old path if new doesn't exist yet + if (!existsSync(newPath) && existsSync(oldPath)) { + mkdirSync(path.dirname(newPath), { recursive: true }); + copyFileSync(oldPath, newPath); + } + + return newPath; } /** @@ -110,7 +119,7 @@ export async function loadProfilesFile(): Promise { /** * Save profiles.json to disk - * Creates the auto-claude directory if it doesn't exist + * Creates the aperant directory if it doesn't exist * Ensures secure file permissions (user read/write only) */ export async function saveProfilesFile(data: ProfilesFile): Promise { diff --git a/apps/desktop/src/main/task-log-service.ts b/apps/desktop/src/main/task-log-service.ts index ec7af2c314..c1fceb9c2e 100644 --- a/apps/desktop/src/main/task-log-service.ts +++ b/apps/desktop/src/main/task-log-service.ts @@ -164,7 +164,7 @@ export class TaskLogService extends EventEmitter { * * @param specDir - Main project spec directory * @param projectPath - Optional: Project root path (needed to find worktree if not registered) - * @param specsRelPath - Optional: Relative path to specs (e.g., "auto-claude/specs") + * @param specsRelPath - Optional: Relative path to specs (e.g., "aperant/specs") * @param specId - Optional: Spec ID (needed to find worktree if not registered) */ loadLogs(specDir: string, projectPath?: string, specsRelPath?: string, specId?: string): TaskLogs | null { @@ -247,7 +247,7 @@ export class TaskLogService extends EventEmitter { * @param specId - The spec ID (e.g., "013-screenshots-on-tasks") * @param specDir - Main project spec directory * @param projectPath - Optional: Project root path (needed to find worktree) - * @param specsRelPath - Optional: Relative path to specs (e.g., "auto-claude/specs") + * @param specsRelPath - Optional: Relative path to specs (e.g., "aperant/specs") */ startWatching(specId: string, specDir: string, projectPath?: string, specsRelPath?: string): void { debugLog('[TaskLogService.startWatching] Starting watch:', { diff --git a/apps/desktop/src/main/task-state-manager.ts b/apps/desktop/src/main/task-state-manager.ts index b7f4002c48..17510c547d 100644 --- a/apps/desktop/src/main/task-state-manager.ts +++ b/apps/desktop/src/main/task-state-manager.ts @@ -8,7 +8,7 @@ import { IPC_CHANNELS } from '../shared/constants'; import { safeSendToRenderer } from './ipc-handlers/utils'; import { getPlanPath, persistPlanStatusAndReasonSync } from './ipc-handlers/task/plan-file-utils'; import { findTaskWorktree } from './worktree-paths'; -import { getSpecsDir, AUTO_BUILD_PATHS } from '../shared/constants'; +import { getSpecsDir, APERANT_PATHS } from '../shared/constants'; import { existsSync } from 'fs'; import path from 'path'; @@ -305,12 +305,12 @@ export class TaskStateManager { const worktreePath = findTaskWorktree(project.path, task.specId); if (!worktreePath) return; - const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specsBaseDir = getSpecsDir(project.aperantPath); const worktreePlanPath = path.join( worktreePath, specsBaseDir, task.specId, - AUTO_BUILD_PATHS.IMPLEMENTATION_PLAN + APERANT_PATHS.IMPLEMENTATION_PLAN ); if (existsSync(worktreePlanPath)) { persistPlanStatusAndReasonSync(worktreePlanPath, status, reviewReason, project.id, xstateState, executionPhase); diff --git a/apps/desktop/src/main/terminal-name-generator.ts b/apps/desktop/src/main/terminal-name-generator.ts index 3f630b382c..e1b77b6f1a 100644 --- a/apps/desktop/src/main/terminal-name-generator.ts +++ b/apps/desktop/src/main/terminal-name-generator.ts @@ -33,7 +33,7 @@ export class TerminalNameGenerator extends EventEmitter { * No-op configure() kept for backward compatibility. * Python source path is no longer needed. */ - configure(_autoBuildSourcePath?: string): void { + configure(_aperantSourcePath?: string): void { // No-op: TypeScript implementation does not need a source path } diff --git a/apps/desktop/src/main/terminal/cli-integration-handler.ts b/apps/desktop/src/main/terminal/cli-integration-handler.ts index e51834e709..d69d0724d4 100644 --- a/apps/desktop/src/main/terminal/cli-integration-handler.ts +++ b/apps/desktop/src/main/terminal/cli-integration-handler.ts @@ -30,6 +30,37 @@ import type { OAuthTokenEvent } from './types'; +// ============================================================================ +// POST-AUTH HELPERS +// ============================================================================ + +/** + * Performs post-authentication side effects for a profile: + * - Clears auth-failed state so the profile is available for agent usage + * - Clears migration state if the profile was migrated + * - Sets the profile as active if it isn't already + * - Triggers a usage check after a short delay + */ +function performPostAuthActions(profileId: string): void { + try { + const usageMonitor = getUsageMonitor(); + if (usageMonitor) { + usageMonitor.clearAuthFailedProfile(profileId); + const profileManager = getClaudeProfileManager(); + if (profileManager.isProfileMigrated(profileId)) { + profileManager.clearMigratedProfile(profileId); + } + const activeProfile = profileManager.getActiveProfile(); + if (activeProfile?.id !== profileId) { + profileManager.setActiveProfile(profileId); + } + setTimeout(() => { usageMonitor.checkNow(); }, 500); + } + } catch (error) { + debugError('[ClaudeIntegration] Failed to trigger post-auth actions:', error); + } +} + // ============================================================================ // CLI DISPATCH UTILITIES // ============================================================================ @@ -529,7 +560,7 @@ export function handleOAuthToken( if (keychainCreds.token) { // NOTE: We intentionally do NOT store the OAuth token in the profile. - // Storing causes AutoClaude to use a stale cached token instead of letting + // Storing causes Aperant to use a stale cached token instead of letting // Claude CLI read fresh tokens from Keychain (which auto-refreshes). // See: docs/LONG_LIVED_AUTH_PLAN.md for full context. @@ -569,6 +600,9 @@ export function handleOAuthToken( ensureOnboardingComplete(profile.configDir); } + // Perform post-auth side effects (clear auth-failed, set active, trigger usage check) + performPostAuthActions(profileId); + safeSendToRenderer(getWindow, IPC_CHANNELS.TERMINAL_OAUTH_TOKEN, { terminalId: terminal.id, profileId, @@ -589,6 +623,9 @@ export function handleOAuthToken( ensureOnboardingComplete(profile.configDir); } + // Perform post-auth side effects (clear auth-failed, set active, trigger usage check) + performPostAuthActions(profileId); + safeSendToRenderer(getWindow, IPC_CHANNELS.TERMINAL_OAUTH_TOKEN, { terminalId: terminal.id, profileId, @@ -786,6 +823,7 @@ export function handleClaudeExit( * Without this flag, it triggers the onboarding wizard even for authenticated profiles. */ export function ensureOnboardingComplete(configDir: string): void { + let tmpPath: string | undefined; try { const expandedDir = path.resolve( configDir.startsWith('~') ? configDir.replace(/^~/, os.homedir()) : configDir @@ -818,11 +856,15 @@ export function ensureOnboardingComplete(configDir: string): void { // Write atomically via temp file + rename to avoid partial writes and satisfy CodeQL js/insecure-temporary-file. // crypto.randomUUID() ensures no collisions; mode 0o600 restricts to owner-only. - const tmpPath = `${claudeJsonPath}.${crypto.randomUUID()}.tmp`; + tmpPath = `${claudeJsonPath}.${crypto.randomUUID()}.tmp`; fs.writeFileSync(tmpPath, updatedContent, { encoding: 'utf-8', mode: 0o600 }); fs.renameSync(tmpPath, claudeJsonPath); debugLog(`[ClaudeIntegration] Set hasCompletedOnboarding in ${claudeJsonPath}`); } catch (error) { + // Clean up temp file if it was created but rename failed + if (tmpPath) { + try { fs.unlinkSync(tmpPath); } catch {} + } // Non-fatal — worst case the user sees onboarding once debugError('[ClaudeIntegration] Failed to set hasCompletedOnboarding:', error); } @@ -1127,7 +1169,7 @@ export function invokeClaude( * * Uses `claude --continue` which resumes the most recent conversation in the * current directory. This is simpler and more reliable than tracking session IDs, - * since Auto Claude already restores terminals to their correct cwd/projectPath. + * since Aperant already restores terminals to their correct cwd/projectPath. * * Note: The sessionId parameter is kept for backwards compatibility but is ignored. * Claude Code's --resume flag expects user-named sessions (set via /rename), not @@ -1152,7 +1194,7 @@ export function resumeClaude( : buildPathPrefix(claudeEnv.PATH || ''); // Always use --continue which resumes the most recent session in the current directory. - // This is more reliable than --resume with session IDs since Auto Claude already restores + // This is more reliable than --resume with session IDs since Aperant already restores // terminals to their correct cwd/projectPath. // // Note: We clear claudeSessionId because --continue doesn't track specific sessions, @@ -1387,7 +1429,7 @@ export async function resumeClaudeAsync( : buildPathPrefix(claudeEnv.PATH || ''); // Always use --continue which resumes the most recent session in the current directory. - // This is more reliable than --resume with session IDs since Auto Claude already restores + // This is more reliable than --resume with session IDs since Aperant already restores // terminals to their correct cwd/projectPath. // // Note: We clear claudeSessionId because --continue doesn't track specific sessions, diff --git a/apps/desktop/src/main/terminal/pty-daemon-client.ts b/apps/desktop/src/main/terminal/pty-daemon-client.ts index 11d2d9366d..5e67487fee 100644 --- a/apps/desktop/src/main/terminal/pty-daemon-client.ts +++ b/apps/desktop/src/main/terminal/pty-daemon-client.ts @@ -18,8 +18,8 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const SOCKET_PATH = isWindows() - ? `\\\\.\\pipe\\auto-claude-pty-${process.getuid?.() || 'default'}` - : `/tmp/auto-claude-pty-${process.getuid?.() || 'default'}.sock`; + ? `\\\\.\\pipe\\aperant-pty-${process.getuid?.() || 'default'}` + : `/tmp/aperant-pty-${process.getuid?.() || 'default'}.sock`; interface DaemonResponseData { exitCode?: number; diff --git a/apps/desktop/src/main/terminal/pty-daemon.ts b/apps/desktop/src/main/terminal/pty-daemon.ts index 4501797cdf..82cfc377a3 100644 --- a/apps/desktop/src/main/terminal/pty-daemon.ts +++ b/apps/desktop/src/main/terminal/pty-daemon.ts @@ -14,8 +14,8 @@ import * as pty from '@lydell/node-pty'; import { isWindows, isUnix } from '../platform'; const SOCKET_PATH = isWindows() - ? `\\\\.\\pipe\\auto-claude-pty-${process.getuid?.() || 'default'}` - : `/tmp/auto-claude-pty-${process.getuid?.() || 'default'}.sock`; + ? `\\\\.\\pipe\\aperant-pty-${process.getuid?.() || 'default'}` + : `/tmp/aperant-pty-${process.getuid?.() || 'default'}.sock`; // Maximum buffer size per PTY (100KB) const MAX_BUFFER_SIZE = 100_000; diff --git a/apps/desktop/src/main/title-generator.ts b/apps/desktop/src/main/title-generator.ts index fe808ec8d2..8982d3db75 100644 --- a/apps/desktop/src/main/title-generator.ts +++ b/apps/desktop/src/main/title-generator.ts @@ -35,7 +35,7 @@ export class TitleGenerator extends EventEmitter { * Python path and source path are no longer needed. */ // biome-ignore lint/suspicious/noExplicitAny: kept for backward compatibility - configure(_pythonPath?: string, _autoBuildSourcePath?: string): void { + configure(_pythonPath?: string, _aperantSourcePath?: string): void { // No-op: TypeScript implementation does not need Python path or source path } diff --git a/apps/desktop/src/main/updater/path-resolver.ts b/apps/desktop/src/main/updater/path-resolver.ts index 22a60f0eb7..d43158baa6 100644 --- a/apps/desktop/src/main/updater/path-resolver.ts +++ b/apps/desktop/src/main/updater/path-resolver.ts @@ -1,5 +1,5 @@ /** - * Path resolution utilities for Auto Claude updater + * Path resolution utilities for Aperant updater */ import { existsSync, readFileSync } from 'fs'; @@ -48,27 +48,27 @@ export function getBundledSourcePath(): string { * Get the path for storing downloaded updates */ export function getUpdateCachePath(): string { - return path.join(app.getPath('userData'), 'auto-claude-updates'); + return path.join(app.getPath('userData'), 'aperant-updates'); } /** * Get the effective source path (considers override from updates and settings) */ export function getEffectiveSourcePath(): string { - // First, check user settings for configured autoBuildPath + // First, check user settings for configured aperantPath try { const settingsPath = path.join(app.getPath('userData'), 'settings.json'); if (existsSync(settingsPath)) { const settings = JSON.parse(readFileSync(settingsPath, 'utf-8')); - if (settings.autoBuildPath && existsSync(settings.autoBuildPath)) { + if (settings.aperantPath && existsSync(settings.aperantPath)) { // Validate it's a proper prompts source (must have planner.md) - const markerPath = path.join(settings.autoBuildPath, 'planner.md'); + const markerPath = path.join(settings.aperantPath, 'planner.md'); if (existsSync(markerPath)) { - return settings.autoBuildPath; + return settings.aperantPath; } // Invalid path - log warning and fall through to auto-detection console.warn( - `[path-resolver] Configured autoBuildPath "${settings.autoBuildPath}" is missing planner.md, falling back to bundled source` + `[path-resolver] Configured aperantPath "${settings.aperantPath}" is missing planner.md, falling back to bundled source` ); } } diff --git a/apps/desktop/src/main/utils/git-isolation.ts b/apps/desktop/src/main/utils/git-isolation.ts index 3c7328b03b..7c13b6bed1 100644 --- a/apps/desktop/src/main/utils/git-isolation.ts +++ b/apps/desktop/src/main/utils/git-isolation.ts @@ -151,7 +151,7 @@ export function detectWorktreeBranch( options: { timeout?: number; logPrefix?: string } = {} ): WorktreeBranchDetectionResult { const { timeout = 30000, logPrefix = '[WORKTREE_BRANCH_DETECTION]' } = options; - const expectedBranch = `auto-claude/${specId}`; + const expectedBranch = `aperant/${specId}`; let branch = expectedBranch; let usingFallback = false; @@ -164,7 +164,7 @@ export function detectWorktreeBranch( }).trim(); // SECURITY: Use strict exact-match validation (not prefix matching) to prevent - // accidentally deleting a different task's auto-claude branch. When git rev-parse + // accidentally deleting a different task's aperant branch. When git rev-parse // returns an unexpected branch, we MUST fall back to the expected pattern rather // than risking deletion of the wrong branch. This is critical for data safety. if (detectedBranch === expectedBranch) { diff --git a/apps/desktop/src/main/utils/profile-manager.test.ts b/apps/desktop/src/main/utils/profile-manager.test.ts index a47804d2c7..e502df2d80 100644 --- a/apps/desktop/src/main/utils/profile-manager.test.ts +++ b/apps/desktop/src/main/utils/profile-manager.test.ts @@ -106,15 +106,15 @@ describe('profile-manager', () => { expect(result).toEqual(mockData); }); - it('should use auto-claude directory for profiles.json path', async () => { + it('should use aperant directory for profiles.json path', async () => { vi.mocked(fsPromises.readFile).mockRejectedValue(new Error('ENOENT')); await loadProfilesFile(); - // Verify the file path includes auto-claude + // Verify the file path includes aperant const readFileCalls = vi.mocked(fsPromises.readFile).mock.calls; const filePath = readFileCalls[0]?.[0]; - expect(filePath).toContain('auto-claude'); + expect(filePath).toContain('aperant'); expect(filePath).toContain('profiles.json'); }); }); @@ -136,7 +136,7 @@ describe('profile-manager', () => { const filePath = writeFileCall?.[0]; const content = writeFileCall?.[1]; - expect(filePath).toContain('auto-claude'); + expect(filePath).toContain('aperant'); expect(filePath).toContain('profiles.json'); expect(content).toBe(JSON.stringify(mockData, null, 2)); }); diff --git a/apps/desktop/src/main/utils/profile-manager.ts b/apps/desktop/src/main/utils/profile-manager.ts index 2b7177ae17..e1bb97f894 100644 --- a/apps/desktop/src/main/utils/profile-manager.ts +++ b/apps/desktop/src/main/utils/profile-manager.ts @@ -1,21 +1,30 @@ /** * Profile Manager - File I/O for API profiles * - * Handles loading and saving profiles.json from the auto-claude directory. + * Handles loading and saving profiles.json from the aperant directory. * Provides graceful handling for missing or corrupted files. */ -import { promises as fs } from 'fs'; +import { promises as fs, existsSync, mkdirSync, copyFileSync } from 'fs'; import path from 'path'; import { app } from 'electron'; import type { ProfilesFile } from '../../shared/types/profile'; /** - * Get the path to profiles.json in the auto-claude directory + * Get the path to profiles.json in the aperant directory */ export function getProfilesFilePath(): string { const userDataPath = app.getPath('userData'); - return path.join(userDataPath, 'auto-claude', 'profiles.json'); + const newPath = path.join(userDataPath, 'aperant', 'profiles.json'); + const oldPath = path.join(userDataPath, 'auto-claude', 'profiles.json'); + + // Migrate from old path if new doesn't exist yet + if (!existsSync(newPath) && existsSync(oldPath)) { + mkdirSync(path.dirname(newPath), { recursive: true }); + copyFileSync(oldPath, newPath); + } + + return newPath; } /** @@ -41,7 +50,7 @@ export async function loadProfilesFile(): Promise { /** * Save profiles.json to disk - * Creates the auto-claude directory if it doesn't exist + * Creates the aperant directory if it doesn't exist */ export async function saveProfilesFile(data: ProfilesFile): Promise { const filePath = getProfilesFilePath(); diff --git a/apps/desktop/src/main/utils/spec-number-lock.ts b/apps/desktop/src/main/utils/spec-number-lock.ts index a6e168bd5e..967ca62b3a 100644 --- a/apps/desktop/src/main/utils/spec-number-lock.ts +++ b/apps/desktop/src/main/utils/spec-number-lock.ts @@ -34,7 +34,7 @@ export class SpecNumberLock { constructor(projectDir: string) { this.projectDir = projectDir; - this.lockDir = path.join(projectDir, '.auto-claude', '.locks'); + this.lockDir = path.join(projectDir, '.aperant', '.locks'); this.lockFile = path.join(this.lockDir, 'spec-numbering.lock'); } @@ -133,7 +133,7 @@ export class SpecNumberLock { /** * Get the next available spec number (must be called while lock is held) */ - getNextSpecNumber(autoBuildPath?: string): number { + getNextSpecNumber(aperantPath?: string): number { if (!this.acquired) { throw new SpecNumberLockError( 'Lock must be acquired before getting next spec number' @@ -147,14 +147,14 @@ export class SpecNumberLock { let maxNumber = 0; // Determine specs directory base path - const specsBase = autoBuildPath || '.auto-claude'; + const specsBase = aperantPath || '.aperant'; // 1. Scan main project specs const mainSpecsDir = path.join(this.projectDir, specsBase, 'specs'); maxNumber = Math.max(maxNumber, this.scanSpecsDir(mainSpecsDir)); // 2. Scan all worktree specs - const worktreesDir = path.join(this.projectDir, '.auto-claude', 'worktrees', 'tasks'); + const worktreesDir = path.join(this.projectDir, '.aperant', 'worktrees', 'tasks'); if (existsSync(worktreesDir)) { try { const worktrees = readdirSync(worktreesDir, { withFileTypes: true }); diff --git a/apps/desktop/src/main/utils/spec-path-helpers.ts b/apps/desktop/src/main/utils/spec-path-helpers.ts index e84174d1dc..f11201502f 100644 --- a/apps/desktop/src/main/utils/spec-path-helpers.ts +++ b/apps/desktop/src/main/utils/spec-path-helpers.ts @@ -29,7 +29,7 @@ export function isValidTaskId(taskId: string): boolean { * A task can exist in multiple locations (main + worktree), so return all paths * * @param projectPath - The root path of the project - * @param specsBaseDir - The relative path to specs directory (e.g., '.auto-claude/specs') + * @param specsBaseDir - The relative path to specs directory (e.g., '.aperant/specs') * @param taskId - The task/spec ID to find * @param logPrefix - Optional prefix for log messages (defaults to '[SpecPathHelpers]') * @returns Array of absolute paths where the spec exists diff --git a/apps/desktop/src/main/utils/worktree-cleanup.ts b/apps/desktop/src/main/utils/worktree-cleanup.ts index 6c5be7b123..7371ee6061 100644 --- a/apps/desktop/src/main/utils/worktree-cleanup.ts +++ b/apps/desktop/src/main/utils/worktree-cleanup.ts @@ -85,8 +85,8 @@ function getWorktreeBranch(worktreePath: string, specId: string, timeout: number return explicitBranchName; } - // Fall back to the naming convention: auto-claude/{spec-id} - return `auto-claude/${specId}`; + // Fall back to the naming convention: aperant/{spec-id} + return `aperant/${specId}`; } /** @@ -163,7 +163,7 @@ async function deleteDirectoryWithRetry( * @example * ```typescript * const result = await cleanupWorktree({ - * worktreePath: 'C:/projects/my-app/.auto-claude/worktrees/tasks/001-feature', + * worktreePath: 'C:/projects/my-app/.aperant/worktrees/tasks/001-feature', * projectPath: 'C:/projects/my-app', * specId: '001-feature', * logPrefix: '[TASK_DELETE]' @@ -191,7 +191,7 @@ export async function cleanupWorktree(options: WorktreeCleanupOptions): Promise< // Security: Validate that worktreePath is within the expected worktree directories // This prevents path traversal attacks and accidental deletion of wrong directories - // Supports both task worktrees (.auto-claude/worktrees/tasks) and terminal worktrees (.auto-claude/worktrees/terminal) + // Supports both task worktrees (.aperant/worktrees/tasks) and terminal worktrees (.aperant/worktrees/terminal) const taskBase = getTaskWorktreeDir(projectPath); const terminalBase = getTerminalWorktreeDir(projectPath); const isValidPath = isPathWithinBase(worktreePath, taskBase) || isPathWithinBase(worktreePath, terminalBase); diff --git a/apps/desktop/src/main/worktree-paths.ts b/apps/desktop/src/main/worktree-paths.ts index 5d4d874620..31159ce308 100644 --- a/apps/desktop/src/main/worktree-paths.ts +++ b/apps/desktop/src/main/worktree-paths.ts @@ -9,11 +9,11 @@ import path from 'path'; import { existsSync } from 'fs'; // Path constants for worktree directories -export const TASK_WORKTREE_DIR = '.auto-claude/worktrees/tasks'; -export const TERMINAL_WORKTREE_DIR = '.auto-claude/worktrees/terminal'; +export const TASK_WORKTREE_DIR = '.aperant/worktrees/tasks'; +export const TERMINAL_WORKTREE_DIR = '.aperant/worktrees/terminal'; // Metadata directories (separate from git worktrees to avoid uncommitted files) -export const TERMINAL_WORKTREE_METADATA_DIR = '.auto-claude/terminal/metadata'; +export const TERMINAL_WORKTREE_METADATA_DIR = '.aperant/terminal/metadata'; // Legacy path for backwards compatibility export const LEGACY_WORKTREE_DIR = '.worktrees'; diff --git a/apps/desktop/src/preload/api/project-api.ts b/apps/desktop/src/preload/api/project-api.ts index 818c257d26..6c72350f3a 100644 --- a/apps/desktop/src/preload/api/project-api.ts +++ b/apps/desktop/src/preload/api/project-api.ts @@ -5,7 +5,7 @@ import type { ProjectSettings, IPCResult, InitializationResult, - AutoBuildVersionInfo, + AperantVersionInfo, ProjectEnvConfig, GitStatus, KanbanPreferences, @@ -29,7 +29,9 @@ export interface ProjectAPI { settings: Partial ) => Promise; initializeProject: (projectId: string) => Promise>; - checkProjectVersion: (projectId: string) => Promise>; + checkProjectVersion: (projectId: string) => Promise>; + needsMigration: (projectId: string) => Promise; + migrateProject: (projectId: string) => Promise>; // Tab State (persisted in main process for reliability) getTabState: () => Promise>; @@ -155,9 +157,15 @@ export const createProjectAPI = (): ProjectAPI => ({ initializeProject: (projectId: string): Promise> => ipcRenderer.invoke(IPC_CHANNELS.PROJECT_INITIALIZE, projectId), - checkProjectVersion: (projectId: string): Promise> => + checkProjectVersion: (projectId: string): Promise> => ipcRenderer.invoke(IPC_CHANNELS.PROJECT_CHECK_VERSION, projectId), + needsMigration: (projectId: string): Promise => + ipcRenderer.invoke(IPC_CHANNELS.PROJECT_NEEDS_MIGRATION, projectId), + + migrateProject: (projectId: string): Promise> => + ipcRenderer.invoke(IPC_CHANNELS.PROJECT_MIGRATE, projectId), + // Tab State (persisted in main process for reliability) getTabState: (): Promise> => ipcRenderer.invoke(IPC_CHANNELS.TAB_STATE_GET), diff --git a/apps/desktop/src/renderer/App.tsx b/apps/desktop/src/renderer/App.tsx index 021d9b1440..869e06d3b7 100644 --- a/apps/desktop/src/renderer/App.tsx +++ b/apps/desktop/src/renderer/App.tsx @@ -155,7 +155,13 @@ export function App() { const [skippedInitProjectId, setSkippedInitProjectId] = useState(null); const [showAddProjectModal, setShowAddProjectModal] = useState(false); - // GitHub setup state (shown after Auto Claude init) + // Migration dialog state + const [showMigrateDialog, setShowMigrateDialog] = useState(false); + const [migrationProject, setMigrationProject] = useState(null); + const [isMigrating, setIsMigrating] = useState(false); + const [migrateError, setMigrateError] = useState(null); + + // GitHub setup state (shown after Aperant init) const [showGitHubSetup, setShowGitHubSetup] = useState(false); const [gitHubSetupProject, setGitHubSetupProject] = useState(null); @@ -372,16 +378,16 @@ export function App() { setInitError(null); }, []); - // Check if selected project needs initialization (e.g., .auto-claude folder was deleted) + // Check if selected project needs initialization (e.g., .aperant folder was deleted) useEffect(() => { // Don't show dialog while initialization is in progress if (isInitializing) return; // Don't reopen dialog after successful initialization - // (project update with autoBuildPath may not have propagated yet) + // (project update with aperantPath may not have propagated yet) if (initSuccess) return; - if (selectedProject && !selectedProject.autoBuildPath && skippedInitProjectId !== selectedProject.id) { + if (selectedProject && !selectedProject.aperantPath && skippedInitProjectId !== selectedProject.id) { // Project exists but isn't initialized - show init dialog setPendingProject(selectedProject); setInitError(null); // Clear any previous errors @@ -411,7 +417,19 @@ export function App() { const project = await addProject(path); if (project) { openProjectTab(project.id); - if (!project.autoBuildPath) { + // Check migration before init + try { + const requiresMigration = await window.electronAPI.needsMigration(project.id); + if (requiresMigration) { + setMigrationProject(project); + setMigrateError(null); + setShowMigrateDialog(true); + return; + } + } catch { + // Non-critical - fall through to init check + } + if (!project.aperantPath) { setPendingProject(project); setInitError(null); setInitSuccess(false); @@ -639,8 +657,22 @@ export function App() { setShowAddProjectModal(true); }; - const handleProjectAdded = (project: Project, needsInit: boolean) => { + const handleProjectAdded = async (project: Project, needsInit: boolean) => { openProjectTab(project.id); + + // Check for migration before showing init dialog + try { + const requiresMigration = await window.electronAPI.needsMigration(project.id); + if (requiresMigration) { + setMigrationProject(project); + setMigrateError(null); + setShowMigrateDialog(true); + return; + } + } catch (error) { + debugLog('[App] Failed to check migration status:', error); + } + if (needsInit) { setPendingProject(project); setInitError(null); @@ -713,6 +745,43 @@ export function App() { } }; + const handleMigrate = async () => { + if (!migrationProject) return; + + setIsMigrating(true); + setMigrateError(null); + try { + const result = await window.electronAPI.migrateProject(migrationProject.id); + if (result.success) { + setMigrateError(null); + setShowMigrateDialog(false); + // Refresh projects so the updated aperantPath is reflected + await loadProjects(); + setMigrationProject(null); + } else { + setMigrateError(result.error || t('initialize.migrateError')); + } + } catch (error) { + setMigrateError(error instanceof Error ? error.message : t('initialize.migrateError')); + } finally { + setIsMigrating(false); + } + }; + + const handleSkipMigrate = () => { + const project = migrationProject; + setShowMigrateDialog(false); + setMigrateError(null); + setMigrationProject(null); + // After skipping migration, show the init dialog so a fresh .aperant/ is created + if (project && !project.aperantPath) { + setPendingProject(project); + setInitError(null); + setInitSuccess(false); + setShowInitDialog(true); + } + }; + const handleInitialize = async () => { if (!pendingProject) return; @@ -835,6 +904,11 @@ export function App() { onNewTaskClick={() => setIsNewTaskDialogOpen(true)} activeView={activeView} onViewChange={setActiveView} + onMigrationNeeded={(project) => { + setMigrationProject(project); + setMigrateError(null); + setShowMigrateDialog(true); + }} /> {/* Main content */} @@ -1014,7 +1088,52 @@ export function App() { onProjectAdded={handleProjectAdded} /> - {/* Initialize Auto Claude Dialog */} + {/* Migrate Project Data Dialog - shown when legacy data dir exists but .aperant does not */} + { + if (!open && !isMigrating) { + handleSkipMigrate(); + } + }}> + + + + + {t('initialize.migrateTitle')} + + + {t('initialize.migrateDescription')} + + + {migrateError && ( +
+
+ +
+

{t('initialize.migrateError')}

+

{migrateError}

+
+
+
+ )} + + + + +
+
+ + {/* Initialize Aperant Dialog */} { console.warn('[InitDialog] onOpenChange called', { open, pendingProject: !!pendingProject, isInitializing, initSuccess }); // Only trigger skip if user manually closed the dialog @@ -1042,7 +1161,7 @@ export function App() {
  • {t('initialize.setupSpecs')}
  • - {!settings.autoBuildPath && ( + {!settings.aperantPath && (
    @@ -1075,7 +1194,7 @@ export function App() {
    - {/* GitHub Setup Modal - shows after Auto Claude init to configure GitHub */} + {/* GitHub Setup Modal - shows after Aperant init to configure GitHub */} {gitHubSetupProject && ( = {}): Project { id: `project-${Date.now()}-${Math.random().toString(36).substring(7)}`, name: 'Test Project', path: '/path/to/test-project', - autoBuildPath: '.auto-claude', + aperantPath: '.aperant', settings: defaultSettings, createdAt: new Date(), updatedAt: new Date(), diff --git a/apps/desktop/src/renderer/components/AddProjectModal.tsx b/apps/desktop/src/renderer/components/AddProjectModal.tsx index 852f3febcd..b0a21ee0bc 100644 --- a/apps/desktop/src/renderer/components/AddProjectModal.tsx +++ b/apps/desktop/src/renderer/components/AddProjectModal.tsx @@ -76,7 +76,7 @@ export function AddProjectModal({ open, onOpenChange, onProjectAdded }: AddProje } catch { // Non-fatal - main branch can be set later in settings } - onProjectAdded?.(project, !project.autoBuildPath); + onProjectAdded?.(project, !project.aperantPath); onOpenChange(false); } } diff --git a/apps/desktop/src/renderer/components/AgentTools.tsx b/apps/desktop/src/renderer/components/AgentTools.tsx index 4142a4ca79..d420781d16 100644 --- a/apps/desktop/src/renderer/components/AgentTools.tsx +++ b/apps/desktop/src/renderer/components/AgentTools.tsx @@ -152,7 +152,7 @@ const AGENT_CONFIGS: Record = { description: 'Creates implementation plan with subtasks', category: 'build', tools: ['Read', 'Glob', 'Grep', 'Write', 'Edit', 'Bash', 'WebFetch', 'WebSearch'], - mcp_servers: ['context7', 'memory', 'auto-claude'], + mcp_servers: ['context7', 'memory', 'aperant'], mcp_optional: ['linear'], settingsSource: { type: 'phase', phase: 'planning' }, }, @@ -161,7 +161,7 @@ const AGENT_CONFIGS: Record = { description: 'Implements individual subtasks', category: 'build', tools: ['Read', 'Glob', 'Grep', 'Write', 'Edit', 'Bash', 'WebFetch', 'WebSearch'], - mcp_servers: ['context7', 'memory', 'auto-claude'], + mcp_servers: ['context7', 'memory', 'aperant'], mcp_optional: ['linear'], settingsSource: { type: 'phase', phase: 'coding' }, }, @@ -172,7 +172,7 @@ const AGENT_CONFIGS: Record = { description: 'Validates acceptance criteria. Uses Electron or Puppeteer based on project type.', category: 'qa', tools: ['Read', 'Glob', 'Grep', 'Bash', 'WebFetch', 'WebSearch'], - mcp_servers: ['context7', 'memory', 'auto-claude'], + mcp_servers: ['context7', 'memory', 'aperant'], mcp_optional: ['linear', 'electron', 'puppeteer'], settingsSource: { type: 'phase', phase: 'qa' }, }, @@ -181,7 +181,7 @@ const AGENT_CONFIGS: Record = { description: 'Fixes QA-reported issues. Uses Electron or Puppeteer based on project type.', category: 'qa', tools: ['Read', 'Glob', 'Grep', 'Write', 'Edit', 'Bash', 'WebFetch', 'WebSearch'], - mcp_servers: ['context7', 'memory', 'auto-claude'], + mcp_servers: ['context7', 'memory', 'aperant'], mcp_optional: ['linear', 'electron', 'puppeteer'], settingsSource: { type: 'phase', phase: 'qa' }, }, @@ -286,17 +286,17 @@ const MCP_SERVERS: Record s.id); const allAvailableMcpIds = [...ALL_MCP_SERVERS, ...customServerIds]; const availableMcps = allAvailableMcpIds.filter( - mcp => !effectiveMcps.includes(mcp) && !removedMcps.includes(mcp) && mcp !== 'auto-claude' + mcp => !effectiveMcps.includes(mcp) && !removedMcps.includes(mcp) && mcp !== 'aperant' ); return ( @@ -495,7 +495,7 @@ function AgentCard({ id, config, modelLabel, thinkingLabel, overrides, mcpServer const serverInfo = allMcpServers[server]; const ServerIcon = serverInfo?.icon || Server; const isAdded = isCustomAdd(server); - const canRemove = server !== 'auto-claude'; + const canRemove = server !== 'aperant'; return (
    @@ -666,7 +666,7 @@ export function AgentTools() { // Load project env config when project changes useEffect(() => { - if (selectedProjectId && selectedProject?.autoBuildPath) { + if (selectedProjectId && selectedProject?.aperantPath) { setIsLoading(true); window.electronAPI.getProjectEnv(selectedProjectId) .then((result) => { @@ -685,7 +685,7 @@ export function AgentTools() { } else { setEnvConfig(null); } - }, [selectedProjectId, selectedProject?.autoBuildPath]); + }, [selectedProjectId, selectedProject?.aperantPath]); // Update MCP server toggle const updateMcpServer = useCallback(async ( @@ -986,7 +986,7 @@ export function AgentTools() { mcpServers.linearMcpEnabled !== false && envConfig?.linearEnabled, mcpServers.electronEnabled, mcpServers.puppeteerEnabled, - true, // auto-claude always enabled + true, // aperant always enabled ].filter(Boolean).length; // Resolve model and thinking for an agent based on its settings source @@ -1067,7 +1067,7 @@ export function AgentTools() { )} {/* Project not initialized message */} - {selectedProject && !selectedProject.autoBuildPath && ( + {selectedProject && !selectedProject.aperantPath && (

    {t('settings:mcp.projectNotInitialized')}

    diff --git a/apps/desktop/src/renderer/components/GitHubSetupModal.tsx b/apps/desktop/src/renderer/components/GitHubSetupModal.tsx index a0a6ae9667..03833f4b85 100644 --- a/apps/desktop/src/renderer/components/GitHubSetupModal.tsx +++ b/apps/desktop/src/renderer/components/GitHubSetupModal.tsx @@ -50,7 +50,7 @@ interface GitHubSetupModalProps { type SetupStep = 'github-auth' | 'claude-auth' | 'repo-confirm' | 'repo' | 'branch' | 'complete'; /** - * Setup Modal - Required setup flow after Auto Claude initialization + * Setup Modal - Required setup flow after Aperant initialization * * Flow: * 1. Authenticate with GitHub (via gh CLI OAuth) - for repo operations @@ -797,7 +797,7 @@ export function GitHubSetupModal({

    All tasks will be created from branches like{' '} - auto-claude/task-name + aperant/task-name {selectedBranch && ( <> based on {selectedBranch} )} diff --git a/apps/desktop/src/renderer/components/Sidebar.tsx b/apps/desktop/src/renderer/components/Sidebar.tsx index c156d8697d..3d2f2e8d28 100644 --- a/apps/desktop/src/renderer/components/Sidebar.tsx +++ b/apps/desktop/src/renderer/components/Sidebar.tsx @@ -67,6 +67,7 @@ interface SidebarProps { onNewTaskClick: () => void; activeView?: SidebarView; onViewChange?: (view: SidebarView) => void; + onMigrationNeeded?: (project: Project) => void; } interface NavItem { @@ -105,7 +106,8 @@ export function Sidebar({ onSettingsClick, onNewTaskClick, activeView = 'kanban', - onViewChange + onViewChange, + onMigrationNeeded }: SidebarProps) { const { t } = useTranslation(['navigation', 'dialogs', 'common']); const projects = useProjectStore((state) => state.projects); @@ -156,7 +158,7 @@ export function Sidebar({ let isCurrent = true; const initializeEnvConfig = async () => { - if (selectedProject?.id && selectedProject?.autoBuildPath) { + if (selectedProject?.id && selectedProject?.aperantPath) { // Only reload if the project ID differs from what we last loaded if (selectedProject.id !== lastLoadedProjectIdRef.current) { lastLoadedProjectIdRef.current = selectedProject.id; @@ -165,7 +167,7 @@ export function Sidebar({ if (!isCurrent) return; } } else { - // Clear the store if no project is selected or has no autoBuildPath + // Clear the store if no project is selected or has no aperantPath lastLoadedProjectIdRef.current = null; clearProjectEnvConfig(); } @@ -176,7 +178,7 @@ export function Sidebar({ return () => { isCurrent = false; }; - }, [selectedProject?.id, selectedProject?.autoBuildPath]); + }, [selectedProject?.id, selectedProject?.aperantPath]); // Keyboard shortcuts useEffect(() => { @@ -235,8 +237,18 @@ export function Sidebar({ checkGit(); }, [selectedProject]); - const handleProjectAdded = (project: Project, needsInit: boolean) => { + const handleProjectAdded = async (project: Project, needsInit: boolean) => { if (needsInit) { + // Check for migration before showing init dialog + try { + const requiresMigration = await window.electronAPI.needsMigration(project.id); + if (requiresMigration) { + onMigrationNeeded?.(project); + return; + } + } catch (error) { + console.error('Failed to check migration status:', error); + } setPendingProject(project); setShowInitDialog(true); } @@ -477,7 +489,7 @@ export function Sidebar({ className="w-full" size={isCollapsed ? "icon" : "default"} onClick={onNewTaskClick} - disabled={!selectedProjectId || !selectedProject?.autoBuildPath} + disabled={!selectedProjectId || !selectedProject?.aperantPath} > {!isCollapsed && t('actions.newTask')} @@ -487,7 +499,7 @@ export function Sidebar({ {t('actions.newTask')} )} - {!isCollapsed && selectedProject && !selectedProject.autoBuildPath && ( + {!isCollapsed && selectedProject && !selectedProject.aperantPath && (

    {t('messages.initializeToCreateTasks')}

    @@ -495,7 +507,7 @@ export function Sidebar({
    - {/* Initialize Auto Claude Dialog */} + {/* Initialize Aperant Dialog */} { // Only allow closing if user manually closes (not during initialization) if (!open && !isInitializing) { @@ -521,7 +533,7 @@ export function Sidebar({
  • {t('dialogs:initialize.setupSpecs')}
  • - {!settings.autoBuildPath && ( + {!settings.aperantPath && (
    @@ -541,7 +553,7 @@ export function Sidebar({ @@ -60,7 +60,7 @@ export function AutoBuildIntegration({ Initialized
    - {autoBuildPath} + {aperantPath}
    {isCheckingVersion ? ( diff --git a/apps/desktop/src/renderer/components/project-settings/GeneralSettings.tsx b/apps/desktop/src/renderer/components/project-settings/GeneralSettings.tsx index 7899f6dbb8..4da909e54a 100644 --- a/apps/desktop/src/renderer/components/project-settings/GeneralSettings.tsx +++ b/apps/desktop/src/renderer/components/project-settings/GeneralSettings.tsx @@ -21,14 +21,14 @@ import { AVAILABLE_MODELS } from '../../../shared/constants'; import type { Project, ProjectSettings as ProjectSettingsType, - AutoBuildVersionInfo + AperantVersionInfo } from '../../../shared/types'; interface GeneralSettingsProps { project: Project; settings: ProjectSettingsType; setSettings: React.Dispatch>; - versionInfo: AutoBuildVersionInfo | null; + versionInfo: AperantVersionInfo | null; isCheckingVersion: boolean; isUpdating: boolean; handleInitialize: () => Promise; @@ -47,17 +47,17 @@ export function GeneralSettings({ return ( <> - {/* Auto-Build Integration */} + {/* Aperant Integration */}
    -

    Auto-Build Integration

    - {!project.autoBuildPath ? ( +

    Aperant Integration

    + {!project.aperantPath ? (

    Not Initialized

    - Initialize Auto-Build to enable task creation and agent workflows. + Initialize Aperant to enable task creation and agent workflows.

    @@ -88,7 +88,7 @@ export function GeneralSettings({ Initialized
    - {project.autoBuildPath} + {project.aperantPath}
    {isCheckingVersion ? ( @@ -105,7 +105,7 @@ export function GeneralSettings({ )}
    - {project.autoBuildPath && ( + {project.aperantPath && ( <> diff --git a/apps/desktop/src/renderer/components/project-settings/GitHubIntegrationSection.tsx b/apps/desktop/src/renderer/components/project-settings/GitHubIntegrationSection.tsx index 47c79c7d22..0788cc551c 100644 --- a/apps/desktop/src/renderer/components/project-settings/GitHubIntegrationSection.tsx +++ b/apps/desktop/src/renderer/components/project-settings/GitHubIntegrationSection.tsx @@ -141,7 +141,7 @@ export function GitHubIntegrationSection({

    Create a token with repo scope from{' '}

    Import Existing Tasks

    - Select which Linear issues to import into AutoBuild as tasks. + Select which Linear issues to import into Aperant as tasks.