Skip to content

Commit feee30e

Browse files
committed
feat: plan mode and auto edit mode
1 parent 581e29f commit feee30e

3 files changed

Lines changed: 76 additions & 6 deletions

File tree

src-node/claude-code-agent.js

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ exports.checkAvailability = async function () {
209209
* aiProgress, aiTextStream, aiToolEdit, aiError, aiComplete
210210
*/
211211
exports.sendPrompt = async function (params) {
212-
const { prompt, projectPath, sessionAction, model, locale, selectionContext, images, envOverrides } = params;
212+
const { prompt, projectPath, sessionAction, model, locale, selectionContext, images, envOverrides, permissionMode } = params;
213213
const requestId = Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
214214

215215
// Handle session
@@ -252,7 +252,7 @@ exports.sendPrompt = async function (params) {
252252
}
253253

254254
// Run the query asynchronously — don't await here so we return requestId immediately
255-
_runQuery(requestId, enrichedPrompt, projectPath, model, currentAbortController.signal, locale, images, envOverrides)
255+
_runQuery(requestId, enrichedPrompt, projectPath, model, currentAbortController.signal, locale, images, envOverrides, permissionMode)
256256
.catch(err => {
257257
console.error("[Phoenix AI] Query error:", err);
258258
});
@@ -370,7 +370,7 @@ exports.clearClarification = async function () {
370370
/**
371371
* Internal: run a Claude SDK query and stream results back to the browser.
372372
*/
373-
async function _runQuery(requestId, prompt, projectPath, model, signal, locale, images, envOverrides) {
373+
async function _runQuery(requestId, prompt, projectPath, model, signal, locale, images, envOverrides, permissionMode) {
374374
let editCount = 0;
375375
let toolCounter = 0;
376376
let queryFn;
@@ -455,7 +455,7 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale,
455455
}
456456
},
457457
mcpServers: { "phoenix-editor": editorMcpServer },
458-
permissionMode: "acceptEdits",
458+
permissionMode: permissionMode || "acceptEdits",
459459
appendSystemPrompt:
460460
"When modifying an existing file, always prefer the Edit tool " +
461461
"(find-and-replace) instead of the Write tool. The Write tool should ONLY be used " +
@@ -496,6 +496,39 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale,
496496
hooks: [
497497
async (input) => {
498498
console.log("[Phoenix AI] Intercepted Edit tool");
499+
// Plan file edits: capture content, write to disk, skip editor
500+
const editPath = (input.tool_input.file_path || "").replace(/\\/g, "/");
501+
if (editPath.includes("/.claude/plans/")) {
502+
try {
503+
let content = "";
504+
if (fs.existsSync(input.tool_input.file_path)) {
505+
content = fs.readFileSync(input.tool_input.file_path, "utf8");
506+
}
507+
if (input.tool_input.old_string && input.tool_input.new_string) {
508+
content = content.replace(input.tool_input.old_string, input.tool_input.new_string);
509+
}
510+
const dir = path.dirname(input.tool_input.file_path);
511+
if (!fs.existsSync(dir)) {
512+
fs.mkdirSync(dir, { recursive: true });
513+
}
514+
fs.writeFileSync(input.tool_input.file_path, content, "utf8");
515+
_lastPlanContent = content;
516+
console.log("[Phoenix AI] Captured plan edit content:", content.length + "ch");
517+
} catch (err) {
518+
console.warn("[Phoenix AI] Failed to edit plan file:", err.message);
519+
}
520+
let planReason = "Plan file updated.";
521+
if (_queuedClarification) {
522+
planReason += CLARIFICATION_HINT;
523+
}
524+
return {
525+
hookSpecificOutput: {
526+
hookEventName: "PreToolUse",
527+
permissionDecision: "deny",
528+
permissionDecisionReason: planReason
529+
}
530+
};
531+
}
499532
const myToolId = toolCounter; // capture before any await
500533
const edit = {
501534
file: input.tool_input.file_path,

src/nls/root/strings.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,6 +1926,8 @@ define({
19261926
"AI_CHAT_PLAN_REVISE": "Revise",
19271927
"AI_CHAT_PLAN_FEEDBACK_PLACEHOLDER": "What would you like changed?",
19281928
"AI_CHAT_PLAN_REVISE_DEFAULT": "Please revise the plan.",
1929+
"AI_CHAT_MODE_PLAN": "Plan Mode",
1930+
"AI_CHAT_MODE_FULL_AUTO": "Full Auto",
19291931
"AI_CHAT_CODE_DEFAULT_LANG": "text",
19301932
"AI_CHAT_CODE_COLLAPSE": "Collapse",
19311933
"AI_CHAT_CODE_EXPAND": "Expand",

src/styles/Extn-AIChatPanel.less

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,11 +1620,46 @@
16201620
}
16211621
}
16221622

1623+
.ai-permission-bar {
1624+
display: flex;
1625+
align-items: center;
1626+
gap: 6px;
1627+
padding: 4px 8px;
1628+
cursor: pointer;
1629+
user-select: none;
1630+
1631+
&:hover {
1632+
background: rgba(255, 255, 255, 0.04);
1633+
border-radius: 4px;
1634+
}
1635+
1636+
.ai-permission-dot {
1637+
width: 8px;
1638+
height: 8px;
1639+
border-radius: 50%;
1640+
flex-shrink: 0;
1641+
1642+
&.mode-auto {
1643+
background-color: #e74c3c;
1644+
}
1645+
1646+
&.mode-plan {
1647+
background-color: #3498db;
1648+
}
1649+
}
1650+
1651+
.ai-permission-label {
1652+
font-size: @sidebar-xs-font-size;
1653+
color: @project-panel-text-2;
1654+
line-height: 1;
1655+
}
1656+
}
1657+
16231658
.ai-chat-context-bar {
16241659
display: none;
16251660
flex-wrap: wrap;
1626-
gap: 4px;
1627-
padding: 0 4px 4px 4px;
1661+
gap: 6px;
1662+
padding: 0 4px 6px 4px;
16281663

16291664
&.has-chips {
16301665
display: flex;

0 commit comments

Comments
 (0)