yycode 0.3.2__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.
- agent/__init__.py +33 -0
- agent/acp/__init__.py +2 -0
- agent/acp/approval_adapter.py +134 -0
- agent/acp/content_adapter.py +45 -0
- agent/acp/jsonrpc.py +92 -0
- agent/acp/server.py +197 -0
- agent/acp/session_manager.py +193 -0
- agent/acp/update_adapter.py +192 -0
- agent/app_paths.py +25 -0
- agent/approval.py +169 -0
- agent/cancellation.py +52 -0
- agent/change_snapshot.py +186 -0
- agent/context_compressor.py +116 -0
- agent/graph.py +137 -0
- agent/llm_retry.py +434 -0
- agent/logger.py +97 -0
- agent/lsp/__init__.py +13 -0
- agent/lsp/client.py +151 -0
- agent/lsp/manager.py +234 -0
- agent/lsp/types.py +119 -0
- agent/message_context_manager.py +322 -0
- agent/message_format.py +105 -0
- agent/nodes/llm_node.py +58 -0
- agent/nodes/state.py +12 -0
- agent/nodes/task_guard_node.py +50 -0
- agent/nodes/tools_node.py +70 -0
- agent/plan_snapshot.py +70 -0
- agent/providers/__init__.py +13 -0
- agent/providers/anthropic_provider.py +268 -0
- agent/providers/base.py +52 -0
- agent/providers/openai_provider.py +279 -0
- agent/providers/text_tool_calls.py +118 -0
- agent/runtime/approval_service.py +184 -0
- agent/runtime/context.py +43 -0
- agent/runtime/tool_events.py +368 -0
- agent/runtime/tool_executor.py +208 -0
- agent/runtime/tool_output.py +261 -0
- agent/runtime/tool_registry.py +91 -0
- agent/runtime/tool_scheduler.py +35 -0
- agent/runtime/workflow_guard.py +217 -0
- agent/runtime/workspace.py +5 -0
- agent/runtime/workspace_tools.py +22 -0
- agent/session.py +787 -0
- agent/session_replay.py +95 -0
- agent/session_store.py +186 -0
- agent/skills.py +254 -0
- agent/streaming.py +248 -0
- agent/subagent.py +634 -0
- agent/task_memory.py +340 -0
- agent/todo_manager.py +304 -0
- agent/tool_retry.py +106 -0
- agent/tui/__init__.py +14 -0
- agent/tui/app.py +1325 -0
- agent/tui/approval.py +53 -0
- agent/tui/commands/__init__.py +6 -0
- agent/tui/commands/base.py +48 -0
- agent/tui/commands/clear.py +37 -0
- agent/tui/commands/help.py +27 -0
- agent/tui/commands/registry.py +94 -0
- agent/tui/help_content.py +108 -0
- agent/tui/renderers.py +1961 -0
- agent/tui/runner.py +439 -0
- agent/tui/state.py +653 -0
- main.py +465 -0
- tools/__init__.py +50 -0
- tools/apply_patch.py +305 -0
- tools/bash.py +76 -0
- tools/diff_utils.py +139 -0
- tools/edit_file.py +40 -0
- tools/git_diff.py +72 -0
- tools/git_show.py +65 -0
- tools/grep.py +149 -0
- tools/list_files.py +90 -0
- tools/list_skills.py +24 -0
- tools/load_skill.py +30 -0
- tools/lsp_definition.py +27 -0
- tools/lsp_diagnostics.py +32 -0
- tools/lsp_document_symbols.py +23 -0
- tools/lsp_hover.py +29 -0
- tools/lsp_references.py +37 -0
- tools/lsp_utils.py +38 -0
- tools/lsp_workspace_symbols.py +23 -0
- tools/read_file.py +61 -0
- tools/read_many_files.py +50 -0
- tools/safety.py +50 -0
- tools/subagent.py +57 -0
- tools/todo.py +89 -0
- tools/verify.py +107 -0
- tools/web_search.py +250 -0
- tools/workspace.py +36 -0
- tools/workspace_state.py +60 -0
- tools/write_file.py +88 -0
- utils/__init__.py +5 -0
- utils/retry.py +13 -0
- yycode-0.3.2.data/data/skills/code_review.md +61 -0
- yycode-0.3.2.data/data/skills/code_workflow.md +404 -0
- yycode-0.3.2.data/data/skills/drawio/SKILL.md +636 -0
- yycode-0.3.2.data/data/skills/drawio/agents/openai.yaml +19 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-erd.drawio +84 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-layered-cn.drawio +91 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-layered-cn.png +0 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-layered.drawio +112 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-layered.png +0 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-ml.drawio +90 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-ring-cn.drawio +68 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-ring-cn.png +0 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-ring.drawio +86 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-ring.png +0 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-sequence.drawio +116 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-star-cn.drawio +66 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-star-cn.png +0 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-star.drawio +79 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-star.png +0 -0
- yycode-0.3.2.data/data/skills/drawio/assets/demo-uml-class.drawio +64 -0
- yycode-0.3.2.data/data/skills/drawio/assets/microservices-example.drawio +173 -0
- yycode-0.3.2.data/data/skills/drawio/assets/microservices-example.png +0 -0
- yycode-0.3.2.data/data/skills/drawio/assets/workflow-cn.drawio +120 -0
- yycode-0.3.2.data/data/skills/drawio/assets/workflow-cn.png +0 -0
- yycode-0.3.2.data/data/skills/drawio/assets/workflow.drawio +120 -0
- yycode-0.3.2.data/data/skills/drawio/assets/workflow.png +0 -0
- yycode-0.3.2.data/data/skills/drawio/docs/index.html +469 -0
- yycode-0.3.2.data/data/skills/drawio/docs/zh.html +456 -0
- yycode-0.3.2.data/data/skills/drawio/references/style-extraction.md +254 -0
- yycode-0.3.2.data/data/skills/drawio/styles/schema.json +112 -0
- yycode-0.3.2.data/data/skills/plan.md +115 -0
- yycode-0.3.2.data/data/skills/ppt/SKILL.md +254 -0
- yycode-0.3.2.dist-info/METADATA +12 -0
- yycode-0.3.2.dist-info/RECORD +131 -0
- yycode-0.3.2.dist-info/WHEEL +5 -0
- yycode-0.3.2.dist-info/entry_points.txt +2 -0
- yycode-0.3.2.dist-info/top_level.txt +4 -0
agent/tui/approval.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Approval adapter for the terminal UI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from agent.approval import ApprovalDecision, ApprovalRequest
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def approval_id_for_request(request: ApprovalRequest) -> str:
|
|
12
|
+
"""Build the approval identifier used by stream events and the TUI."""
|
|
13
|
+
target = request.path or request.command
|
|
14
|
+
return "|".join([request.action, request.tool_name, target])
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TuiApprovalAdapter:
|
|
18
|
+
"""Bridge runtime approval callbacks to TUI-driven user decisions."""
|
|
19
|
+
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
self._pending: dict[str, asyncio.Future[ApprovalDecision]] = {}
|
|
22
|
+
|
|
23
|
+
async def callback(self, request: ApprovalRequest) -> bool:
|
|
24
|
+
"""Wait for the TUI to approve or deny the request."""
|
|
25
|
+
return (await self.decide(request)).approved
|
|
26
|
+
|
|
27
|
+
async def decide(self, request: ApprovalRequest) -> ApprovalDecision:
|
|
28
|
+
"""Wait for the TUI to return an approval decision."""
|
|
29
|
+
approval_id = approval_id_for_request(request)
|
|
30
|
+
future: asyncio.Future[ApprovalDecision] = asyncio.get_running_loop().create_future()
|
|
31
|
+
self._pending[approval_id] = future
|
|
32
|
+
try:
|
|
33
|
+
return await future
|
|
34
|
+
finally:
|
|
35
|
+
self._pending.pop(approval_id, None)
|
|
36
|
+
|
|
37
|
+
def resolve(self, approval_id: str, approved: bool) -> bool:
|
|
38
|
+
"""Resolve one pending approval request."""
|
|
39
|
+
future: Optional[asyncio.Future[ApprovalDecision]] = self._pending.get(approval_id)
|
|
40
|
+
if future is None or future.done():
|
|
41
|
+
return False
|
|
42
|
+
future.set_result(ApprovalDecision("approved" if approved else "denied"))
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
def cancel_pending(self) -> int:
|
|
46
|
+
"""Cancel all pending approvals and return how many were resolved."""
|
|
47
|
+
count = 0
|
|
48
|
+
for future in list(self._pending.values()):
|
|
49
|
+
if future.done():
|
|
50
|
+
continue
|
|
51
|
+
future.set_result(ApprovalDecision("cancelled"))
|
|
52
|
+
count += 1
|
|
53
|
+
return count
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Base types for TUI-only commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING, Literal, Protocol
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
9
|
+
from agent.tui.runner import AgentTuiRunner
|
|
10
|
+
|
|
11
|
+
from .registry import CommandRegistry
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Severity = Literal["information", "warning", "error"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class CommandContext:
|
|
19
|
+
"""Execution dependencies passed to TUI commands."""
|
|
20
|
+
|
|
21
|
+
runner: "AgentTuiRunner"
|
|
22
|
+
registry: "CommandRegistry"
|
|
23
|
+
confirmed: bool = False
|
|
24
|
+
raw_text: str = ""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class CommandResult:
|
|
29
|
+
"""Result displayed in the TUI after a command executes."""
|
|
30
|
+
|
|
31
|
+
title: str
|
|
32
|
+
content: str = ""
|
|
33
|
+
severity: Severity = "information"
|
|
34
|
+
status: str = "completed"
|
|
35
|
+
clear_input: bool = True
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TuiCommand(Protocol):
|
|
39
|
+
"""Protocol implemented by TUI-only commands."""
|
|
40
|
+
|
|
41
|
+
name: str
|
|
42
|
+
description: str
|
|
43
|
+
usage: str
|
|
44
|
+
aliases: tuple[str, ...]
|
|
45
|
+
destructive: bool
|
|
46
|
+
|
|
47
|
+
async def execute(self, ctx: CommandContext, args: str) -> CommandResult:
|
|
48
|
+
"""Execute the command without sending it to the LLM."""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Clear session history command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from .base import CommandContext, CommandResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class ClearCommand:
|
|
12
|
+
name: str = "clear"
|
|
13
|
+
description: str = "Clear current session message history and TUI timeline"
|
|
14
|
+
usage: str = ":clear"
|
|
15
|
+
aliases: tuple[str, ...] = ()
|
|
16
|
+
destructive: bool = True
|
|
17
|
+
|
|
18
|
+
async def execute(self, ctx: CommandContext, args: str) -> CommandResult:
|
|
19
|
+
if args.strip():
|
|
20
|
+
return CommandResult(
|
|
21
|
+
title="Usage: :clear",
|
|
22
|
+
content="The :clear command does not accept arguments.",
|
|
23
|
+
severity="warning",
|
|
24
|
+
status="warning",
|
|
25
|
+
)
|
|
26
|
+
if not ctx.confirmed:
|
|
27
|
+
return CommandResult(
|
|
28
|
+
title="Confirm clear",
|
|
29
|
+
content="Clear session history? Type :clear! to confirm.",
|
|
30
|
+
severity="warning",
|
|
31
|
+
status="waiting_for_confirmation",
|
|
32
|
+
)
|
|
33
|
+
await ctx.runner.clear_session_history()
|
|
34
|
+
return CommandResult(title="Session cleared", content="Session history cleared.")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
COMMAND = ClearCommand()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Help command for TUI commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from agent.tui.help_content import render_help_page
|
|
8
|
+
|
|
9
|
+
from .base import CommandContext, CommandResult
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class HelpCommand:
|
|
14
|
+
name: str = "help"
|
|
15
|
+
description: str = "List available TUI commands"
|
|
16
|
+
usage: str = ":help [command]"
|
|
17
|
+
aliases: tuple[str, ...] = ("?",)
|
|
18
|
+
destructive: bool = False
|
|
19
|
+
|
|
20
|
+
async def execute(self, ctx: CommandContext, args: str) -> CommandResult:
|
|
21
|
+
return CommandResult(
|
|
22
|
+
title="yycode Help",
|
|
23
|
+
content=render_help_page(ctx.registry.list_commands()),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
COMMAND = HelpCommand()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Registry and auto-discovery for TUI-only commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import pkgutil
|
|
7
|
+
from collections.abc import Iterable
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
from .base import TuiCommand
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class ParsedCommand:
|
|
15
|
+
"""Parsed command line."""
|
|
16
|
+
|
|
17
|
+
command: TuiCommand
|
|
18
|
+
args: str = ""
|
|
19
|
+
confirmed: bool = False
|
|
20
|
+
raw_text: str = ""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CommandRegistry:
|
|
24
|
+
"""Lookup table for TUI commands."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, commands: Iterable[TuiCommand] = ()) -> None:
|
|
27
|
+
self._commands: dict[str, TuiCommand] = {}
|
|
28
|
+
self._aliases: dict[str, TuiCommand] = {}
|
|
29
|
+
for command in commands:
|
|
30
|
+
self.register(command)
|
|
31
|
+
|
|
32
|
+
def register(self, command: TuiCommand) -> None:
|
|
33
|
+
"""Register one command by its lowercase name and aliases."""
|
|
34
|
+
name = command.name.strip().lower()
|
|
35
|
+
if not name:
|
|
36
|
+
raise ValueError("Command name cannot be empty")
|
|
37
|
+
if name in self._commands or name in self._aliases:
|
|
38
|
+
raise ValueError(f"Duplicate TUI command name: {name}")
|
|
39
|
+
self._commands[name] = command
|
|
40
|
+
for alias in getattr(command, "aliases", ()) or ():
|
|
41
|
+
normalized = alias.strip().lower()
|
|
42
|
+
if not normalized:
|
|
43
|
+
continue
|
|
44
|
+
if normalized in self._commands or normalized in self._aliases:
|
|
45
|
+
raise ValueError(f"Duplicate TUI command alias: {normalized}")
|
|
46
|
+
self._aliases[normalized] = command
|
|
47
|
+
|
|
48
|
+
def get(self, name: str) -> TuiCommand | None:
|
|
49
|
+
"""Return a command by name or alias, if present."""
|
|
50
|
+
normalized = name.strip().lower().lstrip(":").rstrip("!")
|
|
51
|
+
return self._commands.get(normalized) or self._aliases.get(normalized)
|
|
52
|
+
|
|
53
|
+
def list_commands(self) -> list[TuiCommand]:
|
|
54
|
+
"""Return commands sorted by name."""
|
|
55
|
+
return [self._commands[name] for name in sorted(self._commands)]
|
|
56
|
+
|
|
57
|
+
def matching(self, token: str, limit: int = 8) -> list[TuiCommand]:
|
|
58
|
+
"""Return commands matching a completion token."""
|
|
59
|
+
normalized = token.strip().lower().lstrip(":")
|
|
60
|
+
commands = self.list_commands()
|
|
61
|
+
rows = [command for command in commands if command.name.startswith(normalized)]
|
|
62
|
+
if normalized and not rows:
|
|
63
|
+
rows = [command for command in commands if normalized in command.name]
|
|
64
|
+
return rows[:limit]
|
|
65
|
+
|
|
66
|
+
def parse(self, text: str) -> ParsedCommand | None:
|
|
67
|
+
"""Parse a command line such as ':clear!' or ':help clear'."""
|
|
68
|
+
stripped = text.strip()
|
|
69
|
+
if not stripped.startswith(":"):
|
|
70
|
+
return None
|
|
71
|
+
command_text = stripped[1:].strip()
|
|
72
|
+
if not command_text:
|
|
73
|
+
return None
|
|
74
|
+
name, _, args = command_text.partition(" ")
|
|
75
|
+
confirmed = name.endswith("!")
|
|
76
|
+
name = name.rstrip("!")
|
|
77
|
+
command = self.get(name)
|
|
78
|
+
if command is None:
|
|
79
|
+
return None
|
|
80
|
+
return ParsedCommand(command=command, args=args.strip(), confirmed=confirmed, raw_text=stripped)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def discover_commands(package_name: str = "agent.tui.commands") -> CommandRegistry:
|
|
84
|
+
"""Discover COMMAND instances from command modules in this package."""
|
|
85
|
+
package = importlib.import_module(package_name)
|
|
86
|
+
registry = CommandRegistry()
|
|
87
|
+
for module_info in pkgutil.iter_modules(package.__path__):
|
|
88
|
+
if module_info.name.startswith("_") or module_info.name in {"base", "registry"}:
|
|
89
|
+
continue
|
|
90
|
+
module = importlib.import_module(f"{package_name}.{module_info.name}")
|
|
91
|
+
command = getattr(module, "COMMAND", None)
|
|
92
|
+
if command is not None:
|
|
93
|
+
registry.register(command)
|
|
94
|
+
return registry
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Single-page help content for the TUI :help command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
|
|
7
|
+
from agent.tui.commands.base import TuiCommand
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
TUI_SHORTCUTS = [
|
|
11
|
+
("Ctrl+Enter / Ctrl+J", "Submit current input"),
|
|
12
|
+
("Ctrl+C", "Cancel current task / interrupt"),
|
|
13
|
+
("Ctrl+Shift+C", "Copy timeline"),
|
|
14
|
+
("Ctrl+L", "Open plain-text timeline"),
|
|
15
|
+
("Ctrl+Q", "Quit"),
|
|
16
|
+
("Ctrl+T", "Open task plan panel"),
|
|
17
|
+
("Ctrl+D", "Open changed files panel"),
|
|
18
|
+
("Ctrl+M", "Open message token manager"),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
COMPLETION_SHORTCUTS = [
|
|
22
|
+
("/", "Open skill completion"),
|
|
23
|
+
("@", "Open subagent role completion"),
|
|
24
|
+
(":", "Open TUI command completion"),
|
|
25
|
+
("Up/Down or Ctrl+P/Ctrl+N", "Select completion item"),
|
|
26
|
+
("Tab/Enter", "Accept selected completion"),
|
|
27
|
+
("Esc", "Close completion or focus input"),
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
TIMELINE_SHORTCUTS = [
|
|
31
|
+
("Up/Down", "Scroll timeline by line"),
|
|
32
|
+
("PageUp/PageDown", "Scroll timeline by page"),
|
|
33
|
+
("Home/End", "Jump to timeline top/bottom"),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
APPROVAL_SHORTCUTS = [
|
|
37
|
+
("Y / Enter", "Approve current request"),
|
|
38
|
+
("N / Esc", "Deny current request"),
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
STARTUP_ARGUMENTS = [
|
|
42
|
+
("WORKDIR", "Workspace directory. Defaults to the current directory."),
|
|
43
|
+
("-d, --debug", "Enable debug logging to console."),
|
|
44
|
+
("--log-file", "Write logs to agent_debug.log."),
|
|
45
|
+
("-a, --auto", "Auto-approve risky actions where supported."),
|
|
46
|
+
("--silent", "Compatibility alias for --auto."),
|
|
47
|
+
("-r, --resume ID", "Resume messages from a persisted session id in the same workspace."),
|
|
48
|
+
("-s, --sessions", "List persisted sessions for WORKDIR and exit."),
|
|
49
|
+
("--list-sessions", "Compatibility alias for --sessions."),
|
|
50
|
+
("-t, --temp", "Temporary session; do not save messages."),
|
|
51
|
+
("--no-persist", "Compatibility alias for --temp."),
|
|
52
|
+
("-x, --delete ID", "Delete a persisted session id for WORKDIR and exit."),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def render_help_page(commands: Iterable[TuiCommand]) -> str:
|
|
57
|
+
"""Render the complete single-page TUI help text."""
|
|
58
|
+
sections = [
|
|
59
|
+
"yycode Help",
|
|
60
|
+
"",
|
|
61
|
+
"TUI Commands",
|
|
62
|
+
*_render_commands(commands),
|
|
63
|
+
"",
|
|
64
|
+
"Keyboard Shortcuts",
|
|
65
|
+
*_render_rows(TUI_SHORTCUTS),
|
|
66
|
+
"",
|
|
67
|
+
"Input Completion",
|
|
68
|
+
*_render_rows(COMPLETION_SHORTCUTS),
|
|
69
|
+
"",
|
|
70
|
+
"Timeline Navigation",
|
|
71
|
+
*_render_rows(TIMELINE_SHORTCUTS),
|
|
72
|
+
"",
|
|
73
|
+
"Approval",
|
|
74
|
+
*_render_rows(APPROVAL_SHORTCUTS),
|
|
75
|
+
"",
|
|
76
|
+
"Subagents",
|
|
77
|
+
" Use @role followed by a focused task. Type @ to complete a role.",
|
|
78
|
+
" Example: @tester verify the command system behavior",
|
|
79
|
+
" Example: @architect /plan design a safer cleanup flow",
|
|
80
|
+
"",
|
|
81
|
+
"Skills",
|
|
82
|
+
" Use /skill-name followed by the task instruction. Type / to complete a skill.",
|
|
83
|
+
" Example: /plan design a command registry",
|
|
84
|
+
" Example: /drawio-skill create an architecture diagram",
|
|
85
|
+
"",
|
|
86
|
+
"Startup Arguments",
|
|
87
|
+
" yycode [WORKDIR] [options]",
|
|
88
|
+
*_render_rows(STARTUP_ARGUMENTS),
|
|
89
|
+
"",
|
|
90
|
+
"More",
|
|
91
|
+
" Run `yycode --help` for raw CLI help.",
|
|
92
|
+
]
|
|
93
|
+
return "\n".join(sections)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _render_commands(commands: Iterable[TuiCommand]) -> list[str]:
|
|
97
|
+
lines: list[str] = []
|
|
98
|
+
for command in sorted(commands, key=lambda item: item.name):
|
|
99
|
+
usage = command.usage or f":{command.name}"
|
|
100
|
+
description = command.description.rstrip(".")
|
|
101
|
+
lines.append(f" {usage:<18} {description}.")
|
|
102
|
+
if command.name == "clear":
|
|
103
|
+
lines.append(" :clear! Confirm clearing current session history.")
|
|
104
|
+
return lines or [" No TUI commands registered."]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _render_rows(rows: Iterable[tuple[str, str]]) -> list[str]:
|
|
108
|
+
return [f" {key:<24} {description}" for key, description in rows]
|