agent-bios 0.19.1 → 0.19.3

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.
Files changed (106) hide show
  1. package/DEPENDENCIES.md +58 -30
  2. package/INSTALL.md +4 -4
  3. package/README.md +105 -28
  4. package/claude/CLAUDE.md +1 -1
  5. package/claude/guides/claude-prompting.md +1 -1
  6. package/claude/guides/cli-multi-model-workflow.md +4 -4
  7. package/claude/guides/coding-staged-workflow.md +17 -0
  8. package/claude/guides/documentation-hygiene.md +3 -0
  9. package/claude/guides/gpt-prompting.md +1 -1
  10. package/claude/guides/korean-writing.md +153 -0
  11. package/claude/guides/learning-flow.md +4 -4
  12. package/claude/guides/llm-capability-boundary.md +7 -1
  13. package/claude/guides/session-distill-workflow.md +8 -8
  14. package/claude/guides/slide-writing/RUNBOOK.md +5 -5
  15. package/claude/guides/tooling-gotchas.md +20 -1
  16. package/claude/guides/ui-design/visual-direction.md +88 -0
  17. package/claude/guides/ui-design.md +90 -0
  18. package/claude/guides/verification-discipline.md +10 -1
  19. package/claude/hooks/tooling-gotchas-hook.py +41 -0
  20. package/claude/skills/repo-charter/SKILL.md +3 -3
  21. package/claude/skills/understand/SKILL.md +5 -5
  22. package/codex/AGENTS.md +1 -1
  23. package/codex/guides/claude-prompting.md +1 -1
  24. package/codex/guides/cli-multi-model-workflow.md +4 -4
  25. package/codex/guides/coding-staged-workflow.md +17 -0
  26. package/codex/guides/documentation-hygiene.md +3 -0
  27. package/codex/guides/gpt-prompting.md +1 -1
  28. package/codex/guides/korean-writing.md +153 -0
  29. package/codex/guides/learning-flow.md +4 -4
  30. package/codex/guides/llm-capability-boundary.md +7 -1
  31. package/codex/guides/session-distill-workflow.md +8 -8
  32. package/codex/guides/slide-writing/RUNBOOK.md +5 -5
  33. package/codex/guides/tooling-gotchas.md +20 -1
  34. package/codex/guides/ui-design/visual-direction.md +88 -0
  35. package/codex/guides/ui-design.md +90 -0
  36. package/codex/guides/verification-discipline.md +10 -1
  37. package/compose/app_bridge/SKILL.md +12 -12
  38. package/compose/app_bridge/scripts/bridge.py +35 -10
  39. package/compose/app_desktop/server.py +250 -0
  40. package/compose/assemble.py +5 -5
  41. package/compose/bootstrap/SKILL.md +18 -18
  42. package/compose/canary.sh +4 -4
  43. package/compose/check-domains.py +6 -6
  44. package/compose/corpus-state.py +16 -1168
  45. package/compose/corpus.py +13 -402
  46. package/compose/corpus_app.py +14 -450
  47. package/compose/corpus_catalog.py +15 -926
  48. package/compose/corpus_import.py +14 -523
  49. package/compose/corpus_install.py +14 -1847
  50. package/compose/corpus_session.py +16 -848
  51. package/compose/corpus_setup.py +16 -672
  52. package/compose/corpus_setup_cli.py +15 -580
  53. package/compose/corpus_setup_i18n.py +20 -324
  54. package/compose/corpus_setup_ui.py +18 -645
  55. package/compose/corpus_store.py +16 -1664
  56. package/compose/corpus_transaction.py +15 -284
  57. package/compose/corpus_ui.py +17 -972
  58. package/compose/corpus_ui_runtime.py +16 -274
  59. package/compose/corpus_understand.py +13 -671
  60. package/compose/domains.json +3 -1
  61. package/compose/host_platform.py +121 -0
  62. package/compose/instructions-state.py +1178 -0
  63. package/compose/instructions.py +409 -0
  64. package/compose/instructions_app.py +697 -0
  65. package/compose/instructions_catalog.py +931 -0
  66. package/compose/instructions_import.py +537 -0
  67. package/compose/instructions_install.py +1932 -0
  68. package/compose/instructions_session.py +852 -0
  69. package/compose/instructions_setup.py +713 -0
  70. package/compose/instructions_setup_cli.py +607 -0
  71. package/compose/instructions_setup_i18n.py +327 -0
  72. package/compose/instructions_setup_ui.py +647 -0
  73. package/compose/instructions_store.py +1668 -0
  74. package/compose/instructions_transaction.py +308 -0
  75. package/compose/instructions_ui.py +975 -0
  76. package/compose/instructions_ui_runtime.py +279 -0
  77. package/compose/instructions_understand.py +678 -0
  78. package/compose/native_cli.py +52 -0
  79. package/compose/register-hooks.py +1 -1
  80. package/compose/runtime_entry.py +58 -0
  81. package/compose/setup/START.md +11 -11
  82. package/compose/windows_deploy.py +719 -0
  83. package/docs/advanced-launch.md +11 -11
  84. package/docs/instructions-compatibility.md +86 -0
  85. package/docs/{corpus.md → instructions.md} +36 -8
  86. package/docs/recovery.md +10 -10
  87. package/docs/releases/0.19.2.md +38 -0
  88. package/docs/releases/0.19.3.md +107 -0
  89. package/docs/session-model.md +31 -20
  90. package/docs/setup.md +63 -26
  91. package/docs/understand.md +6 -6
  92. package/docs/windows.md +99 -0
  93. package/install.sh +71 -69
  94. package/launch/agent-launch.py +309 -293
  95. package/launch/agent-launch.toml +2 -2
  96. package/launch/agent-launch.zsh +11 -1
  97. package/launch/i18n/en.toml +55 -55
  98. package/launch/i18n/ja.toml +56 -56
  99. package/launch/i18n/ko.toml +56 -56
  100. package/launch/shell_integration.py +4 -4
  101. package/learn/collect-learning.py +10 -10
  102. package/learn/learning.schema.json +1 -1
  103. package/learn/migrate-learnings.py +51 -51
  104. package/package.json +33 -12
  105. package/provenance.json +1 -1
  106. /package/docs/assets/{corpus-studio.svg → instructions-studio.svg} +0 -0
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env python3
2
+ """Instructions Studio and machine CLI over the private, revision-checked instructions store.
3
+
4
+ The command is deliberately a client. It renders records and translates user
5
+ input into semantic plan payloads; ``InstructionsStore`` remains the only writer and
6
+ owns ids, revisions, paths, serialization, validation, and publication.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import difflib
12
+ import importlib
13
+ import json
14
+ import os
15
+ from pathlib import Path
16
+ import shlex
17
+ import subprocess
18
+ import sys
19
+ import tempfile
20
+ from typing import Any, Sequence
21
+
22
+ try:
23
+ from instructions_store import InstructionsStore, InstructionsStoreError
24
+ except ImportError: # package-style import in tests
25
+ from .instructions_store import InstructionsStore, InstructionsStoreError
26
+
27
+
28
+ SURFACES = ("always", "relevant", "requested", "event", "delegated")
29
+
30
+
31
+ def default_repo() -> Path:
32
+ return Path(os.environ.get("AGENT_BIOS_REPO", Path(__file__).resolve().parents[1]))
33
+
34
+
35
+ def venv_python() -> Path | None:
36
+ roots: list[Path] = []
37
+ if override := os.environ.get("AGENT_LAUNCH_VENV"):
38
+ roots.append(Path(override).expanduser())
39
+ roots.append(Path.home() / ".local/share/agent-launch/venv")
40
+ for root in roots:
41
+ interpreter = root / "bin" / "python"
42
+ if interpreter.is_file() and os.access(interpreter, os.X_OK):
43
+ return interpreter
44
+ return None
45
+
46
+
47
+ def build_parser() -> argparse.ArgumentParser:
48
+ parser = argparse.ArgumentParser(
49
+ prog="agent-bios instructions",
50
+ description="Inspect and change the private instructions used by future activated sessions.",
51
+ )
52
+ parser.add_argument("--repo", type=Path, default=default_repo())
53
+ parser.add_argument("--state-dir", type=Path)
54
+ parser.add_argument("--user-dir", type=Path)
55
+ parser.add_argument("--json", action="store_true", dest="as_json")
56
+ sub = parser.add_subparsers(dest="command")
57
+
58
+ list_cmd = sub.add_parser("list", help="list instructions items")
59
+ list_cmd.add_argument("--active-only", action="store_true")
60
+
61
+ search = sub.add_parser("search", help="search item text and metadata")
62
+ search.add_argument("query")
63
+ search.add_argument("--active-only", action="store_true")
64
+
65
+ show = sub.add_parser("show", help="show one instructions item")
66
+ show.add_argument("ref")
67
+ show.add_argument(
68
+ "--view", default="effective",
69
+ choices=("effective", "installed", "change", "diff", "history"),
70
+ )
71
+
72
+ plan = sub.add_parser("plan", help="validate a semantic change without publishing it")
73
+ plan.add_argument("--input", default="-", metavar="FILE")
74
+
75
+ apply_cmd = sub.add_parser("apply", help="publish a previously validated plan")
76
+ apply_cmd.add_argument("plan")
77
+ apply_cmd.add_argument("--expected-revision")
78
+
79
+ history = sub.add_parser("history", help="list recoverable authoring revisions")
80
+ history.add_argument("ref", nargs="?")
81
+
82
+ sub.add_parser("status", help="show private store state")
83
+
84
+ snapshot = sub.add_parser("snapshot", help="compose current authoring or inspect an immutable snapshot")
85
+ snapshot_view = snapshot.add_mutually_exclusive_group(required=True)
86
+ snapshot_view.add_argument("--host", choices=("claude", "codex"))
87
+ snapshot_view.add_argument("--content-ref", help="read this stored snapshot without resolving current authoring")
88
+ snapshot.add_argument("--native", action="store_true", help="opt into selected native hooks on either host and Claude agents for this snapshot")
89
+ snapshot.add_argument("--selection-mode", choices=("default", "selected", "none"))
90
+ snapshot.add_argument("--select", action="append", help="one-session package/domain/item selection")
91
+ snapshot.add_argument("--cwd", type=Path, help="project scope for this snapshot")
92
+
93
+ install = sub.add_parser("install", help="record the current package as a private baseline")
94
+ install.add_argument("--domains", help="comma-separated qualified selection values")
95
+ return parser
96
+
97
+
98
+ def _load_json(source: str) -> dict[str, Any]:
99
+ try:
100
+ text = sys.stdin.read() if source == "-" else Path(source).read_text(encoding="utf-8")
101
+ payload = json.loads(text)
102
+ except (OSError, ValueError) as exc:
103
+ raise InstructionsStoreError(f"cannot read plan input {source!r}: {exc}") from exc
104
+ if not isinstance(payload, dict):
105
+ raise InstructionsStoreError("plan input must be one JSON object")
106
+ return payload
107
+
108
+
109
+ def _matches(item: dict[str, Any], query: str) -> bool:
110
+ needle = query.casefold()
111
+ values: list[str] = []
112
+ for key in ("ref", "title", "body", "surface", "tier", "kind", "state", "package_id"):
113
+ value = item.get(key)
114
+ if isinstance(value, str):
115
+ values.append(value)
116
+ values.extend(str(value) for value in item.get("domains", []) if isinstance(value, str))
117
+ return needle in "\n".join(values).casefold()
118
+
119
+
120
+ def search_items(store: InstructionsStore, query: str, *, include_removed: bool = True) -> list[dict[str, Any]]:
121
+ return [item for item in store.list_items(include_removed=include_removed) if _matches(item, query)]
122
+
123
+
124
+ def _human_item(item: dict[str, Any]) -> str:
125
+ state = item.get("state", "active")
126
+ return f"{item.get('ref', '?')}\t{state}\t{item.get('surface', '?')}\t{item.get('title', '')}"
127
+
128
+
129
+ def _human_show(result: dict[str, Any]) -> str:
130
+ item = result.get("item")
131
+ if isinstance(item, dict):
132
+ metadata = (
133
+ f"`{item.get('ref', result.get('ref', ''))}` · {result.get('state', 'active')} · "
134
+ f"{item.get('surface', '?')} · {item.get('kind', '?')}"
135
+ )
136
+ return f"# {item.get('title', item.get('ref', 'Instructions item'))}\n\n{metadata}\n\n{item.get('body', '')}"
137
+ return json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True)
138
+
139
+
140
+ def emit(value: Any, *, as_json: bool, command: str) -> None:
141
+ if as_json:
142
+ print(json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True))
143
+ return
144
+ if command in {"list", "search"} and isinstance(value, list):
145
+ for item in value:
146
+ print(_human_item(item))
147
+ elif command == "show" and isinstance(value, dict):
148
+ print(_human_show(value))
149
+ elif command == "history" and isinstance(value, list):
150
+ for row in value:
151
+ print(f"{row.get('history_id', '?')}\t{row.get('revision', '?')}\t{row.get('path', '')}")
152
+ elif command == "status" and isinstance(value, dict):
153
+ for key, current in value.items():
154
+ print(f"{key}: {json.dumps(current, ensure_ascii=False)}")
155
+ elif command == "snapshot" and isinstance(value, dict):
156
+ print(f"content_ref: {value.get('content_ref')}\npath: {value.get('path')}\nrevision: {value.get('revision')}")
157
+ if value.get("unavailable"):
158
+ print("activation unverified or unavailable:")
159
+ for row in value["unavailable"]:
160
+ print(f"- {row.get('ref')}: {row.get('reason')}")
161
+ else:
162
+ print(json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True))
163
+
164
+
165
+ def run_command(args: argparse.Namespace, store: InstructionsStore) -> Any:
166
+ command = args.command
167
+ if command == "list":
168
+ return store.list_items(include_removed=not args.active_only)
169
+ if command == "search":
170
+ return search_items(store, args.query, include_removed=not args.active_only)
171
+ if command == "show":
172
+ return store.show(args.ref, view=args.view)
173
+ if command == "plan":
174
+ return store.plan(_load_json(args.input))
175
+ if command == "apply":
176
+ return store.apply(args.plan, expected_revision=args.expected_revision)
177
+ if command == "history":
178
+ return store.history(args.ref)
179
+ if command == "status":
180
+ return store.status()
181
+ if command == "snapshot":
182
+ if args.content_ref:
183
+ if args.native or args.selection_mode or args.select or args.cwd:
184
+ raise InstructionsStoreError("a stored snapshot is immutable; selection and scope options require --host")
185
+ return store.snapshot_inventory(args.content_ref)
186
+ return store.snapshot(args.host, selection=args.select, native=args.native,
187
+ selection_mode=args.selection_mode or ("selected" if args.select is not None else None),
188
+ cwd=args.cwd)
189
+ if command == "install":
190
+ domains = None
191
+ if args.domains is not None:
192
+ domains = [part.strip() for part in args.domains.split(",") if part.strip()]
193
+ if domains == ["none"]:
194
+ domains = []
195
+ return store.install(domains=domains)
196
+ raise InstructionsStoreError(f"unknown instructions command: {command}")
197
+
198
+
199
+ def _draft_text(initial: str = "") -> str:
200
+ editor = os.environ.get("VISUAL") or os.environ.get("EDITOR")
201
+ if editor:
202
+ with tempfile.TemporaryDirectory(prefix="agent-bios-instructions-draft-") as directory:
203
+ path = Path(directory) / "draft.md"
204
+ path.write_text(initial, encoding="utf-8")
205
+ try:
206
+ completed = subprocess.run([*shlex.split(editor), str(path)], check=False)
207
+ except OSError as exc:
208
+ raise InstructionsStoreError(f"cannot run editor: {exc}") from exc
209
+ if completed.returncode != 0:
210
+ raise InstructionsStoreError(f"editor exited with status {completed.returncode}")
211
+ return path.read_text(encoding="utf-8")
212
+ print("Enter Markdown. Finish with a line containing only '.'")
213
+ if initial:
214
+ print("Current body follows; enter a complete replacement.")
215
+ print(initial)
216
+ lines: list[str] = []
217
+ while True:
218
+ line = input()
219
+ if line == ".":
220
+ return "\n".join(lines) + ("\n" if lines else "")
221
+ lines.append(line)
222
+
223
+
224
+ def _choose_surface(current: str = "requested") -> str:
225
+ print("Consumption surface:")
226
+ for index, surface in enumerate(SURFACES, 1):
227
+ marker = " *" if surface == current else ""
228
+ print(f" {index}. {surface}{marker}")
229
+ raw = input(f"Select [default {SURFACES.index(current) + 1}]: ").strip()
230
+ if not raw:
231
+ return current
232
+ try:
233
+ return SURFACES[int(raw) - 1]
234
+ except (ValueError, IndexError) as exc:
235
+ raise InstructionsStoreError("invalid surface selection") from exc
236
+
237
+
238
+ def _preview_and_apply(store: InstructionsStore, payload: dict[str, Any], preview: str) -> None:
239
+ plan = store.plan(payload)
240
+ print(preview)
241
+ print(json.dumps(plan, ensure_ascii=False, indent=2, sort_keys=True))
242
+ if input("Apply this plan? [y/N]: ").strip().casefold() not in {"y", "yes"}:
243
+ print("Plan was not applied.")
244
+ return
245
+ applied = store.apply(plan["plan_id"], expected_revision=plan["expected_revision"])
246
+ print(json.dumps(applied, ensure_ascii=False, indent=2, sort_keys=True))
247
+ print("Future activated sessions use the new authoring state; this running session is unchanged.")
248
+
249
+
250
+ def run_numbered(store: InstructionsStore) -> int:
251
+ """Useful fallback when Textual is unavailable; canonical writes still use the store."""
252
+ while True:
253
+ items = store.list_items(include_removed=True)
254
+ print("\nInstructions Studio (numbered fallback)")
255
+ for index, item in enumerate(items, 1):
256
+ print(f" {index}. {_human_item(item)}")
257
+ print(" c. create e. edit r. remove s. restore v. recover x. reset /. search q. quit")
258
+ choice = input("Choice: ").strip()
259
+ if choice.casefold() == "q":
260
+ return 0
261
+ if choice == "/":
262
+ query = input("Search: ")
263
+ for item in search_items(store, query):
264
+ print(_human_item(item))
265
+ continue
266
+ if choice.casefold() == "c":
267
+ title = input("Title: ").strip()
268
+ body = _draft_text()
269
+ surface = _choose_surface()
270
+ item = {"title": title, "body": body, "surface": surface,
271
+ "tier": "env-personal", "domains": ["personal"], "kind": "rule",
272
+ "members": {"content.md": body}}
273
+ _preview_and_apply(store, {"operation": "create", "item": item}, f"Create {title!r} on {surface}.")
274
+ continue
275
+ if choice.casefold() == "x":
276
+ _preview_and_apply(store, {"operation": "reset"}, "Reset future instructions authoring to the last successful install tuple.")
277
+ continue
278
+ if choice.casefold() in {"e", "r", "s", "v"}:
279
+ ref = input("InstructionsRef: ").strip()
280
+ row = next((item for item in items if item.get("ref") == ref), None)
281
+ if row is None:
282
+ print("Unknown InstructionsRef.", file=sys.stderr)
283
+ continue
284
+ if choice.casefold() == "e":
285
+ body = _draft_text(str(row.get("body", "")))
286
+ surface = _choose_surface(str(row.get("surface", "requested")))
287
+ patch = {"body": body, "surface": surface}
288
+ if row.get("kind") == "hook":
289
+ try:
290
+ from instructions_catalog import HOOK_EVENTS
291
+ except ImportError: # package-style import from repository root
292
+ from .instructions_catalog import HOOK_EVENTS
293
+ binding = row.get("hook") if isinstance(row.get("hook"), dict) else {}
294
+ default_event = str(binding.get("event", sorted(HOOK_EVENTS)[0]))
295
+ while True:
296
+ event = input(f"Hook event [{default_event}]: ").strip() or default_event
297
+ if event in HOOK_EVENTS:
298
+ break
299
+ print("Unsupported hook event.", file=sys.stderr)
300
+ default_matcher = str(binding.get("matcher", ""))
301
+ matcher = input(f"Hook matcher [{default_matcher}]: ")
302
+ matcher = matcher if matcher else default_matcher
303
+ if not matcher or "\n" in matcher or "\r" in matcher:
304
+ print("Hook matcher must be one non-empty line.", file=sys.stderr)
305
+ continue
306
+ patch["hook"] = {"event": event, "matcher": matcher}
307
+ diff = "".join(difflib.unified_diff(
308
+ str(row.get("body", "")).splitlines(True), body.splitlines(True),
309
+ fromfile="current", tofile="planned",
310
+ )) or "(metadata-only change)"
311
+ hook_preview = (f"\nHook binding: {patch['hook']['event']} / {patch['hook']['matcher']}"
312
+ if "hook" in patch else "")
313
+ _preview_and_apply(store, {"operation": "update", "ref": ref,
314
+ "item_digest": row["digest"], "patch": patch}, diff + hook_preview)
315
+ elif choice.casefold() == "r":
316
+ _preview_and_apply(store, {"operation": "remove", "ref": ref,
317
+ "item_digest": row.get("digest")}, f"Remove {ref} from future snapshots.")
318
+ elif choice.casefold() == "s":
319
+ _preview_and_apply(store, {"operation": "restore", "ref": ref},
320
+ f"Restore {ref} from the selected installed baseline.")
321
+ else:
322
+ _preview_and_apply(store, {"operation": "recover", "ref": ref},
323
+ f"Recover personal item {ref} for future snapshots.")
324
+ continue
325
+ try:
326
+ index = int(choice) - 1
327
+ print(_human_show(store.show(items[index]["ref"])))
328
+ except (ValueError, IndexError):
329
+ print("Invalid choice.", file=sys.stderr)
330
+
331
+
332
+ def _run_tui_or_fallback(args: argparse.Namespace, store: InstructionsStore) -> int:
333
+ module_name = f"{__package__}.instructions_ui" if __package__ else "instructions_ui"
334
+ try:
335
+ module = importlib.import_module(module_name)
336
+ except ImportError as exc:
337
+ missing_ui_dependency = any(exc.name == name or (exc.name or "").startswith(name + ".")
338
+ for name in ("textual", "rich"))
339
+ if not missing_ui_dependency and exc.name not in {"instructions_ui", module_name}:
340
+ raise
341
+ interpreter = venv_python()
342
+ if (
343
+ missing_ui_dependency
344
+ and interpreter is not None
345
+ and Path(sys.executable).absolute() != interpreter.absolute()
346
+ and os.environ.get("AGENT_BIOS_INSTRUCTIONS_TUI_REEXEC") != "1"
347
+ ):
348
+ env = os.environ.copy()
349
+ env["AGENT_BIOS_INSTRUCTIONS_TUI_REEXEC"] = "1"
350
+ try:
351
+ os.execve(
352
+ str(interpreter),
353
+ [str(interpreter), str(Path(__file__).resolve()), *sys.argv[1:]],
354
+ env,
355
+ )
356
+ except OSError as reexec_error:
357
+ print(
358
+ f"agent-bios instructions: managed Textual runtime is unusable ({reexec_error}); "
359
+ "using numbered fallback.",
360
+ file=sys.stderr,
361
+ )
362
+ return run_numbered(store)
363
+ app = module.InstructionsStudio(store)
364
+ app.run()
365
+ return 0
366
+
367
+
368
+ def _run_bundled_tui(store: InstructionsStore) -> int:
369
+ try:
370
+ from instructions_ui_runtime import activate_ui_runtime
371
+ activate_ui_runtime(Path(__file__).resolve().parents[1])
372
+ module_name = f"{__package__}.instructions_ui" if __package__ else "instructions_ui"
373
+ module = importlib.import_module(module_name)
374
+ except (ImportError, OSError, RuntimeError) as exc:
375
+ raise InstructionsStoreError(f"bundled terminal UI could not start: {exc}") from exc
376
+ app = module.InstructionsStudio(store)
377
+ app.run()
378
+ return 0
379
+
380
+
381
+ def main(argv: Sequence[str] | None = None, *, bundled_ui: bool = False) -> int:
382
+ parser = build_parser()
383
+ raw = list(sys.argv[1:] if argv is None else argv)
384
+ # Machine callers naturally put --json after the verb. argparse only accepts
385
+ # a parent option before a subparser, so normalize this one order-independent
386
+ # presentation flag without changing any semantic argument.
387
+ json_requested = "--json" in raw
388
+ raw = [value for value in raw if value != "--json"]
389
+ args = parser.parse_args(raw)
390
+ args.as_json = args.as_json or json_requested
391
+ store = InstructionsStore(args.repo, state_root=args.state_dir, user_root=args.user_dir)
392
+ try:
393
+ if args.command is None:
394
+ if sys.stdin.isatty() and sys.stdout.isatty():
395
+ if bundled_ui:
396
+ return _run_bundled_tui(store)
397
+ return _run_tui_or_fallback(args, store)
398
+ args.command = "list"
399
+ args.active_only = False
400
+ result = run_command(args, store)
401
+ emit(result, as_json=args.as_json, command=args.command)
402
+ return 0
403
+ except InstructionsStoreError as exc:
404
+ print(f"agent-bios instructions: {exc}", file=sys.stderr)
405
+ return 2
406
+
407
+
408
+ if __name__ == "__main__":
409
+ raise SystemExit(main(bundled_ui=True))