@tinoy/pi-canon 0.2.0 → 0.4.0

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
@@ -16,12 +16,13 @@ pi install npm:@tinoy/pi-canon
16
16
  - **Tools** — `canon_add`, `canon_remove`, `canon_edit`, `canon_category`.
17
17
  - **Commands** — `/canon` (list, add, remove, edit, category management) and `/canon-dump`.
18
18
  - **Injection** — the block is appended at `before_agent_start` and re-normalized on every provider request, so a run started by an injected message carries the same bytes as an interactive prompt.
19
+ - **Category headings** — a category sub-heading inside a scope group prints the store id after the word `category` (`#### Behavioural Preferences [category 1cg5lr]`), which is the same id a `canon_add` refusal lists; the word keeps it from reading as an entry handle, which the block renders as `[1i15c2]`. Uncategorized entries carry no id, and a scope group holding a single category stays flat with no sub-heading.
19
20
  - **Peer notices** — entry changes are broadcast over the pi-intercom bus (namespace `canon`); receivers match the entry scope against their own model and audience.
20
21
  - **Tail sections** — another extension contributes prompt text through the `canon:section` event; its ids are published on `canon:sections`.
21
22
 
22
23
  ## Exports
23
24
 
24
- `canon.ts` is the package entry, and it is also the module API:
25
+ `index.ts` is the package entry, and it is also the module API:
25
26
 
26
27
  | Export | What it is |
27
28
  | --- | --- |
@@ -31,11 +31,13 @@
31
31
  *
32
32
  * Store: ~/.pi/agent/canon/canon.json — { entries: [{id, text, model, audience, reason?, category?}], categories: [{id, title, description?}] }
33
33
  * Categories are un-ordered; entries reference them by id. They render as sub-headings
34
- * inside scope groups (store insertion order), with uncategorized entries last.
34
+ * inside scope groups (store insertion order), with uncategorized entries last, and
35
+ * each heading carries the store's own id after the word `category`
36
+ * (`#### Behavioural Preferences [category 1cg5lr]`) — the word keeps the category id
37
+ * from reading as an entry handle, which the block renders as `[1i15c2]`.
35
38
  * canon_add REQUIRES a category and canon_edit can only CHANGE one: every refusal
36
- * lists the valid ids with their titles, so a retry costs no lookup call. The
37
- * injected block renders titles only, so the refusal is also the only place an id
38
- * reaches the model. The store validator counts entries that are uncategorized or
39
+ * lists the valid ids with their titles, so a retry costs no lookup call. The store
40
+ * validator counts entries that are uncategorized or
39
41
  * carry a category id the store no longer holds (both render as Uncategorized) and
40
42
  * reports the count to the hook log once per distinct state.
41
43
  * Both the injected block and /canon-dump group entries under scope headers (model ×
@@ -339,7 +341,8 @@ function scopeGroups(entries: CanonEntry[]): ScopeGroup[] {
339
341
  );
340
342
  }
341
343
 
342
- /** Render the injected block: header, scope groups, category sub-headings inside. */
344
+ /** Render the injected block: header, scope groups, category sub-headings inside, each
345
+ * naming its store category id. */
343
346
  function render(
344
347
  store: Store,
345
348
  model: string,
@@ -367,7 +370,7 @@ function render(
367
370
  // only show category sub-headings when a scope group actually splits
368
371
  const showCatHeadings = orderedCats.length + (uncat.length ? 1 : 0) >= 2;
369
372
  for (const c of orderedCats) {
370
- if (showCatHeadings) block += `\n\n#### ${c.title}`;
373
+ if (showCatHeadings) block += `\n\n#### ${c.title} [category ${c.id}]`;
371
374
  block += `\n${buckets
372
375
  .get(c.id)!
373
376
  .map((e) => `[${e.id}] ${e.text}`)
@@ -417,11 +420,11 @@ function renderDump(
417
420
  /**
418
421
  * Resolve a category reference to its id.
419
422
  *
420
- * The injected canon block shows category TITLES as sub-headings (`#### System
421
- * Knowledge`) and never shows an id, so a model naturally passes the title back to
422
- * canon_add — refusing that costs a call and teaches nothing. An exact id wins; a
423
- * title matches case-insensitively; anything else fails with a short reason that
424
- * the caller turns into a refusal.
423
+ * The block prints each heading as its title followed by the store id
424
+ * (`#### <title> [category <id>]`), and a model that copies the heading copies the
425
+ * title most of the time, so both forms are accepted: an exact id wins, a title
426
+ * matches case-insensitively, and anything else fails with a short reason that the
427
+ * caller turns into a refusal.
425
428
  */
426
429
  function lookupCategory(
427
430
  store: Store,
@@ -447,10 +450,8 @@ function lookupCategory(
447
450
 
448
451
  /**
449
452
  * The refusal for every rejected category value: what failed, then every valid id
450
- * with its title so a retry needs no lookup call of its own, then the one
451
- * instruction that answers it. Terse by construction — it lands in a model's
452
- * context, and the listing is the only place a category id is ever shown (the
453
- * injected block renders titles).
453
+ * with its title, then the one instruction that answers it. Terse by construction —
454
+ * it lands in a model's context.
454
455
  */
455
456
  function categoryRefusal(
456
457
  store: Store,
@@ -747,7 +748,7 @@ export default function (pi: ExtensionAPI) {
747
748
  }
748
749
  }
749
750
 
750
- // ---------- intercom channel (mirrors intercom-broadcast.ts) ----------
751
+ // ---------- intercom channel (mirrors the intercom-broadcast package) ----------
751
752
 
752
753
  const registration: CanonRegistration = {
753
754
  namespace: NAMESPACE,
@@ -1243,7 +1244,7 @@ export default function (pi: ExtensionAPI) {
1243
1244
  name: "canon_category",
1244
1245
  label: "Manage canon categories",
1245
1246
  description:
1246
- "Manage canon categories: op add (title, description?) creates one; op edit (id, title?, description?) updates it; op remove (id) deletes it and detaches its entries to Uncategorized; op list shows all. Categories are un-ordered and render as sub-headings inside scope groups in store insertion order.",
1247
+ "Manage canon categories: op add (title, description?) creates one; op edit (id, title?, description?) updates it; op remove (id) deletes it and detaches its entries to Uncategorized; op list shows all. Categories are un-ordered and render as sub-headings inside scope groups in store insertion order, each heading naming the category id (`#### Tools [category u3q9pr]`).",
1247
1248
  promptSnippet: "Manage canon categories (add/edit/remove/list)",
1248
1249
  parameters: Type.Object({
1249
1250
  op: Type.String({ description: "add | edit | remove | list" }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinoy/pi-canon",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Durable system-prompt rules for pi: an on-disk canon store, its tools, and the /canon commands.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -13,18 +13,18 @@
13
13
  "url": "https://github.com/tinoy1336/pi-extensions/issues"
14
14
  },
15
15
  "type": "module",
16
- "main": "canon.ts",
16
+ "main": "index.ts",
17
17
  "keywords": [
18
18
  "pi-package"
19
19
  ],
20
20
  "files": [
21
- "canon.ts",
21
+ "index.ts",
22
22
  "README.md",
23
23
  "LICENSE"
24
24
  ],
25
25
  "pi": {
26
26
  "extensions": [
27
- "./canon.ts"
27
+ "./index.ts"
28
28
  ]
29
29
  },
30
30
  "engines": {