cgcardona / muse public
intent.py python
136 lines 4.3 KB
a748f814 feat(code): Phase 7 — semantic versioning metadata on StructuredDelta +… Gabriel Cardona <cgcardona@gmail.com> 1d 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 from __future__ import annotations
45
46 import json
47 import logging
48
49 import typer
50
51 from muse.core.coordination import create_intent
52 from muse.core.errors import ExitCode
53 from muse.core.repo import require_repo
54
55 logger = logging.getLogger(__name__)
56
57 app = typer.Typer()
58
59 _VALID_OPS = frozenset({
60 "rename", "move", "modify", "extract", "delete", "inline", "split", "merge",
61 })
62
63
64 @app.callback(invoke_without_command=True)
65 def intent(
66 ctx: typer.Context,
67 addresses: list[str] = typer.Argument(
68 ..., metavar="ADDRESS...",
69 help='Symbol addresses this intent applies to.',
70 ),
71 operation: str = typer.Option(
72 ..., "--op", metavar="OPERATION",
73 help="Operation to declare: rename, move, modify, extract, delete, inline, split, merge.",
74 ),
75 detail: str = typer.Option(
76 "", "--detail", metavar="TEXT",
77 help="Human-readable description of the intended change.",
78 ),
79 reservation_id: str = typer.Option(
80 "", "--reservation-id", metavar="UUID",
81 help="Link to an existing reservation.",
82 ),
83 run_id: str = typer.Option(
84 "unknown", "--run-id", metavar="ID",
85 help="Agent identifier (used when --reservation-id is not provided).",
86 ),
87 as_json: bool = typer.Option(False, "--json", help="Emit intent details as JSON."),
88 ) -> None:
89 """Declare a specific operation before executing it.
90
91 ``muse intent`` extends a reservation with operational detail. The
92 operation type enables ``muse forecast`` to compute more precise conflict
93 predictions — a rename conflicts differently from a delete.
94
95 Intents are write-once records stored under ``.muse/coordination/intents/``.
96 They are purely advisory and never affect VCS correctness.
97 """
98 root = require_repo()
99
100 if operation not in _VALID_OPS:
101 typer.echo(
102 f"❌ Unknown operation '{operation}'. "
103 f"Valid: {', '.join(sorted(_VALID_OPS))}",
104 err=True,
105 )
106 raise typer.Exit(code=ExitCode.USER_ERROR)
107
108 head_ref = (root / ".muse" / "HEAD").read_text().strip()
109 branch = head_ref.removeprefix("refs/heads/").strip()
110
111 intent_record = create_intent(
112 root=root,
113 reservation_id=reservation_id,
114 run_id=run_id,
115 branch=branch,
116 addresses=addresses,
117 operation=operation,
118 detail=detail,
119 )
120
121 if as_json:
122 typer.echo(json.dumps(intent_record.to_dict(), indent=2))
123 return
124
125 typer.echo(
126 f"\n✅ Intent recorded\n"
127 f" Intent ID: {intent_record.intent_id}\n"
128 f" Operation: {operation}\n"
129 f" Addresses: {len(addresses)}\n"
130 f" Run ID: {intent_record.run_id}"
131 )
132 if detail:
133 typer.echo(f" Detail: {detail}")
134 if reservation_id:
135 typer.echo(f" Reservation: {reservation_id}")
136 typer.echo("\nRun 'muse forecast' to check for predicted conflicts.")