intent-cli-python 2.0.0__py3-none-any.whl → 2.1.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/cli.py CHANGED
@@ -4,28 +4,20 @@ import argparse
4
4
  import sys
5
5
 
6
6
  from intent_cli.commands.core import (
7
- cmd_decision_attach,
8
7
  cmd_decision_create,
9
8
  cmd_decision_deprecate,
10
- cmd_decision_list,
11
- cmd_decision_show,
12
9
  cmd_doctor,
13
10
  cmd_init,
14
11
  cmd_inspect,
15
12
  cmd_intent_activate,
16
13
  cmd_intent_create,
17
14
  cmd_intent_done,
18
- cmd_intent_list,
19
- cmd_intent_show,
20
15
  cmd_intent_suspend,
21
16
  cmd_snap_create,
22
17
  cmd_snap_feedback,
23
- cmd_snap_list,
24
- cmd_snap_revert,
25
- cmd_snap_show,
26
18
  cmd_version,
27
19
  )
28
- from intent_cli.commands.hub import cmd_hub_link, cmd_hub_login, cmd_hub_sync
20
+ from intent_cli.commands.hub import cmd_hub_link, cmd_hub_sync
29
21
 
30
22
 
31
23
  def main():
@@ -42,10 +34,6 @@ def main():
42
34
  p_hub = sub.add_parser("hub")
43
35
  s_hub = p_hub.add_subparsers(dest="sub")
44
36
 
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
37
  p = s_hub.add_parser("link")
50
38
  p.add_argument("--project-name", default=None)
51
39
  p.add_argument("--api-base-url", default=None)
@@ -65,21 +53,14 @@ def main():
65
53
  p.add_argument("--query", default="")
66
54
  p.add_argument("--rationale", default="")
67
55
 
68
- p = s_intent.add_parser("list")
69
- p.add_argument("--status", default=None)
70
- p.add_argument("--decision", default=None)
71
-
72
- p = s_intent.add_parser("show")
73
- p.add_argument("id")
74
-
75
56
  p = s_intent.add_parser("activate")
76
- p.add_argument("id")
57
+ p.add_argument("id", nargs="?")
77
58
 
78
59
  p = s_intent.add_parser("suspend")
79
- p.add_argument("id")
60
+ p.add_argument("id", nargs="?")
80
61
 
81
62
  p = s_intent.add_parser("done")
82
- p.add_argument("id")
63
+ p.add_argument("id", nargs="?")
83
64
 
84
65
  # --- snap ---
85
66
  p_snap = sub.add_parser("snap")
@@ -87,26 +68,19 @@ def main():
87
68
 
88
69
  p = s_snap.add_parser("create")
89
70
  p.add_argument("title")
90
- p.add_argument("--intent", required=True)
91
- p.add_argument("--query", default="")
92
- p.add_argument("--rationale", default="")
93
- p.add_argument("--summary", default="")
94
- p.add_argument("--feedback", default="")
95
-
96
- p = s_snap.add_parser("list")
97
71
  p.add_argument("--intent", default=None)
98
- p.add_argument("--status", default=None)
99
-
100
- p = s_snap.add_parser("show")
101
- p.add_argument("id")
72
+ p.add_argument(
73
+ "--origin",
74
+ default=None,
75
+ metavar="LABEL",
76
+ help="Override auto-detected snap origin label (default: from env, see docs)",
77
+ )
78
+ p.add_argument("--summary", required=True)
102
79
 
103
80
  p = s_snap.add_parser("feedback")
104
81
  p.add_argument("id")
105
82
  p.add_argument("feedback")
106
83
 
107
- p = s_snap.add_parser("revert")
108
- p.add_argument("id")
109
-
110
84
  # --- decision ---
111
85
  p_decision = sub.add_parser("decision")
112
86
  s_decision = p_decision.add_subparsers(dest="sub")
@@ -115,20 +89,9 @@ def main():
115
89
  p.add_argument("title")
116
90
  p.add_argument("--rationale", default="")
117
91
 
118
- p = s_decision.add_parser("list")
119
- p.add_argument("--status", default=None)
120
- p.add_argument("--intent", default=None)
121
-
122
- p = s_decision.add_parser("show")
123
- p.add_argument("id")
124
-
125
92
  p = s_decision.add_parser("deprecate")
126
93
  p.add_argument("id")
127
94
 
128
- p = s_decision.add_parser("attach")
129
- p.add_argument("id")
130
- p.add_argument("--intent", required=True)
131
-
132
95
  args = parser.parse_args()
133
96
 
134
97
  if args.command is None:
@@ -155,24 +118,15 @@ def main():
155
118
  sys.exit(1)
156
119
 
157
120
  dispatch = {
158
- ("hub", "login"): cmd_hub_login,
159
121
  ("hub", "link"): cmd_hub_link,
160
122
  ("hub", "sync"): cmd_hub_sync,
161
123
  ("intent", "create"): cmd_intent_create,
162
- ("intent", "list"): cmd_intent_list,
163
- ("intent", "show"): cmd_intent_show,
164
124
  ("intent", "activate"): cmd_intent_activate,
165
125
  ("intent", "suspend"): cmd_intent_suspend,
166
126
  ("intent", "done"): cmd_intent_done,
167
127
  ("snap", "create"): cmd_snap_create,
168
- ("snap", "list"): cmd_snap_list,
169
- ("snap", "show"): cmd_snap_show,
170
128
  ("snap", "feedback"): cmd_snap_feedback,
171
- ("snap", "revert"): cmd_snap_revert,
172
129
  ("decision", "create"): cmd_decision_create,
173
- ("decision", "list"): cmd_decision_list,
174
- ("decision", "show"): cmd_decision_show,
175
130
  ("decision", "deprecate"): cmd_decision_deprecate,
176
- ("decision", "attach"): cmd_decision_attach,
177
131
  }
178
132
  dispatch[(args.command, args.sub)](args)
@@ -3,15 +3,15 @@
3
3
  import json
4
4
 
5
5
  from intent_cli import __version__
6
- from intent_cli.commands.common import now_utc, require_init, validate_status_filter
6
+ from intent_cli.commands.common import now_utc, require_init
7
7
  from intent_cli.output import error, success
8
+ from intent_cli.origin import detect_origin
8
9
  from intent_cli.store import (
9
10
  VALID_STATUSES,
10
11
  git_root,
11
12
  init_workspace,
12
13
  list_objects,
13
14
  next_id,
14
- read_config,
15
15
  read_object,
16
16
  validate_graph,
17
17
  write_object,
@@ -41,43 +41,43 @@ def cmd_init(_args):
41
41
 
42
42
  def cmd_inspect(_args):
43
43
  base = require_init()
44
- config = read_config(base)
44
+
45
+ all_snaps = list_objects(base, "snap")
46
+ snap_by_id = {snap["id"]: snap for snap in all_snaps}
45
47
 
46
48
  active_intents = []
47
- suspend_intents = []
49
+ suspended = []
48
50
  for obj in list_objects(base, "intent"):
49
- entry = {
50
- "id": obj["id"],
51
- "title": obj["title"],
52
- "status": obj["status"],
53
- "decision_ids": obj.get("decision_ids", []),
54
- "latest_snap_id": obj["snap_ids"][-1] if obj.get("snap_ids") else None,
55
- }
51
+ latest_snap_id = obj["snap_ids"][-1] if obj.get("snap_ids") else None
56
52
  if obj["status"] == "active":
57
- active_intents.append(entry)
53
+ latest_snap = None
54
+ if latest_snap_id:
55
+ snap = snap_by_id.get(latest_snap_id)
56
+ if snap is not None:
57
+ latest_snap = {
58
+ "id": snap["id"],
59
+ "title": snap["title"],
60
+ "summary": snap.get("summary", ""),
61
+ "feedback": snap.get("feedback", ""),
62
+ "origin": snap.get("origin", ""),
63
+ }
64
+ active_intents.append({
65
+ "id": obj["id"],
66
+ "title": obj["title"],
67
+ "latest_snap": latest_snap,
68
+ })
58
69
  elif obj["status"] == "suspend":
59
- suspend_intents.append(entry)
70
+ suspended.append({
71
+ "id": obj["id"],
72
+ "title": obj["title"],
73
+ "latest_snap_id": latest_snap_id,
74
+ })
60
75
 
61
76
  active_decisions = []
62
77
  for obj in list_objects(base, "decision", status="active"):
63
78
  active_decisions.append({
64
79
  "id": obj["id"],
65
80
  "title": obj["title"],
66
- "status": obj["status"],
67
- "intent_ids": obj.get("intent_ids", []),
68
- })
69
-
70
- all_snaps = list_objects(base, "snap")
71
- all_snaps.sort(key=lambda s: s.get("created_at", ""), reverse=True)
72
- recent_snaps = []
73
- for snap in all_snaps[:10]:
74
- recent_snaps.append({
75
- "id": snap["id"],
76
- "title": snap["title"],
77
- "intent_id": snap["intent_id"],
78
- "status": snap["status"],
79
- "summary": snap.get("summary", ""),
80
- "feedback": snap.get("feedback", ""),
81
81
  })
82
82
 
83
83
  warnings = []
@@ -88,11 +88,9 @@ def cmd_inspect(_args):
88
88
 
89
89
  print(json.dumps({
90
90
  "ok": True,
91
- "schema_version": config.get("schema_version", "1.0"),
92
91
  "active_intents": active_intents,
93
- "suspend_intents": suspend_intents,
94
92
  "active_decisions": active_decisions,
95
- "recent_snaps": recent_snaps,
93
+ "suspended": suspended,
96
94
  "warnings": warnings,
97
95
  }, indent=2, ensure_ascii=False))
98
96
 
@@ -102,6 +100,45 @@ def cmd_doctor(_args):
102
100
  success("doctor", validate_graph(base))
103
101
 
104
102
 
103
+ def _intent_result_for_json(intent):
104
+ result = dict(intent)
105
+ result.pop("decision_ids", None)
106
+ return result
107
+
108
+
109
+ def _resolve_inferred_intent_id(
110
+ base,
111
+ explicit_id,
112
+ *,
113
+ status,
114
+ none_code,
115
+ none_message,
116
+ multi_code,
117
+ multi_message,
118
+ suggested_fix,
119
+ ):
120
+ if explicit_id:
121
+ return explicit_id, False
122
+
123
+ candidates = sorted(
124
+ (
125
+ {"id": obj["id"], "title": obj["title"]}
126
+ for obj in list_objects(base, "intent", status=status)
127
+ ),
128
+ key=lambda c: c["id"],
129
+ )
130
+ if not candidates:
131
+ error(none_code, none_message, suggested_fix=suggested_fix)
132
+ if len(candidates) > 1:
133
+ error(
134
+ multi_code,
135
+ multi_message,
136
+ details={"candidates": candidates},
137
+ suggested_fix=suggested_fix,
138
+ )
139
+ return candidates[0]["id"], True
140
+
141
+
105
142
  def cmd_intent_create(args):
106
143
  base = require_init()
107
144
  obj_id = next_id(base, "intent")
@@ -131,36 +168,29 @@ def cmd_intent_create(args):
131
168
  decision.setdefault("intent_ids", []).append(obj_id)
132
169
  write_object(base, "decision", decision["id"], decision)
133
170
 
134
- success("intent.create", intent, warnings)
135
-
136
-
137
- def cmd_intent_list(args):
138
- base = require_init()
139
- validate_status_filter("intent", args.status)
140
- objects = list_objects(base, "intent", status=args.status)
141
- if args.decision:
142
- objects = [obj for obj in objects if args.decision in obj.get("decision_ids", [])]
143
- success("intent.list", objects)
144
-
145
-
146
- def cmd_intent_show(args):
147
- base = require_init()
148
- obj = read_object(base, "intent", args.id)
149
- if obj is None:
150
- error("OBJECT_NOT_FOUND", f"Intent {args.id} not found.")
151
- success("intent.show", obj)
152
-
171
+ success("intent.create", _intent_result_for_json(intent), warnings)
153
172
 
154
173
  def cmd_intent_activate(args):
155
174
  base = require_init()
156
- obj = read_object(base, "intent", args.id)
175
+ intent_id, inferred = _resolve_inferred_intent_id(
176
+ base,
177
+ args.id,
178
+ status="suspend",
179
+ none_code="NO_SUSPENDED_INTENT",
180
+ none_message="No suspended intent to activate.",
181
+ multi_code="MULTIPLE_SUSPENDED_INTENTS",
182
+ multi_message="Multiple suspended intents; specify which one with ID.",
183
+ suggested_fix="itt intent activate <id> or use: itt inspect",
184
+ )
185
+
186
+ obj = read_object(base, "intent", intent_id)
157
187
  if obj is None:
158
- error("OBJECT_NOT_FOUND", f"Intent {args.id} not found.")
188
+ error("OBJECT_NOT_FOUND", f"Intent {intent_id} not found.")
159
189
  if obj["status"] != "suspend":
160
190
  error(
161
191
  "STATE_CONFLICT",
162
192
  f"Cannot activate intent with status '{obj['status']}'. Only 'suspend' intents can be activated.",
163
- suggested_fix=f"itt intent show {args.id}",
193
+ suggested_fix="Use: itt inspect",
164
194
  )
165
195
 
166
196
  obj["status"] = "active"
@@ -169,51 +199,105 @@ def cmd_intent_activate(args):
169
199
  for decision in active_decisions:
170
200
  if decision["id"] not in obj["decision_ids"]:
171
201
  obj["decision_ids"].append(decision["id"])
172
- if args.id not in decision.get("intent_ids", []):
173
- decision.setdefault("intent_ids", []).append(args.id)
202
+ if intent_id not in decision.get("intent_ids", []):
203
+ decision.setdefault("intent_ids", []).append(intent_id)
174
204
  write_object(base, "decision", decision["id"], decision)
175
205
 
176
- write_object(base, "intent", args.id, obj)
177
- success("intent.activate", obj)
206
+ write_object(base, "intent", intent_id, obj)
207
+ warnings = []
208
+ if inferred:
209
+ warnings.append(f"Inferred intent {intent_id} (only suspended intent).")
210
+ success("intent.activate", _intent_result_for_json(obj), warnings)
178
211
 
179
212
 
180
213
  def cmd_intent_suspend(args):
181
214
  base = require_init()
182
- obj = read_object(base, "intent", args.id)
215
+ intent_id, inferred = _resolve_inferred_intent_id(
216
+ base,
217
+ args.id,
218
+ status="active",
219
+ none_code="NO_ACTIVE_INTENT",
220
+ none_message="No active intent to suspend.",
221
+ multi_code="MULTIPLE_ACTIVE_INTENTS",
222
+ multi_message="Multiple active intents; specify which one with ID.",
223
+ suggested_fix="itt intent suspend <id> or use: itt inspect",
224
+ )
225
+
226
+ obj = read_object(base, "intent", intent_id)
183
227
  if obj is None:
184
- error("OBJECT_NOT_FOUND", f"Intent {args.id} not found.")
228
+ error("OBJECT_NOT_FOUND", f"Intent {intent_id} not found.")
185
229
  if obj["status"] != "active":
186
230
  error(
187
231
  "STATE_CONFLICT",
188
232
  f"Cannot suspend intent with status '{obj['status']}'. Only 'active' intents can be suspended.",
189
- suggested_fix=f"itt intent show {args.id}",
233
+ suggested_fix="Use: itt inspect",
190
234
  )
191
235
 
192
236
  obj["status"] = "suspend"
193
- write_object(base, "intent", args.id, obj)
194
- success("intent.suspend", obj)
237
+ write_object(base, "intent", intent_id, obj)
238
+ warnings = []
239
+ if inferred:
240
+ warnings.append(f"Inferred intent {intent_id} (only active intent).")
241
+ success("intent.suspend", _intent_result_for_json(obj), warnings)
195
242
 
196
243
 
197
244
  def cmd_intent_done(args):
198
245
  base = require_init()
199
- obj = read_object(base, "intent", args.id)
246
+ intent_id, inferred = _resolve_inferred_intent_id(
247
+ base,
248
+ args.id,
249
+ status="active",
250
+ none_code="NO_ACTIVE_INTENT",
251
+ none_message="No active intent to mark done.",
252
+ multi_code="MULTIPLE_ACTIVE_INTENTS",
253
+ multi_message="Multiple active intents; specify which one with ID.",
254
+ suggested_fix="itt intent done <id> or use: itt inspect",
255
+ )
256
+
257
+ obj = read_object(base, "intent", intent_id)
200
258
  if obj is None:
201
- error("OBJECT_NOT_FOUND", f"Intent {args.id} not found.")
259
+ error("OBJECT_NOT_FOUND", f"Intent {intent_id} not found.")
202
260
  if obj["status"] != "active":
203
261
  error(
204
262
  "STATE_CONFLICT",
205
263
  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}",
264
+ suggested_fix="Use: itt inspect",
207
265
  )
208
266
 
209
267
  obj["status"] = "done"
210
- write_object(base, "intent", args.id, obj)
211
- success("intent.done", obj)
268
+ write_object(base, "intent", intent_id, obj)
269
+ warnings = []
270
+ if inferred:
271
+ warnings.append(f"Inferred intent {intent_id} (only active intent).")
272
+ success("intent.done", _intent_result_for_json(obj), warnings)
212
273
 
213
274
 
214
275
  def cmd_snap_create(args):
215
276
  base = require_init()
216
- intent_id = args.intent
277
+ if args.intent:
278
+ intent_id = args.intent
279
+ inferred = False
280
+ else:
281
+ active = list_objects(base, "intent", status="active")
282
+ if not active:
283
+ error(
284
+ "NO_ACTIVE_INTENT",
285
+ "No active intent to attach the snap to.",
286
+ suggested_fix='Create or activate an intent first, e.g. itt intent create "TITLE" --query "..." or itt intent activate <id>',
287
+ )
288
+ if len(active) > 1:
289
+ candidates = sorted(
290
+ ({"id": o["id"], "title": o["title"]} for o in active),
291
+ key=lambda c: c["id"],
292
+ )
293
+ error(
294
+ "MULTIPLE_ACTIVE_INTENTS",
295
+ "Multiple active intents; specify which one with --intent ID.",
296
+ details={"candidates": candidates},
297
+ suggested_fix="itt snap create TITLE --intent <id> --summary ...",
298
+ )
299
+ intent_id = active[0]["id"]
300
+ inferred = True
217
301
 
218
302
  intent = read_object(base, "intent", intent_id)
219
303
  if intent is None:
@@ -225,6 +309,11 @@ def cmd_snap_create(args):
225
309
  suggested_fix=f"itt intent activate {intent_id}",
226
310
  )
227
311
 
312
+ if args.origin is not None:
313
+ origin = (args.origin or "").strip()
314
+ else:
315
+ origin = detect_origin()
316
+
228
317
  obj_id = next_id(base, "snap")
229
318
  snap = {
230
319
  "id": obj_id,
@@ -233,34 +322,19 @@ def cmd_snap_create(args):
233
322
  "title": args.title,
234
323
  "status": "active",
235
324
  "intent_id": intent_id,
236
- "query": args.query,
237
- "rationale": args.rationale,
238
325
  "summary": args.summary,
239
- "feedback": args.feedback,
326
+ "feedback": "",
327
+ "origin": origin,
240
328
  }
241
329
  write_object(base, "snap", obj_id, snap)
242
330
 
243
331
  intent.setdefault("snap_ids", []).append(obj_id)
244
332
  write_object(base, "intent", intent_id, intent)
245
333
 
246
- success("snap.create", snap)
247
-
248
-
249
- def cmd_snap_list(args):
250
- base = require_init()
251
- validate_status_filter("snap", args.status)
252
- objects = list_objects(base, "snap", status=args.status)
253
- if args.intent:
254
- objects = [snap for snap in objects if snap.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)
334
+ warnings = []
335
+ if inferred:
336
+ warnings.append(f"Inferred intent {intent_id} (only active intent).")
337
+ success("snap.create", snap, warnings)
264
338
 
265
339
 
266
340
  def cmd_snap_feedback(args):
@@ -273,23 +347,6 @@ def cmd_snap_feedback(args):
273
347
  success("snap.feedback", obj)
274
348
 
275
349
 
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(
283
- "STATE_CONFLICT",
284
- f"Cannot revert snap with status '{obj['status']}'. Only 'active' snaps can be reverted.",
285
- suggested_fix=f"itt snap show {args.id}",
286
- )
287
-
288
- obj["status"] = "reverted"
289
- write_object(base, "snap", args.id, obj)
290
- success("snap.revert", obj)
291
-
292
-
293
350
  def cmd_decision_create(args):
294
351
  base = require_init()
295
352
  obj_id = next_id(base, "decision")
@@ -320,23 +377,6 @@ def cmd_decision_create(args):
320
377
  success("decision.create", decision, warnings)
321
378
 
322
379
 
323
- def cmd_decision_list(args):
324
- base = require_init()
325
- validate_status_filter("decision", args.status)
326
- objects = list_objects(base, "decision", status=args.status)
327
- if args.intent:
328
- objects = [obj for obj in objects if args.intent in obj.get("intent_ids", [])]
329
- success("decision.list", objects)
330
-
331
-
332
- def cmd_decision_show(args):
333
- base = require_init()
334
- obj = read_object(base, "decision", args.id)
335
- if obj is None:
336
- error("OBJECT_NOT_FOUND", f"Decision {args.id} not found.")
337
- success("decision.show", obj)
338
-
339
-
340
380
  def cmd_decision_deprecate(args):
341
381
  base = require_init()
342
382
  obj = read_object(base, "decision", args.id)
@@ -346,31 +386,10 @@ def cmd_decision_deprecate(args):
346
386
  error(
347
387
  "STATE_CONFLICT",
348
388
  f"Cannot deprecate decision with status '{obj['status']}'. Only 'active' decisions can be deprecated.",
349
- suggested_fix=f"itt decision show {args.id}",
389
+ suggested_fix="Use: itt inspect",
350
390
  )
351
391
 
352
392
  obj["status"] = "deprecated"
353
393
  write_object(base, "decision", args.id, obj)
354
394
  success("decision.deprecate", obj)
355
395
 
356
-
357
- def cmd_decision_attach(args):
358
- base = require_init()
359
- decision = read_object(base, "decision", args.id)
360
- if decision is None:
361
- error("OBJECT_NOT_FOUND", f"Decision {args.id} not found.")
362
-
363
- intent_id = args.intent
364
- intent = read_object(base, "intent", intent_id)
365
- if intent is None:
366
- error("OBJECT_NOT_FOUND", f"Intent {intent_id} not found.")
367
-
368
- if intent_id not in decision.get("intent_ids", []):
369
- decision.setdefault("intent_ids", []).append(intent_id)
370
- write_object(base, "decision", args.id, decision)
371
-
372
- if args.id not in intent.get("decision_ids", []):
373
- intent.setdefault("decision_ids", []).append(args.id)
374
- write_object(base, "intent", intent_id, intent)
375
-
376
- success("decision.attach", decision)
@@ -13,33 +13,26 @@ from intent_cli.output import error, success
13
13
  from intent_cli.store import make_runtime_id, write_hub_config
14
14
 
15
15
 
16
- def cmd_hub_login(args):
16
+ def cmd_hub_link(args):
17
17
  base = require_init()
18
18
  hub = load_hub(base)
19
+ repo = current_github_repo()
19
20
 
20
- api_base_url = args.api_base_url or hub.get("api_base_url")
21
+ if args.api_base_url:
22
+ hub["api_base_url"] = args.api_base_url.rstrip("/")
23
+ api_base_url = hub.get("api_base_url")
21
24
  if not api_base_url:
22
25
  error(
23
- "INVALID_INPUT",
24
- "Missing IntHub API base URL.",
25
- suggested_fix="Run: itt hub login --api-base-url http://127.0.0.1:8000",
26
+ "HUB_NOT_CONFIGURED",
27
+ "IntHub API base URL is not configured.",
28
+ suggested_fix="Run: itt hub link --api-base-url http://127.0.0.1:8000",
26
29
  )
27
30
 
28
- hub["api_base_url"] = api_base_url.rstrip("/")
29
31
  if args.token:
30
32
  hub["auth_token"] = args.token
31
33
  elif "auth_token" not in hub:
32
34
  hub["auth_token"] = ""
33
35
 
34
- write_hub_config(base, hub)
35
- success("hub.login", sanitize_hub_config(hub))
36
-
37
-
38
- def cmd_hub_link(args):
39
- base = require_init()
40
- hub = load_hub(base)
41
- repo = current_github_repo()
42
- api_base_url = hub_api_base(base, args)
43
36
  token = hub_auth_token(base, args)
44
37
 
45
38
  workspace_id = hub.get("workspace_id") or make_runtime_id("wks")
@@ -59,7 +52,7 @@ def cmd_hub_link(args):
59
52
 
60
53
  updated = {
61
54
  "api_base_url": api_base_url,
62
- "auth_token": token or hub.get("auth_token", ""),
55
+ "auth_token": hub.get("auth_token", ""),
63
56
  "workspace_id": result["workspace_id"],
64
57
  "project_id": result["project_id"],
65
58
  "repo_binding": result["repo_binding"],
intent_cli/hub/runtime.py CHANGED
@@ -24,7 +24,7 @@ def hub_api_base(base, args):
24
24
  error(
25
25
  "HUB_NOT_CONFIGURED",
26
26
  "IntHub API base URL is not configured.",
27
- suggested_fix="Run: itt hub login --api-base-url http://127.0.0.1:8000",
27
+ suggested_fix="Run: itt hub link --api-base-url http://127.0.0.1:8000",
28
28
  )
29
29
  return api_base_url.rstrip("/")
30
30
 
intent_cli/origin.py ADDED
@@ -0,0 +1,57 @@
1
+ """Infer snap origin (host / tool label) from the process environment.
2
+
3
+ The `itt` subprocess inherits env from its parent (IDE terminal, CI, etc.).
4
+ Override with ITT_ORIGIN or INTENT_ORIGIN.
5
+ """
6
+
7
+ import os
8
+ import re
9
+
10
+
11
+ def _slugify_origin(value):
12
+ """Normalize a host/tool label into a short stable slug."""
13
+ text = re.sub(r"[^a-z0-9]+", "-", value.strip().lower())
14
+ return text.strip("-")
15
+
16
+
17
+ def detect_origin(environ=None):
18
+ """Return a short stable label, or \"\" when unknown.
19
+
20
+ Precedence:
21
+ 1. ITT_ORIGIN, then INTENT_ORIGIN (trimmed, non-empty)
22
+ 2. Built-in heuristics for common host environments
23
+ """
24
+ env = environ if environ is not None else os.environ
25
+
26
+ for key in ("ITT_ORIGIN", "INTENT_ORIGIN"):
27
+ raw = (env.get(key) or "").strip()
28
+ if raw:
29
+ return raw
30
+
31
+ if env.get("CLAUDECODE"):
32
+ return "claude-code"
33
+
34
+ if env.get("CURSOR_TRACE_ID"):
35
+ return "cursor"
36
+
37
+ codex_originator = (env.get("CODEX_INTERNAL_ORIGINATOR_OVERRIDE") or "").strip()
38
+ if codex_originator:
39
+ return _slugify_origin(codex_originator)
40
+
41
+ if env.get("CODEX_THREAD_ID") or env.get("CODEX_SHELL") or env.get("CODEX_CI"):
42
+ return "codex"
43
+
44
+ term = (env.get("TERM_PROGRAM") or "").lower()
45
+ if term == "vscode":
46
+ return "vscode"
47
+
48
+ if env.get("CODESPACES") == "true":
49
+ return "codespaces"
50
+
51
+ if env.get("GITHUB_ACTIONS") == "true":
52
+ return "github-actions"
53
+
54
+ if env.get("GITPOD_WORKSPACE_ID"):
55
+ return "gitpod"
56
+
57
+ return ""
intent_cli/store.py CHANGED
@@ -10,7 +10,7 @@ SUBDIRS = {"intent": "intents", "snap": "snaps", "decision": "decisions"}
10
10
  HUB_CONFIG = "hub.json"
11
11
  VALID_STATUSES = {
12
12
  "intent": {"active", "suspend", "done"},
13
- "snap": {"active", "reverted"},
13
+ "snap": {"active"},
14
14
  "decision": {"active", "deprecated"},
15
15
  }
16
16
 
@@ -313,12 +313,5 @@ def validate_graph(base):
313
313
 
314
314
  return {
315
315
  "healthy": not issues,
316
- "issue_count": len(issues),
317
- "summary": {
318
- "schema_version": config.get("schema_version", "1.0"),
319
- "intent_count": len(intents),
320
- "snap_count": len(snaps),
321
- "decision_count": len(decisions),
322
- },
323
316
  "issues": issues,
324
317
  }
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: intent-cli-python
3
+ Version: 2.1.0
4
+ Summary: Semantic history for agent-driven development. Records what you did and why.
5
+ Author: Zeng Deyang
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/dozybot001/Intent
8
+ Project-URL: Repository, https://github.com/dozybot001/Intent
9
+ Keywords: agent,git,semantic-history,intent,developer-tools
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Version Control :: Git
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Dynamic: license-file
24
+
25
+ # Intent
26
+
27
+ [中文](README.CN.md) | English
28
+
29
+ Semantic history for agent-driven development. Preserves **how the product took shape** and **how work resumes across sessions and agents**.
30
+
31
+ ## Why
32
+
33
+ Git records how code changes. But it doesn't record **why you're on this path**, what you decided along the way, or where you left off.
34
+
35
+ Intent adds that missing layer: **semantic history** — a small set of formal objects that preserve product formation history and survive context loss.
36
+
37
+ > Development is moving from *writing code* to *guiding agents and distilling decisions*. The history layer should reflect that.
38
+
39
+ ```mermaid
40
+ flowchart LR
41
+ subgraph traditional["Traditional Coding"]
42
+ direction TB
43
+ H1["Human"]
44
+ C1["Code"]
45
+ H1 -->|"Git"| C1
46
+ end
47
+ subgraph agent["Agent Driven Development"]
48
+ direction TB
49
+ H2["Human"]
50
+ AG["Agent"]
51
+ C2["Code"]
52
+ H2 -."❌ no semantic history".-> AG
53
+ AG -->|"Git"| C2
54
+ end
55
+ subgraph withintent["Agent with Intent"]
56
+ direction TB
57
+ H3["Human"]
58
+ AG2["Agent"]
59
+ C3["Code"]
60
+ H3 -->|"Intent"| AG2
61
+ AG2 -->|"Git"| C3
62
+ end
63
+ traditional ~~~ agent ~~~ withintent
64
+ ```
65
+
66
+ ## Three objects, one graph
67
+
68
+ | Object | What it captures |
69
+ |---|---|
70
+ | **Intent** | A goal recognized from your query |
71
+ | **Snap** | A semantic checkpoint that captures what changed, what was learned, and later feedback |
72
+ | **Decision** | A long-lived constraint that spans multiple intents |
73
+
74
+ Objects link automatically. Decisions auto-attach to every active intent; intents auto-attach to every active decision. Relationships are bidirectional and append-only.
75
+
76
+ ```mermaid
77
+ flowchart LR
78
+ D1["🔶 Decision 1"]
79
+ D2["🔶 Decision 2"]
80
+
81
+ subgraph Intent1["🎯 Intent 1"]
82
+ direction LR
83
+ S1["Snap 1"] --> S2["Snap 2"] --> S3["..."]
84
+ end
85
+
86
+ subgraph Intent2["🎯 Intent 2"]
87
+ direction LR
88
+ S4["Snap 1"] --> S5["Snap 2"] --> S6["..."]
89
+ end
90
+
91
+ D1 -- auto-attach --> Intent1
92
+ D1 -- auto-attach --> Intent2
93
+ D2 -- auto-attach --> Intent2
94
+ ```
95
+
96
+ ## Install
97
+
98
+ ```bash
99
+ pipx install intent-cli-python # CLI
100
+ npx skills add dozybot001/Intent -g # Agent skill
101
+ ```
102
+
103
+ Requires Python 3.9+ and Git. The CLI provides the commands; the skill teaches the agent when to use them.
104
+
105
+ > **Tips:** Because `itt` is a new command, agents are not trained on it yet. We recommend typing `/` at the start of each session, selecting the skill, and pressing Enter to enter the workflow.
106
+
107
+ ## IntHub
108
+
109
+ ```mermaid
110
+ flowchart TB
111
+ Hub["IntHub — Collaboration Layer"]
112
+ Intent["Intent — Semantic History Layer"]
113
+ Git["Git — Code History Layer"]
114
+ Hub <--> Intent <--> Git
115
+ ```
116
+
117
+ IntHub is the remote collaboration layer on top of Intent. The first path is **IntHub Local** — download from a [GitHub release](https://github.com/dozybot001/Intent/releases), then:
118
+
119
+ ```bash
120
+ itt hub link --api-base-url http://127.0.0.1:7210
121
+ itt hub sync
122
+ ```
123
+
124
+ Open `http://127.0.0.1:7210` in the browser.
125
+
126
+ ## Docs
127
+
128
+ - [Vision](docs/EN/vision.md) — why semantic history matters
129
+ - [CLI Design](docs/EN/cli.md) — object model, commands, JSON contract
130
+ - [Roadmap](docs/EN/roadmap.md) — phase plan
131
+ - [Dogfooding](docs/EN/dogfooding.md) — cross-agent case study
132
+ - [IntHub Local](docs/EN/inthub-local.md) — run a local IntHub instance
133
+
134
+ ## License
135
+
136
+ MIT
@@ -0,0 +1,20 @@
1
+ intent_cli/__init__.py,sha256=_19_tcL7KLgyD54NiZ-8tcAMCDKyqu_9k5K2-4Lc8ng,1058
2
+ intent_cli/__main__.py,sha256=pWAakBzI50WtmbfDBrUudHbZYBPi8VJ_PlPbRRFGCP4,40
3
+ intent_cli/cli.py,sha256=ShTjzjhK6e7UeKf0dw4Z7nhGtK-OaX35PHv9n1VjBIo,3761
4
+ intent_cli/origin.py,sha256=m_vevJLJ5vUZDIuCC5k3K-LeVIl9YNtRsm7pBqXLQA0,1533
5
+ intent_cli/output.py,sha256=_ZmivWZfY_AOcGRCWFMx09lUGB-Ku0g5xyjrqL6UWKg,680
6
+ intent_cli/store.py,sha256=7GVgY6Y1Era_4ztMBtv8txfrpzF-6z9Kf3UgvWpr3lE,9846
7
+ intent_cli/commands/__init__.py,sha256=XtBJys-Al_64_nLnWGXoOFR2pnYLmqbzanB5Nuy1BUM,43
8
+ intent_cli/commands/common.py,sha256=P2MLcBf3S16Z_iuhxvQqAFV2ioAbvpgQd6XHj3ppROA,1193
9
+ intent_cli/commands/core.py,sha256=tCBHdOAJh-sS6nNA85ujLU_TF-TjqsQHQgLCm0qWgS4,12599
10
+ intent_cli/commands/hub.py,sha256=xiGF5IJNSPxg8PzOhQXUgBxewPhiQ4xAWPyUyLFdQ5s,3094
11
+ intent_cli/hub/__init__.py,sha256=ujLxjGwZo_JCKhCXSlx1lwtJYDWd0y5tAoo57F1VaKM,46
12
+ intent_cli/hub/client.py,sha256=QB6fsPZdAQD7iSnhCmbkBwPlEZVwl7QrYXE4E0S88to,1811
13
+ intent_cli/hub/payload.py,sha256=fjweASqpHIu54HvoQDQd-ibJ0UJx6zpzjIsllVhWmrs,1930
14
+ intent_cli/hub/runtime.py,sha256=O6dvJQMHbkMMa7yM7KV-wxu-IadykspHac4t9v8MD1I,1042
15
+ intent_cli_python-2.1.0.dist-info/licenses/LICENSE,sha256=XWjTStLaoDw-UgLwMecejVxeaHH8JibnSFYARGzRc6I,1068
16
+ intent_cli_python-2.1.0.dist-info/METADATA,sha256=YCQLSr36zpIfCo8rdwZ6r_v67zL1pCgNA-cyjz26T-s,4245
17
+ intent_cli_python-2.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
18
+ intent_cli_python-2.1.0.dist-info/entry_points.txt,sha256=Y1kziqgaGUgTHg3CCfc2Su1XoDvIMJ2vi-dPEDZfuTo,44
19
+ intent_cli_python-2.1.0.dist-info/top_level.txt,sha256=jkyOMCXA-G6FlEj69GA4SKn3RoO1KNL9w2iit7OUpuU,11
20
+ intent_cli_python-2.1.0.dist-info/RECORD,,
@@ -1,218 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: intent-cli-python
3
- Version: 2.0.0
4
- Summary: Semantic history for agent-driven development. Records what you did and why.
5
- Author: Zeng Deyang
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/dozybot001/Intent
8
- Project-URL: Repository, https://github.com/dozybot001/Intent
9
- Keywords: agent,git,semantic-history,intent,developer-tools
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: Environment :: Console
12
- Classifier: Intended Audience :: Developers
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.9
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.12
18
- Classifier: Programming Language :: Python :: 3.13
19
- Classifier: Topic :: Software Development :: Version Control :: Git
20
- Requires-Python: >=3.9
21
- Description-Content-Type: text/markdown
22
- License-File: LICENSE
23
- Dynamic: license-file
24
-
25
- # Intent CLI
26
-
27
- [中文](README.CN.md) | English
28
-
29
- Semantic history for agent-driven development. Records **what you did** and **why**.
30
-
31
- Intent CLI gives AI agents a structured way to track goals, interactions, and decisions across sessions. Instead of losing context when a conversation ends, agents persist their understanding into three simple objects stored alongside your code.
32
-
33
- ## Why
34
-
35
- Git records how code changes. But it doesn't record **why you're on this path**, what you decided along the way, or where you left off.
36
-
37
- Today that context lives in chat logs, PR threads, and your head. It works — until the session ends, the agent forgets, or a teammate picks up your work blind.
38
-
39
- Intent treats these as a missing layer: **semantic history**. Not more docs, not better commit messages — a small set of formal objects that capture goals, interactions, and decisions so they survive context loss.
40
-
41
- > The shift is simple: development is moving from *writing code* to *guiding agents and distilling decisions*. The history layer should reflect that.
42
-
43
- ## Three objects, one graph
44
-
45
- | Object | What it captures |
46
- |---|---|
47
- | **Intent** | A goal the agent identified from your query |
48
- | **Snap** | One agent interaction — query, summary, feedback |
49
- | **Decision** | A long-lived decision that spans multiple intents |
50
-
51
- Objects link automatically: creating an intent attaches all active decisions; creating a decision attaches all active intents. Relationships are always bidirectional and append-only.
52
-
53
- ### How decisions are created
54
-
55
- Decisions require human involvement. Two paths:
56
-
57
- - **Explicit**: include `decision-[text]` (or `决定-[text]`) in your query and the agent creates it directly. E.g. "decision-all API responses use envelope format"
58
- - **Agent-proposed**: the agent spots a potential long-term constraint in conversation and asks you to confirm before recording it
59
-
60
- ## Install
61
-
62
- ```bash
63
- # Clone the repository
64
- git clone https://github.com/dozybot001/Intent.git
65
-
66
- # Install the CLI (pipx recommended)
67
- pipx install intent-cli-python
68
-
69
- # Or using pip
70
- pip install intent-cli-python
71
- ```
72
-
73
- Requires Python 3.9+ and Git.
74
-
75
- ### IntHub boundary
76
-
77
- `pipx install intent-cli-python` installs the CLI only.
78
-
79
- This repository is the umbrella project for both `Intent` and `IntHub`, but the distribution boundary is narrower than the repository boundary:
80
-
81
- - PyPI ships only the `itt` CLI
82
- - IntHub Web is a separate static frontend and is a good fit for GitHub Pages
83
- - IntHub API is a separate service and is not part of the PyPI package
84
-
85
- If you are running IntHub from source inside this repository, the current local entrypoints are:
86
-
87
- ```bash
88
- python -m apps.inthub_api --db-path .inthub/inthub.db
89
- python -m apps.inthub_web --api-base-url http://127.0.0.1:8000
90
- ```
91
-
92
- Then use `itt hub login`, `itt hub link`, and `itt hub sync` from a local Intent workspace to populate the read-only IntHub project view.
93
-
94
- ### Versioning and releases
95
-
96
- `Intent` is the umbrella project and monorepo. It does not maintain one shared project version anymore.
97
-
98
- Release versions now belong to concrete deliverables:
99
-
100
- - CLI releases use the PyPI package version from `pyproject.toml` and Git tags like `cli-v2.0.0`
101
- - IntHub releases use their own track and Git tags like `hub-v0.1.0`
102
-
103
- Historical bare tags such as `v1.3.0` remain as history, but new releases use deliverable-prefixed tags.
104
-
105
- ### Install the skills.sh skill
106
-
107
- ```bash
108
- npx skills add dozybot001/Intent -g
109
- ```
110
-
111
- This installs the `intent-cli` skill into your global skills library for supported agents such as Codex and Claude Code.
112
-
113
- > **Tip:** `itt` is a new tool — current models have never seen it in training data. Your agent may forget to call it mid-conversation. A short nudge like *"use itt to record this"* is usually enough to bring it back on track.
114
- >
115
- > This isn't busywork — every record is a **semantic asset**. An upcoming platform, **IntHub**, will turn these assets into searchable, shareable project intelligence.
116
-
117
- ## Quick start
118
-
119
- ```bash
120
- # Initialize in any git repo
121
- itt init
122
-
123
- # Agent identifies a new intent from user query
124
- itt intent create "Fix the login timeout bug" \
125
- --query "why does login timeout after 5s?"
126
-
127
- # Record what the agent did
128
- itt snap create "Raise timeout to 30s" \
129
- --intent intent-001 \
130
- --query "login timeout still fails on slow networks" \
131
- --summary "Updated timeout config and ran the login test"
132
-
133
- # Capture a long-lived decision
134
- itt decision create "Timeout must stay configurable" \
135
- --rationale "Different deployments have different latency envelopes"
136
-
137
- # See the full object graph
138
- itt inspect
139
- ```
140
-
141
- ## Commands
142
-
143
- ### Global
144
-
145
- | Command | Description |
146
- |---|---|
147
- | `itt version` | Print version |
148
- | `itt init` | Initialize `.intent/` in current git repo |
149
- | `itt inspect` | Show the live object graph snapshot |
150
- | `itt doctor` | Validate the object graph for broken references and invalid states |
151
-
152
- ### Intent
153
-
154
- | Command | Description |
155
- |---|---|
156
- | `itt intent create TITLE --query Q` | Create a new intent |
157
- | `itt intent list [--status S] [--decision ID]` | List intents |
158
- | `itt intent show ID` | Show intent details |
159
- | `itt intent activate ID` | Resume a suspended intent |
160
- | `itt intent suspend ID` | Suspend an active intent |
161
- | `itt intent done ID` | Mark intent as completed |
162
-
163
- ### Snap
164
-
165
- | Command | Description |
166
- |---|---|
167
- | `itt snap create TITLE --intent ID` | Record an interaction snapshot |
168
- | `itt snap list [--intent ID] [--status S]` | List snaps |
169
- | `itt snap show ID` | Show snap details |
170
- | `itt snap feedback ID TEXT` | Set or overwrite feedback |
171
- | `itt snap revert ID` | Mark a snap as reverted |
172
-
173
- ### Decision
174
-
175
- | Command | Description |
176
- |---|---|
177
- | `itt decision create TITLE --rationale R` | Create a long-lived decision |
178
- | `itt decision list [--status S] [--intent ID]` | List decisions |
179
- | `itt decision show ID` | Show decision details |
180
- | `itt decision deprecate ID` | Deprecate a decision |
181
- | `itt decision attach ID --intent ID` | Manually link a decision to an intent |
182
-
183
- ## Design principles
184
-
185
- - **Agent-first**: designed to be called by AI agents, not typed by hand
186
- - **Append-only history**: content fields are immutable after creation; correct mistakes in new snaps, don't rewrite old ones
187
- - **Relationships only grow**: no detach — deprecate a decision instead
188
- - **All output is JSON**: machine-readable by default
189
- - **Zero dependencies**: pure Python, stdlib only
190
-
191
- ## Storage
192
-
193
- All data lives in `.intent/` at your git repo root:
194
-
195
- ```
196
- .intent/
197
- config.json
198
- intents/
199
- intent-001.json
200
- snaps/
201
- snap-001.json
202
- decisions/
203
- decision-001.json
204
- ```
205
-
206
- `.intent/` is local semantic-workspace metadata. It should stay out of Git history and should remain ignored by `.gitignore`.
207
-
208
- ## Docs
209
-
210
- - [Vision](docs/EN/vision.md) — why semantic history matters. **If this project interests you, start here.**
211
- - [CLI Design](docs/EN/cli.md) — object model, commands, JSON contract
212
- - [Roadmap](docs/EN/roadmap.md) — phase plan
213
- - [IntHub MVP](docs/EN/inthub-mvp.md) — first remote collaboration-layer scope
214
- - [IntHub Sync Contract](docs/EN/inthub-sync-contract.md) — first sync, identity, and API contract
215
-
216
- ## License
217
-
218
- MIT
@@ -1,19 +0,0 @@
1
- intent_cli/__init__.py,sha256=_19_tcL7KLgyD54NiZ-8tcAMCDKyqu_9k5K2-4Lc8ng,1058
2
- intent_cli/__main__.py,sha256=pWAakBzI50WtmbfDBrUudHbZYBPi8VJ_PlPbRRFGCP4,40
3
- intent_cli/cli.py,sha256=WUDi9hNUqOin2ETkSJOrrdaGGUN66gzZe5HyhPMmCM0,5245
4
- intent_cli/output.py,sha256=_ZmivWZfY_AOcGRCWFMx09lUGB-Ku0g5xyjrqL6UWKg,680
5
- intent_cli/store.py,sha256=AOVXbxA1OJ7gIxlvYSNzsXylJLyx_57TEmn0e9kRQN4,10119
6
- intent_cli/commands/__init__.py,sha256=XtBJys-Al_64_nLnWGXoOFR2pnYLmqbzanB5Nuy1BUM,43
7
- intent_cli/commands/common.py,sha256=P2MLcBf3S16Z_iuhxvQqAFV2ioAbvpgQd6XHj3ppROA,1193
8
- intent_cli/commands/core.py,sha256=X7G9ta0vfhneAN8DffW8zS0mNHhb7Zmauklu_sh7WG0,11874
9
- intent_cli/commands/hub.py,sha256=Hk_ZZB9LFPeXlE0Wv6yDzMLZr530ORvEAj9BZQL9VWo,3280
10
- intent_cli/hub/__init__.py,sha256=ujLxjGwZo_JCKhCXSlx1lwtJYDWd0y5tAoo57F1VaKM,46
11
- intent_cli/hub/client.py,sha256=QB6fsPZdAQD7iSnhCmbkBwPlEZVwl7QrYXE4E0S88to,1811
12
- intent_cli/hub/payload.py,sha256=fjweASqpHIu54HvoQDQd-ibJ0UJx6zpzjIsllVhWmrs,1930
13
- intent_cli/hub/runtime.py,sha256=U2U9uipBjB0MIeA10i-T1lJQariYv99iSovcgl6ca0M,1043
14
- intent_cli_python-2.0.0.dist-info/licenses/LICENSE,sha256=XWjTStLaoDw-UgLwMecejVxeaHH8JibnSFYARGzRc6I,1068
15
- intent_cli_python-2.0.0.dist-info/METADATA,sha256=2cSVuK778T_ayTowh9siWhEsjviMhb2ubOOOxzgf7b4,8134
16
- intent_cli_python-2.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
17
- intent_cli_python-2.0.0.dist-info/entry_points.txt,sha256=Y1kziqgaGUgTHg3CCfc2Su1XoDvIMJ2vi-dPEDZfuTo,44
18
- intent_cli_python-2.0.0.dist-info/top_level.txt,sha256=jkyOMCXA-G6FlEj69GA4SKn3RoO1KNL9w2iit7OUpuU,11
19
- intent_cli_python-2.0.0.dist-info/RECORD,,