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.
Files changed (19) hide show
  1. package/node_modules/@orient-cli/agent-core/package.json +1 -1
  2. package/node_modules/@orient-cli/ai/package.json +1 -1
  3. package/node_modules/@orient-cli/coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  4. package/node_modules/@orient-cli/coding-agent/dist/core/extensions/runner.js +1 -0
  5. package/node_modules/@orient-cli/coding-agent/dist/core/extensions/runner.js.map +1 -1
  6. package/node_modules/@orient-cli/coding-agent/dist/core/keybindings.d.ts +6 -1
  7. package/node_modules/@orient-cli/coding-agent/dist/core/keybindings.d.ts.map +1 -1
  8. package/node_modules/@orient-cli/coding-agent/dist/core/keybindings.js +9 -1
  9. package/node_modules/@orient-cli/coding-agent/dist/core/keybindings.js.map +1 -1
  10. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  11. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/components/model-selector.js +1 -1
  12. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  13. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.d.ts +24 -0
  14. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  15. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.js +107 -1
  16. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  17. package/node_modules/@orient-cli/coding-agent/package.json +1 -1
  18. package/node_modules/@orient-cli/tui/package.json +1 -1
  19. 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) {