@lambdacurry/arbor 0.9.0 → 0.10.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.
Files changed (2) hide show
  1. package/dist/arbor.js +45 -17
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -30,15 +30,6 @@ var CONTRIBUTION_TYPES = [
30
30
  "correction",
31
31
  "decision"
32
32
  ];
33
- var CONTRIBUTION_LINK_RELS = [
34
- "fulfills",
35
- "cites",
36
- "contests",
37
- "inReplyTo",
38
- "produces",
39
- "tracked-as",
40
- "delivered-by"
41
- ];
42
33
  var STAMP_FACETS = ["quality", "impact", "fit", "originality"];
43
34
  // ../../node_modules/.pnpm/drizzle-orm@0.38.4_@cloudflare+workers-types@4.20260529.1_@prisma+client@5.22.0_@types+_885285c61ee4b1c788385eea24f801ee/node_modules/drizzle-orm/entity.js
44
35
  var entityKind = Symbol.for("drizzle:entityKind");
@@ -1776,7 +1767,8 @@ var INDEX_ON = new Set([
1776
1767
  "artifact.edited",
1777
1768
  "review.added",
1778
1769
  "thread.created",
1779
- "topic.created"
1770
+ "topic.created",
1771
+ "topic.edited"
1780
1772
  ]);
1781
1773
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
1782
1774
  var exports_external = {};
@@ -16055,6 +16047,14 @@ function date4(params) {
16055
16047
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
16056
16048
  config(en_default());
16057
16049
  // ../actions/src/index.ts
16050
+ var CONTRIBUTION_ADD_LINK_RELS = [
16051
+ "cites",
16052
+ "contests",
16053
+ "inReplyTo",
16054
+ "produces",
16055
+ "tracked-as",
16056
+ "delivered-by"
16057
+ ];
16058
16058
  var ORIENTATION = `Arbor is your team's deliberation room and shared memory — people and agents settle typed work here, and Arbor remembers what's decided. To work well:
16059
16059
 
16060
16060
  1. RECALL FIRST — but Arbor is MEMORY, NOT TRUTH. Run \`recall\` before re-deriving or restating anything — it may already be settled; cite prior work ([label](#con_…)) and build on it. Phrase the query in PROBLEM-LANGUAGE (a natural-language question — "how do agents handle X"), not extracted keywords; it ranks better. Empty recall is itself worth noting. A contribution records what was true when it was WRITTEN, so before you assert the CURRENT state of anything outside Arbor — a PR, a build, a deploy, a config — check the live source; another contribution is not evidence of the present, and neither is a local copy of something whose home is elsewhere. And when you DO check, post the RECEIPT with the claim (AD-209): attach the actual output/screenshot (an attached file becomes a durable artifact carrying who-captured-it and when, citable as #art_… forever) or name exactly what you checked and when ("CI run #841, green, checked just now") — a "verified" with no receipt is a claim the next reader must take on faith or re-derive. When a receipted claim has AGED and matters again, don't re-trust it and don't silently re-argue it: \`request\` a re-check ("re-run this check"), and whoever runs it answers through the request with fresh evidence. WHY: a room where everyone re-derives is just a chat log — but a room that mistakes its own memory for the world confidently reports blockers that no longer exist, and this week's failures were exactly that: stale claims re-asserted as current because nothing distinguished a receipted observation from confident prose. And orient to the ROOM the way you orient to the record: \`tree\` is the map AND the way in — it carries each room's \`lanesForYou\`, the durable contribution lanes that match what you said you do; follow one into \`space_get\`/\`topic_get\`, where the room's purpose, guidance, goals, and full lane list live. A lane is an invitation, never an obligation: what you OWE is only ever in \`inbox\`.
@@ -16104,7 +16104,7 @@ var ACTIONS = [
16104
16104
  {
16105
16105
  name: "contribute",
16106
16106
  title: "Contribute to a thread",
16107
- description: "Add your own typed contribution (proposal/critique/question/evidence/…) — ONE point per contribution, on the record. BEFORE ADDING, ask what the most additive move is — not just whether to say something (AD-205): if your reaction to a point already on the record fits in one line — agree OR disagree — STAMP it (vouch, or push back with your one-line why), don't restate it as a contribution; contribute what's genuinely NEW (a typed move — a developed critique counts — that advances THIS thread's objective); if you'd only be echoing, reviewing IS the contribution and staying out is fine. RECALL FIRST (recalling beats re-deriving), and put references IN THE BODY: URLs and inline [label](#con_…) refs become typed reference edges + smart pills automatically. Body refs are CITATIONS (AD-196) — they never change where your contribution sits in the thread, so cite freely. An artifact ref ([label](#art_…)) on its OWN LINE unfurls into a preview CARD (a peek of the doc/table); inline in a sentence it stays a compact pill (AD-204) — so put an artifact on its own line when you want the reader to SEE it. To thread your contribution UNDER a specific one (a reply), pass links: [{rel: 'inReplyTo', targetId}] — that deliberate edge is what nests it. AUTOMATED/RETRYABLE callers should pass a stable idempotencyKey for the one logical contribution; replay returns the original card without another event or fan-out.",
16107
+ description: "Add your own typed contribution (proposal/critique/question/evidence/…) — ONE point per contribution, on the record. BEFORE ADDING, ask what the most additive move is — not just whether to say something (AD-205): if your reaction to a point already on the record fits in one line — agree OR disagree — STAMP it (vouch, or push back with your one-line why), don't restate it as a contribution; contribute what's genuinely NEW (a typed move — a developed critique counts — that advances THIS thread's objective); if you'd only be echoing, reviewing IS the contribution and staying out is fine. RECALL FIRST (recalling beats re-deriving), and put references IN THE BODY: URLs and inline [label](#con_…) refs become typed reference edges + smart pills automatically. Body refs are CITATIONS (AD-196) — they never change where your contribution sits in the thread, so cite freely. An artifact ref ([label](#art_…)) on its OWN LINE unfurls into a preview CARD (a peek of the doc/table); inline in a sentence it stays a compact pill (AD-204) — so put an artifact on its own line when you want the reader to SEE it. To thread your contribution UNDER a specific one (a reply), pass links: [{rel: 'inReplyTo', targetId}] — that deliberate edge is what nests it. To respond to a request, use `respond` or `mark_as_response`; `contribute` cannot create a `fulfills` link. AUTOMATED/RETRYABLE callers should pass a stable idempotencyKey for the one logical contribution; replay returns the original card without another event or fan-out.",
16108
16108
  inputSchema: {
16109
16109
  threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
16110
16110
  type: exports_external.enum(CONTRIBUTION_TYPES).describe("the contribution type (AD-066 taxonomy)"),
@@ -16117,7 +16117,7 @@ var ACTIONS = [
16117
16117
  id: exports_external.string(),
16118
16118
  label: exports_external.string()
16119
16119
  })).optional().describe("inline @-mentions in the body; person mentions notify"),
16120
- links: exports_external.array(exports_external.object({ rel: exports_external.enum(CONTRIBUTION_LINK_RELS), targetId: exports_external.string() })).optional().describe("internal typed links: { rel, targetId } (same-space) — a DELIBERATE edge: inReplyTo/contests nests your contribution under the target (AD-196); body refs never do. External refs go IN THE BODY as URLs.")
16120
+ links: exports_external.array(exports_external.object({ rel: exports_external.enum(CONTRIBUTION_ADD_LINK_RELS), targetId: exports_external.string() })).optional().describe("internal typed links: { rel, targetId } (same-space) — a DELIBERATE edge: inReplyTo/contests nests your contribution under the target (AD-196); body refs never do. External refs go IN THE BODY as URLs.")
16121
16121
  },
16122
16122
  surfaces: ["mcp", "cli"],
16123
16123
  toolset: "loop",
@@ -16181,10 +16181,23 @@ var ACTIONS = [
16181
16181
  toolset: "structure",
16182
16182
  run: forward("topic.create")
16183
16183
  },
16184
+ {
16185
+ name: "topic_update",
16186
+ title: "Edit a topic's title or purpose",
16187
+ description: "Edit a topic's TITLE and/or PURPOSE — the facts of what this area of work IS, distinct from its soft `guidance` (set_topic_guidance) and its lanes (set_topic_lanes). Patches ONLY the fields you pass. Whoever may open a topic in this space may correct one. Reach for this when a purpose has gone stale or wrong — a moved repo, a renamed area, a stub written at creation — so `topic_get` and recall read true; guidance is for norms, never for facts that rot.",
16188
+ inputSchema: {
16189
+ topicId: exports_external.string().describe("the topic to edit, top_…"),
16190
+ title: exports_external.string().min(1).optional().describe("a new title — an area of work, not a question"),
16191
+ purpose: exports_external.string().optional().describe("a new one-line purpose — what belongs here")
16192
+ },
16193
+ surfaces: ["mcp", "cli"],
16194
+ toolset: "spaces",
16195
+ run: forward("topic.setConfig")
16196
+ },
16184
16197
  {
16185
16198
  name: "set_topic_guidance",
16186
16199
  title: "Set a topic's guidance",
16187
- description: "Set a topic's SOFT guidance — the local how-we-work norms for this topic (culture/quality, AD-063), e.g. 'recall before re-deriving', 'one typed point per contribution'. Soft norms only; hard governance (permissions, rate, join) lives at the space. You READ guidance to learn the room's habits — it's distinct from `set_ways_to_help` (what's open on a thread) and `request` (obligations). Replaces the whole list; pass [] to clear.",
16200
+ description: "Set a topic's SOFT guidance — the local how-we-work norms for this topic (culture/quality, AD-063), e.g. 'recall before re-deriving', 'one typed point per contribution'. Soft norms only; hard governance (permissions, rate, join) lives at the space, and FACTS about the area (where its code lives, what it covers) belong in the topic's purpose — `topic_update`, not here. You READ guidance to learn the room's habits — it's distinct from `set_ways_to_help` (what's open on a thread) and `request` (obligations). Replaces the whole list; pass [] to clear.",
16188
16201
  inputSchema: {
16189
16202
  topicId: exports_external.string().describe("the topic, top_…"),
16190
16203
  guidance: exports_external.array(exports_external.string()).describe("the full guidance list — replaces the existing one; [] clears it")
@@ -17550,14 +17563,29 @@ var KNOWN_MISREACHES = [
17550
17563
  { tried: "spaces", use: "tree", why: "the tree lists spaces" },
17551
17564
  { tried: "space list", use: "tree", why: "the tree lists spaces" },
17552
17565
  { tried: "topics", use: "tree --depth topics", why: "topics are a tree depth" },
17553
- { tried: "search", use: "recall <query>", why: "recall is the search surface" }
17566
+ { tried: "search", use: "recall <query>", why: "recall is the search surface" },
17567
+ { tried: "update topic", use: "topic update --topic-id top_…", why: "this command is noun-verb" },
17568
+ { tried: "update space", use: "space update --space-id spc_…", why: "this command is noun-verb" }
17554
17569
  ];
17570
+ function familyHelp(positionals) {
17571
+ if (positionals.length !== 1)
17572
+ return;
17573
+ const word = positionals[0].toLowerCase().replace(/_/g, " ");
17574
+ const members = CLI_ACTIONS.filter((a) => commandWords(a).split(" ").includes(word)).sort((a, b) => commandWords(a).localeCompare(commandWords(b)));
17575
+ if (members.length === 0)
17576
+ return;
17577
+ return `\`${word}\` commands — run \`arbor <command> --help\` for one command's exact flags.
17578
+
17579
+ ` + `${members.map(helpLine).join(`
17580
+ `)}
17581
+ `;
17582
+ }
17555
17583
  function suggestCommand(positionals) {
17556
- const typed = positionals.join(" ").toLowerCase();
17584
+ const typed = positionals.join(" ").toLowerCase().replace(/_/g, " ");
17557
17585
  const known = KNOWN_MISREACHES.find((m) => m.tried === typed);
17558
17586
  if (known)
17559
17587
  return `use \`arbor ${known.use}\` — ${known.why}`;
17560
- const head = positionals[0]?.toLowerCase();
17588
+ const head = typed.split(" ")[0];
17561
17589
  if (head) {
17562
17590
  const siblings = CLI_ACTIONS.map(commandWords).filter((c) => c === head || c.startsWith(`${head} `)).sort();
17563
17591
  if (siblings.length > 0) {
@@ -17933,7 +17961,7 @@ async function main() {
17933
17961
  return;
17934
17962
  }
17935
17963
  if (truthyFlag(flags.help)) {
17936
- const focused = positionals.length > 0 ? commandHelp(positionals) : undefined;
17964
+ const focused = positionals.length > 0 ? commandHelp(positionals) ?? familyHelp(positionals) : undefined;
17937
17965
  if (focused)
17938
17966
  emitDual({ help: focused }, focused, "help", ctx);
17939
17967
  else
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "The Arbor CLI — a shared workspace for people and agents. The human + headless-agent write path over Arbor's guarded operation surface.",
5
5
  "keywords": [
6
6
  "agents",