agent-bios 0.17.0 → 0.17.1

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.
package/README.md CHANGED
@@ -283,6 +283,14 @@ arbitrary package authoring/import, automatic semantic conflict resolution, or v
283
283
  native skill-menu registration; those design stages must not be inferred from the
284
284
  library UI.
285
285
 
286
+ Studio marks rules containing explicit `guides/*.md` references with **→ GUIDE**
287
+ and the guide names in the library. Their **Guide pointer** section links to exact
288
+ guide members in the same package and shows each target's current consumption surface
289
+ and state. Missing or ambiguous targets are disclosed rather than guessed. These
290
+ are display-only references, not new dependencies or proof of loading: rule bodies,
291
+ IDs, ordering, selection, and delivery remain unchanged. Personal body edits are
292
+ reflected when the library refreshes; ordinary rules are not classified by meaning.
293
+
286
294
  New personal identities are allocated once in the creation plan and remain stable
287
295
  on retry; reset and history rollback do not make retired identities reusable.
288
296
  Member files are the content authority: `primary_member` identifies the main file,
@@ -11,7 +11,9 @@ import json
11
11
  import re
12
12
  from pathlib import PurePosixPath
13
13
  from typing import Any
14
- from urllib.parse import unquote
14
+ from urllib.parse import quote, unquote
15
+
16
+ from rich.text import Text
15
17
 
16
18
  from textual.app import App, ComposeResult
17
19
  from textual.binding import Binding
@@ -41,6 +43,34 @@ SURFACES = ("always", "relevant", "requested", "event", "delegated")
41
43
  VIEWS = ("effective", "installed", "change", "diff", "history")
42
44
 
43
45
 
46
+ def guide_pointers(item: dict[str, Any], inventory: list[dict[str, Any]]) -> list[dict[str, Any]]:
47
+ """Display literal guide references, not inferred dependencies or new routing."""
48
+ if item.get("kind") != "rule":
49
+ return []
50
+ paths = dict.fromkeys(re.findall(r"\bguides/[A-Za-z0-9][A-Za-z0-9_./-]*\.md\b", item.get("body", "")))
51
+ result = []
52
+ for path in paths:
53
+ if ".." in PurePosixPath(path).parts:
54
+ continue
55
+ matches = [candidate for candidate in inventory
56
+ if candidate.get("package_id") == item.get("package_id")
57
+ and candidate.get("kind") == "guide" and path in candidate.get("members", {})]
58
+ result.append({"path": path, "target": matches[0] if len(matches) == 1 else None,
59
+ "problem": "Ambiguous guide reference" if matches else "Not found in this package"})
60
+ return result
61
+
62
+
63
+ def library_label(item: dict[str, Any], inventory: list[dict[str, Any]]) -> Text:
64
+ pointers = guide_pointers(item, inventory)
65
+ label = Text()
66
+ if pointers:
67
+ label.append("→ GUIDE ", style="bold cyan")
68
+ label.append(", ".join(PurePosixPath(pointer["path"]).stem for pointer in pointers))
69
+ label.append(" · ")
70
+ label.append(f"{item.get('title', item.get('ref'))} · {item.get('surface', '?')}")
71
+ return label
72
+
73
+
44
74
  def render_member_body(body: str, member: str | None, kind: str | None = None) -> str:
45
75
  """Render prose as Markdown and preserve source files as literal code."""
46
76
  suffix = PurePosixPath(member).suffix.lower() if member else ""
@@ -83,6 +113,9 @@ class CorpusMarkdownViewer(MarkdownViewer):
83
113
  """Keep all links inside Studio; never hand a URL to the operating system."""
84
114
 
85
115
  async def _on_markdown_link_clicked(self, message: Markdown.LinkClicked) -> None:
116
+ # stop() prevents bubbling, not the inherited MarkdownViewer handler.
117
+ # Its default go() would try to load corpus:// as a filesystem path.
118
+ message.prevent_default()
86
119
  message.stop()
87
120
  if message.href.startswith("corpus://"):
88
121
  ref = unquote(message.href.removeprefix("corpus://"))
@@ -252,7 +285,7 @@ class CorpusStudio(App[None]):
252
285
  groups[key] = state_nodes[state].add(package)
253
286
  groups[key].expand()
254
287
  groups[key].add_leaf(
255
- f"{item.get('title', item.get('ref'))} · {item.get('surface', '?')}",
288
+ library_label(item, self.items),
256
289
  data=item.get("ref"),
257
290
  )
258
291
  tree.root.expand()
@@ -327,8 +360,7 @@ class CorpusStudio(App[None]):
327
360
  members[self.current_member] = self.query_one("#editor-body", TextArea).text
328
361
  return members
329
362
 
330
- @staticmethod
331
- def _render_item(row: dict[str, Any], result: dict[str, Any], view: str) -> str:
363
+ def _render_item(self, row: dict[str, Any], result: dict[str, Any], view: str) -> str:
332
364
  if view == "effective" and isinstance(result.get("item"), dict):
333
365
  item = result["item"]
334
366
  links = "\n".join(
@@ -344,11 +376,28 @@ class CorpusStudio(App[None]):
344
376
  + (f" Available members: {members}." if members else "") + "\n"
345
377
  )
346
378
  body = render_member_body(item.get("body", ""), item.get("primary_member"), item.get("kind"))
379
+ pointers = guide_pointers(item, self.items)
380
+ guide_links = ""
381
+ if pointers:
382
+ lines = []
383
+ for pointer in pointers:
384
+ target = pointer["target"]
385
+ if target is None:
386
+ lines.append(f"- `{pointer['path']}` — {pointer['problem']}")
387
+ else:
388
+ destination = quote(target["ref"], safe="@/:")
389
+ lines.append(f"- [{pointer['path']}](corpus://{destination}) — "
390
+ f"**{target['surface']}** · {target.get('state', 'active')}")
391
+ guide_links = (
392
+ "## Guide pointer\n\nThis rule explicitly references the following guide(s). "
393
+ "The rule keeps its own consumption surface; these links do not change "
394
+ "delivery or prove that a guide was loaded.\n\n" + "\n".join(lines) + "\n\n## Rule\n\n"
395
+ )
347
396
  return (
348
397
  f"# {item.get('title', item.get('ref'))}\n\n"
349
398
  f"`{item.get('ref')}` · **{row.get('state', 'active')}** · "
350
399
  f"{item.get('surface')} · {item.get('kind')}\n\n"
351
- f"{reconciliation}\n{body}\n\n## Dependencies\n\n{links}\n"
400
+ f"{guide_links}{reconciliation}\n{body}\n\n## Dependencies\n\n{links}\n"
352
401
  + ("\n## Native consumption\n\nRequires explicit `agent-launch --corpus-native`.\n"
353
402
  if item.get("kind") == "hook" else "")
354
403
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bios",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "releaseDate": "2026-09-11",
5
5
  "description": "A thin, low-level instruction layer for LLM CLI agents: one set of principles and behavior whichever model you run. Stores a private, editable corpus for explicitly activated sessions.",
6
6
  "bin": {
package/provenance.json CHANGED
@@ -1 +1 @@
1
- {"commit":"7b616f4ae5366b1395560b3a88556287ac36122d","committedAt":"2026-09-11T15:52:49+09:00","dirty":false}
1
+ {"commit":"ef9c746993ca2908f76ea41e2bfb7e097a18047f","committedAt":"2026-09-11T16:37:10+09:00","dirty":false}