Files
AIPass/src/aipass/flow/apps/handlers/plan/command_parser.py
T
18acb30f83 chore(system): session 34 — maintenance + multi-branch work (#70)
* fix(ai_mail): resolve relative mailbox_path bug + archive dead handlers

Fix get_user_by_email() and get_all_users() returning relative mailbox_path
values from registry instead of absolute paths (causing doubled paths in reply).
Added path resolution matching get_current_user() pattern.

Archive 7 unused handler files to apps/.archive/ (gitignored):
- pending_work.py — planned feature, never wired
- lock_utils.py — superseded by inbox_lock.py
- data_ops.py — consumer (error_monitor) was archived
- config_generator.py, users/load.py, registry/validate.py — scaffolded, never used
- trigger/error_handler.py — superseded by trigger branch's event system

Added test_user_paths.py (13 tests) covering absolute path resolution.

Co-Authored-By: @ai_mail <ai_mail@aipass>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(spawn): seedgo compliance cleanup — 93% to 98%

Archive legacy files (dev.local.md, ai_mail.local/, .seed/). Update META
Modified dates on 22 files. Update README architecture tree and freshness.
Fix 15 Pyright unused warnings. Add introspection no-args gates to 6
modules. Wire json_handler + log_operation to 15 operational files. Add
seedgo bypass for 5 pure data files and log_structure. Remove deprecated
dev.local.md from builder template.

Co-Authored-By: @spawn <spawn@aipass>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(system): session 34 — maintenance day, multi-branch work

System changes:
- setup.sh: symlink install replaces .bashrc PATH export for drone/seedgo
- .claude/settings.json: deny list moved to per-branch settings.local.json
- .gitignore: updated
- CLAUDE.md culture doc: stale refs fixed (Cortex→spawn, id.json→passport.json)

Branch work (accumulated from 15-branch rounds):
- daemon: stale term cleanup, plugin removals, json handler modernization
- drone: module refactor (discovery, registry, router, resolver, config)
- commons: 50+ file cleanup (imports, handlers, modules)
- flow: template loader, plan type system, command parser

Per-branch settings.local.json: reworked deny lists — allow PR workflow,
deny destructive git ops (reset, rebase, merge, clean, force push, rm -rf).
Fixed pattern syntax from :* to * glob.

Co-Authored-By: @devpulse <devpulse@aipass>

---------

Co-authored-by: @ai_mail <ai_mail@aipass>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: @spawn <spawn@aipass>
Co-authored-by: @devpulse <devpulse@aipass>
2026-03-17 16:45:05 -07:00

187 lines
5.7 KiB
Python

# =================== AIPass ====================
# Name: command_parser.py
# Description: Command Argument Parser
# Version: 0.2.0
# Created: 2025-11-15
# Modified: 2025-11-15
# =============================================
"""
Command Argument Parser
Parses command-line arguments for plan operations.
"""
from typing import List, Tuple
def parse_create_plan_args(args: List[str]) -> Tuple[str | None, str, str]:
"""
Parse arguments for plan creation
The third argument is treated as a plan type identifier and mapped
to a plan_type_key understood by the plan_types plugin system:
- No 3rd arg or "default" -> "flow_plans" (FPLAN with default template)
- "master" -> "master" (FPLAN with master template)
- "dplan" -> "dev_plans" (DPLAN with default template)
- Any other value -> passed through for plan_type_loader lookup
Args:
args: List of command arguments
Returns:
Tuple of (location, subject, plan_type_key)
- location: First arg or None
- subject: Second arg or empty string
- plan_type_key: Resolved plan type key for the plugin system
Examples:
>>> parse_create_plan_args(["@flow", "My task", "master"])
("@flow", "My task", "master")
>>> parse_create_plan_args(["@flow", "My task", "dplan"])
("@flow", "My task", "dev_plans")
>>> parse_create_plan_args([])
(None, "", "flow_plans")
>>> parse_create_plan_args(["@flow"])
("@flow", "", "flow_plans")
"""
location = args[0] if len(args) > 0 else None
subject = args[1] if len(args) > 1 else ""
raw_type = args[2] if len(args) > 2 else "default"
# Map raw type argument to plan_type_key
_TYPE_MAP = {
"default": "flow_plans",
"dplan": "dev_plans",
}
plan_type_key = _TYPE_MAP.get(raw_type.lower(), raw_type)
return location, subject, plan_type_key
def parse_delete_command_args(args: List[str]) -> Tuple[str | None, bool, str | None]:
"""
Parse arguments for delete command (DEPRECATED - use parse_close_command_args)
Args:
args: Command arguments
Returns:
Tuple of (plan_num, confirm, error_message)
- plan_num: Plan number from first arg, or None if missing
- confirm: False if --yes or -y flag present, True otherwise
- error_message: None if valid, error string if plan_num missing
Examples:
>>> parse_delete_command_args(["42"])
("42", True, None)
>>> parse_delete_command_args(["42", "--yes"])
("42", False, None)
>>> parse_delete_command_args([])
(None, True, "Plan number required")
"""
if len(args) < 1:
return None, True, "Plan number required"
plan_num = args[0]
confirm = '--yes' not in args and '-y' not in args
return plan_num, confirm, None
def parse_close_command_args(args: List[str]) -> Tuple[str | None, bool, bool, str | None]:
"""
Parse arguments for close command
Auto-confirms by default (running 'close' IS the intent).
Use --confirm or --interactive to explicitly request a confirmation prompt.
--yes/-y kept for backwards compatibility (now redundant, already auto-confirms).
Args:
args: Command arguments
Returns:
Tuple of (plan_num, confirm, all_plans, error_message)
- plan_num: Plan number from first arg, or None if --all or missing
- confirm: True only if --confirm or --interactive flag present, False otherwise
- all_plans: True if --all flag present, False otherwise
- error_message: None if valid, error string if invalid args
Examples:
>>> parse_close_command_args(["42"])
("42", False, False, None)
>>> parse_close_command_args(["42", "--yes"])
("42", False, False, None)
>>> parse_close_command_args(["42", "--confirm"])
("42", True, False, None)
>>> parse_close_command_args(["42", "--interactive"])
("42", True, False, None)
>>> parse_close_command_args(["--all"])
(None, False, True, None)
>>> parse_close_command_args(["--all", "--confirm"])
(None, True, True, None)
>>> parse_close_command_args([])
(None, False, False, "Plan number or --all required")
"""
# Check for --all flag
all_plans = '--all' in args
# Default: auto-confirm (confirm=False means no prompt)
# --confirm or --interactive explicitly requests a prompt
# --yes/-y kept for backwards compat (redundant, already auto-confirms)
confirm = '--confirm' in args or '--interactive' in args
# If --all, plan_num is None
if all_plans:
return None, confirm, True, None
# Otherwise, need plan number
# Filter out flag args to find the plan number
non_flag_args = [a for a in args if not a.startswith('--') and a not in ('-y',)]
if not non_flag_args:
return None, False, False, "Plan number or --all required"
plan_num = non_flag_args[0]
return plan_num, confirm, False, None
def parse_restore_command_args(args: List[str]) -> Tuple[str | None, str | None]:
"""
Parse arguments for restore command
Args:
args: Command arguments
Returns:
Tuple of (plan_num, error_message)
- plan_num: Plan number from first arg, or None if missing
- error_message: None if valid, error string if plan_num missing
Examples:
>>> parse_restore_command_args(["42"])
("42", None)
>>> parse_restore_command_args(["0034"])
("0034", None)
>>> parse_restore_command_args([])
(None, "Plan number required")
"""
if len(args) < 1:
return None, "Plan number required"
plan_num = args[0]
return plan_num, None