intent-cli-python 1.0.0__py3-none-any.whl → 1.2.0__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.
- intent_cli/__init__.py +7 -0
- intent_cli/cli.py +71 -390
- intent_cli/commands/__init__.py +1 -0
- intent_cli/commands/common.py +41 -0
- intent_cli/commands/core.py +376 -0
- intent_cli/commands/hub.py +107 -0
- intent_cli/hub/__init__.py +1 -0
- intent_cli/hub/client.py +59 -0
- intent_cli/hub/payload.py +65 -0
- intent_cli/hub/runtime.py +40 -0
- intent_cli/store.py +234 -0
- {intent_cli_python-1.0.0.dist-info → intent_cli_python-1.2.0.dist-info}/METADATA +26 -5
- intent_cli_python-1.2.0.dist-info/RECORD +19 -0
- intent_cli_python-1.0.0.dist-info/RECORD +0 -11
- {intent_cli_python-1.0.0.dist-info → intent_cli_python-1.2.0.dist-info}/WHEEL +0 -0
- {intent_cli_python-1.0.0.dist-info → intent_cli_python-1.2.0.dist-info}/entry_points.txt +0 -0
- {intent_cli_python-1.0.0.dist-info → intent_cli_python-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {intent_cli_python-1.0.0.dist-info → intent_cli_python-1.2.0.dist-info}/top_level.txt +0 -0
intent_cli/__init__.py
CHANGED
intent_cli/cli.py
CHANGED
|
@@ -1,389 +1,60 @@
|
|
|
1
|
-
"""Intent CLI —
|
|
1
|
+
"""Intent CLI — parser and command dispatch."""
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
-
import json
|
|
5
4
|
import sys
|
|
6
|
-
from datetime import datetime, timezone
|
|
7
5
|
|
|
8
|
-
from intent_cli.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
from intent_cli.commands.core import (
|
|
7
|
+
cmd_decision_attach,
|
|
8
|
+
cmd_decision_create,
|
|
9
|
+
cmd_decision_deprecate,
|
|
10
|
+
cmd_decision_list,
|
|
11
|
+
cmd_decision_show,
|
|
12
|
+
cmd_doctor,
|
|
13
|
+
cmd_init,
|
|
14
|
+
cmd_inspect,
|
|
15
|
+
cmd_intent_activate,
|
|
16
|
+
cmd_intent_create,
|
|
17
|
+
cmd_intent_done,
|
|
18
|
+
cmd_intent_list,
|
|
19
|
+
cmd_intent_show,
|
|
20
|
+
cmd_intent_suspend,
|
|
21
|
+
cmd_snap_create,
|
|
22
|
+
cmd_snap_feedback,
|
|
23
|
+
cmd_snap_list,
|
|
24
|
+
cmd_snap_revert,
|
|
25
|
+
cmd_snap_show,
|
|
26
|
+
cmd_version,
|
|
12
27
|
)
|
|
13
|
-
|
|
14
|
-
VERSION = "1.0.0"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _now():
|
|
18
|
-
return datetime.now(timezone.utc).isoformat()
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def _require_init():
|
|
22
|
-
"""Return .intent/ base path, or exit with error."""
|
|
23
|
-
base = ensure_init()
|
|
24
|
-
if base is not None:
|
|
25
|
-
return base
|
|
26
|
-
if git_root() is None:
|
|
27
|
-
error("GIT_STATE_INVALID", "Not inside a Git repository.",
|
|
28
|
-
suggested_fix="cd into a git repo and run: itt init")
|
|
29
|
-
error("NOT_INITIALIZED", ".intent/ directory not found.",
|
|
30
|
-
suggested_fix="itt init")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# ---------------------------------------------------------------------------
|
|
34
|
-
# Global commands
|
|
35
|
-
# ---------------------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
def cmd_version(_args):
|
|
38
|
-
success("version", {"version": VERSION})
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def cmd_init(_args):
|
|
42
|
-
path, err = init_workspace()
|
|
43
|
-
if err == "GIT_STATE_INVALID":
|
|
44
|
-
error("GIT_STATE_INVALID", "Not inside a Git repository.",
|
|
45
|
-
suggested_fix="cd into a git repo and run: itt init")
|
|
46
|
-
if err == "ALREADY_EXISTS":
|
|
47
|
-
error("ALREADY_EXISTS", ".intent/ already exists.",
|
|
48
|
-
suggested_fix="Remove .intent/ first if you want to reinitialize.")
|
|
49
|
-
success("init", {"path": str(path)})
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def cmd_inspect(_args):
|
|
53
|
-
base = _require_init()
|
|
54
|
-
config = read_config(base)
|
|
55
|
-
|
|
56
|
-
active_intents = []
|
|
57
|
-
suspend_intents = []
|
|
58
|
-
for obj in list_objects(base, "intent"):
|
|
59
|
-
entry = {
|
|
60
|
-
"id": obj["id"],
|
|
61
|
-
"title": obj["title"],
|
|
62
|
-
"status": obj["status"],
|
|
63
|
-
"decision_ids": obj.get("decision_ids", []),
|
|
64
|
-
"latest_snap_id": obj["snap_ids"][-1] if obj.get("snap_ids") else None,
|
|
65
|
-
}
|
|
66
|
-
if obj["status"] == "active":
|
|
67
|
-
active_intents.append(entry)
|
|
68
|
-
elif obj["status"] == "suspend":
|
|
69
|
-
suspend_intents.append(entry)
|
|
70
|
-
|
|
71
|
-
active_decisions = []
|
|
72
|
-
for obj in list_objects(base, "decision", status="active"):
|
|
73
|
-
active_decisions.append({
|
|
74
|
-
"id": obj["id"],
|
|
75
|
-
"title": obj["title"],
|
|
76
|
-
"status": obj["status"],
|
|
77
|
-
"intent_ids": obj.get("intent_ids", []),
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
all_snaps = list_objects(base, "snap")
|
|
81
|
-
all_snaps.sort(key=lambda s: s.get("created_at", ""), reverse=True)
|
|
82
|
-
recent_snaps = []
|
|
83
|
-
for s in all_snaps[:10]:
|
|
84
|
-
recent_snaps.append({
|
|
85
|
-
"id": s["id"],
|
|
86
|
-
"title": s["title"],
|
|
87
|
-
"intent_id": s["intent_id"],
|
|
88
|
-
"status": s["status"],
|
|
89
|
-
"summary": s.get("summary", ""),
|
|
90
|
-
"feedback": s.get("feedback", ""),
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
warnings = []
|
|
94
|
-
intent_ids_on_disk = {o["id"] for o in list_objects(base, "intent")}
|
|
95
|
-
for s in all_snaps:
|
|
96
|
-
if s.get("intent_id") and s["intent_id"] not in intent_ids_on_disk:
|
|
97
|
-
warnings.append(f"Orphan snap {s['id']}: intent {s['intent_id']} not found")
|
|
98
|
-
|
|
99
|
-
print(json.dumps({
|
|
100
|
-
"ok": True,
|
|
101
|
-
"schema_version": config.get("schema_version", "1.0"),
|
|
102
|
-
"active_intents": active_intents,
|
|
103
|
-
"suspend_intents": suspend_intents,
|
|
104
|
-
"active_decisions": active_decisions,
|
|
105
|
-
"recent_snaps": recent_snaps,
|
|
106
|
-
"warnings": warnings,
|
|
107
|
-
}, indent=2, ensure_ascii=False))
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# ---------------------------------------------------------------------------
|
|
111
|
-
# Intent commands
|
|
112
|
-
# ---------------------------------------------------------------------------
|
|
113
|
-
|
|
114
|
-
def cmd_intent_create(args):
|
|
115
|
-
base = _require_init()
|
|
116
|
-
obj_id = next_id(base, "intent")
|
|
117
|
-
|
|
118
|
-
active_decisions = list_objects(base, "decision", status="active")
|
|
119
|
-
decision_ids = [d["id"] for d in active_decisions]
|
|
120
|
-
|
|
121
|
-
warnings = []
|
|
122
|
-
if not decision_ids:
|
|
123
|
-
warnings.append("No active decisions to attach.")
|
|
124
|
-
|
|
125
|
-
intent = {
|
|
126
|
-
"id": obj_id,
|
|
127
|
-
"object": "intent",
|
|
128
|
-
"created_at": _now(),
|
|
129
|
-
"title": args.title,
|
|
130
|
-
"status": "active",
|
|
131
|
-
"source_query": args.query,
|
|
132
|
-
"rationale": args.rationale,
|
|
133
|
-
"decision_ids": decision_ids,
|
|
134
|
-
"snap_ids": [],
|
|
135
|
-
}
|
|
136
|
-
write_object(base, "intent", obj_id, intent)
|
|
137
|
-
|
|
138
|
-
for d in active_decisions:
|
|
139
|
-
if obj_id not in d.get("intent_ids", []):
|
|
140
|
-
d.setdefault("intent_ids", []).append(obj_id)
|
|
141
|
-
write_object(base, "decision", d["id"], d)
|
|
142
|
-
|
|
143
|
-
success("intent.create", intent, warnings)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def cmd_intent_list(args):
|
|
147
|
-
base = _require_init()
|
|
148
|
-
success("intent.list", list_objects(base, "intent", status=args.status))
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def cmd_intent_show(args):
|
|
152
|
-
base = _require_init()
|
|
153
|
-
obj = read_object(base, "intent", args.id)
|
|
154
|
-
if obj is None:
|
|
155
|
-
error("OBJECT_NOT_FOUND", f"Intent {args.id} not found.")
|
|
156
|
-
success("intent.show", obj)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def cmd_intent_activate(args):
|
|
160
|
-
base = _require_init()
|
|
161
|
-
obj = read_object(base, "intent", args.id)
|
|
162
|
-
if obj is None:
|
|
163
|
-
error("OBJECT_NOT_FOUND", f"Intent {args.id} not found.")
|
|
164
|
-
if obj["status"] != "suspend":
|
|
165
|
-
error("STATE_CONFLICT",
|
|
166
|
-
f"Cannot activate intent with status '{obj['status']}'. Only 'suspend' intents can be activated.",
|
|
167
|
-
suggested_fix=f"itt intent show {args.id}")
|
|
168
|
-
|
|
169
|
-
obj["status"] = "active"
|
|
170
|
-
|
|
171
|
-
active_decisions = list_objects(base, "decision", status="active")
|
|
172
|
-
for d in active_decisions:
|
|
173
|
-
if d["id"] not in obj["decision_ids"]:
|
|
174
|
-
obj["decision_ids"].append(d["id"])
|
|
175
|
-
if args.id not in d.get("intent_ids", []):
|
|
176
|
-
d.setdefault("intent_ids", []).append(args.id)
|
|
177
|
-
write_object(base, "decision", d["id"], d)
|
|
178
|
-
|
|
179
|
-
write_object(base, "intent", args.id, obj)
|
|
180
|
-
success("intent.activate", obj)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def cmd_intent_suspend(args):
|
|
184
|
-
base = _require_init()
|
|
185
|
-
obj = read_object(base, "intent", args.id)
|
|
186
|
-
if obj is None:
|
|
187
|
-
error("OBJECT_NOT_FOUND", f"Intent {args.id} not found.")
|
|
188
|
-
if obj["status"] != "active":
|
|
189
|
-
error("STATE_CONFLICT",
|
|
190
|
-
f"Cannot suspend intent with status '{obj['status']}'. Only 'active' intents can be suspended.",
|
|
191
|
-
suggested_fix=f"itt intent show {args.id}")
|
|
192
|
-
|
|
193
|
-
obj["status"] = "suspend"
|
|
194
|
-
write_object(base, "intent", args.id, obj)
|
|
195
|
-
success("intent.suspend", obj)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def cmd_intent_done(args):
|
|
199
|
-
base = _require_init()
|
|
200
|
-
obj = read_object(base, "intent", args.id)
|
|
201
|
-
if obj is None:
|
|
202
|
-
error("OBJECT_NOT_FOUND", f"Intent {args.id} not found.")
|
|
203
|
-
if obj["status"] != "active":
|
|
204
|
-
error("STATE_CONFLICT",
|
|
205
|
-
f"Cannot mark intent as done with status '{obj['status']}'. Only 'active' intents can be marked done.",
|
|
206
|
-
suggested_fix=f"itt intent show {args.id}")
|
|
207
|
-
|
|
208
|
-
obj["status"] = "done"
|
|
209
|
-
write_object(base, "intent", args.id, obj)
|
|
210
|
-
success("intent.done", obj)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
# ---------------------------------------------------------------------------
|
|
214
|
-
# Snap commands
|
|
215
|
-
# ---------------------------------------------------------------------------
|
|
216
|
-
|
|
217
|
-
def cmd_snap_create(args):
|
|
218
|
-
base = _require_init()
|
|
219
|
-
intent_id = args.intent
|
|
220
|
-
|
|
221
|
-
intent = read_object(base, "intent", intent_id)
|
|
222
|
-
if intent is None:
|
|
223
|
-
error("OBJECT_NOT_FOUND", f"Intent {intent_id} not found.")
|
|
224
|
-
if intent["status"] != "active":
|
|
225
|
-
error("STATE_CONFLICT",
|
|
226
|
-
f"Cannot add snap to intent with status '{intent['status']}'. Only 'active' intents accept new snaps.",
|
|
227
|
-
suggested_fix=f"itt intent activate {intent_id}")
|
|
228
|
-
|
|
229
|
-
obj_id = next_id(base, "snap")
|
|
230
|
-
snap = {
|
|
231
|
-
"id": obj_id,
|
|
232
|
-
"object": "snap",
|
|
233
|
-
"created_at": _now(),
|
|
234
|
-
"title": args.title,
|
|
235
|
-
"status": "active",
|
|
236
|
-
"intent_id": intent_id,
|
|
237
|
-
"query": args.query,
|
|
238
|
-
"rationale": args.rationale,
|
|
239
|
-
"summary": args.summary,
|
|
240
|
-
"feedback": args.feedback,
|
|
241
|
-
}
|
|
242
|
-
write_object(base, "snap", obj_id, snap)
|
|
243
|
-
|
|
244
|
-
intent.setdefault("snap_ids", []).append(obj_id)
|
|
245
|
-
write_object(base, "intent", intent_id, intent)
|
|
246
|
-
|
|
247
|
-
success("snap.create", snap)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
def cmd_snap_list(args):
|
|
251
|
-
base = _require_init()
|
|
252
|
-
objects = list_objects(base, "snap", status=args.status)
|
|
253
|
-
if args.intent:
|
|
254
|
-
objects = [s for s in objects if s.get("intent_id") == args.intent]
|
|
255
|
-
success("snap.list", objects)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
def cmd_snap_show(args):
|
|
259
|
-
base = _require_init()
|
|
260
|
-
obj = read_object(base, "snap", args.id)
|
|
261
|
-
if obj is None:
|
|
262
|
-
error("OBJECT_NOT_FOUND", f"Snap {args.id} not found.")
|
|
263
|
-
success("snap.show", obj)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
def cmd_snap_feedback(args):
|
|
267
|
-
base = _require_init()
|
|
268
|
-
obj = read_object(base, "snap", args.id)
|
|
269
|
-
if obj is None:
|
|
270
|
-
error("OBJECT_NOT_FOUND", f"Snap {args.id} not found.")
|
|
271
|
-
obj["feedback"] = args.feedback
|
|
272
|
-
write_object(base, "snap", args.id, obj)
|
|
273
|
-
success("snap.feedback", obj)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
def cmd_snap_revert(args):
|
|
277
|
-
base = _require_init()
|
|
278
|
-
obj = read_object(base, "snap", args.id)
|
|
279
|
-
if obj is None:
|
|
280
|
-
error("OBJECT_NOT_FOUND", f"Snap {args.id} not found.")
|
|
281
|
-
if obj["status"] != "active":
|
|
282
|
-
error("STATE_CONFLICT",
|
|
283
|
-
f"Cannot revert snap with status '{obj['status']}'. Only 'active' snaps can be reverted.",
|
|
284
|
-
suggested_fix=f"itt snap show {args.id}")
|
|
285
|
-
|
|
286
|
-
obj["status"] = "reverted"
|
|
287
|
-
write_object(base, "snap", args.id, obj)
|
|
288
|
-
success("snap.revert", obj)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
# ---------------------------------------------------------------------------
|
|
292
|
-
# Decision commands
|
|
293
|
-
# ---------------------------------------------------------------------------
|
|
294
|
-
|
|
295
|
-
def cmd_decision_create(args):
|
|
296
|
-
base = _require_init()
|
|
297
|
-
obj_id = next_id(base, "decision")
|
|
298
|
-
|
|
299
|
-
active_intents = list_objects(base, "intent", status="active")
|
|
300
|
-
intent_ids = [i["id"] for i in active_intents]
|
|
301
|
-
|
|
302
|
-
warnings = []
|
|
303
|
-
if not intent_ids:
|
|
304
|
-
warnings.append("No active intents to attach.")
|
|
305
|
-
|
|
306
|
-
decision = {
|
|
307
|
-
"id": obj_id,
|
|
308
|
-
"object": "decision",
|
|
309
|
-
"created_at": _now(),
|
|
310
|
-
"title": args.title,
|
|
311
|
-
"status": "active",
|
|
312
|
-
"rationale": args.rationale,
|
|
313
|
-
"intent_ids": intent_ids,
|
|
314
|
-
}
|
|
315
|
-
write_object(base, "decision", obj_id, decision)
|
|
316
|
-
|
|
317
|
-
for i in active_intents:
|
|
318
|
-
if obj_id not in i.get("decision_ids", []):
|
|
319
|
-
i.setdefault("decision_ids", []).append(obj_id)
|
|
320
|
-
write_object(base, "intent", i["id"], i)
|
|
321
|
-
|
|
322
|
-
success("decision.create", decision, warnings)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
def cmd_decision_list(args):
|
|
326
|
-
base = _require_init()
|
|
327
|
-
success("decision.list", list_objects(base, "decision", status=args.status))
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
def cmd_decision_show(args):
|
|
331
|
-
base = _require_init()
|
|
332
|
-
obj = read_object(base, "decision", args.id)
|
|
333
|
-
if obj is None:
|
|
334
|
-
error("OBJECT_NOT_FOUND", f"Decision {args.id} not found.")
|
|
335
|
-
success("decision.show", obj)
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
def cmd_decision_deprecate(args):
|
|
339
|
-
base = _require_init()
|
|
340
|
-
obj = read_object(base, "decision", args.id)
|
|
341
|
-
if obj is None:
|
|
342
|
-
error("OBJECT_NOT_FOUND", f"Decision {args.id} not found.")
|
|
343
|
-
if obj["status"] != "active":
|
|
344
|
-
error("STATE_CONFLICT",
|
|
345
|
-
f"Cannot deprecate decision with status '{obj['status']}'. Only 'active' decisions can be deprecated.",
|
|
346
|
-
suggested_fix=f"itt decision show {args.id}")
|
|
347
|
-
|
|
348
|
-
obj["status"] = "deprecated"
|
|
349
|
-
write_object(base, "decision", args.id, obj)
|
|
350
|
-
success("decision.deprecate", obj)
|
|
28
|
+
from intent_cli.commands.hub import cmd_hub_link, cmd_hub_login, cmd_hub_sync
|
|
351
29
|
|
|
352
30
|
|
|
353
|
-
def cmd_decision_attach(args):
|
|
354
|
-
base = _require_init()
|
|
355
|
-
decision = read_object(base, "decision", args.id)
|
|
356
|
-
if decision is None:
|
|
357
|
-
error("OBJECT_NOT_FOUND", f"Decision {args.id} not found.")
|
|
358
|
-
|
|
359
|
-
intent_id = args.intent
|
|
360
|
-
intent = read_object(base, "intent", intent_id)
|
|
361
|
-
if intent is None:
|
|
362
|
-
error("OBJECT_NOT_FOUND", f"Intent {intent_id} not found.")
|
|
363
|
-
|
|
364
|
-
if intent_id not in decision.get("intent_ids", []):
|
|
365
|
-
decision.setdefault("intent_ids", []).append(intent_id)
|
|
366
|
-
write_object(base, "decision", args.id, decision)
|
|
367
|
-
|
|
368
|
-
if args.id not in intent.get("decision_ids", []):
|
|
369
|
-
intent.setdefault("decision_ids", []).append(args.id)
|
|
370
|
-
write_object(base, "intent", intent_id, intent)
|
|
371
|
-
|
|
372
|
-
success("decision.attach", decision)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
# ---------------------------------------------------------------------------
|
|
376
|
-
# Argument parser
|
|
377
|
-
# ---------------------------------------------------------------------------
|
|
378
|
-
|
|
379
31
|
def main():
|
|
380
32
|
parser = argparse.ArgumentParser(prog="itt", description="Intent CLI")
|
|
381
33
|
sub = parser.add_subparsers(dest="command")
|
|
382
34
|
|
|
383
|
-
# version / init / inspect
|
|
35
|
+
# version / init / inspect / doctor
|
|
384
36
|
sub.add_parser("version")
|
|
385
37
|
sub.add_parser("init")
|
|
386
38
|
sub.add_parser("inspect")
|
|
39
|
+
sub.add_parser("doctor")
|
|
40
|
+
|
|
41
|
+
# --- hub ---
|
|
42
|
+
p_hub = sub.add_parser("hub")
|
|
43
|
+
s_hub = p_hub.add_subparsers(dest="sub")
|
|
44
|
+
|
|
45
|
+
p = s_hub.add_parser("login")
|
|
46
|
+
p.add_argument("--api-base-url", default=None)
|
|
47
|
+
p.add_argument("--token", default=None)
|
|
48
|
+
|
|
49
|
+
p = s_hub.add_parser("link")
|
|
50
|
+
p.add_argument("--project-name", default=None)
|
|
51
|
+
p.add_argument("--api-base-url", default=None)
|
|
52
|
+
p.add_argument("--token", default=None)
|
|
53
|
+
|
|
54
|
+
p = s_hub.add_parser("sync")
|
|
55
|
+
p.add_argument("--api-base-url", default=None)
|
|
56
|
+
p.add_argument("--token", default=None)
|
|
57
|
+
p.add_argument("--dry-run", action="store_true")
|
|
387
58
|
|
|
388
59
|
# --- intent ---
|
|
389
60
|
p_intent = sub.add_parser("intent")
|
|
@@ -396,6 +67,7 @@ def main():
|
|
|
396
67
|
|
|
397
68
|
p = s_intent.add_parser("list")
|
|
398
69
|
p.add_argument("--status", default=None)
|
|
70
|
+
p.add_argument("--decision", default=None)
|
|
399
71
|
|
|
400
72
|
p = s_intent.add_parser("show")
|
|
401
73
|
p.add_argument("id")
|
|
@@ -445,6 +117,7 @@ def main():
|
|
|
445
117
|
|
|
446
118
|
p = s_decision.add_parser("list")
|
|
447
119
|
p.add_argument("--status", default=None)
|
|
120
|
+
p.add_argument("--intent", default=None)
|
|
448
121
|
|
|
449
122
|
p = s_decision.add_parser("show")
|
|
450
123
|
p.add_argument("id")
|
|
@@ -456,7 +129,6 @@ def main():
|
|
|
456
129
|
p.add_argument("id")
|
|
457
130
|
p.add_argument("--intent", required=True)
|
|
458
131
|
|
|
459
|
-
# --- dispatch ---
|
|
460
132
|
args = parser.parse_args()
|
|
461
133
|
|
|
462
134
|
if args.command is None:
|
|
@@ -467,31 +139,40 @@ def main():
|
|
|
467
139
|
"version": cmd_version,
|
|
468
140
|
"init": cmd_init,
|
|
469
141
|
"inspect": cmd_inspect,
|
|
142
|
+
"doctor": cmd_doctor,
|
|
470
143
|
}
|
|
471
144
|
if args.command in dispatch_global:
|
|
472
145
|
dispatch_global[args.command](args)
|
|
473
146
|
return
|
|
474
147
|
|
|
475
148
|
if not getattr(args, "sub", None):
|
|
476
|
-
{
|
|
149
|
+
{
|
|
150
|
+
"hub": p_hub,
|
|
151
|
+
"intent": p_intent,
|
|
152
|
+
"snap": p_snap,
|
|
153
|
+
"decision": p_decision,
|
|
154
|
+
}[args.command].print_help()
|
|
477
155
|
sys.exit(1)
|
|
478
156
|
|
|
479
157
|
dispatch = {
|
|
480
|
-
("
|
|
481
|
-
("
|
|
482
|
-
("
|
|
483
|
-
("intent", "
|
|
484
|
-
("intent", "
|
|
485
|
-
("intent", "
|
|
486
|
-
("
|
|
487
|
-
("
|
|
488
|
-
("
|
|
489
|
-
("snap", "
|
|
490
|
-
("snap", "
|
|
491
|
-
("
|
|
492
|
-
("
|
|
493
|
-
("
|
|
494
|
-
("decision", "
|
|
495
|
-
("decision", "
|
|
158
|
+
("hub", "login"): cmd_hub_login,
|
|
159
|
+
("hub", "link"): cmd_hub_link,
|
|
160
|
+
("hub", "sync"): cmd_hub_sync,
|
|
161
|
+
("intent", "create"): cmd_intent_create,
|
|
162
|
+
("intent", "list"): cmd_intent_list,
|
|
163
|
+
("intent", "show"): cmd_intent_show,
|
|
164
|
+
("intent", "activate"): cmd_intent_activate,
|
|
165
|
+
("intent", "suspend"): cmd_intent_suspend,
|
|
166
|
+
("intent", "done"): cmd_intent_done,
|
|
167
|
+
("snap", "create"): cmd_snap_create,
|
|
168
|
+
("snap", "list"): cmd_snap_list,
|
|
169
|
+
("snap", "show"): cmd_snap_show,
|
|
170
|
+
("snap", "feedback"): cmd_snap_feedback,
|
|
171
|
+
("snap", "revert"): cmd_snap_revert,
|
|
172
|
+
("decision", "create"): cmd_decision_create,
|
|
173
|
+
("decision", "list"): cmd_decision_list,
|
|
174
|
+
("decision", "show"): cmd_decision_show,
|
|
175
|
+
("decision", "deprecate"): cmd_decision_deprecate,
|
|
176
|
+
("decision", "attach"): cmd_decision_attach,
|
|
496
177
|
}
|
|
497
178
|
dispatch[(args.command, args.sub)](args)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Command handlers for the Intent CLI."""
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Shared helpers for CLI command handlers."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
|
|
5
|
+
from intent_cli.output import error
|
|
6
|
+
from intent_cli.store import VALID_STATUSES, ensure_init, git_root
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def now_utc():
|
|
10
|
+
return datetime.now(timezone.utc).isoformat()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def require_init():
|
|
14
|
+
"""Return .intent/ base path, or exit with a structured error."""
|
|
15
|
+
base = ensure_init()
|
|
16
|
+
if base is not None:
|
|
17
|
+
return base
|
|
18
|
+
if git_root() is None:
|
|
19
|
+
error(
|
|
20
|
+
"GIT_STATE_INVALID",
|
|
21
|
+
"Not inside a Git repository.",
|
|
22
|
+
suggested_fix="cd into a git repo and run: itt init",
|
|
23
|
+
)
|
|
24
|
+
error(
|
|
25
|
+
"NOT_INITIALIZED",
|
|
26
|
+
".intent/ directory not found.",
|
|
27
|
+
suggested_fix="itt init",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_status_filter(object_type, status):
|
|
32
|
+
"""Validate a --status filter against the object's state machine."""
|
|
33
|
+
if status is None:
|
|
34
|
+
return
|
|
35
|
+
allowed = sorted(VALID_STATUSES[object_type])
|
|
36
|
+
if status not in allowed:
|
|
37
|
+
error(
|
|
38
|
+
"INVALID_INPUT",
|
|
39
|
+
f"Invalid status '{status}' for {object_type}. Allowed values: {', '.join(allowed)}.",
|
|
40
|
+
suggested_fix=f"Use one of: {', '.join(allowed)}",
|
|
41
|
+
)
|