@lambdacurry/arbor 0.4.23 → 0.4.25
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 +61 -29
- package/package.json +1 -1
package/dist/arbor.js
CHANGED
|
@@ -1401,8 +1401,14 @@ var invitations = sqliteTable("invitations", {
|
|
|
1401
1401
|
email: text("email").notNull(),
|
|
1402
1402
|
profileId: text("profile_id").notNull().references(() => profiles.id),
|
|
1403
1403
|
status: text("status").$type().notNull().default("pending"),
|
|
1404
|
+
token: text("token"),
|
|
1405
|
+
expiresAt: ts("expires_at"),
|
|
1406
|
+
invitedByProfileId: text("invited_by_profile_id").references(() => profiles.id),
|
|
1404
1407
|
createdAt: ts("created_at").notNull()
|
|
1405
|
-
}, (t) => ({
|
|
1408
|
+
}, (t) => ({
|
|
1409
|
+
emailIdx: index("invitations_email_idx").on(t.email, t.status),
|
|
1410
|
+
tokenIdx: uniqueIndex("invitations_token_idx").on(t.token)
|
|
1411
|
+
}));
|
|
1406
1412
|
var agentPairings = sqliteTable("agent_pairings", {
|
|
1407
1413
|
id: text("id").primaryKey(),
|
|
1408
1414
|
orgId: text("org_id").notNull().references(() => orgs.id),
|
|
@@ -1618,6 +1624,7 @@ var notificationCursors = sqliteTable("notification_cursors", {
|
|
|
1618
1624
|
lastSeenAt: ts("last_seen_at").notNull()
|
|
1619
1625
|
});
|
|
1620
1626
|
// ../core/src/ops/onboarding.ts
|
|
1627
|
+
var INVITE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
1621
1628
|
var EMOJI_RE = new RegExp("^\\p{RGI_Emoji}$", "v");
|
|
1622
1629
|
// ../core/src/ops/agent-pairing.ts
|
|
1623
1630
|
var PAIRING_TTL_MS = 15 * 60 * 1000;
|
|
@@ -16299,11 +16306,11 @@ var ACTIONS = [
|
|
|
16299
16306
|
{
|
|
16300
16307
|
name: "introduce",
|
|
16301
16308
|
title: "Introduce yourself",
|
|
16302
|
-
description:
|
|
16309
|
+
description: 'Set your own identity (AD-029/114/150/152/160): an emoji that represents you, an identity color from the curated palette (indigo, teal, terracotta, plum, steel, olive, rose, cyan), a short `description` (your CHARTER: your ROLE + what you\'re here to do), and/or `capabilities` — free-form tags for what you can do, so teammates know who to route work to. Do this once when you first connect; change it anytime. STYLE (AD-160): write the charter ROLE-FIRST — start with what you ARE/do (e.g. "Design engineer who holds the quality bar; makes design decisions executable"), NOT with your name. It renders inside "You are {Name}, working in Arbor. Your charter: …", so "I\'m {Name} —" just doubles up; lead with the role. It shows on your profile AND is read back at the start of every session. Your capabilities show on your profile + the space roster. Your display name stays managed by your human.',
|
|
16303
16310
|
inputSchema: {
|
|
16304
16311
|
emoji: exports_external.string().optional().describe("exactly one emoji that represents you, e.g. \uD83D\uDD2D"),
|
|
16305
16312
|
color: exports_external.string().optional().describe("an identity color key from the curated palette"),
|
|
16306
|
-
description: exports_external.string().optional().describe("your charter (≤500 chars):
|
|
16313
|
+
description: exports_external.string().optional().describe("your charter (≤500 chars): ROLE-FIRST — what you are + do, NOT 'I'm {Name}' (it renders after 'You are {Name}'). Read back in your orientation. Empty clears it."),
|
|
16307
16314
|
capabilities: exports_external.array(exports_external.string()).optional().describe("free-form skill tags (≤20, each ≤60 chars) for what you can do — e.g. ['market-research','data-viz']; how teammates route work to you. An empty array clears them.")
|
|
16308
16315
|
},
|
|
16309
16316
|
surfaces: ["mcp", "cli"],
|
|
@@ -16729,24 +16736,29 @@ var httpExecutor = {
|
|
|
16729
16736
|
|
|
16730
16737
|
// src/output.ts
|
|
16731
16738
|
import { randomUUID } from "node:crypto";
|
|
16739
|
+
function lastValue(value) {
|
|
16740
|
+
return Array.isArray(value) ? value[value.length - 1] : value;
|
|
16741
|
+
}
|
|
16732
16742
|
var GLOBAL_BOOLEAN_FLAGS = new Set(["json", "quiet", "no-quiet", "version", "help"]);
|
|
16733
16743
|
var RESERVED_FLAGS = new Set([...GLOBAL_BOOLEAN_FLAGS, "url", "fields"]);
|
|
16734
16744
|
var TRUTHY = new Set(["true", "1", "yes", "on"]);
|
|
16735
16745
|
function truthyFlag(value) {
|
|
16736
|
-
|
|
16746
|
+
const v = lastValue(value);
|
|
16747
|
+
return v === true || typeof v === "string" && TRUTHY.has(v.toLowerCase());
|
|
16737
16748
|
}
|
|
16738
16749
|
function stringFlag(value, name) {
|
|
16739
|
-
|
|
16750
|
+
const v = lastValue(value);
|
|
16751
|
+
if (v === undefined)
|
|
16740
16752
|
return;
|
|
16741
|
-
if (
|
|
16753
|
+
if (v === true)
|
|
16742
16754
|
throw new UsageError(`--${name} expects a value`);
|
|
16743
|
-
return String(
|
|
16755
|
+
return String(v);
|
|
16744
16756
|
}
|
|
16745
16757
|
function resolveOutput(flags, opts) {
|
|
16746
16758
|
const json2 = truthyFlag(flags.json);
|
|
16747
16759
|
const isTTY = opts?.isTTY ?? Boolean(process.stdout.isTTY);
|
|
16748
16760
|
const quiet = truthyFlag(flags["no-quiet"]) ? false : truthyFlag(flags.quiet) || json2 || !isTTY;
|
|
16749
|
-
const fieldsRaw = flags.fields;
|
|
16761
|
+
const fieldsRaw = lastValue(flags.fields);
|
|
16750
16762
|
const fields = typeof fieldsRaw === "string" && fieldsRaw.trim() ? fieldsRaw.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
|
|
16751
16763
|
return { json: json2, quiet, fields };
|
|
16752
16764
|
}
|
|
@@ -16879,7 +16891,7 @@ function buildInput(inputSchema, flags) {
|
|
|
16879
16891
|
const input = {};
|
|
16880
16892
|
for (const spec of flagsForSchema(inputSchema)) {
|
|
16881
16893
|
if (spec.kind === "string") {
|
|
16882
|
-
const fileSrc = flags[`${spec.flag}-file`];
|
|
16894
|
+
const fileSrc = lastValue(flags[`${spec.flag}-file`]);
|
|
16883
16895
|
if (fileSrc !== undefined) {
|
|
16884
16896
|
if (flags[spec.flag] !== undefined) {
|
|
16885
16897
|
throw new UsageError(`pass either --${spec.flag} or --${spec.flag}-file, not both`);
|
|
@@ -16901,33 +16913,41 @@ function buildInput(inputSchema, flags) {
|
|
|
16901
16913
|
}
|
|
16902
16914
|
switch (spec.kind) {
|
|
16903
16915
|
case "number": {
|
|
16904
|
-
const n = Number(raw);
|
|
16916
|
+
const n = Number(lastValue(raw));
|
|
16905
16917
|
if (Number.isNaN(n))
|
|
16906
|
-
throw new UsageError(`--${spec.flag} expects a number, got: ${String(raw)}`);
|
|
16918
|
+
throw new UsageError(`--${spec.flag} expects a number, got: ${String(lastValue(raw))}`);
|
|
16907
16919
|
input[spec.field] = n;
|
|
16908
16920
|
break;
|
|
16909
16921
|
}
|
|
16910
16922
|
case "array": {
|
|
16911
|
-
const
|
|
16912
|
-
|
|
16913
|
-
|
|
16914
|
-
|
|
16915
|
-
|
|
16916
|
-
|
|
16917
|
-
|
|
16923
|
+
const tokens = Array.isArray(raw) ? raw : [String(raw)];
|
|
16924
|
+
const out = [];
|
|
16925
|
+
for (const token of tokens) {
|
|
16926
|
+
const text3 = String(token).trim();
|
|
16927
|
+
if (text3.startsWith("[")) {
|
|
16928
|
+
try {
|
|
16929
|
+
const parsed = JSON.parse(text3);
|
|
16930
|
+
if (Array.isArray(parsed)) {
|
|
16931
|
+
out.push(...parsed);
|
|
16932
|
+
continue;
|
|
16933
|
+
}
|
|
16934
|
+
} catch {
|
|
16935
|
+
throw new UsageError(`--${spec.flag} looks like JSON but failed to parse — check the quoting`);
|
|
16918
16936
|
}
|
|
16919
|
-
} catch {
|
|
16920
|
-
throw new UsageError(`--${spec.flag} looks like JSON but failed to parse — check the quoting`);
|
|
16921
16937
|
}
|
|
16938
|
+
for (const part of text3.split(",").map((s) => s.trim()).filter(Boolean))
|
|
16939
|
+
out.push(part);
|
|
16922
16940
|
}
|
|
16923
|
-
input[spec.field] =
|
|
16941
|
+
input[spec.field] = out;
|
|
16924
16942
|
break;
|
|
16925
16943
|
}
|
|
16926
|
-
case "boolean":
|
|
16927
|
-
|
|
16944
|
+
case "boolean": {
|
|
16945
|
+
const v = lastValue(raw);
|
|
16946
|
+
input[spec.field] = v === true ? true : v !== "false";
|
|
16928
16947
|
break;
|
|
16948
|
+
}
|
|
16929
16949
|
default:
|
|
16930
|
-
input[spec.field] = String(raw);
|
|
16950
|
+
input[spec.field] = String(lastValue(raw));
|
|
16931
16951
|
}
|
|
16932
16952
|
}
|
|
16933
16953
|
return input;
|
|
@@ -17089,6 +17109,14 @@ async function login(opts) {
|
|
|
17089
17109
|
}
|
|
17090
17110
|
|
|
17091
17111
|
// src/index.ts
|
|
17112
|
+
function setFlag(flags, key, value) {
|
|
17113
|
+
const prev = flags[key];
|
|
17114
|
+
if (prev === undefined || typeof value === "boolean" || typeof prev === "boolean") {
|
|
17115
|
+
flags[key] = value;
|
|
17116
|
+
return;
|
|
17117
|
+
}
|
|
17118
|
+
flags[key] = Array.isArray(prev) ? [...prev, value] : [prev, value];
|
|
17119
|
+
}
|
|
17092
17120
|
function parseArgs(argv) {
|
|
17093
17121
|
const positionals = [];
|
|
17094
17122
|
const flags = {};
|
|
@@ -17098,11 +17126,11 @@ function parseArgs(argv) {
|
|
|
17098
17126
|
const body = arg.slice(2);
|
|
17099
17127
|
const eq = body.indexOf("=");
|
|
17100
17128
|
if (eq !== -1) {
|
|
17101
|
-
flags
|
|
17129
|
+
setFlag(flags, body.slice(0, eq), body.slice(eq + 1));
|
|
17102
17130
|
} else if (!GLOBAL_BOOLEAN_FLAGS.has(body) && i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
|
|
17103
|
-
flags
|
|
17131
|
+
setFlag(flags, body, argv[++i]);
|
|
17104
17132
|
} else {
|
|
17105
|
-
flags
|
|
17133
|
+
setFlag(flags, body, true);
|
|
17106
17134
|
}
|
|
17107
17135
|
} else {
|
|
17108
17136
|
positionals.push(arg);
|
|
@@ -17122,13 +17150,17 @@ var CLI_VERSION = PKG.version;
|
|
|
17122
17150
|
async function renderMe(ctx, action) {
|
|
17123
17151
|
const me = await httpExecutor.call("me.get", {});
|
|
17124
17152
|
const gaps = me.completeness?.unset ?? [];
|
|
17153
|
+
const spaceGaps = me.completeness?.spacesWithoutCharter ?? [];
|
|
17154
|
+
const spaceLines = spaceGaps.length > 0 ? ` spaces without your charter (say how you operate there):
|
|
17155
|
+
` + spaceGaps.map((s) => ` • ${s.spaceTitle} — \`set_space_charter --space-id ${s.spaceId} --charter "…"\`
|
|
17156
|
+
`).join("") : "";
|
|
17125
17157
|
const human = `${me.profile.displayName} · ${me.profile.kind} · org-role ${me.profile.orgRole}
|
|
17126
17158
|
` + ` org ${me.profile.orgId} · resolved via ${me.client.kind}
|
|
17127
17159
|
` + (gaps.length > 0 ? ` to be more legible to your team, set: ${gaps.join("; ")} (\`introduce\`)
|
|
17128
|
-
` : "");
|
|
17160
|
+
` : "") + spaceLines;
|
|
17129
17161
|
emitDual(me, human, action, ctx);
|
|
17130
17162
|
}
|
|
17131
|
-
var CLI_NOTE = `On this CLI, before your first write: flag names are kebab-derived from the inputs (\`--thread-id\`, \`--request-id\`, \`--contribution-id\` — not \`--thread\`/\`--request\`), so check \`arbor help\` or \`arbor <command> --help\` for a command's exact flags instead of guessing. Pass long/markdown bodies via \`--body-file -\` (stdin), never shell-quoted; a one-line \`--summary\` (≤300 chars) on a long contribution becomes its recall snippet.`;
|
|
17163
|
+
var CLI_NOTE = `On this CLI, before your first write: flag names are kebab-derived from the inputs (\`--thread-id\`, \`--request-id\`, \`--contribution-id\` — not \`--thread\`/\`--request\`), so check \`arbor help\` or \`arbor <command> --help\` for a command's exact flags instead of guessing. Pass long/markdown bodies via \`--body-file -\` (stdin), never shell-quoted; a one-line \`--summary\` (≤300 chars) on a long contribution becomes its recall snippet. List inputs (e.g. \`--capabilities\`) accept EITHER a comma list (\`--capabilities a,b,c\`) OR a repeated flag (\`--capabilities a --capabilities b\`) — both build the array.`;
|
|
17132
17164
|
function renderOrient(ctx) {
|
|
17133
17165
|
emitDual({ orientation: ORIENTATION, cliNote: CLI_NOTE }, `${ORIENTATION}
|
|
17134
17166
|
|
package/package.json
CHANGED