Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hans/
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ blob-report/

docker-compose.override.yml
.claude/docker-compose.override.yml
.claude/hans/

pnpm-lock.yaml
yarn.lock
35 changes: 34 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1,34 @@
npx lint-staged
#!/usr/bin/env sh

# Try to load nvm if available (optional - works without it too)
if [ -z "$NVM_DIR" ]; then
# Check for Herd's nvm first (macOS with Herd)
if [ -s "$HOME/Library/Application Support/Herd/config/nvm/nvm.sh" ]; then
export NVM_DIR="$HOME/Library/Application Support/Herd/config/nvm"
# Then check standard nvm location
elif [ -s "$HOME/.nvm/nvm.sh" ]; then
export NVM_DIR="$HOME/.nvm"
fi
fi

# Source nvm if found (silently skip if not available)
[ -n "$NVM_DIR" ] && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 2>/dev/null

# Load node version from .nvmrc if using nvm (silently skip if nvm not available)
[ -f .nvmrc ] && command -v nvm >/dev/null 2>&1 && nvm use >/dev/null 2>&1

# Ensure common system paths are in PATH (for systems without nvm)
# This helps find node/npm installed via Homebrew, system packages, etc.
export PATH="$PATH:/usr/local/bin:/opt/homebrew/bin:/usr/bin"

# Run lint-staged - works with or without nvm
# Prefer npx, fallback to npm exec, both work with system-installed Node.js
if command -v npx >/dev/null 2>&1; then
npx lint-staged
elif command -v npm >/dev/null 2>&1; then
npm exec -- lint-staged
else
echo "Error: Neither npx nor npm found in PATH."
echo "Please ensure Node.js is installed (via nvm, Homebrew, system package manager, etc.)"
exit 1
fi
36 changes: 22 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,29 +120,37 @@ npm install
# 3. Build shared packages (Now can be skipped npm install / run dev does it automaticly)
npm run build:packages

# 4. Set up authentication (skip if using Claude Code CLI)
# If using Claude Code CLI: credentials are detected automatically
# If using API key directly, choose one method:

# Option A: Environment variable
export ANTHROPIC_API_KEY="sk-ant-..."

# Option B: Create .env file in project root
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env

# 5. Start Automaker (interactive launcher)
npm run dev
# 4. Start Automaker (production mode)
npm run start
# Choose between:
# 1. Web Application (browser at localhost:3007)
# 2. Desktop Application (Electron - recommended)
```

**Note:** The `npm run dev` command will:
**Note:** The `npm run start` command will:

- Check for dependencies and install if needed
- Install Playwright browsers for E2E tests
- Build the application if needed
- Kill any processes on ports 3007/3008
- Present an interactive menu to choose your run mode
- Run in production mode (no hot reload)

**Authentication Setup:** On first run, Automaker will automatically show a setup wizard where you can configure authentication. You can choose to:

- Use **Claude Code CLI** (recommended) - Automaker will detect your CLI credentials automatically
- Enter an **API key** directly in the wizard

If you prefer to set up authentication before running (e.g., for headless deployments or CI/CD), you can set it manually:

```bash
# Option A: Environment variable
export ANTHROPIC_API_KEY="sk-ant-..."

# Option B: Create .env file in project root
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env
```

**For Development:** If you want to develop on Automaker with Vite live reload and hot module replacement, use `npm run dev` instead. This will start the development server with fast refresh and instant updates as you make changes.

## How to Run

Expand Down
17 changes: 17 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Bugs

- Setting the default model does not seem like it works.

# UX

- Consolidate all models to a single place in the settings instead of having AI profiles and all this other stuff
- Simplify the create feature modal. It should just be one page. I don't need nessa tabs and all these nested buttons. It's too complex.
- added to do's list checkbox directly into the card so as it's going through if there's any to do items we can see those update live
- When the feature is done, I want to see a summary of the LLM. That's the first thing I should see when I double click the card.
- I went away to mass edit all my features. For example, when I created a new project, it added auto testing on every single feature card. Now I have to manually go through one by one and change those. Have a way to mass edit those, the configuration of all them.
- Double check and debug if there's memory leaks. It seems like the memory of automaker grows like 3 gigabytes. It's 5gb right now and I'm running three different cursor cli features implementing at the same time.
- Typing in the text area of the plan mode was super laggy.
- When I have a bunch of features running at the same time, it seems like I cannot edit the features in the backlog. Like they don't persist their file changes and I think this is because of the secure FS file has an internal queue to prevent hitting that file open write limit. We may have to reconsider refactoring away from file system and do Postgres or SQLite or something.
- modals are not scrollable if height of the screen is small enough
- and the Agent Runner add an archival button for the new sessions.
- investigate a potential issue with the feature cards not refreshing. I see a lock icon on the feature card But it doesn't go away until I open the card and edit it and I turn the testing mode off. I think there's like a refresh sync issue.
4 changes: 4 additions & 0 deletions apps/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ import { createMCPRoutes } from './routes/mcp/index.js';
import { MCPTestService } from './services/mcp-test-service.js';
import { createPipelineRoutes } from './routes/pipeline/index.js';
import { pipelineService } from './services/pipeline-service.js';
import { createIdeationRoutes } from './routes/ideation/index.js';
import { IdeationService } from './services/ideation-service.js';

// Load environment variables
dotenv.config();
Expand Down Expand Up @@ -162,6 +164,7 @@ const featureLoader = new FeatureLoader();
const autoModeService = new AutoModeService(events, settingsService);
const claudeUsageService = new ClaudeUsageService();
const mcpTestService = new MCPTestService(settingsService);
const ideationService = new IdeationService(events, settingsService, featureLoader);

// Initialize services
(async () => {
Expand Down Expand Up @@ -215,6 +218,7 @@ app.use('/api/context', createContextRoutes(settingsService));
app.use('/api/backlog-plan', createBacklogPlanRoutes(events, settingsService));
app.use('/api/mcp', createMCPRoutes(mcpTestService));
app.use('/api/pipeline', createPipelineRoutes(pipelineService));
app.use('/api/ideation', createIdeationRoutes(events, ideationService, featureLoader));

// Create HTTP server
const server = createServer(app);
Expand Down
48 changes: 19 additions & 29 deletions apps/server/src/lib/sdk-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,14 @@ export function getModelForUseCase(

/**
* Base options that apply to all SDK calls
*
* AUTONOMOUS MODE: Always bypass permissions and allow dangerous operations
* for fully autonomous operation without user prompts.
*/
function getBaseOptions(): Partial<Options> {
return {
permissionMode: 'acceptEdits',
permissionMode: 'bypassPermissions',
allowDangerouslySkipPermissions: true,
};
}

Expand All @@ -276,31 +280,27 @@ interface McpPermissionOptions {
* Centralizes the logic for determining permission modes and tool restrictions
* when MCP servers are configured.
*
* AUTONOMOUS MODE: Always bypass permissions for fully autonomous operation.
* Always allow unrestricted tools when MCP servers are configured.
*
* @param config - The SDK options config
* @returns Object with MCP permission settings to spread into final options
*/
function buildMcpOptions(config: CreateSdkOptionsConfig): McpPermissionOptions {
const hasMcpServers = config.mcpServers && Object.keys(config.mcpServers).length > 0;
// Default to true for autonomous workflow. Security is enforced when adding servers
// via the security warning dialog that explains the risks.
const mcpAutoApprove = config.mcpAutoApproveTools ?? true;
const mcpUnrestricted = config.mcpUnrestrictedTools ?? true;

// Determine if we should bypass permissions based on settings
const shouldBypassPermissions = hasMcpServers && mcpAutoApprove;
// Determine if we should restrict tools (only when no MCP or unrestricted is disabled)
const shouldRestrictTools = !hasMcpServers || !mcpUnrestricted;
// AUTONOMOUS MODE: Always bypass permissions and allow unrestricted tools
// Only restrict tools when no MCP servers are configured
const shouldRestrictTools = !hasMcpServers;

return {
shouldRestrictTools,
// Only include bypass options when MCP is configured and auto-approve is enabled
bypassOptions: shouldBypassPermissions
? {
permissionMode: 'bypassPermissions' as const,
// Required flag when using bypassPermissions mode
allowDangerouslySkipPermissions: true,
}
: {},
// AUTONOMOUS MODE: Always include bypass options (though base options already set this)
bypassOptions: {
permissionMode: 'bypassPermissions' as const,
// Required flag when using bypassPermissions mode
allowDangerouslySkipPermissions: true,
},
// Include MCP servers if configured
mcpServerOptions: config.mcpServers ? { mcpServers: config.mcpServers } : {},
};
Expand Down Expand Up @@ -392,12 +392,6 @@ export interface CreateSdkOptionsConfig {

/** MCP servers to make available to the agent */
mcpServers?: Record<string, McpServerConfig>;

/** Auto-approve MCP tool calls without permission prompts */
mcpAutoApproveTools?: boolean;

/** Allow unrestricted tools when MCP servers are enabled */
mcpUnrestrictedTools?: boolean;
}

// Re-export MCP types from @automaker/types for convenience
Expand Down Expand Up @@ -426,10 +420,7 @@ export function createSpecGenerationOptions(config: CreateSdkOptionsConfig): Opt

return {
...getBaseOptions(),
// Override permissionMode - spec generation only needs read-only tools
// Using "acceptEdits" can cause Claude to write files to unexpected locations
// See: https://github.com/AutoMaker-Org/automaker/issues/149
permissionMode: 'default',
// AUTONOMOUS MODE: Base options already set bypassPermissions and allowDangerouslySkipPermissions
model: getModelForUseCase('spec', config.model),
maxTurns: MAX_TURNS.maximum,
cwd: config.cwd,
Expand Down Expand Up @@ -458,8 +449,7 @@ export function createFeatureGenerationOptions(config: CreateSdkOptionsConfig):

return {
...getBaseOptions(),
// Override permissionMode - feature generation only needs read-only tools
permissionMode: 'default',
// AUTONOMOUS MODE: Base options already set bypassPermissions and allowDangerouslySkipPermissions
model: getModelForUseCase('features', config.model),
maxTurns: MAX_TURNS.quick,
cwd: config.cwd,
Expand Down
35 changes: 0 additions & 35 deletions apps/server/src/lib/settings-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,41 +191,6 @@ export async function getMCPServersFromSettings(
}
}

/**
* Get MCP permission settings from global settings.
*
* @param settingsService - Optional settings service instance
* @param logPrefix - Prefix for log messages (e.g., '[AgentService]')
* @returns Promise resolving to MCP permission settings
*/
export async function getMCPPermissionSettings(
settingsService?: SettingsService | null,
logPrefix = '[SettingsHelper]'
): Promise<{ mcpAutoApproveTools: boolean; mcpUnrestrictedTools: boolean }> {
// Default to true for autonomous workflow. Security is enforced when adding servers
// via the security warning dialog that explains the risks.
const defaults = { mcpAutoApproveTools: true, mcpUnrestrictedTools: true };

if (!settingsService) {
return defaults;
}

try {
const globalSettings = await settingsService.getGlobalSettings();
const result = {
mcpAutoApproveTools: globalSettings.mcpAutoApproveTools ?? true,
mcpUnrestrictedTools: globalSettings.mcpUnrestrictedTools ?? true,
};
logger.info(
`${logPrefix} MCP permission settings: autoApprove=${result.mcpAutoApproveTools}, unrestricted=${result.mcpUnrestrictedTools}`
);
return result;
} catch (error) {
logger.error(`${logPrefix} Failed to load MCP permission settings:`, error);
return defaults;
}
}

/**
* Convert a settings MCPServerConfig to SDK McpServerConfig format.
* Validates required fields and throws informative errors if missing.
Expand Down
22 changes: 7 additions & 15 deletions apps/server/src/providers/claude-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,13 @@ export class ClaudeProvider extends BaseProvider {
} = options;

// Build Claude SDK options
// MCP permission logic - determines how to handle tool permissions when MCP servers are configured.
// This logic mirrors buildMcpOptions() in sdk-options.ts but is applied here since
// the provider is the final point where SDK options are constructed.
// AUTONOMOUS MODE: Always bypass permissions for fully autonomous operation
const hasMcpServers = options.mcpServers && Object.keys(options.mcpServers).length > 0;
// Default to true for autonomous workflow. Security is enforced when adding servers
// via the security warning dialog that explains the risks.
const mcpAutoApprove = options.mcpAutoApproveTools ?? true;
const mcpUnrestricted = options.mcpUnrestrictedTools ?? true;
const defaultTools = ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch'];

// Determine permission mode based on settings
const shouldBypassPermissions = hasMcpServers && mcpAutoApprove;
// Determine if we should restrict tools (only when no MCP or unrestricted is disabled)
const shouldRestrictTools = !hasMcpServers || !mcpUnrestricted;
// AUTONOMOUS MODE: Always bypass permissions and allow unrestricted tools
// Only restrict tools when no MCP servers are configured
const shouldRestrictTools = !hasMcpServers;

const sdkOptions: Options = {
model,
Expand All @@ -88,10 +81,9 @@ export class ClaudeProvider extends BaseProvider {
// Only restrict tools if explicitly set OR (no MCP / unrestricted disabled)
...(allowedTools && shouldRestrictTools && { allowedTools }),
...(!allowedTools && shouldRestrictTools && { allowedTools: defaultTools }),
// When MCP servers are configured and auto-approve is enabled, use bypassPermissions
permissionMode: shouldBypassPermissions ? 'bypassPermissions' : 'default',
// Required when using bypassPermissions mode
...(shouldBypassPermissions && { allowDangerouslySkipPermissions: true }),
// AUTONOMOUS MODE: Always bypass permissions and allow dangerous operations
permissionMode: 'bypassPermissions',
allowDangerouslySkipPermissions: true,
abortController,
// Resume existing SDK session if we have a session ID
...(sdkSessionId && conversationHistory && conversationHistory.length > 0
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/features/routes/generate-title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export function createGenerateTitleHandler(): (req: Request, res: Response) => P
systemPrompt: SYSTEM_PROMPT,
maxTurns: 1,
allowedTools: [],
permissionMode: 'acceptEdits',
permissionMode: 'default',
},
});

Expand Down
12 changes: 12 additions & 0 deletions apps/server/src/routes/ideation/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Common utilities for ideation routes
*/

import { createLogger } from '@automaker/utils';
import { getErrorMessage as getErrorMessageShared, createLogError } from '../common.js';

const logger = createLogger('Ideation');

// Re-export shared utilities
export { getErrorMessageShared as getErrorMessage };
export const logError = createLogError(logger);
Loading