seedcode-cli 7.2.5__py3-none-any.whl → 8.1.0__py3-none-any.whl

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.
seedcode/__init__.py CHANGED
@@ -6,7 +6,7 @@ Plant ideas. Grow code.
6
6
  # Single source of truth for the application version. Every consumer reads it:
7
7
  # the PyPI build (via [tool.hatch.version]), the CLI --version flag, the
8
8
  # Windows installer metadata, the GitHub release tag, and the WinGet manifest.
9
- __version__ = "7.2.5"
9
+ __version__ = "8.1.0"
10
10
  __author__ = "Al Shahriar Sowan"
11
11
  __publisher__ = "Eagox Studio"
12
12
  # Current author/company links, surfaced by /about and the README credits.
seedcode/app.py CHANGED
@@ -150,23 +150,28 @@ def _key_status(config: AppConfig) -> str:
150
150
 
151
151
 
152
152
  def _mode_status(config: AppConfig) -> str:
153
- """Menu status for the Code Mode item (ON only when really active)."""
153
+ """Menu status for the Code Mode item (ON only when really active).
154
+
155
+ Code Mode has its own workspace session, so its status must reflect that
156
+ session alone. Reading ``agent_mode`` here made the item read ON whenever
157
+ Agent Mode was on, even though no Code Mode workspace was active — a live
158
+ status that did not match the live state.
159
+ """
154
160
  try:
155
161
  from .codemode_state import codemode_state
156
162
 
157
- if codemode_state().enabled:
158
- return "ON"
163
+ return "ON" if codemode_state().enabled else "OFF"
159
164
  except Exception:
160
- pass
161
- return "ON" if config.agent_mode else "OFF"
165
+ return "OFF"
162
166
 
163
167
 
164
168
  def _main_menu(config: AppConfig):
165
169
  """The interactive main menu; returns an action id or None (exit).
166
170
 
167
- v6.2.5 reference layout: the six mode/setup actions carry Ctrl+1..Ctrl+6
171
+ v6.2.5 reference layout: the mode/setup actions carry Ctrl+1..Ctrl+5
168
172
  shortcuts that execute the real handlers (no decorative items), followed
169
- by the chat/setup actions kept from earlier releases.
173
+ by the chat/setup actions kept from earlier releases. There are exactly
174
+ three modes; Assist Mode no longer exists as a menu entry.
170
175
  """
171
176
  provider = PROVIDERS.get(config.provider)
172
177
  badge = badge_for_status(provider.status if provider is not None else "")
@@ -174,10 +179,9 @@ def _main_menu(config: AppConfig):
174
179
  [
175
180
  MenuItem("Code Mode", "codemode", status=_mode_status(config), shortcut="1"),
176
181
  MenuItem("Agent Mode", "agent", shortcut="2"),
177
- MenuItem("Assist Mode", "assist", shortcut="3"),
178
- MenuItem("Project Memory", "memory", shortcut="4"),
179
- MenuItem("Settings", "settings", shortcut="5"),
180
- MenuItem("Exit", "exit", shortcut="6"),
182
+ MenuItem("Project Memory", "memory", shortcut="3"),
183
+ MenuItem("Settings", "settings", shortcut="4"),
184
+ MenuItem("Exit", "exit", shortcut="5"),
181
185
  MenuItem("Start Chat", "chat", status=_model_status(config), badge=badge),
182
186
  MenuItem("Provider", "provider", status=_provider_status(config)),
183
187
  MenuItem("API Key", "apikey", status=_key_status(config)),
@@ -324,7 +328,7 @@ class _TaskPresenter:
324
328
  elif kind == "retry":
325
329
  self.ui.dim(f" {detail}")
326
330
  elif kind == "limit":
327
- self.ui.warning(f"Assist stopped: {detail}")
331
+ self.ui.warning(f"Agent Mode stopped: {detail}")
328
332
  elif kind == "call" and flow is None:
329
333
  # With a task view on screen the step list already shows the tool
330
334
  # work; without one, narrate it (the plain/legacy experience).
@@ -497,7 +501,7 @@ def agent_presenter(agent: AgentEngine) -> _TaskPresenter | None:
497
501
 
498
502
 
499
503
  def _task_mode_label(config: AppConfig) -> str:
500
- """The mode named in a task header (Code Mode sharpens Assist Mode)."""
504
+ """The mode named in a task header (Chat / Code Mode / Agent Mode)."""
501
505
  try:
502
506
  from .codemode_state import codemode_state
503
507
 
@@ -505,7 +509,9 @@ def _task_mode_label(config: AppConfig) -> str:
505
509
  return "Code Mode"
506
510
  except Exception:
507
511
  pass
508
- return "Assist Mode"
512
+ from .core.modes import active_mode, mode_title
513
+
514
+ return mode_title(active_mode(config))
509
515
 
510
516
 
511
517
  def _save_codemode_summary(agent: AgentEngine, text: str, outcome: str) -> None:
@@ -681,7 +687,7 @@ def _handle_agent(
681
687
  text: str,
682
688
  presenter: _TaskPresenter | None = None,
683
689
  ) -> None:
684
- """Run one full Assist/Code turn (tool loop) and render the final answer.
690
+ """Run one full Agent/Code turn (tool loop) and render the final answer.
685
691
 
686
692
  The turn is shown as a compact live task flow whose step states follow real
687
693
  engine activity (see :mod:`seedcode.ui.tasks`) — never a fake progress bar.
@@ -766,7 +772,7 @@ def _handle_agent(
766
772
  def _make_agent(
767
773
  ui: UI, config: AppConfig, presenter: "_TaskPresenter | None" = None
768
774
  ) -> AgentEngine:
769
- """Build an Assist engine bound to the CWD and the configured permissions."""
775
+ """Build an Agent Mode engine bound to the CWD and configured permissions."""
770
776
  # Lazy import (matching _make_desktop_session): the optional, platform-
771
777
  # specific computer package stays out of app.py's top-level import graph.
772
778
  from .computer import is_available
@@ -818,7 +824,7 @@ def _make_desktop_session(ui: UI):
818
824
 
819
825
  The session is bound to the process-wide
820
826
  :class:`~seedcode.computer.SessionPermissionManager` so a permission the
821
- user granted once at ``/assist on`` keeps holding even though this gate is
827
+ user granted once at ``/agent on`` keeps holding even though this gate is
822
828
  rebuilt whenever the permission level changes.
823
829
  """
824
830
  from .computer import DesktopGrant, DesktopSession, session_permissions
@@ -866,14 +872,14 @@ def _chat_loop(
866
872
  ) -> None:
867
873
  """Interactive chat until /exit (returns to the main menu).
868
874
 
869
- Only an explicit exit leaves here: a finished task (Code Mode, Assist
870
- Mode, Agent Mode or a plain chat turn) returns to the prompt with
875
+ Only an explicit exit leaves here: a finished task (Code Mode, Agent
876
+ Mode or a plain chat turn) returns to the prompt with
871
877
  ``Ready for next task.`` and the session keeps running.
872
878
  """
873
879
  ctx = CommandContext(ui=ui, config=config, engine=engine)
874
880
  ui.dim(INPUT_HINT)
875
881
 
876
- # The Assist engine is built lazily on the first assist-mode turn and
882
+ # The Agent engine is built lazily on the first agent-mode turn and
877
883
  # rebuilt when the permission or desktop mode changes (its system
878
884
  # prompt and permission gates reflect both). The presenter that owns the
879
885
  # live step view lives for the whole chat session, so each turn can bind
@@ -1120,8 +1126,6 @@ def run(ui: UI) -> None:
1120
1126
  _dispatch_command(ui, config, engine, "/codemode on")
1121
1127
  elif choice == "agent":
1122
1128
  _dispatch_command(ui, config, engine, "/agent on")
1123
- elif choice == "assist":
1124
- _dispatch_command(ui, config, engine, "/assist on")
1125
1129
  elif choice == "memory":
1126
1130
  _dispatch_command(ui, config, engine, "/codemode status")
1127
1131
  elif choice == "settings":
@@ -1,6 +1,6 @@
1
1
  """Code Mode state (v6.2.0): workspace-aware coding agent session state.
2
2
 
3
- Code Mode is Assist Mode sharpened for the current project:
3
+ Code Mode is the workspace-aware coding mode for the current project:
4
4
 
5
5
  * the CWD at ``/codemode on`` becomes the *workspace* root;
6
6
  * ``.seedcode/`` project memory is ensured and the index refreshed
@@ -1,6 +1,8 @@
1
- """Legacy commands: /agent and /permission.
1
+ """Agent Mode commands: /agent and /permission (v8.1.0).
2
2
 
3
- The old Agent Mode was merged into Assist Mode — /agent now routes there.
3
+ /agent is the primary route into Agent Mode — the single general-purpose
4
+ execution mode that the retired Assist Mode folded into. /assist and
5
+ /desktop remain accepted aliases so an existing habit keeps working.
4
6
  /permission keeps its dedicated interactive picker; /index and /tools stay
5
7
  as inspection commands.
6
8
  """
@@ -15,7 +17,7 @@ from . import CommandContext, CommandResult, command, show_session_bar
15
17
  from .assist import disable_assist, enable_assist
16
18
 
17
19
 
18
- @command("agent", "Legacy alias for Assist Mode. Usage: /agent [on|off]")
20
+ @command("agent", "Select Agent Mode (general-purpose execution). Usage: /agent [on|off]")
19
21
  def _agent(ctx: CommandContext, arg: str) -> CommandResult:
20
22
  raw = arg.strip().lower()
21
23
  if raw in ("on", "off"):
@@ -27,8 +29,6 @@ def _agent(ctx: CommandContext, arg: str) -> CommandResult:
27
29
  ctx.ui.dim("Expected: /agent on|off")
28
30
  return CommandResult()
29
31
 
30
- # Agent Mode was merged into Assist Mode — route there transparently.
31
- ctx.ui.dim("(/agent is now Assist Mode)")
32
32
  if enable:
33
33
  enable_assist(ctx.ui, ctx.config)
34
34
  else:
@@ -39,7 +39,7 @@ def _agent(ctx: CommandContext, arg: str) -> CommandResult:
39
39
 
40
40
  @command(
41
41
  "permission",
42
- "Show or set the Assist permission mode",
42
+ "Show or set the tool permission level",
43
43
  aliases=("perm", "permissions"),
44
44
  )
45
45
  def _permission(ctx: CommandContext, arg: str) -> CommandResult:
@@ -85,7 +85,7 @@ def _index(ctx: CommandContext, arg: str) -> CommandResult:
85
85
  return CommandResult()
86
86
 
87
87
 
88
- @command("tools", "List the tools available in Assist Mode")
88
+ @command("tools", "List the tools available to the agent modes")
89
89
  def _tools(ctx: CommandContext, arg: str) -> CommandResult:
90
90
  from ..ui.layout import columns_grid
91
91
 
@@ -95,6 +95,6 @@ def _tools(ctx: CommandContext, arg: str) -> CommandResult:
95
95
  kind = "changes files/system" if tool.mutates else "read-only"
96
96
  rows.append((name, f"{tool.description} ({kind})"))
97
97
  ctx.ui.panel(
98
- columns_grid(rows, ("seed.primary", "seed.text")), title="Assist Tools"
98
+ columns_grid(rows, ("seed.primary", "seed.text")), title="Agent Tools"
99
99
  )
100
100
  return CommandResult()
@@ -1,12 +1,14 @@
1
- """Assist Mode: unified AI + computer control.
1
+ """Agent Mode: unified AI + tool/computer execution (v8.1.0).
2
2
 
3
- /assist on — enables the full capability set (AI, filesystem, terminal,
4
- git, browser, keyboard, mouse, windows, vision, OCR, desktop
5
- automation).
6
- /assist off — back to plain chat.
3
+ /agent on — selects Agent Mode: the full capability set (AI, filesystem,
4
+ terminal, git, browser, keyboard, mouse, windows, vision, OCR,
5
+ desktop automation).
6
+ /agent off — back to plain Chat Mode.
7
7
 
8
- Assist Mode is the ONLY automation mode Seed Code exposes. The old Agent
9
- and Desktop modes were merged into it; their commands now route here.
8
+ Agent Mode is the single general-purpose execution mode. The retired Assist
9
+ Mode is this mode under its old name: ``/assist`` and ``/desktop`` still work
10
+ and route here, but they never create a fourth mode. Code Mode is the
11
+ workspace-aware coding agent (see :mod:`seedcode.commands.codemode`).
10
12
  """
11
13
 
12
14
  from __future__ import annotations
@@ -18,7 +20,7 @@ from ..config import save_config
18
20
  from ..tools import TOOL_REGISTRY, PermissionMode
19
21
  from . import CommandContext, CommandResult, command, show_session_bar
20
22
 
21
- # The Assist capability set, in display order. Desktop-engine rows are
23
+ # The Agent Mode capability set, in display order. Desktop-engine rows are
22
24
  # marked so they can be dimmed when the Computer Engine is unavailable.
23
25
  _CAPABILITIES: tuple[tuple[str, str, bool], ...] = (
24
26
  ("AI", "Chat, reasoning, and code generation", False),
@@ -35,7 +37,7 @@ _CAPABILITIES: tuple[tuple[str, str, bool], ...] = (
35
37
  )
36
38
 
37
39
 
38
- @command("assist", "Enable/disable Assist Mode (unified AI + computer control)")
40
+ @command("assist", "Legacy alias for Agent Mode. Usage: /assist [on|off]")
39
41
  def _assist(ctx: CommandContext, arg: str) -> CommandResult:
40
42
  raw = arg.strip().lower()
41
43
  if raw in ("on", "off"):
@@ -46,7 +48,7 @@ def _assist(ctx: CommandContext, arg: str) -> CommandResult:
46
48
  return CommandResult()
47
49
  else:
48
50
  ctx.ui.warning("[Command Error] Invalid syntax.")
49
- ctx.ui.dim("Expected: /assist on|off")
51
+ ctx.ui.dim("Expected: /agent on|off (or /assist on|off)")
50
52
  return CommandResult()
51
53
 
52
54
  if enable:
@@ -58,7 +60,7 @@ def _assist(ctx: CommandContext, arg: str) -> CommandResult:
58
60
 
59
61
 
60
62
  def capability_table(desktop_ok: bool) -> Table:
61
- """The Assist capability list (✓ rows; unavailable rows dim).
63
+ """The Agent Mode capability list (✓ rows; unavailable rows dim).
62
64
 
63
65
  OCR is probed independently of the rest of the desktop stack: it needs a
64
66
  native engine the Python packages do not supply, so marking it available
@@ -91,12 +93,13 @@ def _ocr_available() -> bool:
91
93
 
92
94
 
93
95
  def enable_assist(ui, config) -> None:
94
- """Enable Assist Mode: the full capability set in one switch."""
95
- config.agent_mode = True
96
+ """Select Agent Mode: the full capability set in one switch."""
97
+ config.mode = "agent"
96
98
 
97
- # Desktop capabilities require the Computer Engine. When available, Assist
98
- # runs at the ``desktop`` level so the AI can drive the computer; otherwise
99
- # it stays at ``workspace`` (AI + filesystem + terminal + git still work).
99
+ # Desktop capabilities require the Computer Engine. When available, Agent
100
+ # Mode runs at the ``desktop`` level so the AI can drive the computer;
101
+ # otherwise it stays at ``workspace`` (AI + filesystem + terminal + git
102
+ # still work).
100
103
  desktop_ok, desktop_reason = is_available()
101
104
  config.permission_mode = (
102
105
  PermissionMode.DESKTOP.value_str if desktop_ok
@@ -104,9 +107,9 @@ def enable_assist(ui, config) -> None:
104
107
  )
105
108
  save_config(config)
106
109
 
107
- ui.success("Assist Mode ON")
110
+ ui.success("Agent Mode ON")
108
111
  ui.blank()
109
- ui.panel(capability_table(desktop_ok), title="Assist Mode")
112
+ ui.panel(capability_table(desktop_ok), title="Agent Mode")
110
113
  if not desktop_ok:
111
114
  ui.dim(f"Desktop capabilities unavailable: {desktop_reason}")
112
115
  ui.blank()
@@ -143,14 +146,14 @@ def request_session_permissions(ui) -> bool:
143
146
  f" • {CATEGORY_LABELS.get(c, c)}" for c in SESSION_GRANT_CATEGORIES
144
147
  )
145
148
  description = (
146
- "Grant these for this session so Assist doesn't interrupt every step:\n"
149
+ "Grant these for this session so Agent Mode doesn't interrupt every step:\n"
147
150
  f"{wanted}\n"
148
151
  "Sensitive actions (registry writes, passwords, system power, deletions, "
149
152
  "purchases) will still ask each time."
150
153
  )
151
154
 
152
155
  try:
153
- answer = ui.confirm_desktop("Assist Mode session permissions", description)
156
+ answer = ui.confirm_desktop("Agent Mode session permissions", description)
154
157
  except Exception:
155
158
  # No interactive prompt available (headless/non-TTY): leave the
156
159
  # per-action flow in place rather than silently granting anything.
@@ -161,25 +164,27 @@ def request_session_permissions(ui) -> bool:
161
164
  session.request(SESSION_GRANT_CATEGORIES, allow=True)
162
165
  ui.dim("Desktop control granted for this session — you won't be asked again.")
163
166
  else:
164
- ui.dim("Declined: Assist will ask before each desktop action.")
167
+ ui.dim("Declined: Agent Mode will ask before each desktop action.")
165
168
  return allowed
166
169
 
167
170
 
168
171
  def disable_assist(ui, config) -> None:
169
- """Disable Assist Mode: back to plain chat."""
172
+ """Leave Agent Mode: back to plain Chat."""
170
173
  from ..computer.permissions import session_permissions
171
174
 
172
- config.agent_mode = False
175
+ config.mode = "chat"
173
176
  # Drop back to the safe editing level (removes desktop capability).
174
177
  config.permission_mode = PermissionMode.WORKSPACE.value_str
175
178
  save_config(config)
176
- # Forget the session-wide desktop grant: turning Assist back on asks again.
179
+ # Forget the session-wide desktop grant: turning Agent Mode back on asks again.
177
180
  session_permissions().reset()
178
- ui.success("Assist Mode OFF — back to plain chat")
181
+ ui.success("Agent Mode OFF — back to plain chat")
179
182
 
180
183
 
181
184
  def _show_status(ctx: CommandContext) -> None:
182
- """Show current Assist Mode status."""
185
+ """Show current Agent Mode status."""
186
+ from ..core.modes import active_mode, mode_title
187
+
183
188
  on = ctx.config.agent_mode
184
189
  desktop_ok, desktop_reason = is_available()
185
190
 
@@ -188,8 +193,8 @@ def _show_status(ctx: CommandContext) -> None:
188
193
  table.add_column()
189
194
 
190
195
  state = "[seed.accent]ON[/seed.accent]" if on else "[seed.dim]OFF[/seed.dim]"
191
- table.add_row("Assist Mode", state)
192
- table.add_row("Mode", "Assist" if on else "Chat")
196
+ table.add_row("Agent Mode", state)
197
+ table.add_row("Mode", mode_title(active_mode(ctx.config)))
193
198
  if not desktop_ok:
194
199
  table.add_row("Desktop", f"Unavailable: {desktop_reason}")
195
200
  table.add_row("Permission", ctx.config.permission_mode.replace("_", " ").title())
@@ -198,6 +203,6 @@ def _show_status(ctx: CommandContext) -> None:
198
203
  desktop_tools = [n for n, t in TOOL_REGISTRY.items() if t.group == "desktop"]
199
204
  table.add_row("Available tools", f"{len(core_tools)} core, {len(desktop_tools)} desktop")
200
205
 
201
- ctx.ui.panel(table, title="Assist Mode")
206
+ ctx.ui.panel(table, title="Agent Mode")
202
207
  ctx.ui.blank()
203
- ctx.ui.dim("Toggle with: /assist on | /assist off")
208
+ ctx.ui.dim("Toggle with: /agent on | /agent off (alias: /assist)")
seedcode/commands/chat.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """/chat — explicit Chat Mode (plain conversation, no tools).
2
2
 
3
3
  /chat — show the current mode.
4
- /chat on — leave Assist/Code Mode and return to plain Chat: the AI answers
4
+ /chat on — leave Code/Agent Mode and return to plain Chat: the AI answers
5
5
  with text only and never touches the filesystem or terminal.
6
6
 
7
7
  Chat Mode is the default runtime mode; this command exists so a user can
@@ -19,7 +19,7 @@ def _chat(ctx: CommandContext, arg: str) -> CommandResult:
19
19
  raw = arg.strip().lower()
20
20
  if raw in ("", "status"):
21
21
  ctx.ui.info(f"Mode: {mode_label(ctx.config)}")
22
- ctx.ui.dim("Switch with: /chat on · /assist on · /codemode on (or /mode chat|assist|code)")
22
+ ctx.ui.dim("Switch with: /chat on · /agent on · /codemode on (or /mode chat|code|agent)")
23
23
  return CommandResult()
24
24
 
25
25
  if raw != "on":
@@ -57,13 +57,13 @@ def _enable_codemode(ctx: CommandContext) -> None:
57
57
  state = codemode_state()
58
58
  workspace = state.workspace # already set? keep it (re-enable same project)
59
59
 
60
- # Agent mode is the engine Code Mode sharpens; turn it on when off.
60
+ # Agent Mode is the engine Code Mode sharpens; turn it on when off.
61
61
  if not config.agent_mode:
62
62
  from .assist import enable_assist
63
63
 
64
64
  enable_assist(ctx.ui, config)
65
- # Assist prefers the desktop level when the engine exists; Code Mode is a
66
- # *coding* workflow, so keep the editing level (workspace) unless the user
65
+ # Agent Mode prefers the desktop level when the engine exists; Code Mode is
66
+ # a *coding* workflow, so keep the editing level (workspace) unless the user
67
67
  # had already chosen something stronger.
68
68
  if PermissionMode.parse(config.permission_mode) == PermissionMode.DESKTOP:
69
69
  config.permission_mode = PermissionMode.WORKSPACE.value_str
@@ -1,9 +1,9 @@
1
- """Desktop-related commands: /desktop (legacy → Assist), /computer,
1
+ """Desktop-related commands: /desktop (legacy → Agent Mode), /computer,
2
2
  /screenshot, /windows.
3
3
 
4
- The old Desktop Mode was merged into Assist Mode — /desktop now routes
4
+ The old Desktop Mode was merged into Agent Mode — /desktop now routes
5
5
  there. /computer shows engine status; /screenshot and /windows work
6
- immediately (no Assist turn needed) so the user can sanity-check the
6
+ immediately (no Agent Mode turn needed) so the user can sanity-check the
7
7
  engine by hand.
8
8
  """
9
9
 
@@ -16,19 +16,19 @@ from . import CommandContext, CommandResult, command, show_session_bar
16
16
  from .assist import disable_assist, enable_assist
17
17
 
18
18
 
19
- @command("desktop", "Legacy alias for Assist Mode. Usage: /desktop [on|off]")
19
+ @command("desktop", "Legacy alias for Agent Mode. Usage: /desktop [on|off]")
20
20
  def _desktop(ctx: CommandContext, arg: str) -> CommandResult:
21
21
  raw = arg.strip().lower()
22
22
  if raw in ("on", "off"):
23
23
  enable = raw == "on"
24
24
  elif not raw:
25
- enable = not ctx.config.agent_mode # bare /desktop toggles Assist
25
+ enable = not ctx.config.agent_mode # bare /desktop toggles Agent Mode
26
26
  else:
27
27
  ctx.ui.warning("Usage: /desktop [on|off]")
28
28
  return CommandResult()
29
29
 
30
- # Desktop Mode was merged into Assist Mode — route there transparently.
31
- ctx.ui.dim("(/desktop is now Assist Mode)")
30
+ # Desktop Mode was merged into Agent Mode — route there transparently.
31
+ ctx.ui.dim("(/desktop is now Agent Mode)")
32
32
  if enable:
33
33
  enable_assist(ctx.ui, ctx.config)
34
34
  else:
@@ -44,7 +44,7 @@ def _computer(ctx: CommandContext, arg: str) -> CommandResult:
44
44
  table = Table.grid(padding=(0, 3))
45
45
  table.add_column(style="seed.dim", justify="right", no_wrap=True)
46
46
  table.add_column(style="seed.text")
47
- table.add_row("Mode", "Assist" if ctx.config.agent_mode else "Chat (/assist on)")
47
+ table.add_row("Mode", "Agent" if ctx.config.agent_mode else "Chat (/agent on)")
48
48
  table.add_row("Engine", reason if not ok else "Available")
49
49
  missing = missing_packages()
50
50
  if missing:
seedcode/commands/mode.py CHANGED
@@ -1,52 +1,55 @@
1
- """/mode — one generic switcher for the runtime modes.
1
+ """/mode — the generic switcher for the three canonical modes (v8.1.0).
2
2
 
3
3
  /mode — show the current mode.
4
4
  /mode chat — plain conversation (no tools).
5
- /mode assist — the unified AI + tools mode (filesystem, terminal, git, ...).
6
- /mode code — Assist sharpened for the current workspace (Code Mode).
7
- /mode agent — legacy alias of assist.
5
+ /mode code — the workspace coding agent (Code Mode).
6
+ /mode agent — general-purpose execution (Agent Mode).
8
7
 
9
- The individual commands (/chat, /assist, /codemode) remain the direct routes;
8
+ ``assist`` is accepted as an input alias and resolves to Agent Mode — the
9
+ retired Assist Mode is Agent Mode now, so no fourth mode can be selected.
10
+
11
+ The individual commands (/chat, /codemode, /agent) remain the direct routes;
10
12
  this command is the single entry point that understands all of them.
11
13
  """
12
14
 
13
15
  from __future__ import annotations
14
16
 
17
+ from ..core.modes import MODE_DESCRIPTIONS, Mode, active_mode, mode_title, parse_mode
15
18
  from . import CommandContext, CommandResult, command, show_session_bar
16
19
  from .status import mode_label
17
20
 
18
21
 
19
- @command("mode", "Show or switch the runtime mode. Usage: /mode [chat|assist|code|agent]")
22
+ @command("mode", "Show or switch the runtime mode. Usage: /mode [chat|code|agent]")
20
23
  def _mode(ctx: CommandContext, arg: str) -> CommandResult:
21
24
  raw = arg.strip().lower()
22
25
 
23
26
  if not raw:
24
27
  ctx.ui.info(f"Mode: {mode_label(ctx.config)}")
25
- ctx.ui.dim("Switch with: /mode chat|assist|code|agent")
28
+ ctx.ui.dim("Switch with: /mode chat|code|agent")
26
29
  return CommandResult()
27
30
 
28
- if raw == "chat":
31
+ mode = parse_mode(raw, default=Mode.CHAT)
32
+ if raw not in ("chat", "code", "agent", "assist", "codemode"):
33
+ ctx.ui.warning("[Command Error] Invalid syntax.")
34
+ ctx.ui.dim("Expected: /mode chat|code|agent")
35
+ return CommandResult()
36
+
37
+ if raw == "assist":
38
+ ctx.ui.dim("(Assist Mode is Agent Mode now — selecting Agent Mode)")
39
+
40
+ if mode is Mode.CHAT:
29
41
  from .chat import _enter_chat
30
42
 
31
43
  _enter_chat(ctx)
32
- elif raw == "assist":
33
- from .assist import enable_assist
34
-
35
- enable_assist(ctx.ui, ctx.config)
36
- elif raw == "code":
44
+ elif mode is Mode.CODE:
37
45
  from .codemode import _enable_codemode
38
46
 
39
47
  _enable_codemode(ctx)
40
- elif raw == "agent":
41
- # Agent Mode merged into Assist Mode; keep the name working.
48
+ else: # Mode.AGENT
42
49
  from .assist import enable_assist
43
50
 
44
51
  enable_assist(ctx.ui, ctx.config)
45
- ctx.ui.dim("(Agent Mode is Assist Mode now — same engine, same tools)")
46
- else:
47
- ctx.ui.warning("[Command Error] Invalid syntax.")
48
- ctx.ui.dim("Expected: /mode chat|assist|code|agent")
49
- return CommandResult()
50
52
 
53
+ ctx.ui.dim(f"Mode: {mode_title(active_mode(ctx.config))} — {MODE_DESCRIPTIONS[mode]}")
51
54
  show_session_bar(ctx.ui, ctx.config)
52
55
  return CommandResult()
@@ -1,7 +1,7 @@
1
1
  """/palette and /files — the command palette (Ctrl+K) and project search (Ctrl+P).
2
2
 
3
3
  The palette lists every high-level action (Change Provider, Change Model,
4
- Settings, History, Doctor, Clear History, About, Theme, Assist Mode, Search
4
+ Settings, History, Doctor, Clear History, About, Theme, Agent Mode, Search
5
5
  Projects) as a fuzzy-searchable selector, VS Code style. The chat REPL
6
6
  binds Ctrl+K / Ctrl+P to these commands so they open mid-conversation.
7
7
  """
@@ -16,7 +16,7 @@ from . import CommandContext, CommandResult, command, dispatch
16
16
 
17
17
 
18
18
  def _actions(ctx: CommandContext) -> list[PaletteAction]:
19
- assist_on = ctx.config.agent_mode
19
+ agent_on = ctx.config.agent_mode
20
20
  return [
21
21
  PaletteAction("Change Provider", "/provider", detail="switch AI backend"),
22
22
  PaletteAction("Change Model", "/model", detail="browse the live catalogue"),
@@ -28,9 +28,9 @@ def _actions(ctx: CommandContext) -> list[PaletteAction]:
28
28
  PaletteAction("About", "/about", detail="version and credits"),
29
29
  PaletteAction("Theme", "/theme", detail="pick a colour theme (live preview)"),
30
30
  PaletteAction(
31
- "Assist Mode",
32
- f"/assist {'off' if assist_on else 'on'}",
33
- detail=f"currently {'on' if assist_on else 'off'}",
31
+ "Agent Mode",
32
+ f"/agent {'off' if agent_on else 'on'}",
33
+ detail=f"currently {'on' if agent_on else 'off'}",
34
34
  ),
35
35
  PaletteAction("Search Projects", "__files__", detail="fuzzy project file search"),
36
36
  PaletteAction("Keyboard Shortcuts", "/shortcuts", detail="key reference"),
@@ -1,7 +1,7 @@
1
1
  """/status — the live runtime state of the current session (v7.1.0).
2
2
 
3
3
  One panel, every value read from real application state: the active provider
4
- and its backend, the selected model, the current mode (Chat / Assist / Code),
4
+ and its backend, the selected model, the current mode (Chat / Code / Agent),
5
5
  the Code Mode session (its state, verified progress and whether a stopped one
6
6
  can be resumed), the workspace, the provider connection status, and where
7
7
  config and project memory live. Nothing here is hardcoded or fabricated — a
@@ -23,14 +23,15 @@ from . import CommandContext, CommandResult, command
23
23
 
24
24
 
25
25
  def mode_label(config) -> str:
26
- """The active mode: Code Mode sharpens Assist, which sharpens Chat.
26
+ """The active mode label: Chat Mode / Code Mode / Agent Mode.
27
27
 
28
- Shared by /status, /mode, and /chat so every surface names the mode the
29
- same way from the same source of truth.
28
+ Delegates to the single mode resolver (:mod:`seedcode.core.modes`) so
29
+ /status, /mode, /chat, the dashboard and the header can never disagree,
30
+ and the retired Assist Mode can never reappear as a fourth name.
30
31
  """
31
- if codemode_state().enabled:
32
- return "Code Mode"
33
- return "Assist Mode" if config.agent_mode else "Chat"
32
+ from ..core.modes import mode_label as _label
33
+
34
+ return _label(config)
34
35
 
35
36
 
36
37
  # Backward-compatible private name.
@@ -50,7 +50,7 @@ SENSITIVE_CATEGORIES = frozenset(
50
50
  }
51
51
  )
52
52
 
53
- # The non-sensitive categories Assist Mode grants up-front in ONE prompt when it
53
+ # The non-sensitive categories Agent Mode grants up-front in ONE prompt when it
54
54
  # starts, so the session never re-asks for routine control during a task. These
55
55
  # are exactly the categories a normal automation session touches; sensitive
56
56
  # categories are deliberately excluded — they always confirm per action.
@@ -84,10 +84,10 @@ def _deny_all(category: str, description: str) -> DesktopGrant:
84
84
 
85
85
  @dataclass
86
86
  class SessionPermissionManager:
87
- """Session-wide desktop grants, requested ONCE when Assist Mode starts.
87
+ """Session-wide desktop grants, requested ONCE when Agent Mode starts.
88
88
 
89
89
  This is the fix for "asks for permission on every action": instead of
90
- prompting lazily the first time each category is touched, Assist Mode calls
90
+ prompting lazily the first time each category is touched, Agent Mode calls
91
91
  :meth:`request` at start-up to grant the whole routine-control set in a
92
92
  single dialog. Every :class:`DesktopSession` created for the session
93
93
  (the app rebuilds them whenever the permission level changes) consults the
@@ -123,7 +123,7 @@ class SessionPermissionManager:
123
123
  return category in self.denied
124
124
 
125
125
  def reset(self) -> None:
126
- """Forget the session decision (Assist Mode turned off)."""
126
+ """Forget the session decision (Agent Mode turned off)."""
127
127
  self.granted.clear()
128
128
  self.denied.clear()
129
129
  self.requested = False