feat(core): rename .auto-claude data folder to .aperant#1956
feat(core): rename .auto-claude data folder to .aperant#1956
Conversation
Renames the project data directory from .auto-claude to .aperant across the entire codebase (113 files) to align with the Aperant rebrand. - All path constants, configs, worktree paths, branch prefixes updated - MCP server ID changed from 'auto-claude' to 'aperant' - Migration dialog added: detects .auto-claude folder and offers rename - .gitignore includes both .auto-claude/ and .aperant/ for compatibility - i18n strings updated (en + fr) - All 207 test files pass (4603 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pre-PR validation fixes for the .auto-claude → .aperant rename: - Align MCP tool name constants (mcp__aperant__*) across registry and tool definitions to prevent silent agent tool call failures - Use IPC_CHANNELS constants and projectId-based lookup instead of raw strings and paths in migration handlers (security) - Add 9 unit tests for needsMigration() and migrateProject() - Replace console.error with debugLog in migration code - Add ensureGitignoreEntries fallback in migrateProject() - Clear migrateError state on dialog close
📝 WalkthroughWalkthroughThis PR rebrands the codebase from "Auto Claude" to "Aperant": renames directory literals (.auto-claude → .aperant), constants/types/fields (autoBuildPath → aperantPath, AutoBuildVersionInfo → AperantVersionInfo), updates MCP/tool identifiers, and adds migration APIs/handlers to detect and migrate existing .auto-claude project data to .aperant. Changes
Sequence Diagram(s)sequenceDiagram
participant Renderer as Renderer (UI)
participant Preload as Preload API
participant Main as Main Process (IPC)
participant Initializer as Project Initializer
participant FS as Filesystem
Renderer->>Preload: needsMigration(projectId)
Preload->>Main: IPC 'project:needs-migration'
Main->>Initializer: needsMigration(projectPath)
Initializer->>FS: exists('.auto-claude')? / exists('.aperant')?
FS-->>Initializer: result
Initializer-->>Main: boolean
Main-->>Preload: IPC result
Preload-->>Renderer: boolean
alt needs migration
Renderer->>Preload: migrateProject(projectId)
Preload->>Main: IPC 'project:migrate'
Main->>Initializer: migrateProject(projectPath)
Initializer->>FS: rename('.auto-claude' -> '.aperant')
Initializer->>FS: update/.gitignore (replace add .aperant)
FS-->>Initializer: success/failure
Initializer-->>Main: InitializationResult
Main-->>Preload: IPC result
Preload-->>Renderer: success/failure
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
| ipcMain.handle(IPC_CHANNELS.PROJECT_MIGRATE, async (_, projectId: string) => { | ||
| const project = projectStore.getProject(projectId); | ||
| if (!project) return { success: false, error: 'Project not found' }; | ||
| return migrateProject(project.path); | ||
| }); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request implements a significant branding change by renaming the core data folder from Highlights
Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request executes a comprehensive rename from .auto-claude to .aperant across the codebase, including the addition of a migration dialog for existing projects. The changes are extensive and largely correct. I've identified a couple of minor issues regarding the .gitignore migration logic and the search exclusion list that could be improved for consistency.
| 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', | ||
| ]); |
There was a problem hiding this comment.
To maintain consistency with the .gitignore changes (which keep both .auto-claude/ and .aperant/), it would be beneficial to also keep .auto-claude in this SKIP_DIRS set. This will prevent the search functionality from indexing old data directories in projects that have been migrated.
| 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', | |
| ]); | |
| const SKIP_DIRS = new Set([ | |
| 'node_modules', '.git', '__pycache__', '.venv', 'venv', 'dist', 'build', | |
| '.next', '.nuxt', 'target', 'vendor', '.idea', '.vscode', | |
| '.auto-claude', '.aperant', '.pytest_cache', '.mypy_cache', 'coverage', '.turbo', '.cache', | |
| 'out', | |
| ]); |
| // Update .gitignore: replace .auto-claude/ with .aperant/ | ||
| const gitignorePath = path.join(projectPath, '.gitignore'); | ||
| try { | ||
| let content = readFileSync(gitignorePath, 'utf-8'); | ||
| content = content.replace(/\.auto-claude\//g, '.aperant/'); | ||
| content = content.replace(/\.auto-claude-security\.json/g, '.aperant-security.json'); | ||
| content = content.replace(/\.auto-claude-status/g, '.aperant-status'); | ||
| writeFileSync(gitignorePath, content, 'utf-8'); | ||
| } catch { | ||
| // .gitignore update is non-critical | ||
| } | ||
| // Ensure .aperant/ is ignored even if .auto-claude/ was never in .gitignore | ||
| ensureGitignoreEntries(projectPath, ['.aperant/']); |
There was a problem hiding this comment.
The current logic replaces .auto-claude entries in .gitignore, which contradicts the goal of keeping both for backward compatibility. A safer approach would be to add the new .aperant entries without removing the old ones. The ensureGitignoreEntries function could be useful here.
// Update .gitignore to ensure new paths are ignored, preserving old ones.
ensureGitignoreEntries(projectPath, [
'.aperant/',
'.aperant-security.json',
'.aperant-status',
]);There was a problem hiding this comment.
Actionable comments posted: 34
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (11)
apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts (1)
332-335:⚠️ Potential issue | 🟡 MinorMissed rename: commit message prefix still uses
auto-claude:.The commit message template still references
auto-claude:while the PR objective is to rename all references toaperant. This should be updated to maintain consistency with the rebranding.
,🔧 Proposed fix
`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` +🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts` around lines 332 - 335, The commit message template still uses the old prefix "auto-claude:"; update the string in the git commit line (the template that builds `git commit -m "auto-claude: ${subtask.id} - ${subtask.description.slice(0, 50)}"`) to use the new brand prefix "aperant:" so the commit message becomes `aperant: ${subtask.id} - ${subtask.description.slice(0, 50)}`.apps/desktop/src/renderer/components/onboarding/GraphitiStep.tsx (1)
118-118:⚠️ Potential issue | 🟠 MajorIncomplete branding update:
auto_claude_memorydefaults should be renamed to align with.aperantdirectory structure.The file uses
.aperantfor the storage path (line 910) but retainsauto_claude_memoryfor the database default (lines 118, 249) and placeholder (line 905). Update these to use a consistent branding name (e.g.,aperant_memoryoraperant). This pattern also appears inSecuritySettings.tsx:429,MemoryBackendSection.tsx:133, and other files—apply the same rename consistently across the codebase.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/onboarding/GraphitiStep.tsx` at line 118, Replace the outdated branding value "auto_claude_memory" with the new consistent name (e.g., "aperant_memory") wherever it is used as a default or placeholder in GraphitiStep.tsx (the database default entry and the placeholder near the storage path usage), and make the same replacement in the other referenced components (SecuritySettings.tsx, MemoryBackendSection.tsx) so defaults, placeholders, and any related keys match the .aperant directory branding; update any variable names/comments that reference auto_claude_memory to the new identifier to keep the codebase consistent.apps/desktop/src/main/ai/runners/__tests__/ideation.test.ts (1)
284-295:⚠️ Potential issue | 🟡 MinorTest name and assertions are out of sync (missing
outputDirassertion).The test claims to validate both
projectDirandoutputDir, but it only asserts project path content. Add an assertion for the.aperant/ideationoutput path to prevent regressions.💡 Suggested test fix
const streamArgs = mockStreamText.mock.calls[0][0]; const systemPrompt = streamArgs.system as string; expect(systemPrompt).toContain('/my/project'); + expect(systemPrompt).toContain('/my/project/.aperant/ideation');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ai/runners/__tests__/ideation.test.ts` around lines 284 - 295, The test for runIdeation names both projectDir and outputDir but only asserts the projectDir; update the test (using mockStreamText and inspecting streamArgs.system as systemPrompt) to also assert that the outputDir is included by adding an expectation that systemPrompt contains the output path '/my/project/.aperant/ideation' (e.g., expect(systemPrompt).toContain('/my/project/.aperant/ideation')); leave the existing projectDir assertion intact.apps/desktop/src/renderer/components/AgentTools.tsx (1)
1186-1192:⚠️ Potential issue | 🟡 MinorAlways-enabled row still uses legacy
autoClaudetranslation keys.If translations were renamed to
aperant, this will surface stale text (or raw keys). Update this row tosettings:mcp.servers.aperant.*for naming consistency with the rest of the migration.Minimal key update
-<span className="text-sm font-medium">{t('settings:mcp.servers.autoClaude.name')}</span> -<p className="text-xs text-muted-foreground">{t('settings:mcp.servers.autoClaude.description')} ({t('settings:mcp.alwaysEnabled')})</p> +<span className="text-sm font-medium">{t('settings:mcp.servers.aperant.name')}</span> +<p className="text-xs text-muted-foreground">{t('settings:mcp.servers.aperant.description')} ({t('settings:mcp.alwaysEnabled')})</p>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/AgentTools.tsx` around lines 1186 - 1192, Update the stale translation keys in the AgentTools component: replace t('settings:mcp.servers.autoClaude.name') and t('settings:mcp.servers.autoClaude.description') with t('settings:mcp.servers.aperant.name') and t('settings:mcp.servers.aperant.description') respectively (the surrounding layout including the always-enabled note t('settings:mcp.alwaysEnabled') stays unchanged); these strings appear in the Auto-Claude/aperant row in AgentTools.tsx where the span and paragraph render the name and description.apps/desktop/src/main/ipc-handlers/sections/task-section.txt (1)
778-784: 🧹 Nitpick | 🔵 TrivialConsider using
getSpecsDir()helper for consistency.Similar to the pattern at line 261, this code uses an inline fallback instead of the
getSpecsDir()helper function used elsewhere in the file. Consolidating to use the helper would reduce code duplication and improve maintainability.♻️ Proposed refactor to use helper function
- const autoBuildDir = project.autoBuildPath || '.aperant'; - const specDir = path.join( - project.path, - autoBuildDir, - 'specs', - task.specId - ); + const specsBaseDir = getSpecsDir(project.autoBuildPath); + const specDir = path.join(project.path, specsBaseDir, task.specId);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ipc-handlers/sections/task-section.txt` around lines 778 - 784, Replace the inline autoBuildDir and specDir path assembly with the existing getSpecsDir helper to match the pattern used at line 261: remove the autoBuildDir and path.join usage that builds specDir from project.autoBuildPath/project.path and task.specId, and instead call getSpecsDir(...) (the same signature used elsewhere, e.g., getSpecsDir(project, task.specId) or getSpecsDir(project) plus specId) to obtain the specs directory; assign specDir from that helper and keep task.specId usage consistent.apps/desktop/src/main/utils/worktree-cleanup.ts (1)
132-142: 🧹 Nitpick | 🔵 TrivialPre-existing:
process.platformusage violates coding guidelines.Line 134 uses
process.platform !== 'win32'directly. Per coding guidelines, platform detection should use functions fromapps/desktop/src/main/platform/such asisWindows().This is pre-existing code, but consider addressing it while touching this file.
♻️ Suggested refactor
+import { isWindows } from '../platform'; ... // All retries exhausted - try shell rm -rf as fallback on Unix // Node's rm() can fail with ENOTEMPTY on macOS .app bundles - if (process.platform !== 'win32') { + if (!isWindows()) { try {As per coding guidelines: "Never use
process.platformdirectly. Import platform detection fromapps/desktop/src/main/platform/instead."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/utils/worktree-cleanup.ts` around lines 132 - 142, Replace the direct process.platform check with the centralized platform helper: import and call isWindows() from the platform utilities and change the condition to if (!isWindows()) around the block that logs and calls execFileSync('/bin/rm', ['-rf', dirPath], ...); ensure you add the import for isWindows at the top of the module and keep existing references to logPrefix and dirPath unchanged so behavior remains the same.apps/desktop/src/renderer/components/project-settings/SecuritySettings.tsx (1)
426-442:⚠️ Potential issue | 🟠 MajorReplace hardcoded UI literals with
react-i18nexttranslation keys.Lines 426, 438, and 441 contain hardcoded user-facing text in TSX. These must use
react-i18nextkeys with entries in bothen/settings.jsonandfr/settings.jsontranslation files.Suggested direction
+ import { useTranslation } from 'react-i18next'; export function SecuritySettings(...) { + const { t } = useTranslation(['settings']); ... - <p className="text-xs text-muted-foreground"> - Stored in ~/.aperant/memories/ - </p> + <p className="text-xs text-muted-foreground"> + {t('settings:security.memory.storedIn', { path: '~/.aperant/memories/' })} + </p> - <p className="text-xs text-muted-foreground"> - Custom storage location. Default: ~/.aperant/memories/ - </p> + <p className="text-xs text-muted-foreground"> + {t('settings:security.memory.customLocationDefault', { path: '~/.aperant/memories/' })} + </p> - <Input placeholder="~/.aperant/memories" ... /> + <Input placeholder={t('settings:security.memory.pathPlaceholder')} ... />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/project-settings/SecuritySettings.tsx` around lines 426 - 442, In SecuritySettings.tsx, replace the hardcoded user-facing literals (the paragraph "Stored in ~/.aperant/memories/", the Label "Database Path (Optional)", the paragraph "Custom storage location. Default: ~/.aperant/memories/", and the two Input placeholders "auto_claude_memory" and "~/.aperant/memories") with react-i18next keys using useTranslation (e.g., t('settings.memoryStored'), t('settings.dbPathLabel'), t('settings.dbPathHelp'), t('settings.placeholderMemory'), t('settings.placeholderDbPath')) and keep wiring to envConfig.memoryDatabase and envConfig.memoryDbPath unchanged; then add corresponding entries to en/settings.json and fr/settings.json with appropriate translations. Ensure you import and call useTranslation at the top of SecuritySettings and replace the literal strings with the t(...) calls.apps/desktop/src/main/utils/git-isolation.ts (1)
166-169: 🧹 Nitpick | 🔵 TrivialMinor: Comment still references "auto-claude branch"
The security comment on lines 166-169 mentions "another task's auto-claude branch" but the actual branch pattern is now
aperant/. Consider updating for consistency.📝 Suggested comment update
// 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.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/utils/git-isolation.ts` around lines 166 - 169, Update the security comment in git-isolation.ts so it references the current branch naming scheme (`aperant/`) instead of "auto-claude branch"; specifically edit the exact comment block that starts with "SECURITY: Use strict exact-match validation..." to mention preventing deletion of another task's `aperant/` branch and ensure the wording makes clear we must fall back to the expected `aperant/` pattern when git rev-parse returns an unexpected branch.apps/desktop/src/renderer/components/project-settings/MemoryBackendSection.tsx (1)
128-145: 🧹 Nitpick | 🔵 TrivialHardcoded strings violate i18n guidelines (pre-existing issue)
The user-facing text in this component uses hardcoded English strings rather than
react-i18nexttranslation keys. While the path updates from.auto-claudeto.aperantare correct, these strings should use i18n for proper localization:
- Line 128:
"Database Name"- Line 130:
"Name for the memory database (stored in ~/.aperant/memories/)"- Line 140:
"Database Path (Optional)"- Line 142:
"Custom storage location. Default: ~/.aperant/memories/"This appears to be a pre-existing issue not introduced by this PR. Consider addressing in a follow-up.
As per coding guidelines: "All frontend user-facing text must use
react-i18nexttranslation keys. Hardcoded strings in JSX/TSX break localization for non-English users."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/project-settings/MemoryBackendSection.tsx` around lines 128 - 145, Replace the hardcoded English strings in MemoryBackendSection.tsx with react-i18next translation keys: import and call useTranslation (e.g., const { t } = useTranslation()) at the top of the component and replace "Database Name", "Name for the memory database (stored in ~/.aperant/memories/)", "Database Path (Optional)", and "Custom storage location. Default: ~/.aperant/memories/" with t('memory.databaseName'), t('memory.databaseDescription'), t('memory.databasePathOptional'), and t('memory.databasePathDescription') (and also translate placeholders like "auto_claude_memory" and "~/.aperant/memories"); ensure the new keys are added to the locale resources in the i18n files in a follow-up change.apps/desktop/src/renderer/lib/mocks/project-mock.ts (1)
9-16:⚠️ Potential issue | 🟡 MinorUpdate mock
autoBuildPathto.aperantfor consistency.
autoBuildPathstill points to.../auto-claude(Line 15), which conflicts with this PR’s canonical folder rename and can skew renderer/mock behavior.Suggested fix
- autoBuildPath: `${projectPath}/auto-claude`, + autoBuildPath: `${projectPath}/.aperant`,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/lib/mocks/project-mock.ts` around lines 9 - 16, The mock addProject function returns an object whose autoBuildPath is hardcoded to `${projectPath}/auto-claude`; update this to use the new canonical folder name `.aperant` by changing the autoBuildPath generation in addProject to `${projectPath}/.aperant` (keep other fields like id, name, path and settings using DEFAULT_PROJECT_SETTINGS unchanged) so mock behavior matches the PR rename.apps/desktop/src/main/ai/config/agent-configs.ts (1)
477-486:⚠️ Potential issue | 🟠 MajorKeep the legacy
auto-claudealias inMCP_SERVER_NAME_MAP.
mapMcpServerName()resolves user-provided MCP override strings. Dropping the old name here turns the rename into a silent breaking change for any existing config that still saysauto-claude.🛠️ Proposed fix
const MCP_SERVER_NAME_MAP: Record<string, string> = { context7: 'context7', 'graphiti-memory': 'memory', graphiti: 'memory', memory: 'memory', linear: 'linear', electron: 'electron', puppeteer: 'puppeteer', + 'auto-claude': 'aperant', 'aperant': 'aperant', };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ai/config/agent-configs.ts` around lines 477 - 486, The MCP_SERVER_NAME_MAP is missing the legacy "auto-claude" alias which mapMcpServerName() still needs to resolve older configs; add an entry for "auto-claude" mapping to the current server key (e.g. map "auto-claude" -> "context7") inside MCP_SERVER_NAME_MAP so older user-provided MCP override strings continue to work and are not silently broken.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/commands/setup-statusline.md:
- Around line 124-126: The guide currently mixes two path names —
`.aperant-status` and `auto-claude/statusline.py` — causing ambiguity; pick one
canonical path (e.g., `.aperant-status/statusline.py` or
`auto-claude/statusline.py`) and update all occurrences in the document so they
match consistently (references in the checklist, the "Try running the command"
example, and the later steps around lines 139–150). Search for strings
`.aperant-status` and `auto-claude/statusline.py` and replace them with the
chosen canonical path in the setup-statusline.md content, and ensure the example
command (the `python ... --format compact` line) and any path-verification steps
reference that same canonical path.
In `@apps/desktop/src/main/__tests__/ipc-handlers.test.ts`:
- Around line 144-147: The fixture directory created in setupTestProject() is
missing the leading dot so tests create "aperant/specs" instead of
".aperant/specs"; update setupTestProject() to create the hidden directory (use
path.join(TEST_PROJECT_PATH, ".aperant", "specs") with mkdirSync and {
recursive: true }) so the TEST_PROJECT_PATH fixture matches the handlers'
expected ".aperant" directory and preserves initialized-project detection.
In `@apps/desktop/src/main/__tests__/project-migration.test.ts`:
- Around line 65-68: The test uses hardcoded POSIX strings for PROJECT,
OLD_PATH, NEW_PATH, and GITIGNORE which won't match path.join outputs on
Windows; update those constants in project-migration.test.ts to be constructed
with path.join(...) (e.g., use path.join('test','project') for PROJECT and then
path.join(PROJECT, '.auto-claude') / path.join(PROJECT, '.aperant') /
path.join(PROJECT, '.gitignore')) so the mocked fs helpers match the paths
produced by project-initializer.ts across platforms.
In `@apps/desktop/src/main/agent/agent-manager.ts`:
- Line 378: The code currently computes resolvedSpecDir using a hardcoded path
('.aperant/specs'), which can diverge from other phases that use
getSpecsDir(project?.autoBuildPath); change resolvedSpecDir to use
getSpecsDir(project?.autoBuildPath) when specDir is undefined and then append
the taskId (i.e., resolvedSpecDir = specDir ??
path.join(getSpecsDir(project?.autoBuildPath), taskId)); update references to
resolvedSpecDir accordingly so spec creation and execution use the same
directory resolution logic.
In `@apps/desktop/src/main/agent/agent-queue.ts`:
- Line 228: The hardcoded '.aperant' fragment is duplicated; replace occurrences
(the outputDir declaration using outputDir = path.join(projectPath, '.aperant',
'ideation') and the other use at line 472) with the centralized constant or
helper used elsewhere (e.g., APERANT_DIR, APERANT_ROOT, or
getAperantPath/projectAperantDir) so both locations derive the same base path;
update the imports to pull that constant/helper and join its returned value with
'ideation' where needed to avoid path drift.
In `@apps/desktop/src/main/ai/memory/graph/incremental-indexer.ts`:
- Line 49: The ignore list in incremental-indexer.ts currently only excludes the
internal folder pattern '**/.aperant/**', so add the legacy internal folder
pattern '**/.auto-claude/**' alongside '**/.aperant/**' wherever it appears (the
occurrences around the existing '**/.aperant/**' entries, including the second
occurrence near line 316) so both internal folders are excluded from indexing
during migration; update the ignore array or glob ignore construction that
contains the '**/.aperant/**' string to include '**/.auto-claude/**'.
In `@apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts`:
- Around line 107-109: The fallback that builds the spec path in
subtask-prompt-generator.ts is using "./aperant/specs/..." which omits the dot
and mismatches the canonical data folder (.aperant) used in
WORKTREE_PATH_PATTERNS; update the returned path construction (the code that
uses resolvedSpec, parts, and returns `./aperant/specs/${parts[parts.length -
1]}`) to include the leading dot so it returns "./.aperant/specs/<name>"
(keeping the same parts extraction logic) to ensure path consistency.
In `@apps/desktop/src/main/ai/security/security-profile.ts`:
- Line 5: Update the top docstring in
apps/desktop/src/main/ai/security/security-profile.ts to accurately describe
where profiles are read from: replace “.aperant/ config” with the actual
root-level filenames used by the module (projectDir/.aperant-security.json and
projectDir/.aperant-allowlist) so the module comment matches the implemented
storage semantics in this file.
In `@apps/desktop/src/main/ai/tools/registry.ts`:
- Line 224: Extract the hardcoded 'aperant' string into a single constant (e.g.,
PROTECTED_SERVER_ID or PROTECTED_TOOL_ID) at the top of
apps/desktop/src/main/ai/tools/registry.ts and replace the literal in the
conditional "if (mapped && mapped !== 'aperant')" with that constant; update any
other occurrences in the same file that refer to the same protected server id so
renames are centralized and avoid drift (search for uses of the literal
'aperant' in this module and replace them with the new constant).
In `@apps/desktop/src/main/changelog/changelog-service.ts`:
- Line 57: Comments in changelog-service.ts incorrectly state the code reads
DEBUG from ".aperant/.env" while the actual implementation uses the path
returned by getAutoBuildSourcePath(); update the comment lines that mention
".aperant/.env" (the ones near the DEBUG checks) to accurately say they load
.env from getAutoBuildSourcePath() (or explicitly from the auto-build
source/prompts path), or alternatively modify the logic to load the .env from an
actual ".aperant" directory if that behavior is intended; locate references to
getAutoBuildSourcePath() and the DEBUG checks and make the comment change or
code change accordingly so the comment matches the behavior.
In `@apps/desktop/src/main/config-paths.ts`:
- Around line 73-77: The comment and variable name in getMemoriesDir are
misleading: rename the variable and update the comment so it accurately reflects
the path being constructed (e.g., change legacyPath to aperantMemoriesPath or
currentMemoriesPath and update the comment to say this is the current
.aperant/memories location rather than “legacy”), or if you intended to keep a
true legacy path, change the path value to the actual legacy location; ensure
all references in getMemoriesDir (the variable name and the explanatory comment)
match the chosen intent.
In `@apps/desktop/src/main/ipc-handlers/github/pr-handlers.ts`:
- Around line 2736-2737: Replace the duplicated path construction with the
centralized helper: use getGitHubDir(project) instead of manually building
githubDir via path.join(project.path, ".aperant", "github"); then build
reviewPath by joining the result of getGitHubDir(project) with "pr" and
`review_${prNumber}.json`; update references to githubDir and reviewPath in this
scope (the variables around the current usage of githubDir, reviewPath,
prNumber) so the code relies on getGitHubDir(project) to avoid path drift.
- Around line 2511-2514: The code writes to tmpFile (const tmpFile =
join(project.path, ".aperant", "tmp_comment_body.txt") and then writeFileSync)
without ensuring the ".aperant" directory exists and uses a fixed filename that
can collide; update the handler to ensure the directory is created (e.g.,
fs.mkdirSync(join(project.path, ".aperant"), { recursive: true }) or equivalent)
before writing and replace the fixed filename with a unique temp filename (use
fs.mkdtempSync, include process.pid/timestamp or use the system tmp API) to
avoid concurrent collisions, then write the body to that unique path and ensure
any needed cleanup after posting the comment; reference tmpFile, join, and
writeFileSync in your changes.
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts`:
- Line 777: Replace the inline path construction that sets gitlabDir (const
gitlabDir = path.join(project.path, '.aperant', 'gitlab')) with a call to the
centralized helper getGitLabDir(project); locate the assignment to gitlabDir in
mr-review-handlers and use getGitLabDir(project) instead so all code uses the
single source of truth for the GitLab directory.
In `@apps/desktop/src/main/ipc-handlers/gitlab/triage-handlers.ts`:
- Line 98: getGitLabDir currently returns path.join(project.path, '.aperant',
'gitlab') without the project-bound validation used in
gitlab/autofix-handlers.ts; update getGitLabDir to build the resolved gitlabDir
(path.join(project.path, '.aperant', 'gitlab')) and then perform the same
explicit boundary check used in autofix-handlers.ts (e.g., resolve both the
project root/expected project base and gitlabDir and ensure gitlabDir is a
subpath of the expected project directory); if the check fails, throw or return
an error instead of returning the path. Ensure you reference and mirror the
validation logic from gitlab/autofix-handlers.ts and keep the check inside the
getGitLabDir function.
In `@apps/desktop/src/main/ipc-handlers/project-handlers.ts`:
- Around line 458-462: The PROJECT_MIGRATE IPC handler currently calls
migrateProject(project.path) but does not persist the updated autoBuildPath into
projectStore, leaving the in-memory project uninitialized; modify the handler
(IPC_CHANNELS.PROJECT_MIGRATE) to await the migrateProject call, extract the new
autoBuildPath (or new path) from its result, assign it to project.autoBuildPath,
and persist the change back into projectStore using the existing persistence
method (e.g., projectStore.updateProject(projectId, { autoBuildPath: newPath })
or projectStore.saveProject(project)). Keep returning the migrateProject result
after persisting so the renderer sees the updated, initialized project.
In `@apps/desktop/src/main/ipc-handlers/README.md`:
- Line 21: Update the README entry for the IPC handler
`project:has-local-source` to replace the stale phrase "auto-claude source" with
"aperant source" so the naming matches the rest of the PR; locate the line that
currently reads "`project:has-local-source` - Check if project has local
auto-claude source" and change the description to mention "aperant" instead.
In `@apps/desktop/src/main/ipc-handlers/sections/task_extracted.txt`:
- Around line 261-262: Replace the duplicated fallback logic that computes
autoBuildDir and specDir with the shared helper: call
getSpecsDir(project.autoBuildPath) to obtain the auto-build dir and then build
specDir via path.join(project.path, specsDir, 'specs', task.specId). Concretely,
remove the autoBuildDir = project.autoBuildPath || '.aperant' code and instead
use const specsDir = getSpecsDir(project.autoBuildPath); const specDir =
path.join(project.path, specsDir, 'specs', task.specId); apply the same
replacement at the other mentioned sites (the other occurrences around lines
778–783 and 1210) so all handlers consistently use getSpecsDir.
In `@apps/desktop/src/main/ipc-handlers/sections/task-section.txt`:
- Line 1210: Replace the inline specDir construction with the existing helper by
calling getSpecsDir(project, task.specId) instead of using
path.join(project.path, project.autoBuildPath || '.aperant', 'specs',
task.specId); find the occurrence that sets specDir and swap it to use
getSpecsDir(...) so behavior and fallback logic are consistent with other
handlers that use getSpecsDir.
- Around line 261-262: Replace the inline fallback and join with the existing
helper: call getSpecsDir(project.autoBuildPath) to get the specs base directory,
then compute specDir by joining that value with task.specId (e.g. const specsDir
= getSpecsDir(project.autoBuildPath); const specDir = path.join(specsDir,
task.specId)); update the references in this handler (currently using
autoBuildDir/project.autoBuildPath and specDir) to use specsDir so path
construction is consistent with other handlers.
In `@apps/desktop/src/main/ipc-handlers/task/crud-handlers.ts`:
- Around line 465-466: The spec path is being built manually using autoBuildDir
and a hardcoded 'specs' folder; replace this with the centralized helper by
calling getSpecsDir(project.autoBuildPath) and then join the returned path with
task.specId to form specDir. Update the code around where specDir is defined
(the block that currently uses autoBuildDir/project.path/'specs'/task.specId) to
use getSpecsDir(project.autoBuildPath) so it matches the approach used in
TASK_CREATE and TASK_DELETE and preserves future changes to the specs
subdirectory structure.
In `@apps/desktop/src/main/ipc-handlers/task/execution-handlers.ts`:
- Line 479: Replace the production console.log call that prints "[TASK_REVIEW]
Cleaned untracked files in main (excluding .aperant)" with the app's production
logger/warning API (e.g., processLogger.warn or the structured logger used
elsewhere) so the message is visible in packaged Electron builds; find the
console.log(...) in execution-handlers.ts (the call containing that exact
message) and change it to use the structured logger method, preserving the
message text and any contextual metadata (task id, path, etc.) as additional
fields.
- Around line 471-473: Replace the hardcoded '.aperant' exclude in the git clean
call with the computed specsBaseDir: in the spawnSync invocation that currently
calls spawnSync(getToolPath('git'), ['clean', '-fd', '-e', '.aperant'], { cwd:
project.path, ... }), pass the exclusion derived from specsBaseDir instead (and
ensure it is expressed relative to project.path if needed, e.g., via
path.relative(project.path, specsBaseDir) or using path.basename if specsBaseDir
is inside the project) so the exclusion follows the resolved
project.autoBuildPath; keep the same spawnSync/getToolPath('git') call and cwd:
project.path settings.
In `@apps/desktop/src/main/ipc-handlers/task/worktree-handlers.ts`:
- Around line 3037-3038: The default PR title uses the old product prefix
("auto-claude:") while branchName is namespaced as `aperant/...`; update the
prTitle assignment to use the new prefix so PR metadata matches the branch
namespace—replace `auto-claude:` with `aperant:` in the prTitle expression
(`const prTitle = options?.title || \`auto-claude: ${task.specId}\``) so it
becomes `const prTitle = options?.title || \`aperant: ${task.specId}\`` (keeping
task.specId and optional override logic unchanged).
- Around line 2702-2703: The orphan-detection uses a hardcoded mainSpecsDir
which builds ".aperant/specs" and can be wrong for projects with a custom
autoBuildPath; update the logic that sets mainSpecsDir (the variable used
alongside tasks and taskLookupSuccessful) to derive the specs directory from
project.autoBuildPath (falling back to the default ".aperant" if autoBuildPath
is unset) before joining the "specs" segment so orphan detection uses the
project's configured build path.
In `@apps/desktop/src/main/project-initializer.ts`:
- Around line 301-316: Before creating the .aperant directory, add a check for
the legacy folder '.auto-claude' (using existsSync with path.join(projectPath,
'.auto-claude')) and if it exists return an error (similar shape to the existing
return when .aperant exists) preventing initialization; update the logic
surrounding dotAutoBuildPath, existsSync, and mkdirSync in the initialization
flow so the presence of '.auto-claude' blocks creating '.aperant' and surfaces a
clear error message instructing the user to migrate or remove the legacy folder.
In `@apps/desktop/src/main/services/profile/profile-manager.ts`:
- Around line 17-21: Comments in profile-manager.ts still reference the old name
"auto-claude" and should be updated to "aperant"; locate occurrences (e.g., near
the getProfilesFilePath function and any other comment blocks mentioning
"auto-claude") and replace those comment strings with "aperant" so all comments
consistently reflect the new path/name.
In `@apps/desktop/src/main/utils/profile-manager.ts`:
- Around line 14-18: The doc comments in profile-manager.ts still refer to the
old "auto-claude" name while the implementation uses "aperant"; update those
stale references—search for occurrences in the file (e.g., the file header
comment and the comment above getProfilesFilePath / any other mentions around
the profiles.json path) and replace "auto-claude" with "aperant" (or otherwise
make the comment consistent with getProfilesFilePath and the path join using
'aperant') so the comments match the code.
In `@apps/desktop/src/renderer/App.tsx`:
- Around line 420-431: The init flow must be gated while needsMigration() is in
flight to prevent concurrent migrate/init dialogs: add a per-project "migration
check pending" flag (e.g., a Map or Set keyed by project.id) and set it before
calling window.electronAPI.needsMigration(...) and clear it in both success and
catch branches (the current code where you call setMigrationProject,
setMigrateError, setShowMigrateDialog should remain). Update the initialization
effect (the effect that opens the init dialog for a project) to check this
pending guard and skip/return if migration check is pending for that project.id
so the migrate path runs exclusively; apply the same guard in the other
add-project path referenced (lines ~660-674).
In `@apps/desktop/src/renderer/components/AgentTools.tsx`:
- Around line 289-292: The new 'aperant' entry in AgentTools (object key
'aperant' with properties name and description in AgentTools.tsx) uses hardcoded
English strings; replace those literals with react-i18next translation keys
(e.g. t('servers.aperant.name') and t('servers.aperant.description') inside the
component where ListChecks is already used), then add corresponding entries for
servers.aperant.name and servers.aperant.description to both the en/*.json and
fr/*.json translation files; ensure the code imports and uses the useTranslation
hook (or existing t) where the metadata is rendered so localization works at
runtime.
In `@apps/desktop/src/renderer/components/onboarding/GraphitiStep.tsx`:
- Around line 909-911: Replace the hardcoded path string in the GraphitiStep
component (GraphitiStep.tsx) with a react-i18next translation key: use the
useTranslation hook and call t('onboarding.graphiti.storagePath', { path:
'~/.aperant/graphs/' }) in place of the literal "Stored in ~/.aperant/graphs/";
also add the matching key "onboarding.graphiti.storagePath": "Stored in
{{path}}" to your en/*.json and fr/*.json locale files so the UI text is
localizable.
In `@apps/desktop/src/shared/i18n/locales/fr/dialogs.json`:
- Line 12: Update the French translation for the initialization failure message
by correcting the elision: locate the JSON key "initFailedDescription" in
dialogs.json and change its value from "Échec de l'initialisation de Aperant.
Veuillez réessayer." to use "d'Aperant" (i.e., "Échec de l'initialisation
d'Aperant. Veuillez réessayer.").
In `@apps/desktop/src/shared/types/project.ts`:
- Around line 223-224: Update the stale comment on the database property:
replace the old default name "auto_claude_memory" with the current canonical
name "aperant_memory" in the comment next to the database?: string declaration
so the documented default matches the rebrand.
In `@CLAUDE.md`:
- Around line 85-90: Update steps 3 and 4 to use the same explicit path prefix
as steps 1-2 so they target the correct files; specifically, change references
to `pr/index.json` and `bot_detection_state.json` to
`.aperant/github/pr/index.json` and `.aperant/github/bot_detection_state.json`
and show the exact reset contents `{"reviews": [], "last_updated": null}` and
`{"reviewed_commits": {}}` respectively.
---
Outside diff comments:
In `@apps/desktop/src/main/ai/config/agent-configs.ts`:
- Around line 477-486: The MCP_SERVER_NAME_MAP is missing the legacy
"auto-claude" alias which mapMcpServerName() still needs to resolve older
configs; add an entry for "auto-claude" mapping to the current server key (e.g.
map "auto-claude" -> "context7") inside MCP_SERVER_NAME_MAP so older
user-provided MCP override strings continue to work and are not silently broken.
In `@apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts`:
- Around line 332-335: The commit message template still uses the old prefix
"auto-claude:"; update the string in the git commit line (the template that
builds `git commit -m "auto-claude: ${subtask.id} -
${subtask.description.slice(0, 50)}"`) to use the new brand prefix "aperant:" so
the commit message becomes `aperant: ${subtask.id} -
${subtask.description.slice(0, 50)}`.
In `@apps/desktop/src/main/ai/runners/__tests__/ideation.test.ts`:
- Around line 284-295: The test for runIdeation names both projectDir and
outputDir but only asserts the projectDir; update the test (using mockStreamText
and inspecting streamArgs.system as systemPrompt) to also assert that the
outputDir is included by adding an expectation that systemPrompt contains the
output path '/my/project/.aperant/ideation' (e.g.,
expect(systemPrompt).toContain('/my/project/.aperant/ideation')); leave the
existing projectDir assertion intact.
In `@apps/desktop/src/main/ipc-handlers/sections/task-section.txt`:
- Around line 778-784: Replace the inline autoBuildDir and specDir path assembly
with the existing getSpecsDir helper to match the pattern used at line 261:
remove the autoBuildDir and path.join usage that builds specDir from
project.autoBuildPath/project.path and task.specId, and instead call
getSpecsDir(...) (the same signature used elsewhere, e.g., getSpecsDir(project,
task.specId) or getSpecsDir(project) plus specId) to obtain the specs directory;
assign specDir from that helper and keep task.specId usage consistent.
In `@apps/desktop/src/main/utils/git-isolation.ts`:
- Around line 166-169: Update the security comment in git-isolation.ts so it
references the current branch naming scheme (`aperant/`) instead of "auto-claude
branch"; specifically edit the exact comment block that starts with "SECURITY:
Use strict exact-match validation..." to mention preventing deletion of another
task's `aperant/` branch and ensure the wording makes clear we must fall back to
the expected `aperant/` pattern when git rev-parse returns an unexpected branch.
In `@apps/desktop/src/main/utils/worktree-cleanup.ts`:
- Around line 132-142: Replace the direct process.platform check with the
centralized platform helper: import and call isWindows() from the platform
utilities and change the condition to if (!isWindows()) around the block that
logs and calls execFileSync('/bin/rm', ['-rf', dirPath], ...); ensure you add
the import for isWindows at the top of the module and keep existing references
to logPrefix and dirPath unchanged so behavior remains the same.
In `@apps/desktop/src/renderer/components/AgentTools.tsx`:
- Around line 1186-1192: Update the stale translation keys in the AgentTools
component: replace t('settings:mcp.servers.autoClaude.name') and
t('settings:mcp.servers.autoClaude.description') with
t('settings:mcp.servers.aperant.name') and
t('settings:mcp.servers.aperant.description') respectively (the surrounding
layout including the always-enabled note t('settings:mcp.alwaysEnabled') stays
unchanged); these strings appear in the Auto-Claude/aperant row in
AgentTools.tsx where the span and paragraph render the name and description.
In `@apps/desktop/src/renderer/components/onboarding/GraphitiStep.tsx`:
- Line 118: Replace the outdated branding value "auto_claude_memory" with the
new consistent name (e.g., "aperant_memory") wherever it is used as a default or
placeholder in GraphitiStep.tsx (the database default entry and the placeholder
near the storage path usage), and make the same replacement in the other
referenced components (SecuritySettings.tsx, MemoryBackendSection.tsx) so
defaults, placeholders, and any related keys match the .aperant directory
branding; update any variable names/comments that reference auto_claude_memory
to the new identifier to keep the codebase consistent.
In
`@apps/desktop/src/renderer/components/project-settings/MemoryBackendSection.tsx`:
- Around line 128-145: Replace the hardcoded English strings in
MemoryBackendSection.tsx with react-i18next translation keys: import and call
useTranslation (e.g., const { t } = useTranslation()) at the top of the
component and replace "Database Name", "Name for the memory database (stored in
~/.aperant/memories/)", "Database Path (Optional)", and "Custom storage
location. Default: ~/.aperant/memories/" with t('memory.databaseName'),
t('memory.databaseDescription'), t('memory.databasePathOptional'), and
t('memory.databasePathDescription') (and also translate placeholders like
"auto_claude_memory" and "~/.aperant/memories"); ensure the new keys are added
to the locale resources in the i18n files in a follow-up change.
In `@apps/desktop/src/renderer/components/project-settings/SecuritySettings.tsx`:
- Around line 426-442: In SecuritySettings.tsx, replace the hardcoded
user-facing literals (the paragraph "Stored in ~/.aperant/memories/", the Label
"Database Path (Optional)", the paragraph "Custom storage location. Default:
~/.aperant/memories/", and the two Input placeholders "auto_claude_memory" and
"~/.aperant/memories") with react-i18next keys using useTranslation (e.g.,
t('settings.memoryStored'), t('settings.dbPathLabel'), t('settings.dbPathHelp'),
t('settings.placeholderMemory'), t('settings.placeholderDbPath')) and keep
wiring to envConfig.memoryDatabase and envConfig.memoryDbPath unchanged; then
add corresponding entries to en/settings.json and fr/settings.json with
appropriate translations. Ensure you import and call useTranslation at the top
of SecuritySettings and replace the literal strings with the t(...) calls.
In `@apps/desktop/src/renderer/lib/mocks/project-mock.ts`:
- Around line 9-16: The mock addProject function returns an object whose
autoBuildPath is hardcoded to `${projectPath}/auto-claude`; update this to use
the new canonical folder name `.aperant` by changing the autoBuildPath
generation in addProject to `${projectPath}/.aperant` (keep other fields like
id, name, path and settings using DEFAULT_PROJECT_SETTINGS unchanged) so mock
behavior matches the PR rename.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 08757deb-77fa-4c73-811d-bc301f2da291
📒 Files selected for processing (122)
.claude/commands/setup-statusline.md.gitignoreCLAUDE.mdapps/desktop/prompts/coder.mdapps/desktop/prompts/planner.mdapps/desktop/prompts/qa_fixer.mdapps/desktop/prompts/qa_reviewer.mdapps/desktop/src/__tests__/e2e/smoke.test.tsapps/desktop/src/__tests__/integration/rate-limit-subtask-recovery.test.tsapps/desktop/src/__tests__/integration/task-lifecycle.test.tsapps/desktop/src/main/__tests__/file-watcher.test.tsapps/desktop/src/main/__tests__/ipc-handlers.test.tsapps/desktop/src/main/__tests__/project-migration.test.tsapps/desktop/src/main/__tests__/project-store.test.tsapps/desktop/src/main/agent/agent-manager.tsapps/desktop/src/main/agent/agent-process.tsapps/desktop/src/main/agent/agent-queue.tsapps/desktop/src/main/ai/client/__tests__/factory.test.tsapps/desktop/src/main/ai/config/__tests__/agent-configs.test.tsapps/desktop/src/main/ai/config/agent-configs.tsapps/desktop/src/main/ai/context/builder.tsapps/desktop/src/main/ai/context/search.tsapps/desktop/src/main/ai/context/types.tsapps/desktop/src/main/ai/mcp/__tests__/client.test.tsapps/desktop/src/main/ai/mcp/__tests__/registry.test.tsapps/desktop/src/main/ai/mcp/registry.tsapps/desktop/src/main/ai/mcp/types.tsapps/desktop/src/main/ai/memory/graph/incremental-indexer.tsapps/desktop/src/main/ai/merge/file-evolution.tsapps/desktop/src/main/ai/merge/orchestrator.tsapps/desktop/src/main/ai/merge/timeline-tracker.tsapps/desktop/src/main/ai/orchestration/__tests__/qa-loop.test.tsapps/desktop/src/main/ai/orchestration/__tests__/qa-reports.test.tsapps/desktop/src/main/ai/orchestration/__tests__/recovery-manager.test.tsapps/desktop/src/main/ai/orchestration/build-orchestrator.tsapps/desktop/src/main/ai/project/analyzer.tsapps/desktop/src/main/ai/project/project-indexer.tsapps/desktop/src/main/ai/prompts/prompt-loader.tsapps/desktop/src/main/ai/prompts/subtask-prompt-generator.tsapps/desktop/src/main/ai/runners/__tests__/commit-message.test.tsapps/desktop/src/main/ai/runners/__tests__/ideation.test.tsapps/desktop/src/main/ai/runners/__tests__/roadmap.test.tsapps/desktop/src/main/ai/runners/commit-message.tsapps/desktop/src/main/ai/runners/github/pr-creator.tsapps/desktop/src/main/ai/runners/ideation.tsapps/desktop/src/main/ai/runners/insights.tsapps/desktop/src/main/ai/runners/roadmap.tsapps/desktop/src/main/ai/security/security-profile.tsapps/desktop/src/main/ai/security/validators/process-validators.tsapps/desktop/src/main/ai/session/__tests__/progress-tracker.test.tsapps/desktop/src/main/ai/tools/__tests__/registry.test.tsapps/desktop/src/main/ai/tools/auto-claude/get-build-progress.tsapps/desktop/src/main/ai/tools/auto-claude/get-session-context.tsapps/desktop/src/main/ai/tools/auto-claude/index.tsapps/desktop/src/main/ai/tools/auto-claude/record-discovery.tsapps/desktop/src/main/ai/tools/auto-claude/record-gotcha.tsapps/desktop/src/main/ai/tools/auto-claude/update-qa-status.tsapps/desktop/src/main/ai/tools/auto-claude/update-subtask-status.tsapps/desktop/src/main/ai/tools/registry.tsapps/desktop/src/main/ai/tools/truncation.tsapps/desktop/src/main/ai/tools/types.tsapps/desktop/src/main/ai/worktree/worktree-manager.tsapps/desktop/src/main/changelog/__tests__/changelog-service.integration.test.tsapps/desktop/src/main/changelog/changelog-service.tsapps/desktop/src/main/config-paths.tsapps/desktop/src/main/insights/paths.tsapps/desktop/src/main/ipc-handlers/README.mdapps/desktop/src/main/ipc-handlers/context/utils.tsapps/desktop/src/main/ipc-handlers/env-handlers.tsapps/desktop/src/main/ipc-handlers/github/__tests__/runner-env-handlers.test.tsapps/desktop/src/main/ipc-handlers/github/autofix-handlers.tsapps/desktop/src/main/ipc-handlers/github/pr-handlers.tsapps/desktop/src/main/ipc-handlers/github/triage-handlers.tsapps/desktop/src/main/ipc-handlers/gitlab/autofix-handlers.tsapps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.tsapps/desktop/src/main/ipc-handlers/gitlab/triage-handlers.tsapps/desktop/src/main/ipc-handlers/project-handlers.tsapps/desktop/src/main/ipc-handlers/sections/task-section.txtapps/desktop/src/main/ipc-handlers/sections/task_extracted.txtapps/desktop/src/main/ipc-handlers/task/__tests__/logs-integration.test.tsapps/desktop/src/main/ipc-handlers/task/__tests__/worktree-branch-validation.test.tsapps/desktop/src/main/ipc-handlers/task/crud-handlers.tsapps/desktop/src/main/ipc-handlers/task/execution-handlers.tsapps/desktop/src/main/ipc-handlers/task/worktree-handlers.tsapps/desktop/src/main/ipc-handlers/terminal/worktree-handlers.tsapps/desktop/src/main/project-initializer.tsapps/desktop/src/main/project-store.tsapps/desktop/src/main/release-service.tsapps/desktop/src/main/services/profile/profile-manager.test.tsapps/desktop/src/main/services/profile/profile-manager.tsapps/desktop/src/main/task-log-service.tsapps/desktop/src/main/utils/git-isolation.tsapps/desktop/src/main/utils/profile-manager.test.tsapps/desktop/src/main/utils/profile-manager.tsapps/desktop/src/main/utils/spec-number-lock.tsapps/desktop/src/main/utils/spec-path-helpers.tsapps/desktop/src/main/utils/worktree-cleanup.tsapps/desktop/src/main/worktree-paths.tsapps/desktop/src/preload/api/project-api.tsapps/desktop/src/renderer/App.tsxapps/desktop/src/renderer/__tests__/project-store-tabs.test.tsapps/desktop/src/renderer/components/AgentTools.tsxapps/desktop/src/renderer/components/__tests__/ProjectTabBar.test.tsxapps/desktop/src/renderer/components/__tests__/SortableProjectTab.test.tsxapps/desktop/src/renderer/components/context/MemoriesTab.tsxapps/desktop/src/renderer/components/github-prs/components/PRDetail.tsxapps/desktop/src/renderer/components/onboarding/GraphitiStep.tsxapps/desktop/src/renderer/components/project-settings/MemoryBackendSection.tsxapps/desktop/src/renderer/components/project-settings/SecuritySettings.tsxapps/desktop/src/renderer/components/task-detail/task-review/CreatePRDialog.test.tsxapps/desktop/src/renderer/lib/mocks/infrastructure-mock.tsapps/desktop/src/renderer/lib/mocks/project-mock.tsapps/desktop/src/renderer/stores/project-store.tsapps/desktop/src/shared/constants/config.tsapps/desktop/src/shared/constants/ipc.tsapps/desktop/src/shared/i18n/locales/en/dialogs.jsonapps/desktop/src/shared/i18n/locales/en/settings.jsonapps/desktop/src/shared/i18n/locales/fr/dialogs.jsonapps/desktop/src/shared/i18n/locales/fr/settings.jsonapps/desktop/src/shared/types/ipc.tsapps/desktop/src/shared/types/project.tsapps/desktop/src/shared/types/terminal.ts
| 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` |
There was a problem hiding this comment.
Use one canonical path name throughout this guide.
Line 124 moved to .aperant-status, but Lines 126/139/145/150 still instruct auto-claude/statusline.py. This mixed naming creates a broken/ambiguous setup path for users.
Proposed doc fix
-3. Try running the command manually: `python auto-claude/statusline.py --format compact`
+3. Try running the command manually: `python aperant/statusline.py --format compact`
...
-python auto-claude/statusline.py --format compact
+python aperant/statusline.py --format compact
...
-python auto-claude/statusline.py --format compact --spec 001-my-feature
+python aperant/statusline.py --format compact --spec 001-my-feature
...
-python ~/projects/my-app/auto-claude/statusline.py --format compact --project-dir ~/projects/my-app
+python ~/projects/my-app/aperant/statusline.py --format compact --project-dir ~/projects/my-appAlso applies to: 139-150
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.claude/commands/setup-statusline.md around lines 124 - 126, The guide
currently mixes two path names — `.aperant-status` and
`auto-claude/statusline.py` — causing ambiguity; pick one canonical path (e.g.,
`.aperant-status/statusline.py` or `auto-claude/statusline.py`) and update all
occurrences in the document so they match consistently (references in the
checklist, the "Try running the command" example, and the later steps around
lines 139–150). Search for strings `.aperant-status` and
`auto-claude/statusline.py` and replace them with the chosen canonical path in
the setup-statusline.md content, and ensure the example command (the `python ...
--format compact` line) and any path-verification steps reference that same
canonical path.
| 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 }); | ||
| } |
There was a problem hiding this comment.
Fix the .aperant fixture path.
setupTestProject() creates aperant/specs without the leading dot, so this shared fixture no longer matches the directory name the handlers detect. That weakens the baseline setup and can hide regressions in initialized-project detection.
🛠️ Proposed fix
function setupTestProject(): void {
mkdirSync(TEST_PROJECT_PATH, { recursive: true });
- mkdirSync(path.join(TEST_PROJECT_PATH, "aperant", "specs"), { recursive: true });
+ mkdirSync(path.join(TEST_PROJECT_PATH, ".aperant", "specs"), { recursive: true });
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/__tests__/ipc-handlers.test.ts` around lines 144 - 147,
The fixture directory created in setupTestProject() is missing the leading dot
so tests create "aperant/specs" instead of ".aperant/specs"; update
setupTestProject() to create the hidden directory (use
path.join(TEST_PROJECT_PATH, ".aperant", "specs") with mkdirSync and {
recursive: true }) so the TEST_PROJECT_PATH fixture matches the handlers'
expected ".aperant" directory and preserves initialized-project detection.
| 'aperant': { | ||
| name: 'Aperant Tools', | ||
| description: 'Build progress tracking, session context, discoveries & gotchas recording', | ||
| icon: ListChecks, |
There was a problem hiding this comment.
Do not add new hardcoded user-facing MCP strings in TSX.
name and description for the new aperant server are English literals and will bypass localization.
Suggested refactor (translation-key based server metadata)
-const MCP_SERVERS: Record<string, { name: string; description: string; icon: React.ElementType; tools?: string[] }> = {
+const MCP_SERVERS: Record<string, { nameKey: string; descriptionKey: string; icon: React.ElementType; tools?: string[] }> = {
...
'aperant': {
- name: 'Aperant Tools',
- description: 'Build progress tracking, session context, discoveries & gotchas recording',
+ nameKey: 'settings:mcp.servers.aperant.name',
+ descriptionKey: 'settings:mcp.servers.aperant.description',
icon: ListChecks,
tools: [
...
],
},-<span className="font-medium">{serverInfo?.name || server}</span>
+<span className="font-medium">{serverInfo ? t(serverInfo.nameKey) : server}</span>-<div className="text-xs text-muted-foreground">{server?.description}</div>
+<div className="text-xs text-muted-foreground">
+ {server ? t(server.descriptionKey) : ''}
+</div>As per coding guidelines, apps/desktop/src/renderer/**/*.{tsx,jsx}: “All frontend user-facing text must use react-i18next translation keys. Hardcoded strings in JSX/TSX break localization for non-English users. Add keys to both en/*.json and fr/*.json translation files.”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/AgentTools.tsx` around lines 289 - 292,
The new 'aperant' entry in AgentTools (object key 'aperant' with properties name
and description in AgentTools.tsx) uses hardcoded English strings; replace those
literals with react-i18next translation keys (e.g. t('servers.aperant.name') and
t('servers.aperant.description') inside the component where ListChecks is
already used), then add corresponding entries for servers.aperant.name and
servers.aperant.description to both the en/*.json and fr/*.json translation
files; ensure the code imports and uses the useTranslation hook (or existing t)
where the metadata is rendered so localization works at runtime.
| <p className="text-xs text-muted-foreground"> | ||
| Stored in ~/.auto-claude/graphs/ | ||
| Stored in ~/.aperant/graphs/ | ||
| </p> |
There was a problem hiding this comment.
Hardcoded user-facing string violates i18n guidelines.
The path text should use a react-i18next translation key instead of a hardcoded string to support localization.
🌐 Proposed fix using translation key
<p className="text-xs text-muted-foreground">
- Stored in ~/.aperant/graphs/
+ {t('onboarding.graphiti.storagePath', { path: '~/.aperant/graphs/' })}
</p>Add the translation key to both en/*.json and fr/*.json:
{
"onboarding": {
"graphiti": {
"storagePath": "Stored in {{path}}"
}
}
}As per coding guidelines: "All frontend user-facing text must use react-i18next translation keys. Hardcoded strings in JSX/TSX break localization for non-English users."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <p className="text-xs text-muted-foreground"> | |
| Stored in ~/.auto-claude/graphs/ | |
| Stored in ~/.aperant/graphs/ | |
| </p> | |
| <p className="text-xs text-muted-foreground"> | |
| {t('onboarding.graphiti.storagePath', { path: '~/.aperant/graphs/' })} | |
| </p> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/onboarding/GraphitiStep.tsx` around
lines 909 - 911, Replace the hardcoded path string in the GraphitiStep component
(GraphitiStep.tsx) with a react-i18next translation key: use the useTranslation
hook and call t('onboarding.graphiti.storagePath', { path: '~/.aperant/graphs/'
}) in place of the literal "Stored in ~/.aperant/graphs/"; also add the matching
key "onboarding.graphiti.storagePath": "Stored in {{path}}" to your en/*.json
and fr/*.json locale files so the UI text is localizable.
| "sourcePathNotConfiguredDescription": "Veuillez définir le chemin source Aperant dans les paramètres de l'application avant d'initialiser.", | ||
| "initFailed": "Échec de l'initialisation", | ||
| "initFailedDescription": "Échec de l'initialisation de Aperant. Veuillez réessayer." | ||
| "initFailedDescription": "Échec de l'initialisation de Aperant. Veuillez réessayer.", |
There was a problem hiding this comment.
Fix French grammar in initialization failure message.
Line [12] should use elision: d'Aperant instead of de Aperant.
✏️ Suggested fix
- "initFailedDescription": "Échec de l'initialisation de Aperant. Veuillez réessayer.",
+ "initFailedDescription": "Échec de l'initialisation d'Aperant. Veuillez réessayer.",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "initFailedDescription": "Échec de l'initialisation de Aperant. Veuillez réessayer.", | |
| "initFailedDescription": "Échec de l'initialisation d'Aperant. Veuillez réessayer.", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/i18n/locales/fr/dialogs.json` at line 12, Update the
French translation for the initialization failure message by correcting the
elision: locate the JSON key "initFailedDescription" in dialogs.json and change
its value from "Échec de l'initialisation de Aperant. Veuillez réessayer." to
use "d'Aperant" (i.e., "Échec de l'initialisation d'Aperant. Veuillez
réessayer.").
| database?: string; // Database name (default: auto_claude_memory) | ||
| dbPath?: string; // Database storage path (default: ~/.auto-claude/memories) | ||
| dbPath?: string; // Database storage path (default: ~/.aperant/memories) |
There was a problem hiding this comment.
Update stale default database-name comment for rebrand consistency.
Line 223 still documents auto_claude_memory as default while this PR standardizes naming to aperant. Please align this comment with the current canonical naming to avoid config/documentation drift.
Suggested doc-only fix
- database?: string; // Database name (default: auto_claude_memory)
+ database?: string; // Database name (default: aperant_memory)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| database?: string; // Database name (default: auto_claude_memory) | |
| dbPath?: string; // Database storage path (default: ~/.auto-claude/memories) | |
| dbPath?: string; // Database storage path (default: ~/.aperant/memories) | |
| database?: string; // Database name (default: aperant_memory) | |
| dbPath?: string; // Database storage path (default: ~/.aperant/memories) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/types/project.ts` around lines 223 - 224, Update the
stale comment on the database property: replace the old default name
"auto_claude_memory" with the current canonical name "aperant_memory" in the
comment next to the database?: string declaration so the documented default
matches the rebrand.
| 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 |
There was a problem hiding this comment.
Use full paths consistently in reset steps 3-4.
In the same instruction block, steps 1-2 use explicit .aperant/github/... paths but steps 3-4 switch to relative names. Please make 3-4 explicit too to avoid targeting the wrong files.
Suggested doc patch
-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
+3. Reset `.aperant/github/pr/index.json` to `{"reviews": [], "last_updated": null}`
+4. Reset `.aperant/github/bot_detection_state.json` to `{"reviewed_commits": {}}` — this is the gatekeeper; without clearing it, the bot detector skips already-seen commits📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 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 | |
| To fully clear all PR review data so reviews run fresh, delete/reset these three things in `.aperant/github/`: | |
| 1. `rm .aperant/github/pr/logs_*.json` — review log files | |
| 2. `rm .aperant/github/pr/review_*.json` — review result files | |
| 3. Reset `.aperant/github/pr/index.json` to `{"reviews": [], "last_updated": null}` | |
| 4. Reset `.aperant/github/bot_detection_state.json` to `{"reviewed_commits": {}}` — this is the gatekeeper; without clearing it, the bot detector skips already-seen commits |
🧰 Tools
🪛 LanguageTool
[uncategorized] ~85-~85: The official name of this software platform is spelled with a capital “H”.
Context: ...esh, delete/reset these three things in .aperant/github/: 1. `rm .aperant/github/pr/logs_*.js...
(GITHUB)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CLAUDE.md` around lines 85 - 90, Update steps 3 and 4 to use the same
explicit path prefix as steps 1-2 so they target the correct files;
specifically, change references to `pr/index.json` and
`bot_detection_state.json` to `.aperant/github/pr/index.json` and
`.aperant/github/bot_detection_state.json` and show the exact reset contents
`{"reviews": [], "last_updated": null}` and `{"reviewed_commits": {}}`
respectively.
Continues the rename initiated in 18453ad. Updates all remaining references in tools, components, handlers, types, tests, and docs from auto-claude/AutoBuild to aperant/Aperant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| }; | ||
| // 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'); |
Check warning
Code scanning / CodeQL
Network data written to file Medium
| }; | ||
| // 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'); |
Check warning
Code scanning / CodeQL
Network data written to file Medium
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 4 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| 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 }); |
There was a problem hiding this comment.
Test setup creates wrong directory path without dot prefix
Medium Severity
The setupTestProject() function creates aperant/specs (no dot prefix) but getAperantPath() only checks for .aperant (with dot). When tests call addProject(), the project won't be detected as initialized because the directory name doesn't match. Other tests in this same file correctly use ".aperant" (e.g., lines 423, 493), confirming the dot prefix is required.
| '.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', |
There was a problem hiding this comment.
SKIP_DIRS dropped .auto-claude breaking unmigrated project searches
Low Severity
The old SKIP_DIRS set contained both auto-claude and .auto-claude. The new set only contains .aperant, dropping .auto-claude entirely. Projects that haven't yet been migrated (still have .auto-claude/ data directories) will now have those directories searched, potentially causing slow searches and irrelevant results from spec/plan metadata files.
There was a problem hiding this comment.
Actionable comments posted: 43
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (12)
apps/desktop/src/renderer/components/AddProjectModal.tsx (1)
121-121:⚠️ Potential issue | 🟡 MinorHardcoded error string violates i18n requirements.
The fallback string
'Failed to create project folder'should use a translation key for localization consistency. As per coding guidelines, all frontend user-facing text must usereact-i18nexttranslation keys.🌐 Proposed fix
- setError(result.error || 'Failed to create project folder'); + setError(result.error || t('addProject.failedToCreate'));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/AddProjectModal.tsx` at line 121, The hardcoded fallback in setError(result.error || 'Failed to create project folder') violates i18n rules; replace the literal with a translation lookup (use the component's i18n hook: e.g. t('projects.createFailed') or an existing key) and ensure useTranslation is imported/used in AddProjectModal.tsx if not already present; keep result.error when present, but call setError(result.error || t('projects.createFailed')) so the UI uses a translation key for the default message.apps/desktop/src/renderer/components/WelcomeScreen.tsx (1)
104-108:⚠️ Potential issue | 🟡 MinorHardcoded string "Initialized" breaks localization.
Line 106 contains a hardcoded English string which violates the coding guideline requiring all frontend user-facing text to use
react-i18nexttranslation keys. The rest of this component correctly uses thet()function.🌐 Proposed fix to use translation key
{project.aperantPath && ( <span className="text-[10px] px-1.5 py-0.5 rounded-full bg-success/20 text-success shrink-0"> - Initialized + {t('welcome:recentProjects.initialized')} </span> )}Add the key to both translation files:
en/welcome.json:
"recentProjects": { ... "initialized": "Initialized" }fr/welcome.json:
"recentProjects": { ... "initialized": "Initialisé" }As per coding guidelines: "All frontend user-facing text must use
react-i18nexttranslation keys. Hardcoded strings in JSX/TSX break localization for non-English users."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/WelcomeScreen.tsx` around lines 104 - 108, Replace the hardcoded "Initialized" string in WelcomeScreen.tsx with a react-i18next lookup (use the t function) — specifically change the span that renders when project.aperantPath to call t('recentProjects.initialized') instead of the literal; then add the key "recentProjects.initialized": "Initialized" to the English translation file and "recentProjects.initialized": "Initialisé" to the French translation file so both locales are covered.apps/desktop/src/main/utils/git-isolation.ts (1)
50-52:⚠️ Potential issue | 🟡 MinorInconsistent naming: "Auto-Claude" reference remains in comment.
The comment on line 51 still references "Auto-Claude manages commits" while this PR renames all references to "Aperant". This should be updated for consistency.
📝 Proposed fix
* Also sets HUSKY=0 to disable the user's pre-commit hooks when - * Auto-Claude manages commits, preventing double-hook execution + * Aperant manages commits, preventing double-hook execution * and potential conflicts.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/utils/git-isolation.ts` around lines 50 - 52, Update the inline comment that currently reads "Auto-Claude manages commits" to use the new product name "Aperant" for consistency; specifically change the sentence that mentions HUSKY=0 disabling the user's pre-commit hooks so it reads that HUSKY=0 is set when "Aperant manages commits", leaving the surrounding text (including the HUSKY=0 reference) unchanged.apps/desktop/src/main/ipc-handlers/changelog-handlers.ts.bk (1)
1-264:⚠️ Potential issue | 🟠 MajorRemove backup file from the repository.
This
.bkfile appears to be an accidentally committed backup and should be deleted. Backup files should be excluded via.gitignoreto prevent accidental commits.#!/bin/bash # Check if there are other backup files accidentally committed fd -e bk -e bak -e orig -e backup🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ipc-handlers/changelog-handlers.ts.bk` around lines 1 - 264, Delete the accidentally committed backup file that contains the duplicate registerChangelogHandlers implementation (the .bk file); remove it from the repository (git rm) and update .gitignore to exclude common backup extensions (bk, bak, orig, backup) so future backups aren’t committed, and ensure the canonical changelog handler implementation (function registerChangelogHandlers) remains in the repo.apps/desktop/src/renderer/components/GitHubSetupModal.tsx (2)
747-748:⚠️ Potential issue | 🟡 MinorUse translation key instead of hardcoded text.
This user-facing description text is hardcoded in English, breaking localization for non-English users. As per coding guidelines, all frontend user-facing text in renderer components must use
react-i18nexttranslation keys. Add a key to bothen/*.jsonandfr/*.jsontranslation files.🌐 Suggested fix using translation key
<DialogDescription> - Choose which branch Aperant should use as the base for creating task branches. + {t('githubSetup.branchSelectionDescription')} </DialogDescription>Then add to translation files:
// en/dialogs.json "githubSetup": { "branchSelectionDescription": "Choose which branch Aperant should use as the base for creating task branches." } // fr/dialogs.json "githubSetup": { "branchSelectionDescription": "Choisissez la branche qu'Aperant doit utiliser comme base pour créer les branches de tâches." }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/GitHubSetupModal.tsx` around lines 747 - 748, Replace the hardcoded English description inside GitHubSetupModal (the DialogDescription element) with a react-i18next translation key; use the component's i18n instance (e.g., t('githubSetup.branchSelectionDescription')) where DialogDescription currently contains the literal string. Add the corresponding key "githubSetup.branchSelectionDescription" with the provided English and French values to the renderer translation files (en/dialogs.json and fr/dialogs.json). Ensure the component imports and uses the useTranslation hook if not already present and update any tests or snapshot text if necessary.
798-804:⚠️ Potential issue | 🟡 MinorUse translation key with interpolation for help text.
This user-facing help text is hardcoded in English. The text should use a translation key that supports interpolation for the branch example and selected branch name.
🌐 Suggested fix using translation key with interpolation
<p className="text-xs text-muted-foreground"> - All tasks will be created from branches like{' '} - <code className="px-1 bg-muted rounded">aperant/task-name</code> - {selectedBranch && ( - <> based on <code className="px-1 bg-muted rounded">{selectedBranch}</code></> - )} + {t('githubSetup.branchPatternHelp', { + pattern: 'aperant/task-name', + baseBranch: selectedBranch + })} </p>Then add to translation files with proper handling of code elements:
// en/dialogs.json "githubSetup": { "branchPatternHelp": "All tasks will be created from branches like <code>{{pattern}}</code>{{baseBranch, ' based on <code>{{baseBranch}}</code>'}}" }Note: You may need to use
Transcomponent fromreact-i18nextfor proper HTML rendering of<code>elements.As per coding guidelines, all frontend user-facing text must use
react-i18nexttranslation keys. Add keys to bothen/*.jsonandfr/*.jsontranslation files.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/GitHubSetupModal.tsx` around lines 798 - 804, The help text in GitHubSetupModal (the paragraph containing the branch pattern and selectedBranch) is hardcoded; replace it with a react-i18next translation key (e.g., "githubSetup.branchPatternHelp") that supports interpolation and HTML/code elements using the Trans component, passing pattern and selectedBranch as interpolation variables (use selectedBranch only when present); add the corresponding key to en and fr translation JSON files (ensuring markup for <code> is preserved or handled via Trans) and update the component to render Trans with values={{ pattern: 'aperant/task-name', baseBranch: selectedBranch }} so the branch example and optional "based on" segment are localized and properly interpolated.apps/desktop/src/main/utils/spec-number-lock.ts (1)
150-167:⚠️ Potential issue | 🟠 MajorReplace hardcoded
.aperantwith variable to match dynamic path handling in main specs scan.Line 157 hardcodes
.aperantwhile line 153 respects thespecsBasevariable. IfaperantPathever differs from the default (via initialization or updates), worktree specs scanning will fail. Use the same variable reference for consistency.💡 Proposed fix
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, '.aperant', 'worktrees', 'tasks'); + const worktreesDir = path.join(this.projectDir, specsBase, 'worktrees', 'tasks'); if (existsSync(worktreesDir)) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/utils/spec-number-lock.ts` around lines 150 - 167, The worktree scan uses a hardcoded '.aperant' path while the initial main specs scan uses the dynamic specsBase variable; update the worktree path construction to use specsBase instead of the literal '.aperant' so worktreeDir and worktreeSpecsDir are built consistently (replace occurrences that join '.aperant' with specsBase in the code around scanSpecsDir, worktreesDir and worktreeSpecsDir where variables like specsBase, worktreesDir, scanSpecsDir and worktree.name are used).apps/desktop/src/main/ipc-handlers/task/crud-handlers.ts (1)
175-195:⚠️ Potential issue | 🟠 MajorUse the global spec-number allocator here too.
This scan only inspects the main specs directory. The store already supports worktree-only specs, so a higher-numbered task that only exists in a worktree can be missed and this handler can reuse its number. Reuse the same global numbering strategy as
ideation/task-converter.tshere.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ipc-handlers/task/crud-handlers.ts` around lines 175 - 195, The handler currently computes specNumber by scanning only the main specsDir (using getSpecsDir and the existingDirs/existingNumbers logic), which can clash with worktree-only specs; replace that local directory-only allocation with the global spec-number allocator used in ideation/task-converter.ts: import and call the allocator (the function exported by ideation/task-converter.ts that returns the next global spec number) to set specNumber instead of the existingDirs -> existingNumbers logic, and remove or fall back from the local scan (the block that computes existingDirs, existingNumbers, and sets specNumber) so the handler uses the same global numbering strategy as the rest of the app.apps/desktop/src/main/ipc-handlers/insights-handlers.ts (1)
81-84:⚠️ Potential issue | 🟡 MinorRemove
console.logstatements from production code.Per coding guidelines,
console.logoutput is invisible in bundled Electron apps. These debug statements should be removed or replaced with proper observability tooling.🛠️ Suggested fix
- console.log("[Insights Handler] Using model config:", { - model: configWithSettings.model, - thinkingLevel: configWithSettings.thinkingLevel, - });As per coding guidelines: "Do not use
console.login production code.console.logoutput is invisible in bundled Electron apps."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ipc-handlers/insights-handlers.ts` around lines 81 - 84, Remove the console.log call in the Insights handler that prints configWithSettings.model and configWithSettings.thinkingLevel; instead use the application's structured logging facility (e.g., replace with processLogger.debug/processLogger.info or the project's main logger) or drop the debug entirely so no console logging remains in production. Update the code referencing configWithSettings.model and configWithSettings.thinkingLevel in the same block (the console.log shown) to call the chosen logger with a descriptive message and include the values as structured fields rather than using console.log.apps/desktop/src/renderer/components/project-settings/GeneralSettings.tsx (2)
20-25: 🛠️ Refactor suggestion | 🟠 MajorUse
@shared/*in this touched import block.These
../../../shared/*imports make the component brittle to moves/renames. Please switch this block to the repo’s shared path aliases while you’re touching it.♻️ Suggested change
-import { AVAILABLE_MODELS } from '../../../shared/constants'; +import { AVAILABLE_MODELS } from '@shared/constants'; import type { Project, ProjectSettings as ProjectSettingsType, AperantVersionInfo -} from '../../../shared/types'; +} from '@shared/types';As per coding guidelines:
apps/desktop/src/**/*.{ts,tsx}: Use path aliases defined in tsconfig.json for imports:@/*(renderer),@shared/*(shared),@preload/*(preload),@features/*,@components/*,@hooks/*,@lib/*.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/project-settings/GeneralSettings.tsx` around lines 20 - 25, The import block in GeneralSettings.tsx uses relative paths ('../../../shared/constants' and '../../../shared/types') which are brittle; replace those with the shared path alias imports using `@shared` — e.g. import { AVAILABLE_MODELS } from '@shared/constants' and import the types Project, ProjectSettings (as ProjectSettingsType), AperantVersionInfo from '@shared/types' — update the import statements referencing AVAILABLE_MODELS, Project, ProjectSettingsType, and AperantVersionInfo accordingly so the component uses the tsconfig path alias `@shared/`*.
50-76:⚠️ Potential issue | 🟠 MajorLocalize the new Aperant copy.
This block adds new English text directly in JSX even though the component already uses
useTranslation. Move the new heading/help/button text intosettingskeys and add matching entries in both locales.As per coding guidelines:
apps/desktop/src/renderer/**/*.{tsx,jsx}: All frontend user-facing text must usereact-i18nexttranslation keys. Hardcoded strings in JSX/TSX break localization for non-English users. Add keys to bothen/*.jsonandfr/*.jsontranslation files.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/project-settings/GeneralSettings.tsx` around lines 50 - 76, The new hardcoded strings in GeneralSettings.tsx (heading "Aperant Integration", "Not Initialized", helper copy "Initialize Aperant to enable task creation and agent workflows.", and button labels "Initializing..." / "Initialize Aperant") must be replaced with react-i18next keys from the settings namespace; update the JSX to use t('settings.aperant.title'), t('settings.aperant.statusNotInitialized'), t('settings.aperant.help'), and t('settings.aperant.initializeButton' / 'settings.aperant.initializing') (or similar) and keep using existing handlers like handleInitialize and isUpdating; then add the corresponding entries under the settings section in both locales (en JSON and fr JSON) so both languages have these new keys.apps/desktop/src/shared/constants/config.ts (1)
98-119:⚠️ Potential issue | 🟡 MinorRemove the unused
MEMORY_STATEconstant or update its path to match the documented.aperant/structure.
APERANT_PATHS.MEMORY_STATEappears to be dead code. A codebase search shows it's never referenced anywhere, while memory state is actually managed viagetMemoriesDir()inconfig-paths.ts, which correctly handles migration from.auto-claude/memoriesto.aperant/memories. Either remove the constant or, if retained for backward compatibility, update it to'.aperant/.memory_state.json'and add a comment explaining its purpose.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/shared/constants/config.ts` around lines 98 - 119, APERANT_PATHS currently defines MEMORY_STATE as '.memory_state.json' but it's unused and inconsistent with the documented .aperant/ layout; either remove APERANT_PATHS.MEMORY_STATE or update it to '.aperant/.memory_state.json' and add a short comment explaining it's kept for backward compatibility and that real memory handling is done via getMemoriesDir() in config-paths.ts (which migrates from .auto-claude/memories to .aperant/memories); modify the APERANT_PATHS object accordingly and run a code search to ensure no references break.
♻️ Duplicate comments (7)
apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts (1)
107-109:⚠️ Potential issue | 🟡 MinorMissing dot prefix on folder name creates path inconsistency.
The fallback path uses
./aperant/specs/...but the canonical data folder is.aperant(hidden folder with dot prefix), as used in theWORKTREE_PATH_PATTERNSabove. This inconsistency could cause spec files to reference an incorrect location.🐛 Proposed fix
// Fallback: just use the spec dir name const parts = resolvedSpec.split(/[/\\]/); - return `./aperant/specs/${parts[parts.length - 1]}`; + return `./.aperant/specs/${parts[parts.length - 1]}`;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ai/prompts/subtask-prompt-generator.ts` around lines 107 - 109, The fallback return path uses the wrong folder name; change the fallback in the subtask path generator so it returns the hidden canonical folder `.aperant` instead of `aperant`—locate the code that builds the fallback from resolvedSpec (the const parts = resolvedSpec.split(/[/\\]/); and the return statement) and update the returned string to use `./.aperant/specs/${parts[parts.length - 1]}` to match WORKTREE_PATH_PATTERNS and ensure consistent spec file resolution.apps/desktop/src/main/ipc-handlers/task/worktree-handlers.ts (1)
2702-2703:⚠️ Potential issue | 🟠 MajorUse the configured specs dir for orphan detection.
mainSpecsDiris still hardcoded to.aperant/specs. If a project stores specs under a non-defaultaperantPath,taskLookupSuccessfulcan flip to true even when the task cache lookup actually failed, and healthy worktrees will be surfaced as orphaned.Minimal fix
- const mainSpecsDir = path.join(project.path, '.aperant', 'specs'); + const mainSpecsDir = path.join(project.path, getSpecsDir(project.aperantPath));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ipc-handlers/task/worktree-handlers.ts` around lines 2702 - 2703, The orphan-detection uses a hardcoded mainSpecsDir; replace it so it joins the project's configured aperant path instead of the literal ".aperant" (e.g. compute mainSpecsDir using project.aperantPath or the project's configured aperant path with a fallback to ".aperant" and then 'specs') so taskLookupSuccessful is based on the real specs location; update the code that sets mainSpecsDir and keep the subsequent taskLookupSuccessful calculation unchanged (look for mainSpecsDir and taskLookupSuccessful in worktree-handlers.ts).apps/desktop/src/main/ipc-handlers/sections/task-section.txt (1)
261-263:⚠️ Potential issue | 🟠 MajorConsolidate spec-dir resolution to
getSpecsDir(project.aperantPath)everywhere.These updated paths still use inline fallback +
'specs', which can drift from the helper-based resolution used elsewhere in this handler.🔧 Proposed refactor pattern
-const aperantDir = project.aperantPath || '.aperant'; -const specDir = path.join(project.path, aperantDir, 'specs', task.specId); +const specsBaseDir = getSpecsDir(project.aperantPath); +const specDir = path.join(project.path, specsBaseDir, task.specId);Also applies to: 778-783, 1210-1210
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ipc-handlers/sections/task-section.txt` around lines 261 - 263, The inline spec-dir resolution using aperantDir and path.join (the variables aperantDir and specDir) should be replaced with the centralized helper getSpecsDir(project.aperantPath) to avoid drift; update every occurrence (including the shown block and the other instances around lines referenced) to call getSpecsDir(project.aperantPath) and use its return value instead of computing aperantDir || '.aperant' and appending 'specs' manually so all spec directory resolution is consistent across this handler.apps/desktop/src/main/agent/agent-manager.ts (1)
378-378:⚠️ Potential issue | 🟠 MajorUse
getSpecsDir()for all fallback spec paths (not hardcoded.aperant/specs).
resolvedSpecDirand restart-timeplanPathbypass project-specific specs resolution, which can target the wrong directory whenaperantPathdiffers.🔧 Proposed fix
- const resolvedSpecDir = specDir ?? path.join(projectPath, '.aperant', 'specs', taskId); + const project = projectStore.getProjects().find((p) => p.id === projectId || p.path === projectPath); + const specsBaseDir = getSpecsDir(project?.aperantPath); + const resolvedSpecDir = specDir ?? path.join(projectPath, specsBaseDir, taskId);- const planPath = context.specDir - ? path.join(context.specDir, APERANT_PATHS.IMPLEMENTATION_PLAN) - : path.join(context.projectPath, APERANT_PATHS.SPECS_DIR, context.specId, APERANT_PATHS.IMPLEMENTATION_PLAN); + const restartProject = context.projectId + ? projectStore.getProject(context.projectId) + : projectStore.getProjects().find((p) => p.path === context.projectPath); + const specsBaseDir = getSpecsDir(restartProject?.aperantPath); + const planPath = context.specDir + ? path.join(context.specDir, APERANT_PATHS.IMPLEMENTATION_PLAN) + : path.join(context.projectPath, specsBaseDir, context.specId, APERANT_PATHS.IMPLEMENTATION_PLAN);Also applies to: 823-825
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/agent/agent-manager.ts` at line 378, resolvedSpecDir is using a hardcoded fallback path (path.join(projectPath, '.aperant', 'specs', taskId)) which bypasses project-specific resolution; replace that fallback with a call to the central resolver getSpecsDir(...) and do the same for the restart-time planPath code paths (the block around the later restart logic referenced at lines ~823-825). Import or reference getSpecsDir and call it with the same inputs used elsewhere (projectPath and taskId or equivalent) to compute the spec directory instead of hardcoding ".aperant/specs", and update both resolvedSpecDir and the restart-time planPath resolution to use getSpecsDir so all code paths use the same project-specific resolution.apps/desktop/src/main/project-initializer.ts (1)
301-316:⚠️ Potential issue | 🟠 MajorBlock initialization while legacy
.auto-claudedata still exists.
initializeProject()still creates.aperantwhenever.aperantis missing, even if.auto-claudeis present. That can split a project between the legacy and new data folders instead of forcing the rename flow.🐛 Suggested change
// Check if already initialized const dotAperantPath = path.join(projectPath, '.aperant'); + + if (needsMigration(projectPath)) { + return { + success: false, + error: 'Legacy .auto-claude data detected. Migrate the project before initializing.' + }; + } if (existsSync(dotAperantPath)) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/project-initializer.ts` around lines 301 - 316, The initializer currently only checks for '.aperant' and will create it even when a legacy '.auto-claude' folder exists; update initializeProject (the block that defines dotAperantPath and checks existsSync) to also compute dotAutoClaudePath = path.join(projectPath, '.auto-claude') and if existsSync(dotAutoClaudePath) return a failure (similar shape to the existing return) that blocks creation of '.aperant' and instructs the user to run the rename/migration flow. Ensure you reference dotAutoClaudePath in the check and do not proceed to mkdirSync(dotAperantPath) when the legacy folder exists.apps/desktop/src/main/changelog/changelog-service.ts (1)
55-57:⚠️ Potential issue | 🟡 MinorThe DEBUG comments still don't describe the actual
.envlookup.
loadAperantEnv()reads from the path returned bygetAperantSourcePath(), not a literalaperant/.env, so these comments are still misleading.Also applies to: 81-81, 119-125
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/changelog/changelog-service.ts` around lines 55 - 57, Update the misleading DEBUG comment to accurately describe where the .env is loaded from: mention that loadAperantEnv() reads the env file from the path returned by getAperantSourcePath() (not a literal "aperant/.env") and update any other DEBUG-related comments (the ones near the loadAperantEnv/getAperantSourcePath usage and similar comments around the DEBUG checks) to state that DEBUG is resolved by loading that path and then checking process.env; adjust wording in the comments near loadAperantEnv(), getAperantSourcePath(), and any DEBUG-checking helpers so they reflect the actual lookup behavior.apps/desktop/src/renderer/App.tsx (1)
381-397:⚠️ Potential issue | 🟠 MajorCheck migration before the init dialog can open.
Line 390 still opens initialization for any selected project without
aperantPath, butneedsMigration()only runs afteropenProjectTab()in the add-project paths. That means previously saved legacy projects bypass migration on startup, and newly added ones can still race into competing migrate/init dialogs. Gate the init effect on a per-project “migration check pending” flag and run the same check on selected-project changes.Also applies to: 419-431, 663-674
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/App.tsx` around lines 381 - 397, The init dialog effect currently shows initialization whenever a selectedProject lacks aperantPath, but it must wait for the project's migration check to complete; add and use a per-project "migration check pending" flag (e.g., migrationCheckPending map keyed by project.id or a migrationChecked set) and ensure the useEffect that shows the init dialog (the effect referencing selectedProject, skippedInitProjectId, isInitializing, initSuccess) also gates on migrationCheckPending[selectedProject.id] being false; run the same needsMigration() invocation that openProjectTab() uses (and set/clear the pending flag) whenever selectedProject changes so previously-saved legacy projects don't bypass migration and to avoid racing migrate vs init dialogs—update calls to setPendingProject/setShowInitDialog to only run after the migration check finishes and indicates no migration is required.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9272a1fb-2601-42d1-822b-e0ecfe96d13f
📒 Files selected for processing (131)
apps/desktop/e2e/flows.e2e.tsapps/desktop/src/__tests__/setup.tsapps/desktop/src/main/__tests__/insights-config.test.tsapps/desktop/src/main/__tests__/project-store.test.tsapps/desktop/src/main/agent/agent-manager.tsapps/desktop/src/main/agent/agent-process.test.tsapps/desktop/src/main/agent/agent-process.tsapps/desktop/src/main/agent/agent-queue.tsapps/desktop/src/main/ai/auth/codex-oauth.tsapps/desktop/src/main/ai/mcp/registry.tsapps/desktop/src/main/ai/prompts/subtask-prompt-generator.tsapps/desktop/src/main/ai/schema/structured-output.tsapps/desktop/src/main/ai/tools/aperant/get-build-progress.tsapps/desktop/src/main/ai/tools/aperant/get-session-context.tsapps/desktop/src/main/ai/tools/aperant/index.tsapps/desktop/src/main/ai/tools/aperant/record-discovery.tsapps/desktop/src/main/ai/tools/aperant/record-gotcha.tsapps/desktop/src/main/ai/tools/aperant/update-qa-status.tsapps/desktop/src/main/ai/tools/aperant/update-subtask-status.tsapps/desktop/src/main/ai/tools/providers/fetch-browse.tsapps/desktop/src/main/ai/worktree/worktree-manager.tsapps/desktop/src/main/changelog/changelog-service.tsapps/desktop/src/main/changelog/generator.tsapps/desktop/src/main/changelog/types.tsapps/desktop/src/main/changelog/version-suggester.tsapps/desktop/src/main/claude-code-settings/index.tsapps/desktop/src/main/config-paths.tsapps/desktop/src/main/index.tsapps/desktop/src/main/insights-service.tsapps/desktop/src/main/insights/README.mdapps/desktop/src/main/insights/REFACTORING_NOTES.mdapps/desktop/src/main/insights/config.tsapps/desktop/src/main/ipc-handlers/README.mdapps/desktop/src/main/ipc-handlers/agent-events-handlers.tsapps/desktop/src/main/ipc-handlers/changelog-handlers.tsapps/desktop/src/main/ipc-handlers/changelog-handlers.ts.bkapps/desktop/src/main/ipc-handlers/claude-code-handlers.tsapps/desktop/src/main/ipc-handlers/context/README.mdapps/desktop/src/main/ipc-handlers/context/project-context-handlers.tsapps/desktop/src/main/ipc-handlers/context/utils.tsapps/desktop/src/main/ipc-handlers/env-handlers.tsapps/desktop/src/main/ipc-handlers/github/__tests__/runner-env-handlers.test.tsapps/desktop/src/main/ipc-handlers/github/spec-utils.tsapps/desktop/src/main/ipc-handlers/github/utils.tsapps/desktop/src/main/ipc-handlers/gitlab/spec-utils.tsapps/desktop/src/main/ipc-handlers/gitlab/utils.tsapps/desktop/src/main/ipc-handlers/ideation/idea-manager.tsapps/desktop/src/main/ipc-handlers/ideation/session-manager.tsapps/desktop/src/main/ipc-handlers/ideation/task-converter.tsapps/desktop/src/main/ipc-handlers/insights-handlers.tsapps/desktop/src/main/ipc-handlers/linear-handlers.tsapps/desktop/src/main/ipc-handlers/mcp-handlers.tsapps/desktop/src/main/ipc-handlers/memory-handlers.tsapps/desktop/src/main/ipc-handlers/project-handlers.tsapps/desktop/src/main/ipc-handlers/roadmap-handlers.tsapps/desktop/src/main/ipc-handlers/sections/context-roadmap-section.txtapps/desktop/src/main/ipc-handlers/sections/context_extracted.txtapps/desktop/src/main/ipc-handlers/sections/ideation-insights-section.txtapps/desktop/src/main/ipc-handlers/sections/integration-section.txtapps/desktop/src/main/ipc-handlers/sections/roadmap_extracted.txtapps/desktop/src/main/ipc-handlers/sections/task-section.txtapps/desktop/src/main/ipc-handlers/sections/task_extracted.txtapps/desktop/src/main/ipc-handlers/settings-handlers.tsapps/desktop/src/main/ipc-handlers/task/README.mdapps/desktop/src/main/ipc-handlers/task/__tests__/logs-integration.test.tsapps/desktop/src/main/ipc-handlers/task/crud-handlers.tsapps/desktop/src/main/ipc-handlers/task/execution-handlers.tsapps/desktop/src/main/ipc-handlers/task/logs-handlers.tsapps/desktop/src/main/ipc-handlers/task/plan-file-utils.tsapps/desktop/src/main/ipc-handlers/task/worktree-handlers.tsapps/desktop/src/main/ipc-handlers/terminal/worktree-handlers.tsapps/desktop/src/main/project-initializer.tsapps/desktop/src/main/project-store.tsapps/desktop/src/main/release-service.tsapps/desktop/src/main/sentry.tsapps/desktop/src/main/services/profile/profile-manager.tsapps/desktop/src/main/task-state-manager.tsapps/desktop/src/main/terminal-name-generator.tsapps/desktop/src/main/terminal/cli-integration-handler.tsapps/desktop/src/main/terminal/pty-daemon-client.tsapps/desktop/src/main/terminal/pty-daemon.tsapps/desktop/src/main/title-generator.tsapps/desktop/src/main/updater/path-resolver.tsapps/desktop/src/main/utils/git-isolation.tsapps/desktop/src/main/utils/profile-manager.tsapps/desktop/src/main/utils/spec-number-lock.tsapps/desktop/src/preload/api/project-api.tsapps/desktop/src/renderer/App.tsxapps/desktop/src/renderer/__tests__/project-store-tabs.test.tsapps/desktop/src/renderer/components/AddProjectModal.tsxapps/desktop/src/renderer/components/AgentTools.tsxapps/desktop/src/renderer/components/GitHubSetupModal.tsxapps/desktop/src/renderer/components/Sidebar.tsxapps/desktop/src/renderer/components/WelcomeScreen.tsxapps/desktop/src/renderer/components/__tests__/ProjectTabBar.test.tsxapps/desktop/src/renderer/components/__tests__/SortableProjectTab.test.tsxapps/desktop/src/renderer/components/github-issues/README.mdapps/desktop/src/renderer/components/linear-import/LinearTaskImportModalRefactored.tsxapps/desktop/src/renderer/components/onboarding/OllamaModelSelector.tsxapps/desktop/src/renderer/components/onboarding/OnboardingWizard.tsxapps/desktop/src/renderer/components/project-settings/AperantIntegration.tsxapps/desktop/src/renderer/components/project-settings/GeneralSettings.tsxapps/desktop/src/renderer/components/project-settings/GitHubIntegrationSection.tsxapps/desktop/src/renderer/components/project-settings/IntegrationSettings.tsxapps/desktop/src/renderer/components/project-settings/LinearIntegrationSection.tsxapps/desktop/src/renderer/components/project-settings/README.mdapps/desktop/src/renderer/components/project-settings/hooks/useProjectSettings.tsapps/desktop/src/renderer/components/project-settings/index.tsapps/desktop/src/renderer/components/settings/AdvancedSettings.tsxapps/desktop/src/renderer/components/settings/GeneralSettings.tsxapps/desktop/src/renderer/components/settings/README.mdapps/desktop/src/renderer/components/settings/REFACTORING_SUMMARY.mdapps/desktop/src/renderer/components/settings/common/InitializationGuard.tsxapps/desktop/src/renderer/components/settings/integrations/GitHubIntegration.tsxapps/desktop/src/renderer/components/settings/integrations/LinearIntegration.tsxapps/desktop/src/renderer/components/settings/sections/SectionRouter.tsxapps/desktop/src/renderer/components/shared/MemoryConfigPanel.tsxapps/desktop/src/renderer/lib/mocks/mock-data.tsapps/desktop/src/renderer/lib/mocks/project-mock.tsapps/desktop/src/renderer/lib/terminal-theme.tsapps/desktop/src/renderer/stores/project-store.tsapps/desktop/src/renderer/stores/settings-store.tsapps/desktop/src/renderer/styles/globals.cssapps/desktop/src/shared/constants.tsapps/desktop/src/shared/constants/config.tsapps/desktop/src/shared/types.tsapps/desktop/src/shared/types/ipc.tsapps/desktop/src/shared/types/project.tsapps/desktop/src/shared/types/settings.tsapps/desktop/src/shared/types/task.tsapps/desktop/src/shared/types/terminal.ts
| // Check for aperant directory detection | ||
| const aperantPath = path.join(projectPath, '.aperant'); | ||
| expect(existsSync(aperantPath)).toBe(true); |
There was a problem hiding this comment.
Assertion cannot pass because fixture setup still creates auto-claude/ paths.
At Line 236, the .aperant existence check is correct for the rename, but this file still creates and writes test data under auto-claude/ (e.g., Line 25, Line 37, and other path joins). That makes this assertion deterministically fail (matches the pipeline error).
💡 Proposed fix (align all test fixtures/paths in this file to .aperant)
function setupTestEnvironment(): void {
TEST_DATA_DIR = mkdtempSync(path.join(os.tmpdir(), 'auto-claude-ui-e2e-'));
TEST_PROJECT_DIR = path.join(TEST_DATA_DIR, 'test-project');
mkdirSync(TEST_PROJECT_DIR, { recursive: true });
- mkdirSync(path.join(TEST_PROJECT_DIR, 'auto-claude', 'specs'), { recursive: true });
+ mkdirSync(path.join(TEST_PROJECT_DIR, '.aperant', 'specs'), { recursive: true });
}
function createTestSpec(specId: string, status: 'pending' | 'in_progress' | 'completed' = 'pending'): void {
- const specDir = path.join(TEST_PROJECT_DIR, 'auto-claude', 'specs', specId);
+ const specDir = path.join(TEST_PROJECT_DIR, '.aperant', 'specs', specId);
mkdirSync(specDir, { recursive: true });
@@
- const specDir = path.join(TEST_PROJECT_DIR, 'auto-claude', 'specs', '001-test-spec');
+ const specDir = path.join(TEST_PROJECT_DIR, '.aperant', 'specs', '001-test-spec');
@@
- const specsDir = path.join(TEST_PROJECT_DIR, 'auto-claude', 'specs');
+ const specsDir = path.join(TEST_PROJECT_DIR, '.aperant', 'specs');
@@
- const specDir = path.join(TEST_PROJECT_DIR, 'auto-claude', 'specs', specId);
+ const specDir = path.join(TEST_PROJECT_DIR, '.aperant', 'specs', specId);
@@
TEST_PROJECT_DIR,
- 'auto-claude',
+ '.aperant',
'specs',
'001-task',
'implementation_plan.json'
@@
TEST_PROJECT_DIR,
- 'auto-claude',
+ '.aperant',
'specs',
'001-review',
'qa_report.md'
@@
TEST_PROJECT_DIR,
- 'auto-claude',
+ '.aperant',
'specs',
'001-reject',
'QA_FIX_REQUEST.md'🧰 Tools
🪛 GitHub Actions: E2E
[error] 236-236: E2E test failed: aperant directory detection assertion. Expected existsSync('.aperant') to be true but it was false.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/e2e/flows.e2e.ts` around lines 234 - 236, The assertion checking
for aperantPath (const aperantPath = path.join(projectPath, '.aperant')) fails
because test fixtures still create/write under "auto-claude/" paths; update all
fixture path joins and writes in this file (e.g., any occurrences that build
paths or write files for "auto-claude" around the top-level fixtures and helper
usages) to use ".aperant" instead so that existsSync(aperantPath) returns true;
search for string "auto-claude" and replace with ".aperant" in path.join and
file-write calls referenced by the flows.e2e.ts test so the fixture setup aligns
with the renamed directory.
| 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'; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Prefer path alias import for shared constants.
Switch this changed import to the configured alias form for consistency with repo conventions.
As per coding guidelines apps/desktop/src/**/*.{ts,tsx}: "Use path aliases defined in tsconfig.json for imports: @/* (renderer), @shared/* (shared), @preload/* (preload), @features/*, @components/*, @hooks/*, @lib/*."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/agent/agent-manager.ts` at line 18, The import in
agent-manager.ts should use the repo's TypeScript path alias for shared code;
replace the relative import of APERANT_PATHS and getSpecsDir with the alias form
(e.g., import { APERANT_PATHS, getSpecsDir } from '@shared/constants') so the
code follows the apps/desktop src import conventions and tsconfig aliases.
| import { RoadmapConfig } from './types'; | ||
| import type { IdeationConfig, Idea } from '../../shared/types'; | ||
| import { AUTO_BUILD_PATHS } from '../../shared/constants'; | ||
| import { APERANT_PATHS } from '../../shared/constants'; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider using path alias for import.
The import uses a relative path instead of the configured @shared/* alias. As per coding guidelines, path aliases should be used for imports in this directory.
♻️ Suggested refactor
-import { APERANT_PATHS } from '../../shared/constants';
+import { APERANT_PATHS } from '@shared/constants';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { APERANT_PATHS } from '../../shared/constants'; | |
| import { APERANT_PATHS } from '@shared/constants'; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/agent/agent-queue.ts` at line 9, Import in
agent-queue.ts currently uses a relative path to access APERANT_PATHS; replace
the relative import with the configured path alias. Update the import that
references APERANT_PATHS to use the '@shared/constants' alias (keeping the
symbol name APERANT_PATHS unchanged) so the file imports from
'@shared/constants' instead of '../../shared/constants', and ensure
ESLint/tsconfig path mappings still resolve correctly.
| transport: { | ||
| type: 'stdio', | ||
| command: 'node', | ||
| args: ['auto-claude-mcp-server.js'], | ||
| args: ['aperant-mcp-server.js'], | ||
| env: { SPEC_DIR: specDir }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== MCP server script files ==="
fd -a '^(auto-claude|aperant)-mcp-server\.js$'
echo
echo "=== Old/new script references and packaging config ==="
rg -n -C2 'auto-claude-mcp-server\.js|aperant-mcp-server\.js|process\.resourcesPath|app\.isPackaged|extraResources|asarUnpack|files:' .
echo
echo "=== Registry call sites ==="
rg -n -C3 'createAperantServer|getMcpServerConfig|resolveMcpServers' apps/desktop/src/mainRepository: AndyMik90/Aperant
Length of output: 50374
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Registry file content (lines 100-125) ==="
sed -n '100,125p' apps/desktop/src/main/ai/mcp/registry.ts
echo
echo "=== All MCP-related files ==="
fd -t f -e js -e ts mcp
echo
echo "=== Electron builder config for resources ==="
fd -t f -e json 'electron-builder|package\.json' | head -5
rg -l 'asarUnpack|extraResources|files.*aperant|files.*mcp' . --type json | head -5Repository: AndyMik90/Aperant
Length of output: 1090
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Electron build config in apps/desktop/package.json ==="
jq '.build' apps/desktop/package.json | head -60
echo
echo "=== Search for script generation or build references ==="
rg -i 'aperant.*mcp|mcp.*server|mcp.*script' --type json --type ts . | grep -v node_modules | head -20
echo
echo "=== Check for separate mcp-server package or directory ==="
find . -type d -name '*mcp*' | grep -v node_modules
echo
echo "=== Look for script imports in registry.ts ==="
head -30 apps/desktop/src/main/ai/mcp/registry.tsRepository: AndyMik90/Aperant
Length of output: 4166
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for where spawned process cwd is set ==="
rg -B5 -A5 'spawning|spawn|execFile|exec.*aperant' apps/desktop/src/main --type ts
echo
echo "=== Check for TODO/FIXME about MCP server script ==="
rg -i 'TODO|FIXME|XXX.*mcp|mcp.*TODO|aperant.*server' apps/desktop/src/main --type ts
echo
echo "=== Look at Memory.md for MCP server guidance ==="
grep -A10 -B2 'mcp.*server\|aperant.*mcp' Memory.md | head -40Repository: AndyMik90/Aperant
Length of output: 50374
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Where createAperantServer is used ==="
rg -n 'createAperantServer\|getMcpServerConfig' apps/desktop/src/main/ai --type ts -B2 -A2
echo
echo "=== Check how transport commands are actually spawned ==="
rg -n 'transport.*command\|spawn.*command\|spawnSync.*command' apps/desktop/src/main --type ts -B3 -A3 | head -80Repository: AndyMik90/Aperant
Length of output: 102
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Full registry.ts file around function definitions ==="
sed -n '80,150p' apps/desktop/src/main/ai/mcp/registry.ts
echo
echo "=== Search for registry exports and usage ==="
rg -n 'export.*registry\|getRegistry\|createServer' apps/desktop/src/main/ai/mcp --type ts -A3
echo
echo "=== Where MCP servers are initialized (any Stdio transport) ==="
rg -n 'type.*stdio\|stdio' apps/desktop/src/main --type ts -B5 -A5 | head -100Repository: AndyMik90/Aperant
Length of output: 2191
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for where stdio MCP servers are spawned ==="
rg -n 'stdio.*spawn\|spawn.*command\|ClientTransport\|StdioClientTransport' apps/desktop/src/main --type ts -B5 -A10 | head -120
echo
echo "=== Check Vercel AI SDK usage for MCP initialization ==="
rg -n 'connectToMCP\|createMCPClient\|@modelcontextprotocol' apps/desktop/src/main --type ts | head -20Repository: AndyMik90/Aperant
Length of output: 115
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Full registry.ts file (get file size first) ==="
wc -l apps/desktop/src/main/ai/mcp/registry.ts
echo
echo "=== Full registry.ts content ==="
cat apps/desktop/src/main/ai/mcp/registry.ts
echo
echo "=== Search for MCP client library imports ==="
rg -n '@modelcontextprotocol|mcp|StdioClient' apps/desktop/src/main --type ts | head -30Repository: AndyMik90/Aperant
Length of output: 9481
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check if aperant-mcp-server is an npm package or should exist in repo ==="
rg -i 'aperant.*mcp.*server\|aperant-mcp-server' . --type json --type md | grep -v node_modules | head -20
echo
echo "=== Check root package.json for aperant-mcp-server dependency ==="
jq '.dependencies, .devDependencies' package.json 2>/dev/null | grep -i aperant
echo
echo "=== Check if there's a separate aperant-mcp-server package/repo reference ==="
find . -type f -name '.npmrc' -o -name 'pnpm-workspace.yaml' -o -name 'turbo.json' | xargs cat 2>/dev/null | head -30Repository: AndyMik90/Aperant
Length of output: 138
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check Memory.md for aperant-mcp-server context ==="
grep -i 'aperant.*mcp\|mcp.*aperant\|build.*management' Memory.md | head -20
echo
echo "=== Check for any .js files in apps/desktop ==="
find apps/desktop -type f -name '*.js' | grep -v node_modules | grep -v dist | grep -v out | head -20
echo
echo "=== Check build/dist output directory structure ==="
ls -la apps/desktop/out/ 2>/dev/null | head -20 || echo "No out/ directory yet (expected - not built)"
echo
echo "=== Check if aperant-mcp-server is supposed to be a separate npm package ==="
grep -r 'aperant-mcp' . --include='*.json' --include='*.md' --include='*.ts' | grep -v node_modules | head -10Repository: AndyMik90/Aperant
Length of output: 115
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for 'aperant' anywhere in codebase except node_modules ==="
rg 'aperant' --type ts --type json --type md . | grep -v node_modules | head -30
echo
echo "=== Check if this is supposed to be a separate npm package ==="
ls -la | head -20
echo
echo "=== Look for monorepo structure ==="
find . -maxdepth 3 -name 'package.json' -type f | grep -v node_modules | head -10Repository: AndyMik90/Aperant
Length of output: 3290
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check if aperant-mcp-server should be built or generated ==="
rg -i 'build.*aperant.*mcp|aperant.*mcp.*build|mcp.*server.*build' apps/desktop --type ts --type json -B2 -A2
echo
echo "=== Check CHANGELOG for aperant-mcp-server mentions ==="
grep -i 'aperant.*mcp\|mcp.*server' CHANGELOG.md | head -10
echo
echo "=== Check build scripts in package.json ==="
jq '.scripts | keys[]' apps/desktop/package.json | grep -i build
echo
echo "=== Does aperant-mcp-server exist as an external npm package? Check package.json ==="
jq '.dependencies, .devDependencies' package.json | grep -i mcpRepository: AndyMik90/Aperant
Length of output: 1214
The aperant-mcp-server.js script is missing and will fail at runtime.
The code references a script that doesn't exist in the repository and isn't included in the build resources. Unlike other MCP servers (context7, linear, electron, puppeteer) that use npx -y <package-name>, the aperant server uses a bare filename args: ['aperant-mcp-server.js'] that:
- Will fail when spawned because the file doesn't exist
- Relies on process working directory instead of explicit path resolution
- Will definitely break in packaged Electron builds
Either implement the aperant MCP server as an external npm package and use npx -y aperant-mcp-server, or bundle the script in extraResources and resolve its path using app.isPackaged and process.resourcesPath (see Memory.md examples for TreeSitterLoader and vec0 extensions).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/ai/mcp/registry.ts` around lines 112 - 116, The
transport configuration in registry.ts uses args: ['aperant-mcp-server.js']
which references a missing script and will fail at runtime; update the aperant
MCP launch to either call the published package via npx (e.g. args: ['-y',
'aperant-mcp-server'] with command 'npx') or bundle the script in extraResources
and resolve its runtime path using app.isPackaged and process.resourcesPath so
the transport.command spawns the correct absolute path to the bundled
aperant-mcp-server.js; modify the transport block (transport.type,
transport.command, transport.args) accordingly and ensure the build includes the
file in extraResources if you choose bundling.
| 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); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use the main-process logger for these startup messages.
These new migration/configuration diagnostics still go through console.*, so they disappear in packaged Electron builds. Please route them through the existing logger/Sentry path instead.
As per coding guidelines: apps/desktop/src/main/**/*.ts: Do not use console.log in production code. console.log output is invisible in bundled Electron apps. Use Sentry for error tracking in production; reserve console.log for development only.
Also applies to: 503-510, 519-523
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/index.ts` around lines 486 - 496, Replace
console.log/console.warn calls used for the aperantPath migration (the messages
referencing validAperantPath, correctedPath, settings, settingsPath and the
writeFileSync try/catch that sets migrated) with the main-process logger and
Sentry path: use the app-wide logger (e.g., processLogger or
mainLogger).info(...) for informational migration messages and logger.warn(...)
for the failed save branch, and call Sentry.captureException(writeError) (or the
project’s Sentry wrapper) inside the catch so errors are tracked in production;
apply the same replacement for the other console.* occurrences around the
migration block.
| <p className="text-sm font-medium text-foreground">Import Existing Tasks</p> | ||
| <p className="text-xs text-muted-foreground mt-1"> | ||
| Select which Linear issues to import into AutoBuild as tasks. | ||
| Select which Linear issues to import into Aperant as tasks. |
There was a problem hiding this comment.
Replace hardcoded UI copy with react-i18next key
Line 163 introduces hardcoded user-facing text in TSX, which breaks localization. Use t(...) and add matching keys in both en/*.json and fr/*.json.
Suggested change
+import { useTranslation } from 'react-i18next';
...
function ImportTasksPrompt({ onOpenLinearImport }: ImportTasksPromptProps) {
+ const { t } = useTranslation();
return (
...
<p className="text-xs text-muted-foreground mt-1">
- Select which Linear issues to import into Aperant as tasks.
+ {t('settings.integrations.linear.importTasksPrompt.description')}
</p>As per coding guidelines: apps/desktop/src/renderer/**/*.{tsx,jsx}: All frontend user-facing text must use react-i18next translation keys. Hardcoded strings in JSX/TSX break localization for non-English users. Add keys to both en/*.json and fr/*.json translation files.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Select which Linear issues to import into Aperant as tasks. | |
| import { useTranslation } from 'react-i18next'; | |
| function ImportTasksPrompt({ onOpenLinearImport }: ImportTasksPromptProps) { | |
| const { t } = useTranslation(); | |
| return ( | |
| <> | |
| <p className="text-xs text-muted-foreground mt-1"> | |
| {t('settings.integrations.linear.importTasksPrompt.description')} | |
| </p> | |
| </> | |
| ); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/components/settings/integrations/LinearIntegration.tsx`
at line 163, The hardcoded string in the LinearIntegration component should be
replaced with a react-i18next key: import and use useTranslation in the
LinearIntegration.tsx (ensure const { t } = useTranslation()) and replace
"Select which Linear issues to import into Aperant as tasks." with
t('integrations.linear.selectIssues'). Then add the key
"integrations.linear.selectIssues" with the English copy and the French
translation to your en/*.json and fr/*.json translation files respectively so
the UI text is localized.
| <div className="rounded-lg border border-warning/30 bg-warning/5 p-3 ml-6"> | ||
| <p className="text-xs text-warning"> | ||
| When enabled, new Linear issues will be automatically imported into AutoBuild. | ||
| When enabled, new Linear issues will be automatically imported into Aperant. |
There was a problem hiding this comment.
Localize warning message instead of hardcoding English
Line 206 hardcodes a user-visible warning message. Please switch it to a translation key and add entries in en and fr locale files.
Based on learnings: Applies to apps/desktop/src/renderer/**/*.{tsx,jsx} : All frontend user-facing text must use react-i18next translation keys. Hardcoded strings in JSX/TSX break localization for non-English users. Add keys to both en/*.json and fr/*.json translation files.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/components/settings/integrations/LinearIntegration.tsx`
at line 206, The JSX in the LinearIntegration component currently hardcodes the
warning "When enabled, new Linear issues will be automatically imported into
Aperant."; replace this literal with a react-i18next translation call (use the
existing useTranslation hook or import it if missing) and reference a new key
like "integrations.linear.autoImportWarning" (call
t('integrations.linear.autoImportWarning') where the string is rendered). Then
add matching entries for that key in both the English and French locale JSONs
(en and fr) with the appropriate translated texts. Ensure the key name is unique
and consistent across the component and both locale files so the message is
localized at runtime.
| <SettingsSection | ||
| title="General" | ||
| description={`Configure Auto-Build, agent model, and notifications for ${project.name}`} | ||
| description={`Configure Aperant, agent model, and notifications for ${project.name}`} | ||
| > |
There was a problem hiding this comment.
Localize the new general-section copy.
This new literal bypasses react-i18next. Please move it to a settings key with { projectName } interpolation and add matching entries to both en and fr.
As per coding guidelines, apps/desktop/src/renderer/**/*.{tsx,jsx}: “All frontend user-facing text must use react-i18next translation keys. Hardcoded strings in JSX/TSX break localization for non-English users. Add keys to both en/*.json and fr/*.json translation files.”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/settings/sections/SectionRouter.tsx`
around lines 80 - 83, Replace the hardcoded description string in the
SettingsSection inside SectionRouter.tsx with a react-i18next key (e.g.
settings.generalDescription) that uses interpolation for the project name
(projectName -> project.name) and update the component to call
t('settings.generalDescription', { projectName: project.name }); then add
matching translation entries for settings.generalDescription in both English and
French locale JSONs (en/*.json and fr/*.json) with the appropriate interpolated
text (e.g. "Configure Aperant, agent model, and notifications for
{{projectName}}") so the UI uses i18n instead of a literal.
| @@ -1,5 +1,5 @@ | |||
| import { create } from 'zustand'; | |||
| import type { Project, ProjectSettings, AutoBuildVersionInfo, InitializationResult } from '../../shared/types'; | |||
| import type { Project, ProjectSettings, AperantVersionInfo, InitializationResult } from '../../shared/types'; | |||
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use shared type alias import instead of relative path.
This changed import should follow the configured path alias convention.
As per coding guidelines apps/desktop/src/**/*.{ts,tsx}: "Use path aliases defined in tsconfig.json for imports: @/* (renderer), @shared/* (shared), @preload/* (preload), @features/*, @components/*, @hooks/*, @lib/*."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/stores/project-store.ts` at line 2, Replace the
relative import in project-store.ts with the shared path-alias import; change
the line that imports Project, ProjectSettings, AperantVersionInfo,
InitializationResult from '../../shared/types' to use the configured alias (e.g.
'@shared/types') so the file imports those types via the path alias instead of a
relative path.
| // - Has the aperant source path configured | ||
| const hasProviderAccounts = useSettingsStore.getState().providerAccounts.length > 0; | ||
| const hasAutoBuildPath = Boolean(settings.autoBuildPath); | ||
| const hasAperantPath = Boolean(settings.aperantPath); | ||
|
|
||
| const isExistingUser = hasProviderAccounts || hasAutoBuildPath; | ||
| const isExistingUser = hasProviderAccounts || hasAperantPath; |
There was a problem hiding this comment.
hasProviderAccounts is read before accounts are loaded.
migrateOnboardingCompleted() consults useSettingsStore.getState().providerAccounts, but loadSettings() does not populate that slice until after the migration runs. On a cold start, returning users who only have provider-account credentials still fall through to onboarding. Load the accounts first or query them directly inside the migration.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/stores/settings-store.ts` around lines 448 - 452,
migrateOnboardingCompleted currently reads
useSettingsStore.getState().providerAccounts before loadSettings() populates
that slice, causing returning users with only provider accounts to be treated as
new; fix by ensuring provider accounts are available when migration runs—either
call the code that loads provider accounts (via loadSettings()) before invoking
migrateOnboardingCompleted, or modify migrateOnboardingCompleted to fetch
provider accounts directly from the source of truth instead of
useSettingsStore.getState().providerAccounts (so the check for provider accounts
is accurate on cold start).
| // 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'), | ||
| ]; |
There was a problem hiding this comment.
Strip aperant in the stale-path repair too.
The comment says the old structure can end in .../aperant, but the first correction still only removes auto-claude. A saved path like /src/aperant becomes /src/aperant/apps/desktop/prompts, so this repair path never succeeds.
🐛 Suggested change
const possibleCorrections = [
- join(validAperantPath.replace(/[/\\]auto-claude[/\\]*$/, ''), 'apps', 'desktop', 'prompts'),
+ join(validAperantPath.replace(/[/\\](?:auto-claude|aperant)[/\\]*$/, ''), 'apps', 'desktop', 'prompts'),
join(validAperantPath.replace(/[/\\]backend[/\\]*$/, ''), 'desktop', 'prompts'),
];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/index.ts` around lines 468 - 474, The stale-path repair
builds possibleCorrections from validAperantPath but only strips "auto-claude",
so paths ending with "aperant" (e.g., /src/aperant) produce invalid corrections;
update the logic that creates possibleCorrections (the array near variables
migrated and validAperantPath) to also strip a trailing "aperant" segment (for
example by using a single replace that matches either "auto-claude" or "aperant"
or by adding an additional replace) so the corrected paths point to the intended
apps/desktop/prompts directory.
| ipcMain.handle( | ||
| IPC_CHANNELS.PROJECT_NEEDS_MIGRATION, | ||
| async (_, projectId: string): Promise<boolean> => { | ||
| try { | ||
| const project = projectStore.getProject(projectId); | ||
| if (!project) return false; | ||
| return needsMigration(project.path); | ||
| } catch { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Differentiate “no migration needed” from “migration check failed”.
This handler returns false both when the project is missing and when needsMigration() throws. The renderer then treats those failures as a clean negative and can push a legacy project into the fresh-init path without ever surfacing the real problem. Return an error shape here so callers can stop on detection failures instead of silently falling through.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/ipc-handlers/project-handlers.ts` around lines 449 -
458, The handler IPC_CHANNELS.PROJECT_NEEDS_MIGRATION currently returns false
for both "no migration needed" and when the check fails; update the handler
registered via ipcMain.handle so its return type is Promise<boolean | { error:
string }>, keep returning a plain boolean from successful checks (e.g.,
needsMigration(project.path) => true/false), but on failure return an error
shape (e.g., { error: 'project not found' } when
projectStore.getProject(projectId) returns null and { error: `migration check
failed: ${err.message}` } inside the catch) so callers can distinguish a
negative result from a failed check; ensure you reference
IPC_CHANNELS.PROJECT_NEEDS_MIGRATION, projectStore.getProject, and
needsMigration when making these changes.
| const aperantSource = getAperantSourcePath(); | ||
|
|
||
| if (!autoBuildSource) { | ||
| if (!aperantSource) { | ||
| return { | ||
| success: false, | ||
| error: 'Auto-build source path not configured' |
There was a problem hiding this comment.
Rename this fallback error string as well.
This branch still returns 'Auto-build source path not configured', so users can hit the new Aperant flow and still see the old product name.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/ipc-handlers/sections/context-roadmap-section.txt`
around lines 610 - 615, The fallback error message returned when aperantSource
is missing still uses the old product name; update the string returned in the
branch that checks getAperantSourcePath() (where aperantSource is evaluated) to
the new product wording (e.g., replace 'Auto-build source path not configured'
with the new Aperant-specific message) so the error text matches the new flow.
Path constants used string interpolation with forward slashes, but the implementation uses path.join() which produces backslashes on Windows. The mock existsSync did exact string matching and never found the paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/main/__tests__/project-migration.test.ts`:
- Around line 139-155: The test currently only checks that '.auto-claude/' was
replaced with '.aperant/'; update the assertions in the 'updates .gitignore
entries during migration' test (the migrateProject invocation and
fs.writeFileSync inspection using GITIGNORE) to also assert that writtenContent
contains the migrated entries for '.aperant-security.json' and '.aperant-status'
and that it does NOT contain the original '.auto-claude-security.json' and
'.auto-claude-status'; use the same gitignoreWrite retrieval (mock.calls.find on
fs.writeFileSync) and the writtenContent variable to add these four assertions
so regressions in migrateProject for security/status entries are caught.
- Around line 108-172: Add a new unit test that covers the renameSync exception
path: arrange by adding OLD_PATH to mockExistingPaths and stub fs.renameSync to
throw a synthetic Error; call migrateProject(PROJECT); assert the result is {
success: false } and that result.error contains the thrown error message (or
Error object), and assert fs.renameSync was called with OLD_PATH and NEW_PATH.
Reference migrateProject, fs.renameSync, mockExistingPaths, PROJECT, OLD_PATH
and NEW_PATH when adding the test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 510bf2f8-f71f-4682-a175-21ae60bd51e9
📒 Files selected for processing (1)
apps/desktop/src/main/__tests__/project-migration.test.ts
| 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<typeof vi.fn>).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<typeof vi.fn>).mock.calls.find( | ||
| (call: unknown[]) => call[0] === GITIGNORE | ||
| ); | ||
| expect(written).toBeDefined(); | ||
| expect(written![1] as string).toContain('.aperant/'); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add an explicit test for the renameSync exception path.
migrateProject has a catch branch that returns { success: false, error }, but no test currently verifies that behavior when the directory rename throws.
🧪 Suggested failure-path test
describe('migrateProject', () => {
+ test('returns error when renaming directory fails', () => {
+ mockExistingPaths.add(OLD_PATH);
+ (fs.renameSync as ReturnType<typeof vi.fn>).mockImplementationOnce(() => {
+ throw new Error('EACCES: permission denied');
+ });
+
+ const result = migrateProject(PROJECT);
+
+ expect(result.success).toBe(false);
+ expect(result.error).toMatch(/permission denied/i);
+ });
+
test('successfully renames .auto-claude to .aperant', () => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/__tests__/project-migration.test.ts` around lines 108 -
172, Add a new unit test that covers the renameSync exception path: arrange by
adding OLD_PATH to mockExistingPaths and stub fs.renameSync to throw a synthetic
Error; call migrateProject(PROJECT); assert the result is { success: false } and
that result.error contains the thrown error message (or Error object), and
assert fs.renameSync was called with OLD_PATH and NEW_PATH. Reference
migrateProject, fs.renameSync, mockExistingPaths, PROJECT, OLD_PATH and NEW_PATH
when adding the test.
| 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<typeof vi.fn>).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/'); | ||
| }); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Strengthen .gitignore assertions to cover all migrated entries.
At Line 153 and Line 154, this test only validates .auto-claude/ → .aperant/. migrateProject also rewrites .auto-claude-security.json and .auto-claude-status; regressions there would currently pass unnoticed.
✅ Suggested test assertion expansion
expect(writtenContent).toContain('.aperant/');
expect(writtenContent).not.toContain('.auto-claude/');
+ expect(writtenContent).toContain('.aperant-security.json');
+ expect(writtenContent).toContain('.aperant-status');
+ expect(writtenContent).not.toContain('.auto-claude-security.json');
+ expect(writtenContent).not.toContain('.auto-claude-status');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/__tests__/project-migration.test.ts` around lines 139 -
155, The test currently only checks that '.auto-claude/' was replaced with
'.aperant/'; update the assertions in the 'updates .gitignore entries during
migration' test (the migrateProject invocation and fs.writeFileSync inspection
using GITIGNORE) to also assert that writtenContent contains the migrated
entries for '.aperant-security.json' and '.aperant-status' and that it does NOT
contain the original '.auto-claude-security.json' and '.auto-claude-status'; use
the same gitignoreWrite retrieval (mock.calls.find on fs.writeFileSync) and the
writtenContent variable to add these four assertions so regressions in
migrateProject for security/status entries are caught.


Base Branch
developbranch (required for all feature/fix PRs)main(hotfix only - maintainers)Description
Renames all
.auto-claudereferences to.aperantacross the entire codebase (~122 files). Adds a migration dialog that detects existing.auto-claude/folders and offers to rename them to.aperant/. Hard cut with no backwards-compatibility fallback logic.Related Issue
N/A - Brand rename initiative
Type of Change
Area
AI Disclosure
Tool(s) used: Claude Code (Opus 4.6)
Testing level:
Untested
Lightly tested
Fully tested -- all tests pass, manually verified behavior
I understand what this PR does and how the underlying code works
Checklist
Platform Testing Checklist
CI/Testing Requirements
Screenshots
N/A - No visual UI changes beyond the new migration dialog (text-only modal).
Feature Toggle
Breaking Changes
Breaking: Yes
Details:
.auto-claude/data folder is now.aperant/. The migration dialog handles this automatically for existing projects.mcp__auto-claude__*tomcp__aperant__*.auto-claude/toaperant/..gitignoreretains both.auto-claude/and.aperant/entries so existing data folders remain ignored.Changes Detail
Bulk Rename (~122 files)
Migration Feature
needsMigration()andmigrateProject()inproject-initializer.tsIPC_CHANNELSconstants andprojectId-based lookupApp.tsxgates project initializationPre-PR Validation Fixes
All 4612 tests pass, TypeScript clean.
Generated with Claude Code
Summary by CodeRabbit
New Features
Chores