gabriel / muse public
intent.py python
137 lines 4.3 KB
f7645c07 feat(store): self-describing HEAD format with typed read/write API (#163) Gabriel Cardona <cgcardona@gmail.com> 3d ago
1 """muse intent — declare a specific operation before executing it.
2
3 Records a structured intent extending an existing reservation. Whereas
4 ``muse reserve`` says "I will touch these symbols", ``muse intent`` says
5 "I will rename src/billing.py::compute_total to compute_invoice_total".
6
7 This additional detail enables:
8
9 * ``muse forecast`` to compute more precise conflict predictions
10 (a rename conflicts differently with a delete than a modify does).
11 * ``muse plan-merge`` to classify conflicts by a semantic taxonomy.
12 * Audit trail of what each agent intended before committing.
13
14 Usage::
15
16 muse intent "src/billing.py::compute_total" \\
17 --op rename --detail "rename to compute_invoice_total" \\
18 --reservation-id <UUID>
19
20 muse intent "src/auth.py::validate_token" \\
21 --op extract --detail "extract into src/auth/validators.py" \\
22 --run-id agent-42
23
24 muse intent "src/core.py::hash_content" --op delete --run-id refactor-bot
25
26 Flags:
27
28 ``--op OPERATION``
29 Required. The operation being declared:
30 rename | move | modify | extract | delete | inline | split | merge.
31
32 ``--detail TEXT``
33 Human-readable description of the intended change.
34
35 ``--reservation-id UUID``
36 Link to an existing reservation (optional; creates standalone intent if omitted).
37
38 ``--run-id ID``
39 Agent identifier (used when --reservation-id is not provided).
40
41 ``--json``
42 Emit intent details as JSON.
43 """
44
45 from __future__ import annotations
46
47 import json
48 import logging
49
50 import typer
51
52 from muse.core.coordination import create_intent
53 from muse.core.errors import ExitCode
54 from muse.core.repo import require_repo
55 from muse.core.store import read_current_branch
56
57 logger = logging.getLogger(__name__)
58
59 app = typer.Typer()
60
61 _VALID_OPS = frozenset({
62 "rename", "move", "modify", "extract", "delete", "inline", "split", "merge",
63 })
64
65
66 @app.callback(invoke_without_command=True)
67 def intent(
68 ctx: typer.Context,
69 addresses: list[str] = typer.Argument(
70 ..., metavar="ADDRESS...",
71 help='Symbol addresses this intent applies to.',
72 ),
73 operation: str = typer.Option(
74 ..., "--op", metavar="OPERATION",
75 help="Operation to declare: rename, move, modify, extract, delete, inline, split, merge.",
76 ),
77 detail: str = typer.Option(
78 "", "--detail", metavar="TEXT",
79 help="Human-readable description of the intended change.",
80 ),
81 reservation_id: str = typer.Option(
82 "", "--reservation-id", metavar="UUID",
83 help="Link to an existing reservation.",
84 ),
85 run_id: str = typer.Option(
86 "unknown", "--run-id", metavar="ID",
87 help="Agent identifier (used when --reservation-id is not provided).",
88 ),
89 as_json: bool = typer.Option(False, "--json", help="Emit intent details as JSON."),
90 ) -> None:
91 """Declare a specific operation before executing it.
92
93 ``muse intent`` extends a reservation with operational detail. The
94 operation type enables ``muse forecast`` to compute more precise conflict
95 predictions — a rename conflicts differently from a delete.
96
97 Intents are write-once records stored under ``.muse/coordination/intents/``.
98 They are purely advisory and never affect VCS correctness.
99 """
100 root = require_repo()
101
102 if operation not in _VALID_OPS:
103 typer.echo(
104 f"❌ Unknown operation '{operation}'. "
105 f"Valid: {', '.join(sorted(_VALID_OPS))}",
106 err=True,
107 )
108 raise typer.Exit(code=ExitCode.USER_ERROR)
109
110 branch = read_current_branch(root)
111
112 intent_record = create_intent(
113 root=root,
114 reservation_id=reservation_id,
115 run_id=run_id,
116 branch=branch,
117 addresses=addresses,
118 operation=operation,
119 detail=detail,
120 )
121
122 if as_json:
123 typer.echo(json.dumps(intent_record.to_dict(), indent=2))
124 return
125
126 typer.echo(
127 f"\n✅ Intent recorded\n"
128 f" Intent ID: {intent_record.intent_id}\n"
129 f" Operation: {operation}\n"
130 f" Addresses: {len(addresses)}\n"
131 f" Run ID: {intent_record.run_id}"
132 )
133 if detail:
134 typer.echo(f" Detail: {detail}")
135 if reservation_id:
136 typer.echo(f" Reservation: {reservation_id}")
137 typer.echo("\nRun 'muse forecast' to check for predicted conflicts.")