Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ This document is the LeapFlow engineering collaboration contract. It is not only
- Consider affected user journeys before changing shared flows; do not introduce regressions, broken links, or worse experiences in adjacent paths
- Keep common paths transparent: long-running work must stream progress, surface recoverable errors clearly, and avoid silent stalls.
- For context assembly, prefer manifest-driven progressive disclosure over shortcuts or intent-handler sprawl: expose compact capability indexes, selected schemas, and targeted memory only when the current plan needs them.
- Avoid rule-based natural-language fitting by default. Do not add keyword/action-verb/alias enumerations, intent-handler taxonomies, or brittle routing rules when LLM-native capability disclosure, manifests, schemas, protocols, or configuration-driven contracts can solve the problem. If a rule-based method is truly unavoidable for a stable protocol boundary, offline fallback, or safety hard gate, explain the necessity, scope, alternatives, and rollback path to a human and obtain explicit second confirmation before implementation.
- Preserve security and audit paths: dangerous actions, file writes, outbound messages, credentials, and path access must flow through the existing policy, approval, redaction, and audit mechanisms.
- Maintain backward-compatible migrations for persistent state, configuration, skills, trajectories, sessions, and profile data.
- Write unit tests before or alongside the implementation
Expand All @@ -62,6 +63,7 @@ This document is the LeapFlow engineering collaboration contract. It is not only
## Review Requirements

- **Deep review for large changes**: When a change substantially affects architecture, runtime behavior, user flows, persistence, safety, or multiple modules, perform an additional deep review before considering the work complete.
- **Human confirmation for TUI changes**: Any TUI layout or interaction-logic change requires a second human confirmation before it is considered ready.
- **Design goal check**: Verify that the implementation actually achieves the intended design goal and is not just a local patch.
- **Optimality check**: Evaluate whether the solution is the simplest robust design, avoids unnecessary abstractions, and fits the existing architecture.
- **Regression impact check**: Inspect affected modules and user journeys for logic bugs, degraded UX, broken compatibility, slower feedback, weaker diagnostics, or worse failure recovery.
Expand Down Expand Up @@ -90,7 +92,7 @@ This document is the LeapFlow engineering collaboration contract. It is not only
- Hardcoded paths, URLs, thresholds without config escape hatch
- Chinese comments in source code (English only)
- Speculative infrastructure: no hooks or extension points without a concrete consumer
- Shortcut-style natural-language fitting and large intent-handler taxonomies; use stable runtime gates plus capability manifests instead.
- Shortcut-style natural-language fitting and large intent-handler taxonomies; use stable runtime gates plus capability manifests instead. Rule-based keyword/action-verb/alias matching is prohibited by default and requires explicit human second confirmation before implementation when unavoidable.
- Bare `except:` clauses — always specify the exception type
- `# TODO: implement` stubs — implement or don't commit

Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ version = {attr = "leapflow.version.__version__"}
where = ["src"]
include = ["leapflow*"]

[tool.setuptools.package-data]
"leapflow.gateway.action_packs" = ["*.yaml"]

[tool.pytest.ini_options]
asyncio_mode = "auto"
pythonpath = ["src", "tests"]
Expand Down
25 changes: 25 additions & 0 deletions src/leapflow/cli/commands/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
_last_hint: Optional["PredictionCandidate"] = None


def _is_app_command(canonical: str) -> bool:
"""Return true only for `/app` or `/app ...`, not `/apple`."""
return canonical == "app" or canonical.startswith("app ")


async def _prompt_stop_daemon_on_exit(
client: "DaemonClient",
settings: Any,
Expand Down Expand Up @@ -253,6 +258,7 @@ async def cmd_interactive(ctx: "Context", *, resume_id: Optional[str] = None) ->
handle_model,
handle_clear,
handle_gateway,
handle_app,
)
from leapflow.utils.terminal_io import TerminalIOProvider
from leapflow.engine.session import SessionMode
Expand Down Expand Up @@ -570,6 +576,12 @@ async def handle_input(text: str) -> None:
handle_gateway(ctx, console, cmd_args)
return

if _is_app_command(canonical):
app_args = cmd_text[len("app"):].strip()
await handle_app(ctx, console, app_args)
_update_status()
return

if canonical == "usage":
handle_usage(ctx, console, cmd_args)
return
Expand Down Expand Up @@ -808,6 +820,7 @@ async def cmd_interactive_daemon(
from leapflow.cli.commands.registry import completion_entries
from leapflow.cli.commands.router import CommandRouter, render_command_result
from leapflow.cli.commands.slash_handlers import (
render_app_payload,
render_model_payload,
render_tools_payload,
render_usage_payload,
Expand Down Expand Up @@ -1155,6 +1168,18 @@ async def handle_input(text: str) -> None:
return
render_model_payload(console, payload)
return
if _is_app_command(canonical):
app_args = invocation.text[len("app"):].strip()
try:
payload = await bridge.call(
lambda current_client: current_client.app_command(app_args),
description="app command",
)
except Exception as exc:
console.warning(f"App Connector unavailable: {exc}")
return
render_app_payload(console, payload)
return
if canonical == "run":
if not cmd_args:
console.warning("Usage: /run <trigger>")
Expand Down
10 changes: 10 additions & 0 deletions src/leapflow/cli/commands/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ def supports_runtime(self, runtime: CommandRuntime) -> bool:
# Gateway
CommandDef("gateway", "Show connected platforms and gateway status", "Gateway", effect=CommandEffect.EXTERNAL),

# App Connector
CommandDef("app", "List supported external apps or open an app setup guide", "App Connector", args_hint="[platform]", supports_daemon=True),
CommandDef("app list", "List supported external apps", "App Connector", supports_daemon=True),
CommandDef("app status", "Show App Connector status", "App Connector", args_hint="[platform]", supports_daemon=True),
CommandDef("app connect", "Connect a supported external app", "App Connector", args_hint="<platform> [--option value]", effect=CommandEffect.EXTERNAL, execution=CommandExecution.SHORT_OPERATION),
CommandDef("app disconnect", "Disconnect an external app but keep configuration", "App Connector", args_hint="<platform>", effect=CommandEffect.EXTERNAL, execution=CommandExecution.SHORT_OPERATION),
CommandDef("app remove", "Remove an app configuration", "App Connector", args_hint="<platform>", effect=CommandEffect.DESTRUCTIVE, execution=CommandExecution.SHORT_OPERATION),
CommandDef("app events", "Inspect or control an app event source", "App Connector", args_hint="[status|start|stop] <platform>", effect=CommandEffect.EXTERNAL, execution=CommandExecution.SHORT_OPERATION),
CommandDef("app actions", "List App Connector action domains", "App Connector", args_hint="<platform>", supports_daemon=True),

# Scheduler
CommandDef("arm", "Schedule a skill for timed execution", "Scheduler", args_hint="<skill> <cron>"),
CommandDef("tasks", "List scheduled tasks", "Scheduler"),
Expand Down
Loading