orient-cli 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/@orient-cli/agent-core/package.json +1 -1
- package/node_modules/@orient-cli/ai/package.json +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/core/extensions/runner.js +1 -0
- package/node_modules/@orient-cli/coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/core/keybindings.d.ts +6 -1
- package/node_modules/@orient-cli/coding-agent/dist/core/keybindings.d.ts.map +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/core/keybindings.js +9 -1
- package/node_modules/@orient-cli/coding-agent/dist/core/keybindings.js.map +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/components/model-selector.js +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.d.ts +24 -0
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.js +107 -1
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@orient-cli/coding-agent/package.json +1 -1
- package/node_modules/@orient-cli/tui/package.json +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import * as os from "node:os";
|
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import { CombinedAutocompleteProvider, Container, fuzzyFilter, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@orient-cli/tui";
|
|
10
10
|
import { spawn, spawnSync } from "child_process";
|
|
11
|
-
import { APP_NAME, getAgentDir, getAuthPath, getDebugLogPath, getShareViewerUrl, getUpdateInstruction, VERSION, } from "../../config.js";
|
|
11
|
+
import { APP_NAME, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getShareViewerUrl, getUpdateInstruction, VERSION, } from "../../config.js";
|
|
12
12
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
13
13
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
14
14
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
@@ -1668,6 +1668,7 @@ export class InteractiveMode {
|
|
|
1668
1668
|
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
1669
1669
|
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
1670
1670
|
this.defaultEditor.onAction("app.suspend", () => this.handleCtrlZ());
|
|
1671
|
+
this.defaultEditor.onAction("app.mode.toggle", () => this.handleModeToggle());
|
|
1671
1672
|
this.defaultEditor.onAction("app.thinking.cycle", () => this.cycleThinkingLevel());
|
|
1672
1673
|
this.defaultEditor.onAction("app.model.cycleForward", () => this.cycleModel("forward"));
|
|
1673
1674
|
this.defaultEditor.onAction("app.model.cycleBackward", () => this.cycleModel("backward"));
|
|
@@ -2450,6 +2451,111 @@ export class InteractiveMode {
|
|
|
2450
2451
|
}
|
|
2451
2452
|
this.ui.requestRender();
|
|
2452
2453
|
}
|
|
2454
|
+
/**
|
|
2455
|
+
* Handle shift+tab — toggle between Plan and Execute sub-modes.
|
|
2456
|
+
*
|
|
2457
|
+
* This is the ORIENT dual-mode UX borrowed from openclaude
|
|
2458
|
+
* (Gitlawb/openclaude) and anomalyco/opencode. Both reference
|
|
2459
|
+
* projects bind shift+tab (or a permission-mode cycle) to flip
|
|
2460
|
+
* between a read-only "plan" agent state and an edit-capable
|
|
2461
|
+
* "execute" state. ORIENT follows the same convention.
|
|
2462
|
+
*
|
|
2463
|
+
* Semantics (plan-execute mode only):
|
|
2464
|
+
* - `idle` → `planning` (first press — starts drafting)
|
|
2465
|
+
* - `planning` → `executing` (approves the current plan, like /execute)
|
|
2466
|
+
* - `executing` → `planning` (revises — same as /plan.revise)
|
|
2467
|
+
*
|
|
2468
|
+
* State lives at `.orient-cli/plan-state.json` at the project
|
|
2469
|
+
* root, so reading / writing from interactive-mode here stays in
|
|
2470
|
+
* sync with the orient extension's /plan /execute commands. The
|
|
2471
|
+
* two entry points manipulate the same on-disk source of truth.
|
|
2472
|
+
*
|
|
2473
|
+
* In ORIENT framework mode, shift+tab is a no-op with a helpful
|
|
2474
|
+
* message — phase transitions are explicit (/orient.transition
|
|
2475
|
+
* and /orient.<phase>), not shortcut-driven.
|
|
2476
|
+
*/
|
|
2477
|
+
handleModeToggle() {
|
|
2478
|
+
const cwd = this.sessionManager.getCwd();
|
|
2479
|
+
const configDirName = CONFIG_DIR_NAME; // ".orient-cli"
|
|
2480
|
+
const configDir = path.join(cwd, configDirName);
|
|
2481
|
+
const modeFile = path.join(configDir, "mode");
|
|
2482
|
+
const stateFile = path.join(configDir, "plan-state.json");
|
|
2483
|
+
// Read the current product mode. Default is plan-execute.
|
|
2484
|
+
let mode = "plan-execute";
|
|
2485
|
+
try {
|
|
2486
|
+
if (fs.existsSync(modeFile)) {
|
|
2487
|
+
const raw = fs.readFileSync(modeFile, "utf-8").trim();
|
|
2488
|
+
if (raw === "orient" || raw === "plan-execute")
|
|
2489
|
+
mode = raw;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
catch {
|
|
2493
|
+
// Fall through — treat unreadable file as plan-execute default.
|
|
2494
|
+
}
|
|
2495
|
+
if (mode === "orient") {
|
|
2496
|
+
this.showStatus("shift+tab is a Plan/Execute shortcut; in ORIENT framework mode, use /orient.<phase> to transition. Switch modes with /orient.mode plan-execute.");
|
|
2497
|
+
return;
|
|
2498
|
+
}
|
|
2499
|
+
let session = {
|
|
2500
|
+
state: "idle",
|
|
2501
|
+
goal: "",
|
|
2502
|
+
updatedAt: new Date().toISOString(),
|
|
2503
|
+
steps: [],
|
|
2504
|
+
currentStepIndex: -1,
|
|
2505
|
+
};
|
|
2506
|
+
try {
|
|
2507
|
+
if (fs.existsSync(stateFile)) {
|
|
2508
|
+
const parsed = JSON.parse(fs.readFileSync(stateFile, "utf-8"));
|
|
2509
|
+
if (parsed && typeof parsed === "object")
|
|
2510
|
+
session = { ...session, ...parsed };
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
catch {
|
|
2514
|
+
// Corrupt state file → reset to idle.
|
|
2515
|
+
}
|
|
2516
|
+
const before = session.state;
|
|
2517
|
+
let after;
|
|
2518
|
+
switch (session.state) {
|
|
2519
|
+
case "planning":
|
|
2520
|
+
after = "executing";
|
|
2521
|
+
break;
|
|
2522
|
+
case "executing":
|
|
2523
|
+
after = "planning";
|
|
2524
|
+
break;
|
|
2525
|
+
default:
|
|
2526
|
+
// idle → planning
|
|
2527
|
+
after = "planning";
|
|
2528
|
+
break;
|
|
2529
|
+
}
|
|
2530
|
+
session.state = after;
|
|
2531
|
+
session.updatedAt = new Date().toISOString();
|
|
2532
|
+
try {
|
|
2533
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
2534
|
+
fs.writeFileSync(stateFile, JSON.stringify(session, null, 2), "utf-8");
|
|
2535
|
+
}
|
|
2536
|
+
catch (err) {
|
|
2537
|
+
this.showError(`Failed to persist mode toggle: ${err.message}`);
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
// Update the status bar and log a one-line banner in chat.
|
|
2541
|
+
// The extension's session_start hook handles the system-reminder
|
|
2542
|
+
// injection on the next user turn; here we just change state +
|
|
2543
|
+
// surface the change visually so the user sees the flip immediately.
|
|
2544
|
+
const statusText = after === "planning" ? "plan" : "execute";
|
|
2545
|
+
this.showStatus(`Mode: ${before} → ${after}`);
|
|
2546
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2547
|
+
const bannerText = after === "planning"
|
|
2548
|
+
? theme.fg("accent", "▣ Plan mode") +
|
|
2549
|
+
theme.fg("muted", " — agent will draft into .orient-cli/plans/current.md without editing any other file. Type /execute or press shift+tab again to approve.")
|
|
2550
|
+
: theme.fg("accent", "▶ Execute mode") +
|
|
2551
|
+
theme.fg("muted", " — agent will read the plan and walk its steps. Press shift+tab to revise the plan.");
|
|
2552
|
+
this.chatContainer.addChild(new Text(bannerText, 1, 0));
|
|
2553
|
+
this.ui.requestRender();
|
|
2554
|
+
// Track the transition in the footer status namespace so the
|
|
2555
|
+
// orient extension's next session_start can read it back. This
|
|
2556
|
+
// is the same channel ctx.ui.setStatus() uses from extensions.
|
|
2557
|
+
this.footerDataProvider.setExtensionStatus("orient", statusText);
|
|
2558
|
+
}
|
|
2453
2559
|
cycleThinkingLevel() {
|
|
2454
2560
|
const newLevel = this.session.cycleThinkingLevel();
|
|
2455
2561
|
if (newLevel === undefined) {
|