@ghostpaw/affinity 0.3.0 → 0.4.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/dist/cli.js CHANGED
@@ -1,18 +1,32 @@
1
1
  #!/usr/bin/env node
2
- import { mkdirSync } from 'node:fs';
2
+ import { readFileSync, mkdirSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { homedir } from 'node:os';
5
+ import { Writable } from 'node:stream';
5
6
 
6
7
  var __defProp = Object.defineProperty;
7
8
  var __getOwnPropNames = Object.getOwnPropertyNames;
8
- var __esm = (fn, res) => function __init() {
9
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ var __esm = (fn, res, err) => function __init() {
10
+ if (err) throw err[0];
11
+ try {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ } catch (e) {
14
+ throw err = [e], e;
15
+ }
10
16
  };
11
17
  var __export = (target, all) => {
12
18
  for (var name in all)
13
19
  __defProp(target, name, { get: all[name], enumerable: true });
14
20
  };
15
21
 
22
+ // src/version.ts
23
+ var affinityVersion;
24
+ var init_version = __esm({
25
+ "src/version.ts"() {
26
+ affinityVersion = "0.4.1";
27
+ }
28
+ });
29
+
16
30
  // src/lib/errors/affinity_error.ts
17
31
  var AffinityError;
18
32
  var init_affinity_error = __esm({
@@ -2478,21 +2492,6 @@ var init_unset_attribute = __esm({
2478
2492
  }
2479
2493
  });
2480
2494
 
2481
- // src/tools/tool_summary.ts
2482
- function summarizeCount(count, singular) {
2483
- if (count === 0) {
2484
- return `No ${singular}s found.`;
2485
- }
2486
- if (count === 1) {
2487
- return `Found 1 ${singular}.`;
2488
- }
2489
- return `Found ${count} ${singular}s.`;
2490
- }
2491
- var init_tool_summary = __esm({
2492
- "src/tools/tool_summary.ts"() {
2493
- }
2494
- });
2495
-
2496
2495
  // src/tools/tool_mutation.ts
2497
2496
  function mutationToolResult(action, receipt, entitySummary) {
2498
2497
  const changedCount = receipt.created.length + receipt.updated.length + receipt.archived.length + receipt.removed.length;
@@ -2506,7 +2505,7 @@ function mutationToolResult(action, receipt, entitySummary) {
2506
2505
  affectedLinks: receipt.affectedLinks,
2507
2506
  derivedEffects: receipt.derivedEffects
2508
2507
  };
2509
- const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` : summarizeCount(changedCount, `${entitySummary} change`);
2508
+ const summary = changedCount === 0 ? `No ${entitySummary} changes were needed.` : describeChanges(receipt);
2510
2509
  if (changedCount === 0) {
2511
2510
  return toolNoOp(summary, data, {
2512
2511
  entities: toReceiptEntityRefs(receipt)
@@ -2516,11 +2515,28 @@ function mutationToolResult(action, receipt, entitySummary) {
2516
2515
  entities: toReceiptEntityRefs(receipt)
2517
2516
  });
2518
2517
  }
2518
+ function describeChanges(receipt) {
2519
+ const parts = [];
2520
+ for (const verb of ["created", "updated", "archived", "removed"]) {
2521
+ const counts = /* @__PURE__ */ new Map();
2522
+ for (const ref of receipt[verb]) {
2523
+ counts.set(ref.kind, (counts.get(ref.kind) ?? 0) + 1);
2524
+ }
2525
+ if (counts.size === 0) continue;
2526
+ const items = [...counts].map(
2527
+ ([kind, n]) => `${n} ${n === 1 ? kind : PLURALS[kind] ?? `${kind}s`}`
2528
+ );
2529
+ parts.push(`${verb} ${items.join(", ")}`);
2530
+ }
2531
+ const text = parts.join("; ");
2532
+ return `${text.charAt(0).toUpperCase()}${text.slice(1)}.`;
2533
+ }
2534
+ var PLURALS;
2519
2535
  var init_tool_mutation = __esm({
2520
2536
  "src/tools/tool_mutation.ts"() {
2521
2537
  init_tool_ref();
2522
- init_tool_summary();
2523
2538
  init_tool_types();
2539
+ PLURALS = { identity: "identities" };
2524
2540
  }
2525
2541
  });
2526
2542
 
@@ -4282,13 +4298,20 @@ function createContact(db, input) {
4282
4298
  if (!KINDS.has(input.kind)) {
4283
4299
  throw new AffinityValidationError("invalid contact kind");
4284
4300
  }
4301
+ if (input.isOwner !== void 0) {
4302
+ throw new AffinityValidationError(
4303
+ "isOwner is read-only; pass bootstrapOwner: true to create the owner contact"
4304
+ );
4305
+ }
4285
4306
  const now = resolveNow(input.now);
4286
4307
  if (input.bootstrapOwner) {
4287
4308
  const existing = db.prepare(
4288
- "SELECT id FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
4309
+ "SELECT id, name FROM contacts WHERE is_owner = 1 AND deleted_at IS NULL"
4289
4310
  ).get();
4290
4311
  if (existing !== void 0) {
4291
- throw new AffinityConflictError("owner already exists");
4312
+ throw new AffinityConflictError(
4313
+ `owner already exists: contact ${existing.id} (${existing.name}); bootstrapOwner can only be used once`
4314
+ );
4292
4315
  }
4293
4316
  }
4294
4317
  const isOwner = input.bootstrapOwner ? 1 : 0;
@@ -7391,6 +7414,21 @@ var init_list_dismissed_duplicates = __esm({
7391
7414
  }
7392
7415
  });
7393
7416
 
7417
+ // src/tools/tool_summary.ts
7418
+ function summarizeCount(count, singular) {
7419
+ if (count === 0) {
7420
+ return `No ${singular}s found.`;
7421
+ }
7422
+ if (count === 1) {
7423
+ return `Found 1 ${singular}.`;
7424
+ }
7425
+ return `Found ${count} ${singular}s.`;
7426
+ }
7427
+ var init_tool_summary = __esm({
7428
+ "src/tools/tool_summary.ts"() {
7429
+ }
7430
+ });
7431
+
7394
7432
  // src/tools/review_affinity_tool.ts
7395
7433
  function buildReadOptions(input) {
7396
7434
  return {
@@ -8177,7 +8215,7 @@ var init_tool_registry = __esm({
8177
8215
  });
8178
8216
 
8179
8217
  // src/surface/affinity_surface_tools.ts
8180
- var affinitySurfaceTools;
8218
+ var affinitySurfaceTools, mapping, affinityCliSurfaceTools;
8181
8219
  var init_affinity_surface_tools = __esm({
8182
8220
  "src/surface/affinity_surface_tools.ts"() {
8183
8221
  init_tool_registry();
@@ -8191,83 +8229,67 @@ var init_affinity_surface_tools = __esm({
8191
8229
  inputSchema: { ...tool.inputSchema, type: "object" },
8192
8230
  handler: tool.handler
8193
8231
  }));
8194
- }
8195
- });
8196
-
8197
- // src/surfaces/surface_types.ts
8198
- function isErrorResult(result) {
8199
- return typeof result === "object" && result !== null && "ok" in result && result.ok === false;
8200
- }
8201
- var init_surface_types = __esm({
8202
- "src/surfaces/surface_types.ts"() {
8203
- }
8204
- });
8205
-
8206
- // src/surfaces/validate_input.ts
8207
- function validateInput(schema, input, path = "$") {
8208
- const errors = [];
8209
- const matchesType = (type) => {
8210
- switch (type) {
8211
- case "object":
8212
- return typeof input === "object" && input !== null && !Array.isArray(input);
8213
- case "array":
8214
- return Array.isArray(input);
8215
- case "integer":
8216
- return typeof input === "number" && Number.isInteger(input);
8217
- case "number":
8218
- return typeof input === "number" && Number.isFinite(input);
8219
- case "null":
8220
- return input === null;
8221
- default:
8222
- return typeof input === type;
8223
- }
8224
- };
8225
- if (schema.type && !(Array.isArray(schema.type) ? schema.type : [schema.type]).some(
8226
- matchesType
8227
- )) {
8228
- return [`${path}: expected ${String(schema.type)}`];
8229
- }
8230
- if (schema.enum && !schema.enum.some(
8231
- (value) => JSON.stringify(value) === JSON.stringify(input)
8232
- ))
8233
- errors.push(`${path}: expected one of ${JSON.stringify(schema.enum)}`);
8234
- if (schema.oneOf && !schema.oneOf.some(
8235
- (branch) => validateInput(branch, input, path).length === 0
8236
- ))
8237
- errors.push(`${path}: must match at least one schema branch`);
8238
- if (typeof input === "object" && input !== null && !Array.isArray(input)) {
8239
- const record = input;
8240
- for (const name of schema.required ?? [])
8241
- if (!Object.hasOwn(record, name))
8242
- errors.push(`${path}.${name}: required`);
8243
- for (const [name, property] of Object.entries(schema.properties ?? {}))
8244
- if (Object.hasOwn(record, name))
8245
- errors.push(
8246
- ...validateInput(property, record[name], `${path}.${name}`)
8247
- );
8248
- }
8249
- if (Array.isArray(input) && schema.items)
8250
- input.forEach((value, index) => {
8251
- errors.push(
8252
- ...validateInput(
8253
- schema.items,
8254
- value,
8255
- `${path}[${index}]`
8256
- )
8257
- );
8258
- });
8259
- return errors;
8260
- }
8261
- var init_validate_input = __esm({
8262
- "src/surfaces/validate_input.ts"() {
8263
- }
8264
- });
8265
-
8266
- // src/version.ts
8267
- var affinityVersion;
8268
- var init_version = __esm({
8269
- "src/version.ts"() {
8270
- affinityVersion = "0.3.0";
8232
+ mapping = {
8233
+ search_affinity: { verb: "search", positionals: ["query"] },
8234
+ review_affinity: { verb: "review", positionals: ["view"] },
8235
+ inspect_affinity_item: { verb: "show", positionals: ["kind"] },
8236
+ manage_contact: {
8237
+ noun: "contact",
8238
+ verb: "manage",
8239
+ action: {
8240
+ property: "action",
8241
+ verbs: { set_lifecycle: "lifecycle" },
8242
+ positionals: {
8243
+ create: ["input"],
8244
+ revise: ["contact"],
8245
+ set_lifecycle: ["contact", "lifecycleState"]
8246
+ }
8247
+ }
8248
+ },
8249
+ merge_contacts: { noun: "contact", verb: "merge" },
8250
+ manage_duplicate_dismissal: { noun: "duplicate", verb: "manage" },
8251
+ manage_identity: {
8252
+ noun: "identity",
8253
+ verb: "manage",
8254
+ action: {
8255
+ property: "action",
8256
+ positionals: {
8257
+ revise: ["identityId"],
8258
+ verify: ["identityId"],
8259
+ remove: ["identityId"]
8260
+ }
8261
+ }
8262
+ },
8263
+ manage_relationship: {
8264
+ noun: "relationship",
8265
+ verb: "manage",
8266
+ action: {
8267
+ property: "action",
8268
+ verbs: {
8269
+ seed_social_link: "add",
8270
+ revise_bond: "bond",
8271
+ override_state: "state",
8272
+ set_structural_tie: "tie",
8273
+ remove_structural_tie: "untie"
8274
+ }
8275
+ }
8276
+ },
8277
+ record_event: { noun: "event", verb: "record" },
8278
+ manage_commitment: { noun: "commitment", verb: "manage" },
8279
+ manage_date_anchor: {
8280
+ noun: "date",
8281
+ verb: "manage",
8282
+ action: {
8283
+ property: "action",
8284
+ positionals: { revise: ["anchorEventId"], remove: ["anchorEventId"] }
8285
+ }
8286
+ },
8287
+ manage_attribute: { noun: "attribute", verb: "manage" }
8288
+ };
8289
+ affinityCliSurfaceTools = /* @__PURE__ */ affinitySurfaceTools.map((tool) => ({
8290
+ ...tool,
8291
+ ...mapping[tool.name] ? { cli: mapping[tool.name] } : {}
8292
+ }));
8271
8293
  }
8272
8294
  });
8273
8295
 
@@ -8812,6 +8834,75 @@ var init_resolve_affinity_db_path = __esm({
8812
8834
  }
8813
8835
  });
8814
8836
 
8837
+ // src/surfaces/surface_types.ts
8838
+ function isErrorResult(result) {
8839
+ return typeof result === "object" && result !== null && "ok" in result && result.ok === false;
8840
+ }
8841
+ var init_surface_types = __esm({
8842
+ "src/surfaces/surface_types.ts"() {
8843
+ }
8844
+ });
8845
+
8846
+ // src/surfaces/validate_input.ts
8847
+ function validateInput(schema, input, path = "$") {
8848
+ const errors = [];
8849
+ const matchesType = (type) => {
8850
+ switch (type) {
8851
+ case "object":
8852
+ return typeof input === "object" && input !== null && !Array.isArray(input);
8853
+ case "array":
8854
+ return Array.isArray(input);
8855
+ case "integer":
8856
+ return typeof input === "number" && Number.isInteger(input);
8857
+ case "number":
8858
+ return typeof input === "number" && Number.isFinite(input);
8859
+ case "null":
8860
+ return input === null;
8861
+ default:
8862
+ return typeof input === type;
8863
+ }
8864
+ };
8865
+ if (schema.type && !(Array.isArray(schema.type) ? schema.type : [schema.type]).some(
8866
+ matchesType
8867
+ )) {
8868
+ return [`${path}: expected ${String(schema.type)}`];
8869
+ }
8870
+ if (schema.enum && !schema.enum.some(
8871
+ (value) => JSON.stringify(value) === JSON.stringify(input)
8872
+ ))
8873
+ errors.push(`${path}: expected one of ${JSON.stringify(schema.enum)}`);
8874
+ if (schema.oneOf && !schema.oneOf.some(
8875
+ (branch) => validateInput(branch, input, path).length === 0
8876
+ ))
8877
+ errors.push(`${path}: must match at least one schema branch`);
8878
+ if (typeof input === "object" && input !== null && !Array.isArray(input)) {
8879
+ const record = input;
8880
+ for (const name of schema.required ?? [])
8881
+ if (!Object.hasOwn(record, name))
8882
+ errors.push(`${path}.${name}: required`);
8883
+ for (const [name, property] of Object.entries(schema.properties ?? {}))
8884
+ if (Object.hasOwn(record, name))
8885
+ errors.push(
8886
+ ...validateInput(property, record[name], `${path}.${name}`)
8887
+ );
8888
+ }
8889
+ if (Array.isArray(input) && schema.items)
8890
+ input.forEach((value, index) => {
8891
+ errors.push(
8892
+ ...validateInput(
8893
+ schema.items,
8894
+ value,
8895
+ `${path}[${index}]`
8896
+ )
8897
+ );
8898
+ });
8899
+ return errors;
8900
+ }
8901
+ var init_validate_input = __esm({
8902
+ "src/surfaces/validate_input.ts"() {
8903
+ }
8904
+ });
8905
+
8815
8906
  // src/surfaces/mcp_server.ts
8816
8907
  function createMcpServer(options) {
8817
8908
  let initialized = false;
@@ -9026,7 +9117,7 @@ function affinityMcpOptions(context) {
9026
9117
  annotations: (tool) => ({
9027
9118
  readOnlyHint: tool.readOnly ?? false,
9028
9119
  destructiveHint: tool.name === "merge_contacts" || /remove|unset|undismiss/.test(
9029
- JSON.stringify(tool.inputSchema.properties?.action?.enum ?? [])
9120
+ String(tool.inputSchema.properties?.action?.enum ?? [])
9030
9121
  ),
9031
9122
  idempotentHint: tool.readOnly ?? false,
9032
9123
  openWorldHint: false
@@ -9929,29 +10020,434 @@ var init_affinity_skill_spec = __esm({
9929
10020
  packageName: "@ghostpaw/affinity",
9930
10021
  server: "ghostpaw-affinity",
9931
10022
  about: affinityMcpInstructions,
9932
- tools: affinitySurfaceTools,
10023
+ tools: affinityCliSurfaceTools,
9933
10024
  workflows: affinitySkills,
9934
10025
  principles: affinitySoulTraits.map((trait) => trait.principle)
9935
10026
  };
9936
10027
  }
9937
10028
  });
9938
10029
 
9939
- // src/cli.ts
10030
+ // src/surface/affinity_cli_options.ts
10031
+ init_version();
9940
10032
  init_affinity_surface_tools();
10033
+ var affinityCliOptions = {
10034
+ bin: "ghostpaw-affinity",
10035
+ version: affinityVersion,
10036
+ description: "Local relationship model and social CRM",
10037
+ tools: affinityCliSurfaceTools,
10038
+ openContext: async (options) => {
10039
+ const { openAffinityDb: openAffinityDb2 } = await Promise.resolve().then(() => (init_open_affinity_db(), open_affinity_db_exports));
10040
+ const { resolveAffinityDbPath: resolveAffinityDbPath2 } = await Promise.resolve().then(() => (init_resolve_affinity_db_path(), resolve_affinity_db_path_exports));
10041
+ return openAffinityDb2(
10042
+ resolveAffinityDbPath2(
10043
+ options.db === void 0 ? {} : { flag: options.db }
10044
+ )
10045
+ );
10046
+ },
10047
+ closeContext: (db) => db.close(),
10048
+ commands: {
10049
+ mcp: async (options) => {
10050
+ const { serveAffinityMcp: serveAffinityMcp2 } = await Promise.resolve().then(() => (init_serve_affinity_mcp(), serve_affinity_mcp_exports));
10051
+ await serveAffinityMcp2(
10052
+ options.db === void 0 ? {} : { dbPath: options.db }
10053
+ );
10054
+ },
10055
+ skill: async () => {
10056
+ const { renderSkillMarkdown: renderSkillMarkdown2 } = await Promise.resolve().then(() => (init_skill_markdown(), skill_markdown_exports));
10057
+ const { affinitySkillSpec: affinitySkillSpec2 } = await Promise.resolve().then(() => (init_affinity_skill_spec(), affinity_skill_spec_exports));
10058
+ return renderSkillMarkdown2(affinitySkillSpec2);
10059
+ }
10060
+ }
10061
+ };
10062
+ var CliUsageError = class extends Error {
10063
+ exitCode = 2;
10064
+ };
10065
+ var keyOf = (name) => name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
10066
+ function parseCliArgs(argv, spec) {
10067
+ const values = {};
10068
+ const positions = [];
10069
+ const flags = spec.flags ?? [];
10070
+ const convert = (raw, arg) => {
10071
+ let value = raw;
10072
+ if (raw.startsWith("@")) {
10073
+ try {
10074
+ value = readFileSync(raw.slice(1), "utf8");
10075
+ } catch {
10076
+ throw new CliUsageError(`Cannot read value file: ${raw.slice(1)}`);
10077
+ }
10078
+ }
10079
+ let result = value;
10080
+ if (arg.type === "number" || arg.type === "integer") {
10081
+ result = Number(value);
10082
+ if (!value.trim() || !Number.isFinite(result) || arg.type === "integer" && !Number.isInteger(result))
10083
+ throw new CliUsageError(
10084
+ `Expected ${arg.type} for ${arg.name}: ${value}`
10085
+ );
10086
+ } else if (arg.type === "boolean") {
10087
+ if (!["true", "false"].includes(value))
10088
+ throw new CliUsageError(`Expected boolean for ${arg.name}`);
10089
+ result = value === "true";
10090
+ } else if (arg.type === "json") {
10091
+ try {
10092
+ result = JSON.parse(value);
10093
+ } catch {
10094
+ throw new CliUsageError(`Invalid JSON for ${arg.name}`);
10095
+ }
10096
+ }
10097
+ if (arg.enum && !arg.enum.includes(result))
10098
+ throw new CliUsageError(
10099
+ `${arg.name} must be one of: ${arg.enum.join(", ")}`
10100
+ );
10101
+ return result;
10102
+ };
10103
+ let ended = false;
10104
+ for (let i = 0; i < argv.length; i++) {
10105
+ const token = argv[i];
10106
+ if (!ended && token === "--") {
10107
+ ended = true;
10108
+ continue;
10109
+ }
10110
+ if (ended || token === "-" || !token.startsWith("-")) {
10111
+ positions.push(token);
10112
+ continue;
10113
+ }
10114
+ const [flag, inline] = token.split(/=(.*)/s);
10115
+ const negative = flag?.startsWith("--no-");
10116
+ const name = flag?.replace(/^--?/, "").replace(/^no-/, "");
10117
+ const arg = flags.find(
10118
+ (entry) => entry.name === name || keyOf(entry.name) === name || entry.short === name
10119
+ );
10120
+ if (!arg) {
10121
+ const prefix = (name ?? "").slice(0, 3);
10122
+ const suggestion = prefix ? flags.find((entry) => entry.name.startsWith(prefix)) : void 0;
10123
+ throw new CliUsageError(
10124
+ `Unknown flag: ${flag}${suggestion ? `; did you mean --${suggestion.name}?` : ""}`
10125
+ );
10126
+ }
10127
+ if (negative && arg.type !== "boolean")
10128
+ throw new CliUsageError(`--no-${arg.name} requires a boolean flag`);
10129
+ let raw = inline;
10130
+ if (arg.type === "boolean" && raw === void 0)
10131
+ raw = negative ? "false" : "true";
10132
+ if (raw === void 0) {
10133
+ const next = argv[i + 1];
10134
+ if (next === void 0 || /^--[a-z]/i.test(next))
10135
+ throw new CliUsageError(`Missing value: --${arg.name}`);
10136
+ raw = next;
10137
+ i++;
10138
+ }
10139
+ const key = keyOf(arg.name);
10140
+ if (arg.type === "array") {
10141
+ const item = convert(raw, { ...arg, type: arg.itemType ?? "string" });
10142
+ values[key] = [...values[key] ?? [], item];
10143
+ } else values[key] = convert(raw, arg);
10144
+ }
10145
+ let index = 0;
10146
+ for (const arg of spec.positionals ?? []) {
10147
+ if (index >= positions.length) continue;
10148
+ values[keyOf(arg.name)] = arg.variadic ? positions.slice(index).map(
10149
+ (value) => convert(value, { ...arg, type: arg.itemType ?? arg.type })
10150
+ ) : convert(positions[index], arg);
10151
+ index = arg.variadic ? positions.length : index + 1;
10152
+ }
10153
+ if (index < positions.length)
10154
+ throw new CliUsageError(`Unexpected positional: ${positions[index]}`);
10155
+ for (const arg of [...flags, ...spec.positionals ?? []]) {
10156
+ if (arg.required && values[keyOf(arg.name)] === void 0)
10157
+ throw new CliUsageError(`Missing required argument: ${arg.name}`);
10158
+ }
10159
+ return values;
10160
+ }
10161
+ function renderCliHelp(spec) {
10162
+ const describe = (arg) => `${arg.type}${arg.enum ? ` (${arg.enum.join("|")})` : ""}${arg.required ? " required" : ""} ${arg.description ?? ""}`;
10163
+ return `Usage: ${spec.command} ${(spec.positionals ?? []).map((arg) => `<${arg.name}${arg.variadic ? "..." : ""}>`).join(" ")} [flags]
10164
+ ${spec.description ?? ""}
10165
+
10166
+ Positionals:
10167
+ ${(spec.positionals ?? []).map((arg) => ` ${arg.name} ${describe(arg)}`).join("\n")}
10168
+ Flags:
10169
+ ${(spec.flags ?? []).map((arg) => ` --${arg.name}${arg.short ? `, -${arg.short}` : ""} ${describe(arg)}`).join("\n")}
10170
+ `;
10171
+ }
10172
+
10173
+ // src/surfaces/cli_commands.ts
10174
+ init_validate_input();
10175
+ function cliCommands(tools) {
10176
+ return tools.flatMap((tool) => {
10177
+ const split = tool.name.indexOf("_");
10178
+ const cli = tool.cli ?? {
10179
+ noun: tool.name.slice(split + 1),
10180
+ verb: tool.name.slice(0, split)
10181
+ };
10182
+ const action = cli.action ?? (tool.inputSchema.properties?.action?.enum ? { property: "action" } : void 0);
10183
+ const actions = action ? tool.inputSchema.properties?.[action.property]?.enum ?? [] : [void 0];
10184
+ return actions.map((value) => {
10185
+ const verb = value === void 0 ? cli.verb : action?.verbs?.[String(value)] ?? String(value);
10186
+ return {
10187
+ tool,
10188
+ path: [cli.noun, verb].filter(Boolean).join(" "),
10189
+ fixed: action && value !== void 0 ? { [action.property]: value } : {},
10190
+ positionals: action?.positionals?.[String(value)] ?? cli.positionals ?? []
10191
+ };
10192
+ });
10193
+ });
10194
+ }
10195
+ async function runCliCommand(argv, options) {
10196
+ const stdout = options.stdout ?? process.stdout;
10197
+ const stderr = options.stderr ?? process.stderr;
10198
+ const stdin = options.stdin ?? process.stdin;
10199
+ const args = [...argv];
10200
+ const tree = cliCommands(options.tools);
10201
+ let json = argv.includes("--json");
10202
+ let quiet = argv.includes("--quiet");
10203
+ let compact = false;
10204
+ let help = false;
10205
+ let db;
10206
+ const fail = (message, code) => {
10207
+ stderr.write(`${message}
10208
+ `);
10209
+ if (json && !quiet)
10210
+ stdout.write(
10211
+ `${JSON.stringify({ ok: false, error: { code: code === 2 ? "usage_error" : "internal_error", message } })}
10212
+ `
10213
+ );
10214
+ return code;
10215
+ };
10216
+ const legacy = ["tools", "describe", "call", "mcp", "skill"];
10217
+ let first;
10218
+ for (let i = 0; i < argv.length; i++) {
10219
+ const token = argv[i];
10220
+ if (token === "--db") {
10221
+ i++;
10222
+ continue;
10223
+ }
10224
+ if (token.startsWith("-")) continue;
10225
+ first = token;
10226
+ break;
10227
+ }
10228
+ if (legacy.includes(first ?? "") || options.tools.some(
10229
+ (tool) => tool.name.replace(/[-_]/g, "") === first?.replace(/[-_]/g, "")
10230
+ ))
10231
+ return void 0;
10232
+ try {
10233
+ for (let i = 0; i < args.length; i++) {
10234
+ const flag = args[i];
10235
+ if (flag === "--") break;
10236
+ if (flag === "--db" || flag?.startsWith("--db=")) {
10237
+ db = flag === "--db" ? args.splice(i + 1, 1)[0] : flag.slice(5);
10238
+ if (!db || db.startsWith("--"))
10239
+ throw new CliUsageError("--db requires a path");
10240
+ } else if (flag === "--json") json = true;
10241
+ else if (flag === "--quiet") quiet = true;
10242
+ else if (flag === "--compact") compact = true;
10243
+ else if (flag === "--help" || flag === "-h") help = true;
10244
+ else if (["--version", "-V", "-v"].includes(flag ?? "")) {
10245
+ if (!quiet) stdout.write(`${options.version}
10246
+ `);
10247
+ return 0;
10248
+ } else if (flag !== "--no-color") continue;
10249
+ args.splice(i--, 1);
10250
+ }
10251
+ if (args[0] === "version") {
10252
+ if (!quiet) stdout.write(`${options.version}
10253
+ `);
10254
+ return 0;
10255
+ }
10256
+ if (args[0] === "help") {
10257
+ help = true;
10258
+ args.shift();
10259
+ }
10260
+ if (!args.length) help = true;
10261
+ const entry = tree.find(
10262
+ (command) => command.path.split(" ").every((part, i) => args[i] === part)
10263
+ );
10264
+ const specFor = (command) => {
10265
+ const argument = (name, schema) => ({
10266
+ name,
10267
+ type: schema.type === "object" || !schema.type ? "json" : schema.type,
10268
+ description: schema.description,
10269
+ enum: schema.enum,
10270
+ ...schema.items?.type ? { itemType: schema.items.type } : {}
10271
+ });
10272
+ const properties = command.tool.inputSchema.properties ?? {};
10273
+ return {
10274
+ command: `${options.bin} ${command.path}`,
10275
+ description: command.tool.description,
10276
+ positionals: command.positionals.map((name) => ({
10277
+ ...argument(name, properties[name] ?? {}),
10278
+ ...properties[name]?.type === "array" ? {
10279
+ type: properties[name]?.items?.type ?? "string",
10280
+ variadic: true
10281
+ } : {}
10282
+ })),
10283
+ flags: Object.entries(properties).filter(([name]) => !(name in command.fixed)).map(([name, schema]) => ({
10284
+ ...argument(
10285
+ name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`),
10286
+ schema
10287
+ ),
10288
+ required: false,
10289
+ description: `${schema.description ?? ""}${command.tool.inputSchema.required?.includes(name) ? " (required)" : ""}`
10290
+ }))
10291
+ };
10292
+ };
10293
+ if (help) {
10294
+ const matches = tree.filter(
10295
+ (command) => command.path.startsWith(args.join(" "))
10296
+ );
10297
+ if (!matches.length)
10298
+ throw new CliUsageError(`Unknown command: ${args.join(" ")}`);
10299
+ if (!quiet)
10300
+ stdout.write(
10301
+ entry ? renderCliHelp(specFor(entry)) : `${options.bin} ${options.version}
10302
+ ${options.description}
10303
+
10304
+ Commands:
10305
+ ${matches.map((command) => ` ${command.path}`).join("\n")}
10306
+ mcp, skill, tools [--json], describe <tool>, call <tool> [JSON|-], completions <bash|zsh|fish>, version
10307
+ Global flags: --db <path>, --json, --quiet, --compact, --help/-h, --version/-V/-v, --no-color
10308
+ `
10309
+ );
10310
+ return 0;
10311
+ }
10312
+ if (args[0] === "completions") {
10313
+ const shell = args[1];
10314
+ if (!["bash", "zsh", "fish"].includes(shell ?? ""))
10315
+ throw new CliUsageError("Expected completion shell: bash, zsh or fish");
10316
+ if (quiet) return 0;
10317
+ const words = [
10318
+ ...new Set(tree.flatMap((command) => command.path.split(" ")))
10319
+ ].concat([
10320
+ "mcp",
10321
+ "skill",
10322
+ "tools",
10323
+ "describe",
10324
+ "call",
10325
+ "completions",
10326
+ "version"
10327
+ ]);
10328
+ if (shell === "bash")
10329
+ stdout.write(`complete -W '${words.join(" ")}' ${options.bin}
10330
+ `);
10331
+ else if (shell === "zsh")
10332
+ stdout.write(
10333
+ `#compdef ${options.bin}
10334
+ _arguments '*:command:(${words.join(" ")})'
10335
+ `
10336
+ );
10337
+ else if (shell === "fish")
10338
+ stdout.write(`complete -c ${options.bin} -f -a '${words.join(" ")}'
10339
+ `);
10340
+ else
10341
+ throw new CliUsageError("Expected completion shell: bash, zsh or fish");
10342
+ return 0;
10343
+ }
10344
+ if (!entry) throw new CliUsageError(`Unknown command: ${args.join(" ")}`);
10345
+ const rest = args.slice(entry.path.split(" ").length);
10346
+ let base = {};
10347
+ const spec = specFor(entry);
10348
+ const positionalIndexes = [];
10349
+ let ended = false;
10350
+ for (let i = 0; i < rest.length; i++) {
10351
+ const token = rest[i];
10352
+ if (!ended && token === "--") {
10353
+ ended = true;
10354
+ continue;
10355
+ }
10356
+ if (!ended && token.startsWith("--")) {
10357
+ const flag = spec.flags?.find((arg) => `--${arg.name}` === token);
10358
+ if (!token.includes("=") && flag && flag.type !== "boolean") i++;
10359
+ } else positionalIndexes.push(i);
10360
+ }
10361
+ const stdinIndex = positionalIndexes.length === 1 && rest[positionalIndexes[0]] === "-" ? positionalIndexes[0] : void 0;
10362
+ if (stdinIndex !== void 0 || !rest.length && !stdin.isTTY) {
10363
+ if (stdinIndex !== void 0) rest.splice(stdinIndex, 1);
10364
+ let raw = "";
10365
+ for await (const chunk of stdin) raw += String(chunk);
10366
+ try {
10367
+ base = raw.trim() ? JSON.parse(raw) : {};
10368
+ } catch {
10369
+ throw new CliUsageError("Invalid stdin JSON");
10370
+ }
10371
+ if (!base || typeof base !== "object" || Array.isArray(base))
10372
+ throw new CliUsageError("Expected stdin JSON object");
10373
+ }
10374
+ const input = {
10375
+ ...base,
10376
+ ...parseCliArgs(rest, spec),
10377
+ ...entry.fixed
10378
+ };
10379
+ const errors = validateInput(entry.tool.inputSchema, input);
10380
+ if (errors.length) throw new CliUsageError(errors.join("\n"));
10381
+ const context = await options.openContext(db === void 0 ? {} : { db });
10382
+ try {
10383
+ const result = await entry.tool.handler(context, input);
10384
+ const record = result;
10385
+ const code = record.outcome === "needs_clarification" || record.error?.code === "not_found" ? 3 : ["conflict", "version_mismatch"].includes(record.error?.code ?? "") ? 4 : record.ok === false ? 1 : 0;
10386
+ if (code)
10387
+ stderr.write(
10388
+ `${record.error?.message ?? record.summary ?? "Tool failed"}
10389
+ `
10390
+ );
10391
+ if (!quiet)
10392
+ stdout.write(
10393
+ json || !stdout.isTTY ? `${JSON.stringify(result, null, compact ? void 0 : 2)}
10394
+ ` : `${record.summary ?? ""}
10395
+ ${JSON.stringify(record.data ?? {}, null, 2)}
10396
+ `
10397
+ );
10398
+ return code;
10399
+ } finally {
10400
+ await options.closeContext?.(context);
10401
+ }
10402
+ } catch (error) {
10403
+ return fail(
10404
+ error instanceof Error ? error.message : String(error),
10405
+ error instanceof CliUsageError ? 2 : 70
10406
+ );
10407
+ }
10408
+ }
9941
10409
 
9942
10410
  // src/surfaces/cli.ts
9943
10411
  init_surface_types();
9944
10412
  init_validate_input();
9945
- async function runToolCli(argv, options) {
10413
+ async function runToolCli(argv, providedOptions) {
10414
+ let options = providedOptions;
10415
+ const end = argv.indexOf("--");
10416
+ if (argv.slice(0, end < 0 ? argv.length : end).some((arg) => ["--version", "-V", "-v"].includes(arg))) {
10417
+ if (!argv.includes("--quiet"))
10418
+ (options.stdout ?? process.stdout).write(`${options.version}
10419
+ `);
10420
+ return 0;
10421
+ }
10422
+ const modern = await runCliCommand(argv, options);
10423
+ if (modern !== void 0) return modern;
10424
+ const quiet = argv.includes("--quiet");
10425
+ const explicitJson = argv.includes("--json") && !argv.includes("tools");
10426
+ if (quiet)
10427
+ options = {
10428
+ ...options,
10429
+ stdout: new Writable({
10430
+ write(_chunk, _encoding, cb) {
10431
+ cb();
10432
+ }
10433
+ })
10434
+ };
9946
10435
  const stdout = options.stdout ?? process.stdout;
9947
10436
  const stderr = options.stderr ?? process.stderr;
9948
10437
  const stdin = options.stdin ?? process.stdin;
9949
- const args = [...argv];
10438
+ const args = argv.filter(
10439
+ (arg) => arg !== "--quiet" && arg !== "--no-color" && !(explicitJson && arg === "--json")
10440
+ );
9950
10441
  let db;
9951
10442
  let compact = false;
9952
10443
  const fail = (message, code = 2) => {
9953
10444
  stderr.write(`${message}
9954
10445
  `);
10446
+ if (explicitJson)
10447
+ stdout.write(
10448
+ `${JSON.stringify({ ok: false, error: { code: "cli_error", message } })}
10449
+ `
10450
+ );
9955
10451
  return code;
9956
10452
  };
9957
10453
  for (let i = 0; i < args.length; i++) {
@@ -9984,7 +10480,7 @@ ${options.tools.map((tool2) => `${tool2.name} ${tool2.description}`).join("\n")
9984
10480
  );
9985
10481
  return 0;
9986
10482
  }
9987
- if (["--version", "-v"].includes(command)) {
10483
+ if (["--version", "-V", "-v", "version"].includes(command)) {
9988
10484
  stdout.write(`${options.version}
9989
10485
  `);
9990
10486
  return 0;
@@ -9992,7 +10488,9 @@ ${options.tools.map((tool2) => `${tool2.name} ${tool2.description}`).join("\n")
9992
10488
  if (command === "tools") {
9993
10489
  stdout.write(
9994
10490
  args.includes("--json") ? `${JSON.stringify(
9995
- options.tools.map(({ handler: _handler, ...tool2 }) => tool2),
10491
+ options.tools.map(
10492
+ ({ handler: _handler, cli: _cli, ...tool2 }) => tool2
10493
+ ),
9996
10494
  null,
9997
10495
  2
9998
10496
  )}
@@ -10127,33 +10625,4 @@ ${JSON.stringify(tool.inputSchema, null, 2)}
10127
10625
  }
10128
10626
 
10129
10627
  // src/cli.ts
10130
- init_version();
10131
- process.exitCode = await runToolCli(process.argv.slice(2), {
10132
- bin: "ghostpaw-affinity",
10133
- version: affinityVersion,
10134
- description: "Local relationship model and social CRM",
10135
- tools: affinitySurfaceTools,
10136
- openContext: async (options) => {
10137
- const { openAffinityDb: openAffinityDb2 } = await Promise.resolve().then(() => (init_open_affinity_db(), open_affinity_db_exports));
10138
- const { resolveAffinityDbPath: resolveAffinityDbPath2 } = await Promise.resolve().then(() => (init_resolve_affinity_db_path(), resolve_affinity_db_path_exports));
10139
- return openAffinityDb2(
10140
- resolveAffinityDbPath2(
10141
- options.db === void 0 ? {} : { flag: options.db }
10142
- )
10143
- );
10144
- },
10145
- closeContext: (db) => db.close(),
10146
- commands: {
10147
- mcp: async (options) => {
10148
- const { serveAffinityMcp: serveAffinityMcp2 } = await Promise.resolve().then(() => (init_serve_affinity_mcp(), serve_affinity_mcp_exports));
10149
- await serveAffinityMcp2(
10150
- options.db === void 0 ? {} : { dbPath: options.db }
10151
- );
10152
- },
10153
- skill: async () => {
10154
- const { renderSkillMarkdown: renderSkillMarkdown2 } = await Promise.resolve().then(() => (init_skill_markdown(), skill_markdown_exports));
10155
- const { affinitySkillSpec: affinitySkillSpec2 } = await Promise.resolve().then(() => (init_affinity_skill_spec(), affinity_skill_spec_exports));
10156
- return renderSkillMarkdown2(affinitySkillSpec2);
10157
- }
10158
- }
10159
- });
10628
+ process.exitCode = await runToolCli(process.argv.slice(2), affinityCliOptions);