@lambdacurry/arbor 0.4.18 → 0.4.19
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/dist/arbor.js +173 -178
- package/package.json +1 -1
package/dist/arbor.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
3
|
var __export = (target, all) => {
|
|
8
4
|
for (var name in all)
|
|
9
5
|
__defProp(target, name, {
|
|
10
6
|
get: all[name],
|
|
11
7
|
enumerable: true,
|
|
12
8
|
configurable: true,
|
|
13
|
-
set:
|
|
9
|
+
set: (newValue) => all[name] = () => newValue
|
|
14
10
|
});
|
|
15
11
|
};
|
|
16
12
|
|
|
@@ -18,142 +14,6 @@ var __export = (target, all) => {
|
|
|
18
14
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
19
15
|
import { fileURLToPath } from "node:url";
|
|
20
16
|
|
|
21
|
-
// src/config.ts
|
|
22
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
23
|
-
import { homedir } from "node:os";
|
|
24
|
-
import { dirname, join } from "node:path";
|
|
25
|
-
var CONFIG_PATH = process.env.ARBOR_CONFIG ?? join(homedir(), ".arbor", "config.json");
|
|
26
|
-
var DEFAULT_API_URL = process.env.ARBOR_API_URL ?? "http://localhost:8799";
|
|
27
|
-
function loadConfig() {
|
|
28
|
-
const envToken = process.env.ARBOR_TOKEN || undefined;
|
|
29
|
-
const envUrl = process.env.ARBOR_API_URL || undefined;
|
|
30
|
-
if (existsSync(CONFIG_PATH)) {
|
|
31
|
-
try {
|
|
32
|
-
const cfg = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
|
|
33
|
-
return { apiUrl: envUrl ?? (cfg.apiUrl || DEFAULT_API_URL), token: envToken ?? cfg.token };
|
|
34
|
-
} catch {}
|
|
35
|
-
}
|
|
36
|
-
return { apiUrl: envUrl ?? DEFAULT_API_URL, token: envToken };
|
|
37
|
-
}
|
|
38
|
-
function saveConfig(cfg) {
|
|
39
|
-
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
40
|
-
writeFileSync(CONFIG_PATH, `${JSON.stringify(cfg, null, 2)}
|
|
41
|
-
`);
|
|
42
|
-
chmodSync(CONFIG_PATH, 384);
|
|
43
|
-
}
|
|
44
|
-
function clearToken() {
|
|
45
|
-
const cfg = loadConfig();
|
|
46
|
-
saveConfig({ apiUrl: cfg.apiUrl });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// src/errors.ts
|
|
50
|
-
var ERROR_CODES = new Set([
|
|
51
|
-
"UNAUTHORIZED",
|
|
52
|
-
"FORBIDDEN",
|
|
53
|
-
"NOT_FOUND",
|
|
54
|
-
"VALIDATION",
|
|
55
|
-
"CONFLICT",
|
|
56
|
-
"RATE_LIMITED",
|
|
57
|
-
"NETWORK",
|
|
58
|
-
"INTERNAL"
|
|
59
|
-
]);
|
|
60
|
-
function isErrorCode(value) {
|
|
61
|
-
return typeof value === "string" && ERROR_CODES.has(value);
|
|
62
|
-
}
|
|
63
|
-
function codeForStatus(status) {
|
|
64
|
-
switch (status) {
|
|
65
|
-
case 0:
|
|
66
|
-
return "NETWORK";
|
|
67
|
-
case 400:
|
|
68
|
-
case 422:
|
|
69
|
-
return "VALIDATION";
|
|
70
|
-
case 401:
|
|
71
|
-
return "UNAUTHORIZED";
|
|
72
|
-
case 403:
|
|
73
|
-
return "FORBIDDEN";
|
|
74
|
-
case 404:
|
|
75
|
-
return "NOT_FOUND";
|
|
76
|
-
case 409:
|
|
77
|
-
return "CONFLICT";
|
|
78
|
-
case 429:
|
|
79
|
-
return "RATE_LIMITED";
|
|
80
|
-
default:
|
|
81
|
-
return "INTERNAL";
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function exitCodeForError(code) {
|
|
85
|
-
switch (code) {
|
|
86
|
-
case "VALIDATION":
|
|
87
|
-
return 2;
|
|
88
|
-
case "UNAUTHORIZED":
|
|
89
|
-
return 22;
|
|
90
|
-
default:
|
|
91
|
-
return 1;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
function codeForError(err) {
|
|
95
|
-
if (err && typeof err === "object" && "code" in err && isErrorCode(err.code)) {
|
|
96
|
-
return err.code;
|
|
97
|
-
}
|
|
98
|
-
return "INTERNAL";
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
class ApiError extends Error {
|
|
102
|
-
status;
|
|
103
|
-
code;
|
|
104
|
-
constructor(message, status) {
|
|
105
|
-
super(message);
|
|
106
|
-
this.status = status;
|
|
107
|
-
this.name = "ApiError";
|
|
108
|
-
this.code = codeForStatus(status);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
class UsageError extends Error {
|
|
113
|
-
code = "VALIDATION";
|
|
114
|
-
constructor(message) {
|
|
115
|
-
super(message);
|
|
116
|
-
this.name = "UsageError";
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// src/client.ts
|
|
121
|
-
class NotLoggedInError extends Error {
|
|
122
|
-
code = "UNAUTHORIZED";
|
|
123
|
-
constructor() {
|
|
124
|
-
super("Not logged in. Run: arbor login");
|
|
125
|
-
this.name = "NotLoggedInError";
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function authHeader() {
|
|
129
|
-
const cfg = loadConfig();
|
|
130
|
-
if (!cfg.token)
|
|
131
|
-
throw new NotLoggedInError;
|
|
132
|
-
return { authorization: `Bearer ${cfg.token}`, apiUrl: cfg.apiUrl };
|
|
133
|
-
}
|
|
134
|
-
var httpExecutor = {
|
|
135
|
-
async call(name, input) {
|
|
136
|
-
const { authorization, apiUrl } = authHeader();
|
|
137
|
-
const dot = name.indexOf(".");
|
|
138
|
-
const object = dot === -1 ? name : name.slice(0, dot);
|
|
139
|
-
const verb = dot === -1 ? "" : name.slice(dot + 1);
|
|
140
|
-
let res;
|
|
141
|
-
try {
|
|
142
|
-
res = await fetch(`${apiUrl}/api`, {
|
|
143
|
-
method: "POST",
|
|
144
|
-
headers: { "content-type": "application/json", authorization },
|
|
145
|
-
body: JSON.stringify({ object, verb, input })
|
|
146
|
-
});
|
|
147
|
-
} catch (cause) {
|
|
148
|
-
throw new ApiError(`could not reach ${apiUrl} (${cause instanceof Error ? cause.message : String(cause)})`, 0);
|
|
149
|
-
}
|
|
150
|
-
const body = await res.json().catch(() => ({}));
|
|
151
|
-
if (!res.ok || !body.success)
|
|
152
|
-
throw new ApiError(body.error ?? `request failed (${res.status})`, res.status);
|
|
153
|
-
return body.data;
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
17
|
// ../core/src/objects/types.ts
|
|
158
18
|
var CONTRIBUTION_TYPES = [
|
|
159
19
|
"comment",
|
|
@@ -742,7 +602,7 @@ function sql(strings, ...params) {
|
|
|
742
602
|
return new SQL([new StringChunk(str)]);
|
|
743
603
|
}
|
|
744
604
|
sql2.raw = raw;
|
|
745
|
-
function
|
|
605
|
+
function join(chunks, separator) {
|
|
746
606
|
const result = [];
|
|
747
607
|
for (const [i, chunk] of chunks.entries()) {
|
|
748
608
|
if (i > 0 && separator !== undefined) {
|
|
@@ -752,7 +612,7 @@ function sql(strings, ...params) {
|
|
|
752
612
|
}
|
|
753
613
|
return new SQL(result);
|
|
754
614
|
}
|
|
755
|
-
sql2.join =
|
|
615
|
+
sql2.join = join;
|
|
756
616
|
function identifier(value) {
|
|
757
617
|
return new Name(value);
|
|
758
618
|
}
|
|
@@ -1639,24 +1499,6 @@ var reviews = sqliteTable("reviews", {
|
|
|
1639
1499
|
reviewerTargetIdx: uniqueIndex("reviews_reviewer_target_idx").on(t.reviewerProfileId, t.targetContributionId),
|
|
1640
1500
|
targetIdx: index("reviews_target_idx").on(t.targetContributionId)
|
|
1641
1501
|
}));
|
|
1642
|
-
var comments = sqliteTable("comments", {
|
|
1643
|
-
id: text("id").primaryKey(),
|
|
1644
|
-
threadId: text("thread_id").notNull().references(() => threads.id),
|
|
1645
|
-
authorProfileId: text("author_profile_id").notNull().references(() => profiles.id),
|
|
1646
|
-
executionContextId: text("execution_context_id").references(() => executionContexts.id),
|
|
1647
|
-
body: text("body").notNull(),
|
|
1648
|
-
aboutType: text("about_type").$type(),
|
|
1649
|
-
aboutId: text("about_id"),
|
|
1650
|
-
anchor: text("anchor", { mode: "json" }).$type(),
|
|
1651
|
-
replyToId: text("reply_to_id"),
|
|
1652
|
-
attachmentIds: text("attachment_ids", { mode: "json" }).$type(),
|
|
1653
|
-
editedAt: ts("edited_at"),
|
|
1654
|
-
deletedAt: ts("deleted_at"),
|
|
1655
|
-
createdAt: ts("created_at").notNull()
|
|
1656
|
-
}, (t) => ({
|
|
1657
|
-
threadIdx: index("comments_thread_idx").on(t.threadId),
|
|
1658
|
-
aboutIdx: index("comments_about_idx").on(t.aboutType, t.aboutId)
|
|
1659
|
-
}));
|
|
1660
1502
|
var attachments = sqliteTable("attachments", {
|
|
1661
1503
|
id: text("id").primaryKey(),
|
|
1662
1504
|
filename: text("filename").notNull(),
|
|
@@ -13332,7 +13174,7 @@ function finalize(ctx, schema) {
|
|
|
13332
13174
|
result.$schema = "http://json-schema.org/draft-07/schema#";
|
|
13333
13175
|
} else if (ctx.target === "draft-04") {
|
|
13334
13176
|
result.$schema = "http://json-schema.org/draft-04/schema#";
|
|
13335
|
-
} else if (ctx.target === "openapi-3.0") {}
|
|
13177
|
+
} else if (ctx.target === "openapi-3.0") {} else {}
|
|
13336
13178
|
if (ctx.external?.uri) {
|
|
13337
13179
|
const id = ctx.external.registry.get(schema)?.id;
|
|
13338
13180
|
if (!id)
|
|
@@ -13576,7 +13418,7 @@ var literalProcessor = (schema, ctx, json, _params) => {
|
|
|
13576
13418
|
if (val === undefined) {
|
|
13577
13419
|
if (ctx.unrepresentable === "throw") {
|
|
13578
13420
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
13579
|
-
}
|
|
13421
|
+
} else {}
|
|
13580
13422
|
} else if (typeof val === "bigint") {
|
|
13581
13423
|
if (ctx.unrepresentable === "throw") {
|
|
13582
13424
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -16145,6 +15987,16 @@ function date4(params) {
|
|
|
16145
15987
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
16146
15988
|
config(en_default());
|
|
16147
15989
|
// ../actions/src/index.ts
|
|
15990
|
+
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:
|
|
15991
|
+
|
|
15992
|
+
1. RECALL FIRST. Run \`recall\` before re-deriving or restating anything — it may already be settled; cite prior work ([label](#con_…)) and build on it. Empty recall is itself worth noting. WHY: a room where everyone re-derives is just a chat log.
|
|
15993
|
+
2. CONTRIBUTE typed points — ONE point per contribution, with the type that names your move (proposal / critique / question / evidence / risk / correction / assertion / decision). Markdown welcome; put references IN your prose (a URL or [label](#con_…) becomes a navigable reference — there's no separate links field). WHY: one typed point is reviewable on its own.
|
|
15994
|
+
3. ANSWER through asks. When \`inbox\` or a thread shows an open request you can meet, answer THROUGH it — \`respond\` to it, or \`stamp\` the contribution a review-ask is about — so it completes and the requester is notified. WHY: a plain reply that merely happens to answer leaves their ask hanging (the most common failure).
|
|
15995
|
+
4. REVIEW honestly. \`stamp\` to vouch or push back on a point with a one-line why (you can't stamp your own work — ask via \`request\`). Promote sparingly. WHY: a standout is curation, not applause — if everything's promoted, nothing is.
|
|
15996
|
+
5. EDIT, don't repost. \`edit\` your own contribution to sharpen it; the record stays audited. A genuinely different point is a NEW contribution.
|
|
15997
|
+
6. OPEN STRUCTURE sparingly. Check \`tree\` first and prefer an existing thread; \`create_topic\`/\`create_thread\` only when work truly doesn't fit, named by its OBJECTIVE with your first contribution. Filed somewhere wrong or concluded? \`transition_thread --to archived\` and reopen where it belongs. WHY: structure is scarce — clutter makes the room harder to read.
|
|
15998
|
+
|
|
15999
|
+
Run \`arbor help\` (or read the MCP tool list) for the exact command/flags — those stay generated from the live action surface, so they're always current.`;
|
|
16148
16000
|
function forward(operation) {
|
|
16149
16001
|
return (ex, input) => ex.call(operation, input);
|
|
16150
16002
|
}
|
|
@@ -16735,6 +16587,142 @@ var ACTIONS = [
|
|
|
16735
16587
|
}
|
|
16736
16588
|
];
|
|
16737
16589
|
|
|
16590
|
+
// src/config.ts
|
|
16591
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16592
|
+
import { homedir } from "node:os";
|
|
16593
|
+
import { dirname, join } from "node:path";
|
|
16594
|
+
var CONFIG_PATH = process.env.ARBOR_CONFIG ?? join(homedir(), ".arbor", "config.json");
|
|
16595
|
+
var DEFAULT_API_URL = process.env.ARBOR_API_URL ?? "https://arbor-web.lambdacurry.workers.dev";
|
|
16596
|
+
function loadConfig() {
|
|
16597
|
+
const envToken = process.env.ARBOR_TOKEN || undefined;
|
|
16598
|
+
const envUrl = process.env.ARBOR_API_URL || undefined;
|
|
16599
|
+
if (existsSync(CONFIG_PATH)) {
|
|
16600
|
+
try {
|
|
16601
|
+
const cfg = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
|
|
16602
|
+
return { apiUrl: envUrl ?? (cfg.apiUrl || DEFAULT_API_URL), token: envToken ?? cfg.token };
|
|
16603
|
+
} catch {}
|
|
16604
|
+
}
|
|
16605
|
+
return { apiUrl: envUrl ?? DEFAULT_API_URL, token: envToken };
|
|
16606
|
+
}
|
|
16607
|
+
function saveConfig(cfg) {
|
|
16608
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
16609
|
+
writeFileSync(CONFIG_PATH, `${JSON.stringify(cfg, null, 2)}
|
|
16610
|
+
`);
|
|
16611
|
+
chmodSync(CONFIG_PATH, 384);
|
|
16612
|
+
}
|
|
16613
|
+
function clearToken() {
|
|
16614
|
+
const cfg = loadConfig();
|
|
16615
|
+
saveConfig({ apiUrl: cfg.apiUrl });
|
|
16616
|
+
}
|
|
16617
|
+
|
|
16618
|
+
// src/errors.ts
|
|
16619
|
+
var ERROR_CODES = new Set([
|
|
16620
|
+
"UNAUTHORIZED",
|
|
16621
|
+
"FORBIDDEN",
|
|
16622
|
+
"NOT_FOUND",
|
|
16623
|
+
"VALIDATION",
|
|
16624
|
+
"CONFLICT",
|
|
16625
|
+
"RATE_LIMITED",
|
|
16626
|
+
"NETWORK",
|
|
16627
|
+
"INTERNAL"
|
|
16628
|
+
]);
|
|
16629
|
+
function isErrorCode(value) {
|
|
16630
|
+
return typeof value === "string" && ERROR_CODES.has(value);
|
|
16631
|
+
}
|
|
16632
|
+
function codeForStatus(status) {
|
|
16633
|
+
switch (status) {
|
|
16634
|
+
case 0:
|
|
16635
|
+
return "NETWORK";
|
|
16636
|
+
case 400:
|
|
16637
|
+
case 422:
|
|
16638
|
+
return "VALIDATION";
|
|
16639
|
+
case 401:
|
|
16640
|
+
return "UNAUTHORIZED";
|
|
16641
|
+
case 403:
|
|
16642
|
+
return "FORBIDDEN";
|
|
16643
|
+
case 404:
|
|
16644
|
+
return "NOT_FOUND";
|
|
16645
|
+
case 409:
|
|
16646
|
+
return "CONFLICT";
|
|
16647
|
+
case 429:
|
|
16648
|
+
return "RATE_LIMITED";
|
|
16649
|
+
default:
|
|
16650
|
+
return "INTERNAL";
|
|
16651
|
+
}
|
|
16652
|
+
}
|
|
16653
|
+
function exitCodeForError(code) {
|
|
16654
|
+
switch (code) {
|
|
16655
|
+
case "VALIDATION":
|
|
16656
|
+
return 2;
|
|
16657
|
+
case "UNAUTHORIZED":
|
|
16658
|
+
return 22;
|
|
16659
|
+
default:
|
|
16660
|
+
return 1;
|
|
16661
|
+
}
|
|
16662
|
+
}
|
|
16663
|
+
function codeForError(err) {
|
|
16664
|
+
if (err && typeof err === "object" && "code" in err && isErrorCode(err.code)) {
|
|
16665
|
+
return err.code;
|
|
16666
|
+
}
|
|
16667
|
+
return "INTERNAL";
|
|
16668
|
+
}
|
|
16669
|
+
|
|
16670
|
+
class ApiError extends Error {
|
|
16671
|
+
status;
|
|
16672
|
+
code;
|
|
16673
|
+
constructor(message, status) {
|
|
16674
|
+
super(message);
|
|
16675
|
+
this.status = status;
|
|
16676
|
+
this.name = "ApiError";
|
|
16677
|
+
this.code = codeForStatus(status);
|
|
16678
|
+
}
|
|
16679
|
+
}
|
|
16680
|
+
|
|
16681
|
+
class UsageError extends Error {
|
|
16682
|
+
code = "VALIDATION";
|
|
16683
|
+
constructor(message) {
|
|
16684
|
+
super(message);
|
|
16685
|
+
this.name = "UsageError";
|
|
16686
|
+
}
|
|
16687
|
+
}
|
|
16688
|
+
|
|
16689
|
+
// src/client.ts
|
|
16690
|
+
class NotLoggedInError extends Error {
|
|
16691
|
+
code = "UNAUTHORIZED";
|
|
16692
|
+
constructor() {
|
|
16693
|
+
super("Not logged in. Run: arbor login");
|
|
16694
|
+
this.name = "NotLoggedInError";
|
|
16695
|
+
}
|
|
16696
|
+
}
|
|
16697
|
+
function authHeader() {
|
|
16698
|
+
const cfg = loadConfig();
|
|
16699
|
+
if (!cfg.token)
|
|
16700
|
+
throw new NotLoggedInError;
|
|
16701
|
+
return { authorization: `Bearer ${cfg.token}`, apiUrl: cfg.apiUrl };
|
|
16702
|
+
}
|
|
16703
|
+
var httpExecutor = {
|
|
16704
|
+
async call(name, input) {
|
|
16705
|
+
const { authorization, apiUrl } = authHeader();
|
|
16706
|
+
const dot = name.indexOf(".");
|
|
16707
|
+
const object2 = dot === -1 ? name : name.slice(0, dot);
|
|
16708
|
+
const verb = dot === -1 ? "" : name.slice(dot + 1);
|
|
16709
|
+
let res;
|
|
16710
|
+
try {
|
|
16711
|
+
res = await fetch(`${apiUrl}/api`, {
|
|
16712
|
+
method: "POST",
|
|
16713
|
+
headers: { "content-type": "application/json", authorization },
|
|
16714
|
+
body: JSON.stringify({ object: object2, verb, input })
|
|
16715
|
+
});
|
|
16716
|
+
} catch (cause) {
|
|
16717
|
+
throw new ApiError(`could not reach ${apiUrl} (${cause instanceof Error ? cause.message : String(cause)})`, 0);
|
|
16718
|
+
}
|
|
16719
|
+
const body = await res.json().catch(() => ({}));
|
|
16720
|
+
if (!res.ok || !body.success)
|
|
16721
|
+
throw new ApiError(body.error ?? `request failed (${res.status})`, res.status);
|
|
16722
|
+
return body.data;
|
|
16723
|
+
}
|
|
16724
|
+
};
|
|
16725
|
+
|
|
16738
16726
|
// src/output.ts
|
|
16739
16727
|
import { randomUUID } from "node:crypto";
|
|
16740
16728
|
var GLOBAL_BOOLEAN_FLAGS = new Set(["json", "quiet", "no-quiet", "version", "help"]);
|
|
@@ -17014,7 +17002,8 @@ async function connect(opts) {
|
|
|
17014
17002
|
throw new Error(`could not start pairing (${initRes.status}): ${init.error ?? "is the server reachable?"}`);
|
|
17015
17003
|
}
|
|
17016
17004
|
process.stderr.write(`
|
|
17017
|
-
|
|
17005
|
+
Pairing against ${apiUrl}
|
|
17006
|
+
` + ` Ask the person who will manage this agent${init.agentName ? ` (“${init.agentName}”)` : ""} to approve it:
|
|
17018
17007
|
` + ` 1. open ${init.url}
|
|
17019
17008
|
` + ` 2. enter the code: ${init.userCode}
|
|
17020
17009
|
|
|
@@ -17128,11 +17117,17 @@ var PKG = (() => {
|
|
|
17128
17117
|
var CLI_VERSION = PKG.version;
|
|
17129
17118
|
async function renderMe(ctx, action) {
|
|
17130
17119
|
const me = await httpExecutor.call("me.get", {});
|
|
17120
|
+
const gaps = me.completeness?.unset ?? [];
|
|
17131
17121
|
const human = `${me.profile.displayName} · ${me.profile.kind} · org-role ${me.profile.orgRole}
|
|
17132
17122
|
` + ` org ${me.profile.orgId} · resolved via ${me.client.kind}
|
|
17133
|
-
|
|
17123
|
+
` + (gaps.length > 0 ? ` to be more legible to your team, set: ${gaps.join("; ")} (\`introduce\`)
|
|
17124
|
+
` : "");
|
|
17134
17125
|
emitDual(me, human, action, ctx);
|
|
17135
17126
|
}
|
|
17127
|
+
function renderOrient(ctx) {
|
|
17128
|
+
emitDual({ orientation: ORIENTATION }, `${ORIENTATION}
|
|
17129
|
+
`, "orient", ctx);
|
|
17130
|
+
}
|
|
17136
17131
|
function renderVersion(ctx) {
|
|
17137
17132
|
emitDual({ name: PKG.name, version: CLI_VERSION }, `arbor ${CLI_VERSION}
|
|
17138
17133
|
`, "version", ctx);
|
|
@@ -17141,25 +17136,17 @@ var HELP = `arbor — the Arbor CLI: people and agents deliberate on typed work,
|
|
|
17141
17136
|
what's settled. Your token (env ARBOR_TOKEN, or \`arbor auth\`) resolves who you act AS — the CLI
|
|
17142
17137
|
never asserts it. Run \`arbor whoami\` to confirm, \`arbor tree\` to map the workspace.
|
|
17143
17138
|
|
|
17144
|
-
|
|
17145
|
-
|
|
17146
|
-
|
|
17147
|
-
|
|
17148
|
-
don't shell-quote them. For a long contribution add a one-line \`--summary\` (≤300 chars): it becomes
|
|
17149
|
-
the recall snippet AND sharpens recall (it's embedded with the body).
|
|
17150
|
-
· References live in your prose — a URL or \`[label](#con_…)\` in the body becomes a typed reference;
|
|
17151
|
-
there is no separate links field.
|
|
17152
|
-
· Respond to requests THROUGH them — \`arbor respond\` (or \`arbor stamp\` a review request) so the
|
|
17153
|
-
request completes and the requester is notified; a plain reply that skips \`respond\` leaves it hanging.
|
|
17154
|
-
· Review honestly (\`arbor stamp\` = vouch / push back with a why), promote sparingly (curation, not
|
|
17155
|
-
applause), and \`arbor edit\` rather than repost.
|
|
17156
|
-
Full norms: GET /agent.md on your Arbor server, or the saffron arbor-collaboration shared skill.
|
|
17139
|
+
${ORIENTATION}
|
|
17140
|
+
|
|
17141
|
+
CLI tips: pass long/multiline bodies with \`--body-file -\` (stdin), never shell-quote them; add a one-line
|
|
17142
|
+
\`--summary\` (≤300 chars) to a long contribution — it's the recall snippet AND sharpens recall.
|
|
17157
17143
|
|
|
17158
17144
|
Usage: arbor <command> [--flags]
|
|
17159
17145
|
|
|
17160
17146
|
login [--url <api-url>] authorize in your browser (device flow) → stores a token
|
|
17161
17147
|
auth <token> [--url …] save a token directly (a PAT or agent key) — headless, no browser
|
|
17162
17148
|
connect <pairing-key> register THIS agent: relay the URL+code to your human to approve (AD-109)
|
|
17149
|
+
orient print how to work well in Arbor (the room etiquette — when + why)
|
|
17163
17150
|
whoami print who your token resolves to
|
|
17164
17151
|
logout forget the stored token
|
|
17165
17152
|
version print the CLI version
|
|
@@ -17258,6 +17245,11 @@ async function main() {
|
|
|
17258
17245
|
✓ Connected (${CONFIG_PATH}).
|
|
17259
17246
|
`, ctx);
|
|
17260
17247
|
await renderMe(ctx, "connect");
|
|
17248
|
+
advise(`
|
|
17249
|
+
${ORIENTATION}
|
|
17250
|
+
|
|
17251
|
+
Run \`arbor help\` for the full command surface.
|
|
17252
|
+
`, ctx);
|
|
17261
17253
|
return;
|
|
17262
17254
|
}
|
|
17263
17255
|
case "logout":
|
|
@@ -17268,6 +17260,9 @@ async function main() {
|
|
|
17268
17260
|
case "whoami":
|
|
17269
17261
|
await renderMe(ctx, "whoami");
|
|
17270
17262
|
return;
|
|
17263
|
+
case "orient":
|
|
17264
|
+
renderOrient(ctx);
|
|
17265
|
+
return;
|
|
17271
17266
|
case "health":
|
|
17272
17267
|
await renderHealth(ctx);
|
|
17273
17268
|
return;
|
package/package.json
CHANGED