mellos-mapping 0.19.0 → 0.20.2

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 (43) hide show
  1. package/README.md +366 -56
  2. package/README.zh-CN.md +319 -47
  3. package/dist/hook-session-start.mjs +239 -0
  4. package/dist/mmap.mjs +338 -0
  5. package/dist/server.mjs +1737 -897
  6. package/dist/store-paths.mjs +107 -0
  7. package/dist/watch.mjs +1612 -902
  8. package/lib/domain/ops.d.ts +171 -0
  9. package/lib/domain/ops.js +384 -0
  10. package/lib/domain/types.d.ts +283 -0
  11. package/lib/domain/types.js +153 -0
  12. package/lib/render/canvas.d.ts +50 -0
  13. package/lib/render/canvas.js +210 -0
  14. package/lib/render/draw.d.ts +37 -0
  15. package/lib/render/draw.js +111 -0
  16. package/lib/render/layout.d.ts +89 -0
  17. package/lib/render/layout.js +200 -0
  18. package/lib/render/options.d.ts +39 -0
  19. package/lib/render/options.js +10 -0
  20. package/lib/render/render.d.ts +88 -0
  21. package/lib/render/render.js +128 -0
  22. package/lib/render/routing.d.ts +56 -0
  23. package/lib/render/routing.js +244 -0
  24. package/lib/render/skins.d.ts +54 -0
  25. package/lib/render/skins.js +99 -0
  26. package/lib/render/width.d.ts +24 -0
  27. package/lib/render/width.js +139 -0
  28. package/lib/render/zoom-geometry.d.ts +52 -0
  29. package/lib/render/zoom-geometry.js +56 -0
  30. package/lib/semantics/semantics.d.ts +169 -0
  31. package/lib/semantics/semantics.js +380 -0
  32. package/lib/semantics/vocabulary.d.ts +79 -0
  33. package/lib/semantics/vocabulary.js +112 -0
  34. package/lib/store/format.d.ts +67 -0
  35. package/lib/store/format.js +334 -0
  36. package/lib/store/store.d.ts +296 -0
  37. package/lib/store/store.js +734 -0
  38. package/package.json +41 -5
  39. package/scripts/codex-register.mjs +89 -20
  40. package/scripts/install-mmap-command.mjs +293 -0
  41. package/scripts/mmap.mjs +213 -0
  42. package/scripts/open-pane.mjs +115 -254
  43. package/scripts/pane-core.mjs +418 -0
package/dist/server.mjs CHANGED
@@ -243,13 +243,13 @@ var require_scope = __commonJS({
243
243
  }
244
244
  };
245
245
  exports.ValueScopeName = ValueScopeName;
246
- var line = (0, code_1._)`\n`;
246
+ var line2 = (0, code_1._)`\n`;
247
247
  var ValueScope = class extends Scope {
248
248
  constructor(opts) {
249
249
  super(opts);
250
250
  this._values = {};
251
251
  this._scope = opts.scope;
252
- this.opts = { ...opts, _n: opts.lines ? line : code_1.nil };
252
+ this.opts = { ...opts, _n: opts.lines ? line2 : code_1.nil };
253
253
  }
254
254
  get() {
255
255
  return this._scope;
@@ -2232,10 +2232,10 @@ var require_resolve = __commonJS({
2232
2232
  }
2233
2233
  return count;
2234
2234
  }
2235
- function getFullPath(resolver, id = "", normalize) {
2235
+ function getFullPath(resolver, id2 = "", normalize) {
2236
2236
  if (normalize !== false)
2237
- id = normalizeId(id);
2238
- const p = resolver.parse(id);
2237
+ id2 = normalizeId(id2);
2238
+ const p = resolver.parse(id2);
2239
2239
  return _getFullPath(resolver, p);
2240
2240
  }
2241
2241
  exports.getFullPath = getFullPath;
@@ -2245,13 +2245,13 @@ var require_resolve = __commonJS({
2245
2245
  }
2246
2246
  exports._getFullPath = _getFullPath;
2247
2247
  var TRAILING_SLASH_HASH = /#\/?$/;
2248
- function normalizeId(id) {
2249
- return id ? id.replace(TRAILING_SLASH_HASH, "") : "";
2248
+ function normalizeId(id2) {
2249
+ return id2 ? id2.replace(TRAILING_SLASH_HASH, "") : "";
2250
2250
  }
2251
2251
  exports.normalizeId = normalizeId;
2252
- function resolveUrl(resolver, baseId, id) {
2253
- id = normalizeId(id);
2254
- return resolver.resolve(baseId, id);
2252
+ function resolveUrl(resolver, baseId, id2) {
2253
+ id2 = normalizeId(id2);
2254
+ return resolver.resolve(baseId, id2);
2255
2255
  }
2256
2256
  exports.resolveUrl = resolveUrl;
2257
2257
  var ANCHOR = /^[a-z_][-a-z0-9._]*$/i;
@@ -3021,8 +3021,8 @@ var require_compile = __commonJS({
3021
3021
  if (Object.keys(root.schema).length > 0 && refPath === baseId) {
3022
3022
  return getJsonPointer.call(this, p, root);
3023
3023
  }
3024
- const id = (0, resolve_1.normalizeId)(refPath);
3025
- const schOrRef = this.refs[id] || this.schemas[id];
3024
+ const id2 = (0, resolve_1.normalizeId)(refPath);
3025
+ const schOrRef = this.refs[id2] || this.schemas[id2];
3026
3026
  if (typeof schOrRef == "string") {
3027
3027
  const sch = resolveSchema.call(this, root, schOrRef);
3028
3028
  if (typeof (sch === null || sch === void 0 ? void 0 : sch.schema) !== "object")
@@ -3033,7 +3033,7 @@ var require_compile = __commonJS({
3033
3033
  return;
3034
3034
  if (!schOrRef.validate)
3035
3035
  compileSchema.call(this, schOrRef);
3036
- if (id === (0, resolve_1.normalizeId)(ref)) {
3036
+ if (id2 === (0, resolve_1.normalizeId)(ref)) {
3037
3037
  const { schema } = schOrRef;
3038
3038
  const { schemaId } = this.opts;
3039
3039
  const schId = schema[schemaId];
@@ -4176,15 +4176,15 @@ var require_core = __commonJS({
4176
4176
  this.addSchema(sch, void 0, _meta, _validateSchema);
4177
4177
  return this;
4178
4178
  }
4179
- let id;
4179
+ let id2;
4180
4180
  if (typeof schema === "object") {
4181
4181
  const { schemaId } = this.opts;
4182
- id = schema[schemaId];
4183
- if (id !== void 0 && typeof id != "string") {
4182
+ id2 = schema[schemaId];
4183
+ if (id2 !== void 0 && typeof id2 != "string") {
4184
4184
  throw new Error(`schema ${schemaId} must be string`);
4185
4185
  }
4186
4186
  }
4187
- key = (0, resolve_1.normalizeId)(key || id);
4187
+ key = (0, resolve_1.normalizeId)(key || id2);
4188
4188
  this._checkUnique(key);
4189
4189
  this.schemas[key] = this._addSchema(schema, _meta, key, _validateSchema, true);
4190
4190
  return this;
@@ -4263,11 +4263,11 @@ var require_core = __commonJS({
4263
4263
  case "object": {
4264
4264
  const cacheKey = schemaKeyRef;
4265
4265
  this._cache.delete(cacheKey);
4266
- let id = schemaKeyRef[this.opts.schemaId];
4267
- if (id) {
4268
- id = (0, resolve_1.normalizeId)(id);
4269
- delete this.schemas[id];
4270
- delete this.refs[id];
4266
+ let id2 = schemaKeyRef[this.opts.schemaId];
4267
+ if (id2) {
4268
+ id2 = (0, resolve_1.normalizeId)(id2);
4269
+ delete this.schemas[id2];
4270
+ delete this.refs[id2];
4271
4271
  }
4272
4272
  return this;
4273
4273
  }
@@ -4374,10 +4374,10 @@ var require_core = __commonJS({
4374
4374
  }
4375
4375
  }
4376
4376
  _addSchema(schema, meta, baseId, validateSchema = this.opts.validateSchema, addSchema = this.opts.addUsedSchema) {
4377
- let id;
4377
+ let id2;
4378
4378
  const { schemaId } = this.opts;
4379
4379
  if (typeof schema == "object") {
4380
- id = schema[schemaId];
4380
+ id2 = schema[schemaId];
4381
4381
  } else {
4382
4382
  if (this.opts.jtd)
4383
4383
  throw new Error("schema must be object");
@@ -4387,7 +4387,7 @@ var require_core = __commonJS({
4387
4387
  let sch = this._cache.get(schema);
4388
4388
  if (sch !== void 0)
4389
4389
  return sch;
4390
- baseId = (0, resolve_1.normalizeId)(id || baseId);
4390
+ baseId = (0, resolve_1.normalizeId)(id2 || baseId);
4391
4391
  const localRefs = resolve_1.getSchemaRefs.call(this, schema, baseId);
4392
4392
  sch = new compile_1.SchemaEnv({ schema, schemaId, meta, baseId, localRefs });
4393
4393
  this._cache.set(sch.schema, sch);
@@ -4400,9 +4400,9 @@ var require_core = __commonJS({
4400
4400
  this.validateSchema(schema, true);
4401
4401
  return sch;
4402
4402
  }
4403
- _checkUnique(id) {
4404
- if (this.schemas[id] || this.refs[id]) {
4405
- throw new Error(`schema with key or id "${id}" already exists`);
4403
+ _checkUnique(id2) {
4404
+ if (this.schemas[id2] || this.refs[id2]) {
4405
+ throw new Error(`schema with key or id "${id2}" already exists`);
4406
4406
  }
4407
4407
  }
4408
4408
  _compileSchemaEnv(sch) {
@@ -6913,8 +6913,10 @@ var require_dist = __commonJS({
6913
6913
  });
6914
6914
 
6915
6915
  // src/server/server.ts
6916
- import { realpathSync } from "node:fs";
6917
- import { join as join2 } from "node:path";
6916
+ import { spawn } from "node:child_process";
6917
+ import { existsSync as existsSync2, realpathSync } from "node:fs";
6918
+ import { homedir } from "node:os";
6919
+ import { dirname as dirname2, join as join2 } from "node:path";
6918
6920
  import { fileURLToPath, pathToFileURL } from "node:url";
6919
6921
 
6920
6922
  // node_modules/zod/v3/external.js
@@ -7451,18 +7453,18 @@ var ParseStatus = class _ParseStatus {
7451
7453
  if (this.value !== "aborted")
7452
7454
  this.value = "aborted";
7453
7455
  }
7454
- static mergeArray(status, results) {
7456
+ static mergeArray(status2, results) {
7455
7457
  const arrayValue = [];
7456
7458
  for (const s of results) {
7457
7459
  if (s.status === "aborted")
7458
7460
  return INVALID;
7459
7461
  if (s.status === "dirty")
7460
- status.dirty();
7462
+ status2.dirty();
7461
7463
  arrayValue.push(s.value);
7462
7464
  }
7463
- return { status: status.value, value: arrayValue };
7465
+ return { status: status2.value, value: arrayValue };
7464
7466
  }
7465
- static async mergeObjectAsync(status, pairs) {
7467
+ static async mergeObjectAsync(status2, pairs) {
7466
7468
  const syncPairs = [];
7467
7469
  for (const pair of pairs) {
7468
7470
  const key = await pair.key;
@@ -7472,9 +7474,9 @@ var ParseStatus = class _ParseStatus {
7472
7474
  value
7473
7475
  });
7474
7476
  }
7475
- return _ParseStatus.mergeObjectSync(status, syncPairs);
7477
+ return _ParseStatus.mergeObjectSync(status2, syncPairs);
7476
7478
  }
7477
- static mergeObjectSync(status, pairs) {
7479
+ static mergeObjectSync(status2, pairs) {
7478
7480
  const finalObject = {};
7479
7481
  for (const pair of pairs) {
7480
7482
  const { key, value } = pair;
@@ -7483,14 +7485,14 @@ var ParseStatus = class _ParseStatus {
7483
7485
  if (value.status === "aborted")
7484
7486
  return INVALID;
7485
7487
  if (key.status === "dirty")
7486
- status.dirty();
7488
+ status2.dirty();
7487
7489
  if (value.status === "dirty")
7488
- status.dirty();
7490
+ status2.dirty();
7489
7491
  if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
7490
7492
  finalObject[key.value] = value.value;
7491
7493
  }
7492
7494
  }
7493
- return { status: status.value, value: finalObject };
7495
+ return { status: status2.value, value: finalObject };
7494
7496
  }
7495
7497
  };
7496
7498
  var INVALID = Object.freeze({
@@ -7950,7 +7952,7 @@ var ZodString = class _ZodString2 extends ZodType {
7950
7952
  });
7951
7953
  return INVALID;
7952
7954
  }
7953
- const status = new ParseStatus();
7955
+ const status2 = new ParseStatus();
7954
7956
  let ctx = void 0;
7955
7957
  for (const check2 of this._def.checks) {
7956
7958
  if (check2.kind === "min") {
@@ -7964,7 +7966,7 @@ var ZodString = class _ZodString2 extends ZodType {
7964
7966
  exact: false,
7965
7967
  message: check2.message
7966
7968
  });
7967
- status.dirty();
7969
+ status2.dirty();
7968
7970
  }
7969
7971
  } else if (check2.kind === "max") {
7970
7972
  if (input.data.length > check2.value) {
@@ -7977,7 +7979,7 @@ var ZodString = class _ZodString2 extends ZodType {
7977
7979
  exact: false,
7978
7980
  message: check2.message
7979
7981
  });
7980
- status.dirty();
7982
+ status2.dirty();
7981
7983
  }
7982
7984
  } else if (check2.kind === "length") {
7983
7985
  const tooBig = input.data.length > check2.value;
@@ -8003,7 +8005,7 @@ var ZodString = class _ZodString2 extends ZodType {
8003
8005
  message: check2.message
8004
8006
  });
8005
8007
  }
8006
- status.dirty();
8008
+ status2.dirty();
8007
8009
  }
8008
8010
  } else if (check2.kind === "email") {
8009
8011
  if (!emailRegex.test(input.data)) {
@@ -8013,7 +8015,7 @@ var ZodString = class _ZodString2 extends ZodType {
8013
8015
  code: ZodIssueCode.invalid_string,
8014
8016
  message: check2.message
8015
8017
  });
8016
- status.dirty();
8018
+ status2.dirty();
8017
8019
  }
8018
8020
  } else if (check2.kind === "emoji") {
8019
8021
  if (!emojiRegex) {
@@ -8026,7 +8028,7 @@ var ZodString = class _ZodString2 extends ZodType {
8026
8028
  code: ZodIssueCode.invalid_string,
8027
8029
  message: check2.message
8028
8030
  });
8029
- status.dirty();
8031
+ status2.dirty();
8030
8032
  }
8031
8033
  } else if (check2.kind === "uuid") {
8032
8034
  if (!uuidRegex.test(input.data)) {
@@ -8036,7 +8038,7 @@ var ZodString = class _ZodString2 extends ZodType {
8036
8038
  code: ZodIssueCode.invalid_string,
8037
8039
  message: check2.message
8038
8040
  });
8039
- status.dirty();
8041
+ status2.dirty();
8040
8042
  }
8041
8043
  } else if (check2.kind === "nanoid") {
8042
8044
  if (!nanoidRegex.test(input.data)) {
@@ -8046,7 +8048,7 @@ var ZodString = class _ZodString2 extends ZodType {
8046
8048
  code: ZodIssueCode.invalid_string,
8047
8049
  message: check2.message
8048
8050
  });
8049
- status.dirty();
8051
+ status2.dirty();
8050
8052
  }
8051
8053
  } else if (check2.kind === "cuid") {
8052
8054
  if (!cuidRegex.test(input.data)) {
@@ -8056,7 +8058,7 @@ var ZodString = class _ZodString2 extends ZodType {
8056
8058
  code: ZodIssueCode.invalid_string,
8057
8059
  message: check2.message
8058
8060
  });
8059
- status.dirty();
8061
+ status2.dirty();
8060
8062
  }
8061
8063
  } else if (check2.kind === "cuid2") {
8062
8064
  if (!cuid2Regex.test(input.data)) {
@@ -8066,7 +8068,7 @@ var ZodString = class _ZodString2 extends ZodType {
8066
8068
  code: ZodIssueCode.invalid_string,
8067
8069
  message: check2.message
8068
8070
  });
8069
- status.dirty();
8071
+ status2.dirty();
8070
8072
  }
8071
8073
  } else if (check2.kind === "ulid") {
8072
8074
  if (!ulidRegex.test(input.data)) {
@@ -8076,7 +8078,7 @@ var ZodString = class _ZodString2 extends ZodType {
8076
8078
  code: ZodIssueCode.invalid_string,
8077
8079
  message: check2.message
8078
8080
  });
8079
- status.dirty();
8081
+ status2.dirty();
8080
8082
  }
8081
8083
  } else if (check2.kind === "url") {
8082
8084
  try {
@@ -8088,7 +8090,7 @@ var ZodString = class _ZodString2 extends ZodType {
8088
8090
  code: ZodIssueCode.invalid_string,
8089
8091
  message: check2.message
8090
8092
  });
8091
- status.dirty();
8093
+ status2.dirty();
8092
8094
  }
8093
8095
  } else if (check2.kind === "regex") {
8094
8096
  check2.regex.lastIndex = 0;
@@ -8100,7 +8102,7 @@ var ZodString = class _ZodString2 extends ZodType {
8100
8102
  code: ZodIssueCode.invalid_string,
8101
8103
  message: check2.message
8102
8104
  });
8103
- status.dirty();
8105
+ status2.dirty();
8104
8106
  }
8105
8107
  } else if (check2.kind === "trim") {
8106
8108
  input.data = input.data.trim();
@@ -8112,7 +8114,7 @@ var ZodString = class _ZodString2 extends ZodType {
8112
8114
  validation: { includes: check2.value, position: check2.position },
8113
8115
  message: check2.message
8114
8116
  });
8115
- status.dirty();
8117
+ status2.dirty();
8116
8118
  }
8117
8119
  } else if (check2.kind === "toLowerCase") {
8118
8120
  input.data = input.data.toLowerCase();
@@ -8126,7 +8128,7 @@ var ZodString = class _ZodString2 extends ZodType {
8126
8128
  validation: { startsWith: check2.value },
8127
8129
  message: check2.message
8128
8130
  });
8129
- status.dirty();
8131
+ status2.dirty();
8130
8132
  }
8131
8133
  } else if (check2.kind === "endsWith") {
8132
8134
  if (!input.data.endsWith(check2.value)) {
@@ -8136,7 +8138,7 @@ var ZodString = class _ZodString2 extends ZodType {
8136
8138
  validation: { endsWith: check2.value },
8137
8139
  message: check2.message
8138
8140
  });
8139
- status.dirty();
8141
+ status2.dirty();
8140
8142
  }
8141
8143
  } else if (check2.kind === "datetime") {
8142
8144
  const regex = datetimeRegex(check2);
@@ -8147,7 +8149,7 @@ var ZodString = class _ZodString2 extends ZodType {
8147
8149
  validation: "datetime",
8148
8150
  message: check2.message
8149
8151
  });
8150
- status.dirty();
8152
+ status2.dirty();
8151
8153
  }
8152
8154
  } else if (check2.kind === "date") {
8153
8155
  const regex = dateRegex;
@@ -8158,7 +8160,7 @@ var ZodString = class _ZodString2 extends ZodType {
8158
8160
  validation: "date",
8159
8161
  message: check2.message
8160
8162
  });
8161
- status.dirty();
8163
+ status2.dirty();
8162
8164
  }
8163
8165
  } else if (check2.kind === "time") {
8164
8166
  const regex = timeRegex(check2);
@@ -8169,7 +8171,7 @@ var ZodString = class _ZodString2 extends ZodType {
8169
8171
  validation: "time",
8170
8172
  message: check2.message
8171
8173
  });
8172
- status.dirty();
8174
+ status2.dirty();
8173
8175
  }
8174
8176
  } else if (check2.kind === "duration") {
8175
8177
  if (!durationRegex.test(input.data)) {
@@ -8179,7 +8181,7 @@ var ZodString = class _ZodString2 extends ZodType {
8179
8181
  code: ZodIssueCode.invalid_string,
8180
8182
  message: check2.message
8181
8183
  });
8182
- status.dirty();
8184
+ status2.dirty();
8183
8185
  }
8184
8186
  } else if (check2.kind === "ip") {
8185
8187
  if (!isValidIP(input.data, check2.version)) {
@@ -8189,7 +8191,7 @@ var ZodString = class _ZodString2 extends ZodType {
8189
8191
  code: ZodIssueCode.invalid_string,
8190
8192
  message: check2.message
8191
8193
  });
8192
- status.dirty();
8194
+ status2.dirty();
8193
8195
  }
8194
8196
  } else if (check2.kind === "jwt") {
8195
8197
  if (!isValidJWT(input.data, check2.alg)) {
@@ -8199,7 +8201,7 @@ var ZodString = class _ZodString2 extends ZodType {
8199
8201
  code: ZodIssueCode.invalid_string,
8200
8202
  message: check2.message
8201
8203
  });
8202
- status.dirty();
8204
+ status2.dirty();
8203
8205
  }
8204
8206
  } else if (check2.kind === "cidr") {
8205
8207
  if (!isValidCidr(input.data, check2.version)) {
@@ -8209,7 +8211,7 @@ var ZodString = class _ZodString2 extends ZodType {
8209
8211
  code: ZodIssueCode.invalid_string,
8210
8212
  message: check2.message
8211
8213
  });
8212
- status.dirty();
8214
+ status2.dirty();
8213
8215
  }
8214
8216
  } else if (check2.kind === "base64") {
8215
8217
  if (!base64Regex.test(input.data)) {
@@ -8219,7 +8221,7 @@ var ZodString = class _ZodString2 extends ZodType {
8219
8221
  code: ZodIssueCode.invalid_string,
8220
8222
  message: check2.message
8221
8223
  });
8222
- status.dirty();
8224
+ status2.dirty();
8223
8225
  }
8224
8226
  } else if (check2.kind === "base64url") {
8225
8227
  if (!base64urlRegex.test(input.data)) {
@@ -8229,13 +8231,13 @@ var ZodString = class _ZodString2 extends ZodType {
8229
8231
  code: ZodIssueCode.invalid_string,
8230
8232
  message: check2.message
8231
8233
  });
8232
- status.dirty();
8234
+ status2.dirty();
8233
8235
  }
8234
8236
  } else {
8235
8237
  util.assertNever(check2);
8236
8238
  }
8237
8239
  }
8238
- return { status: status.value, value: input.data };
8240
+ return { status: status2.value, value: input.data };
8239
8241
  }
8240
8242
  _regex(regex, validation, message) {
8241
8243
  return this.refinement((data) => regex.test(data), {
@@ -8511,7 +8513,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8511
8513
  return INVALID;
8512
8514
  }
8513
8515
  let ctx = void 0;
8514
- const status = new ParseStatus();
8516
+ const status2 = new ParseStatus();
8515
8517
  for (const check2 of this._def.checks) {
8516
8518
  if (check2.kind === "int") {
8517
8519
  if (!util.isInteger(input.data)) {
@@ -8522,7 +8524,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8522
8524
  received: "float",
8523
8525
  message: check2.message
8524
8526
  });
8525
- status.dirty();
8527
+ status2.dirty();
8526
8528
  }
8527
8529
  } else if (check2.kind === "min") {
8528
8530
  const tooSmall = check2.inclusive ? input.data < check2.value : input.data <= check2.value;
@@ -8536,7 +8538,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8536
8538
  exact: false,
8537
8539
  message: check2.message
8538
8540
  });
8539
- status.dirty();
8541
+ status2.dirty();
8540
8542
  }
8541
8543
  } else if (check2.kind === "max") {
8542
8544
  const tooBig = check2.inclusive ? input.data > check2.value : input.data >= check2.value;
@@ -8550,7 +8552,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8550
8552
  exact: false,
8551
8553
  message: check2.message
8552
8554
  });
8553
- status.dirty();
8555
+ status2.dirty();
8554
8556
  }
8555
8557
  } else if (check2.kind === "multipleOf") {
8556
8558
  if (floatSafeRemainder(input.data, check2.value) !== 0) {
@@ -8560,7 +8562,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8560
8562
  multipleOf: check2.value,
8561
8563
  message: check2.message
8562
8564
  });
8563
- status.dirty();
8565
+ status2.dirty();
8564
8566
  }
8565
8567
  } else if (check2.kind === "finite") {
8566
8568
  if (!Number.isFinite(input.data)) {
@@ -8569,13 +8571,13 @@ var ZodNumber = class _ZodNumber extends ZodType {
8569
8571
  code: ZodIssueCode.not_finite,
8570
8572
  message: check2.message
8571
8573
  });
8572
- status.dirty();
8574
+ status2.dirty();
8573
8575
  }
8574
8576
  } else {
8575
8577
  util.assertNever(check2);
8576
8578
  }
8577
8579
  }
8578
- return { status: status.value, value: input.data };
8580
+ return { status: status2.value, value: input.data };
8579
8581
  }
8580
8582
  gte(value, message) {
8581
8583
  return this.setLimit("min", value, true, errorUtil.toString(message));
@@ -8740,7 +8742,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
8740
8742
  return this._getInvalidInput(input);
8741
8743
  }
8742
8744
  let ctx = void 0;
8743
- const status = new ParseStatus();
8745
+ const status2 = new ParseStatus();
8744
8746
  for (const check2 of this._def.checks) {
8745
8747
  if (check2.kind === "min") {
8746
8748
  const tooSmall = check2.inclusive ? input.data < check2.value : input.data <= check2.value;
@@ -8753,7 +8755,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
8753
8755
  inclusive: check2.inclusive,
8754
8756
  message: check2.message
8755
8757
  });
8756
- status.dirty();
8758
+ status2.dirty();
8757
8759
  }
8758
8760
  } else if (check2.kind === "max") {
8759
8761
  const tooBig = check2.inclusive ? input.data > check2.value : input.data >= check2.value;
@@ -8766,7 +8768,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
8766
8768
  inclusive: check2.inclusive,
8767
8769
  message: check2.message
8768
8770
  });
8769
- status.dirty();
8771
+ status2.dirty();
8770
8772
  }
8771
8773
  } else if (check2.kind === "multipleOf") {
8772
8774
  if (input.data % check2.value !== BigInt(0)) {
@@ -8776,13 +8778,13 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
8776
8778
  multipleOf: check2.value,
8777
8779
  message: check2.message
8778
8780
  });
8779
- status.dirty();
8781
+ status2.dirty();
8780
8782
  }
8781
8783
  } else {
8782
8784
  util.assertNever(check2);
8783
8785
  }
8784
8786
  }
8785
- return { status: status.value, value: input.data };
8787
+ return { status: status2.value, value: input.data };
8786
8788
  }
8787
8789
  _getInvalidInput(input) {
8788
8790
  const ctx = this._getOrReturnCtx(input);
@@ -8940,7 +8942,7 @@ var ZodDate = class _ZodDate extends ZodType {
8940
8942
  });
8941
8943
  return INVALID;
8942
8944
  }
8943
- const status = new ParseStatus();
8945
+ const status2 = new ParseStatus();
8944
8946
  let ctx = void 0;
8945
8947
  for (const check2 of this._def.checks) {
8946
8948
  if (check2.kind === "min") {
@@ -8954,7 +8956,7 @@ var ZodDate = class _ZodDate extends ZodType {
8954
8956
  minimum: check2.value,
8955
8957
  type: "date"
8956
8958
  });
8957
- status.dirty();
8959
+ status2.dirty();
8958
8960
  }
8959
8961
  } else if (check2.kind === "max") {
8960
8962
  if (input.data.getTime() > check2.value) {
@@ -8967,14 +8969,14 @@ var ZodDate = class _ZodDate extends ZodType {
8967
8969
  maximum: check2.value,
8968
8970
  type: "date"
8969
8971
  });
8970
- status.dirty();
8972
+ status2.dirty();
8971
8973
  }
8972
8974
  } else {
8973
8975
  util.assertNever(check2);
8974
8976
  }
8975
8977
  }
8976
8978
  return {
8977
- status: status.value,
8979
+ status: status2.value,
8978
8980
  value: new Date(input.data.getTime())
8979
8981
  };
8980
8982
  }
@@ -9160,7 +9162,7 @@ ZodVoid.create = (params) => {
9160
9162
  };
9161
9163
  var ZodArray = class _ZodArray extends ZodType {
9162
9164
  _parse(input) {
9163
- const { ctx, status } = this._processInputParams(input);
9165
+ const { ctx, status: status2 } = this._processInputParams(input);
9164
9166
  const def = this._def;
9165
9167
  if (ctx.parsedType !== ZodParsedType.array) {
9166
9168
  addIssueToContext(ctx, {
@@ -9183,7 +9185,7 @@ var ZodArray = class _ZodArray extends ZodType {
9183
9185
  exact: true,
9184
9186
  message: def.exactLength.message
9185
9187
  });
9186
- status.dirty();
9188
+ status2.dirty();
9187
9189
  }
9188
9190
  }
9189
9191
  if (def.minLength !== null) {
@@ -9196,7 +9198,7 @@ var ZodArray = class _ZodArray extends ZodType {
9196
9198
  exact: false,
9197
9199
  message: def.minLength.message
9198
9200
  });
9199
- status.dirty();
9201
+ status2.dirty();
9200
9202
  }
9201
9203
  }
9202
9204
  if (def.maxLength !== null) {
@@ -9209,20 +9211,20 @@ var ZodArray = class _ZodArray extends ZodType {
9209
9211
  exact: false,
9210
9212
  message: def.maxLength.message
9211
9213
  });
9212
- status.dirty();
9214
+ status2.dirty();
9213
9215
  }
9214
9216
  }
9215
9217
  if (ctx.common.async) {
9216
9218
  return Promise.all([...ctx.data].map((item, i) => {
9217
9219
  return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i));
9218
9220
  })).then((result2) => {
9219
- return ParseStatus.mergeArray(status, result2);
9221
+ return ParseStatus.mergeArray(status2, result2);
9220
9222
  });
9221
9223
  }
9222
9224
  const result = [...ctx.data].map((item, i) => {
9223
9225
  return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i));
9224
9226
  });
9225
- return ParseStatus.mergeArray(status, result);
9227
+ return ParseStatus.mergeArray(status2, result);
9226
9228
  }
9227
9229
  get element() {
9228
9230
  return this._def.type;
@@ -9311,7 +9313,7 @@ var ZodObject = class _ZodObject extends ZodType {
9311
9313
  });
9312
9314
  return INVALID;
9313
9315
  }
9314
- const { status, ctx } = this._processInputParams(input);
9316
+ const { status: status2, ctx } = this._processInputParams(input);
9315
9317
  const { shape, keys: shapeKeys } = this._getCached();
9316
9318
  const extraKeys = [];
9317
9319
  if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) {
@@ -9346,7 +9348,7 @@ var ZodObject = class _ZodObject extends ZodType {
9346
9348
  code: ZodIssueCode.unrecognized_keys,
9347
9349
  keys: extraKeys
9348
9350
  });
9349
- status.dirty();
9351
+ status2.dirty();
9350
9352
  }
9351
9353
  } else if (unknownKeys === "strip") {
9352
9354
  } else {
@@ -9380,10 +9382,10 @@ var ZodObject = class _ZodObject extends ZodType {
9380
9382
  }
9381
9383
  return syncPairs;
9382
9384
  }).then((syncPairs) => {
9383
- return ParseStatus.mergeObjectSync(status, syncPairs);
9385
+ return ParseStatus.mergeObjectSync(status2, syncPairs);
9384
9386
  });
9385
9387
  } else {
9386
- return ParseStatus.mergeObjectSync(status, pairs);
9388
+ return ParseStatus.mergeObjectSync(status2, pairs);
9387
9389
  }
9388
9390
  }
9389
9391
  get shape() {
@@ -9861,7 +9863,7 @@ function mergeValues(a, b) {
9861
9863
  }
9862
9864
  var ZodIntersection = class extends ZodType {
9863
9865
  _parse(input) {
9864
- const { status, ctx } = this._processInputParams(input);
9866
+ const { status: status2, ctx } = this._processInputParams(input);
9865
9867
  const handleParsed = (parsedLeft, parsedRight) => {
9866
9868
  if (isAborted(parsedLeft) || isAborted(parsedRight)) {
9867
9869
  return INVALID;
@@ -9874,9 +9876,9 @@ var ZodIntersection = class extends ZodType {
9874
9876
  return INVALID;
9875
9877
  }
9876
9878
  if (isDirty(parsedLeft) || isDirty(parsedRight)) {
9877
- status.dirty();
9879
+ status2.dirty();
9878
9880
  }
9879
- return { status: status.value, value: merged.data };
9881
+ return { status: status2.value, value: merged.data };
9880
9882
  };
9881
9883
  if (ctx.common.async) {
9882
9884
  return Promise.all([
@@ -9914,7 +9916,7 @@ ZodIntersection.create = (left, right, params) => {
9914
9916
  };
9915
9917
  var ZodTuple = class _ZodTuple extends ZodType {
9916
9918
  _parse(input) {
9917
- const { status, ctx } = this._processInputParams(input);
9919
+ const { status: status2, ctx } = this._processInputParams(input);
9918
9920
  if (ctx.parsedType !== ZodParsedType.array) {
9919
9921
  addIssueToContext(ctx, {
9920
9922
  code: ZodIssueCode.invalid_type,
@@ -9942,7 +9944,7 @@ var ZodTuple = class _ZodTuple extends ZodType {
9942
9944
  exact: false,
9943
9945
  type: "array"
9944
9946
  });
9945
- status.dirty();
9947
+ status2.dirty();
9946
9948
  }
9947
9949
  const items = [...ctx.data].map((item, itemIndex) => {
9948
9950
  const schema = this._def.items[itemIndex] || this._def.rest;
@@ -9952,10 +9954,10 @@ var ZodTuple = class _ZodTuple extends ZodType {
9952
9954
  }).filter((x) => !!x);
9953
9955
  if (ctx.common.async) {
9954
9956
  return Promise.all(items).then((results) => {
9955
- return ParseStatus.mergeArray(status, results);
9957
+ return ParseStatus.mergeArray(status2, results);
9956
9958
  });
9957
9959
  } else {
9958
- return ParseStatus.mergeArray(status, items);
9960
+ return ParseStatus.mergeArray(status2, items);
9959
9961
  }
9960
9962
  }
9961
9963
  get items() {
@@ -9987,7 +9989,7 @@ var ZodRecord = class _ZodRecord extends ZodType {
9987
9989
  return this._def.valueType;
9988
9990
  }
9989
9991
  _parse(input) {
9990
- const { status, ctx } = this._processInputParams(input);
9992
+ const { status: status2, ctx } = this._processInputParams(input);
9991
9993
  if (ctx.parsedType !== ZodParsedType.object) {
9992
9994
  addIssueToContext(ctx, {
9993
9995
  code: ZodIssueCode.invalid_type,
@@ -10007,9 +10009,9 @@ var ZodRecord = class _ZodRecord extends ZodType {
10007
10009
  });
10008
10010
  }
10009
10011
  if (ctx.common.async) {
10010
- return ParseStatus.mergeObjectAsync(status, pairs);
10012
+ return ParseStatus.mergeObjectAsync(status2, pairs);
10011
10013
  } else {
10012
- return ParseStatus.mergeObjectSync(status, pairs);
10014
+ return ParseStatus.mergeObjectSync(status2, pairs);
10013
10015
  }
10014
10016
  }
10015
10017
  get element() {
@@ -10040,7 +10042,7 @@ var ZodMap = class extends ZodType {
10040
10042
  return this._def.valueType;
10041
10043
  }
10042
10044
  _parse(input) {
10043
- const { status, ctx } = this._processInputParams(input);
10045
+ const { status: status2, ctx } = this._processInputParams(input);
10044
10046
  if (ctx.parsedType !== ZodParsedType.map) {
10045
10047
  addIssueToContext(ctx, {
10046
10048
  code: ZodIssueCode.invalid_type,
@@ -10067,11 +10069,11 @@ var ZodMap = class extends ZodType {
10067
10069
  return INVALID;
10068
10070
  }
10069
10071
  if (key.status === "dirty" || value.status === "dirty") {
10070
- status.dirty();
10072
+ status2.dirty();
10071
10073
  }
10072
10074
  finalMap.set(key.value, value.value);
10073
10075
  }
10074
- return { status: status.value, value: finalMap };
10076
+ return { status: status2.value, value: finalMap };
10075
10077
  });
10076
10078
  } else {
10077
10079
  const finalMap = /* @__PURE__ */ new Map();
@@ -10082,11 +10084,11 @@ var ZodMap = class extends ZodType {
10082
10084
  return INVALID;
10083
10085
  }
10084
10086
  if (key.status === "dirty" || value.status === "dirty") {
10085
- status.dirty();
10087
+ status2.dirty();
10086
10088
  }
10087
10089
  finalMap.set(key.value, value.value);
10088
10090
  }
10089
- return { status: status.value, value: finalMap };
10091
+ return { status: status2.value, value: finalMap };
10090
10092
  }
10091
10093
  }
10092
10094
  };
@@ -10100,7 +10102,7 @@ ZodMap.create = (keyType, valueType, params) => {
10100
10102
  };
10101
10103
  var ZodSet = class _ZodSet extends ZodType {
10102
10104
  _parse(input) {
10103
- const { status, ctx } = this._processInputParams(input);
10105
+ const { status: status2, ctx } = this._processInputParams(input);
10104
10106
  if (ctx.parsedType !== ZodParsedType.set) {
10105
10107
  addIssueToContext(ctx, {
10106
10108
  code: ZodIssueCode.invalid_type,
@@ -10120,7 +10122,7 @@ var ZodSet = class _ZodSet extends ZodType {
10120
10122
  exact: false,
10121
10123
  message: def.minSize.message
10122
10124
  });
10123
- status.dirty();
10125
+ status2.dirty();
10124
10126
  }
10125
10127
  }
10126
10128
  if (def.maxSize !== null) {
@@ -10133,7 +10135,7 @@ var ZodSet = class _ZodSet extends ZodType {
10133
10135
  exact: false,
10134
10136
  message: def.maxSize.message
10135
10137
  });
10136
- status.dirty();
10138
+ status2.dirty();
10137
10139
  }
10138
10140
  }
10139
10141
  const valueType = this._def.valueType;
@@ -10143,10 +10145,10 @@ var ZodSet = class _ZodSet extends ZodType {
10143
10145
  if (element.status === "aborted")
10144
10146
  return INVALID;
10145
10147
  if (element.status === "dirty")
10146
- status.dirty();
10148
+ status2.dirty();
10147
10149
  parsedSet.add(element.value);
10148
10150
  }
10149
- return { status: status.value, value: parsedSet };
10151
+ return { status: status2.value, value: parsedSet };
10150
10152
  }
10151
10153
  const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i)));
10152
10154
  if (ctx.common.async) {
@@ -10477,15 +10479,15 @@ var ZodEffects = class extends ZodType {
10477
10479
  return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema;
10478
10480
  }
10479
10481
  _parse(input) {
10480
- const { status, ctx } = this._processInputParams(input);
10482
+ const { status: status2, ctx } = this._processInputParams(input);
10481
10483
  const effect = this._def.effect || null;
10482
10484
  const checkCtx = {
10483
10485
  addIssue: (arg) => {
10484
10486
  addIssueToContext(ctx, arg);
10485
10487
  if (arg.fatal) {
10486
- status.abort();
10488
+ status2.abort();
10487
10489
  } else {
10488
- status.dirty();
10490
+ status2.dirty();
10489
10491
  }
10490
10492
  },
10491
10493
  get path() {
@@ -10497,7 +10499,7 @@ var ZodEffects = class extends ZodType {
10497
10499
  const processed = effect.transform(ctx.data, checkCtx);
10498
10500
  if (ctx.common.async) {
10499
10501
  return Promise.resolve(processed).then(async (processed2) => {
10500
- if (status.value === "aborted")
10502
+ if (status2.value === "aborted")
10501
10503
  return INVALID;
10502
10504
  const result = await this._def.schema._parseAsync({
10503
10505
  data: processed2,
@@ -10508,12 +10510,12 @@ var ZodEffects = class extends ZodType {
10508
10510
  return INVALID;
10509
10511
  if (result.status === "dirty")
10510
10512
  return DIRTY(result.value);
10511
- if (status.value === "dirty")
10513
+ if (status2.value === "dirty")
10512
10514
  return DIRTY(result.value);
10513
10515
  return result;
10514
10516
  });
10515
10517
  } else {
10516
- if (status.value === "aborted")
10518
+ if (status2.value === "aborted")
10517
10519
  return INVALID;
10518
10520
  const result = this._def.schema._parseSync({
10519
10521
  data: processed,
@@ -10524,7 +10526,7 @@ var ZodEffects = class extends ZodType {
10524
10526
  return INVALID;
10525
10527
  if (result.status === "dirty")
10526
10528
  return DIRTY(result.value);
10527
- if (status.value === "dirty")
10529
+ if (status2.value === "dirty")
10528
10530
  return DIRTY(result.value);
10529
10531
  return result;
10530
10532
  }
@@ -10549,17 +10551,17 @@ var ZodEffects = class extends ZodType {
10549
10551
  if (inner.status === "aborted")
10550
10552
  return INVALID;
10551
10553
  if (inner.status === "dirty")
10552
- status.dirty();
10554
+ status2.dirty();
10553
10555
  executeRefinement(inner.value);
10554
- return { status: status.value, value: inner.value };
10556
+ return { status: status2.value, value: inner.value };
10555
10557
  } else {
10556
10558
  return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => {
10557
10559
  if (inner.status === "aborted")
10558
10560
  return INVALID;
10559
10561
  if (inner.status === "dirty")
10560
- status.dirty();
10562
+ status2.dirty();
10561
10563
  return executeRefinement(inner.value).then(() => {
10562
- return { status: status.value, value: inner.value };
10564
+ return { status: status2.value, value: inner.value };
10563
10565
  });
10564
10566
  });
10565
10567
  }
@@ -10577,13 +10579,13 @@ var ZodEffects = class extends ZodType {
10577
10579
  if (result instanceof Promise) {
10578
10580
  throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
10579
10581
  }
10580
- return { status: status.value, value: result };
10582
+ return { status: status2.value, value: result };
10581
10583
  } else {
10582
10584
  return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => {
10583
10585
  if (!isValid(base))
10584
10586
  return INVALID;
10585
10587
  return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({
10586
- status: status.value,
10588
+ status: status2.value,
10587
10589
  value: result
10588
10590
  }));
10589
10591
  });
@@ -10762,7 +10764,7 @@ var ZodBranded = class extends ZodType {
10762
10764
  };
10763
10765
  var ZodPipeline = class _ZodPipeline extends ZodType {
10764
10766
  _parse(input) {
10765
- const { status, ctx } = this._processInputParams(input);
10767
+ const { status: status2, ctx } = this._processInputParams(input);
10766
10768
  if (ctx.common.async) {
10767
10769
  const handleAsync = async () => {
10768
10770
  const inResult = await this._def.in._parseAsync({
@@ -10773,7 +10775,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
10773
10775
  if (inResult.status === "aborted")
10774
10776
  return INVALID;
10775
10777
  if (inResult.status === "dirty") {
10776
- status.dirty();
10778
+ status2.dirty();
10777
10779
  return DIRTY(inResult.value);
10778
10780
  } else {
10779
10781
  return this._def.out._parseAsync({
@@ -10793,7 +10795,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
10793
10795
  if (inResult.status === "aborted")
10794
10796
  return INVALID;
10795
10797
  if (inResult.status === "dirty") {
10796
- status.dirty();
10798
+ status2.dirty();
10797
10799
  return {
10798
10800
  status: "dirty",
10799
10801
  value: inResult.value
@@ -12132,8 +12134,8 @@ var Doc = class {
12132
12134
  const lines = content.split("\n").filter((x) => x);
12133
12135
  const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));
12134
12136
  const dedented = lines.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
12135
- for (const line of dedented) {
12136
- this.content.push(line);
12137
+ for (const line2 of dedented) {
12138
+ this.content.push(line2);
12137
12139
  }
12138
12140
  }
12139
12141
  compile() {
@@ -12728,38 +12730,38 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
12728
12730
  doc.write(`const newResult = {}`);
12729
12731
  for (const key of normalized.keys) {
12730
12732
  if (normalized.optionalKeys.has(key)) {
12731
- const id = ids[key];
12732
- doc.write(`const ${id} = ${parseStr(key)};`);
12733
+ const id2 = ids[key];
12734
+ doc.write(`const ${id2} = ${parseStr(key)};`);
12733
12735
  const k = esc(key);
12734
12736
  doc.write(`
12735
- if (${id}.issues.length) {
12737
+ if (${id2}.issues.length) {
12736
12738
  if (input[${k}] === undefined) {
12737
12739
  if (${k} in input) {
12738
12740
  newResult[${k}] = undefined;
12739
12741
  }
12740
12742
  } else {
12741
12743
  payload.issues = payload.issues.concat(
12742
- ${id}.issues.map((iss) => ({
12744
+ ${id2}.issues.map((iss) => ({
12743
12745
  ...iss,
12744
12746
  path: iss.path ? [${k}, ...iss.path] : [${k}],
12745
12747
  }))
12746
12748
  );
12747
12749
  }
12748
- } else if (${id}.value === undefined) {
12750
+ } else if (${id2}.value === undefined) {
12749
12751
  if (${k} in input) newResult[${k}] = undefined;
12750
12752
  } else {
12751
- newResult[${k}] = ${id}.value;
12753
+ newResult[${k}] = ${id2}.value;
12752
12754
  }
12753
12755
  `);
12754
12756
  } else {
12755
- const id = ids[key];
12756
- doc.write(`const ${id} = ${parseStr(key)};`);
12757
+ const id2 = ids[key];
12758
+ doc.write(`const ${id2} = ${parseStr(key)};`);
12757
12759
  doc.write(`
12758
- if (${id}.issues.length) payload.issues = payload.issues.concat(${id}.issues.map(iss => ({
12760
+ if (${id2}.issues.length) payload.issues = payload.issues.concat(${id2}.issues.map(iss => ({
12759
12761
  ...iss,
12760
12762
  path: iss.path ? [${esc(key)}, ...iss.path] : [${esc(key)}]
12761
12763
  })));`);
12762
- doc.write(`newResult[${esc(key)}] = ${id}.value`);
12764
+ doc.write(`newResult[${esc(key)}] = ${id2}.value`);
12763
12765
  }
12764
12766
  }
12765
12767
  doc.write(`payload.value = newResult;`);
@@ -14492,13 +14494,13 @@ var JSONSchemaGenerator = class {
14492
14494
  const defsSegment = this.target === "draft-2020-12" ? "$defs" : "definitions";
14493
14495
  if (params.external) {
14494
14496
  const externalId = params.external.registry.get(entry[0])?.id;
14495
- const uriGenerator = params.external.uri ?? ((id2) => id2);
14497
+ const uriGenerator = params.external.uri ?? ((id3) => id3);
14496
14498
  if (externalId) {
14497
14499
  return { ref: uriGenerator(externalId) };
14498
14500
  }
14499
- const id = entry[1].defId ?? entry[1].schema.id ?? `schema${this.counter++}`;
14500
- entry[1].defId = id;
14501
- return { defId: id, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id}` };
14501
+ const id2 = entry[1].defId ?? entry[1].schema.id ?? `schema${this.counter++}`;
14502
+ entry[1].defId = id2;
14503
+ return { defId: id2, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id2}` };
14502
14504
  }
14503
14505
  if (entry[1] === root) {
14504
14506
  return { ref: "#" };
@@ -14546,8 +14548,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14546
14548
  continue;
14547
14549
  }
14548
14550
  }
14549
- const id = this.metadataRegistry.get(entry[0])?.id;
14550
- if (id) {
14551
+ const id2 = this.metadataRegistry.get(entry[0])?.id;
14552
+ if (id2) {
14551
14553
  extractToDef(entry);
14552
14554
  continue;
14553
14555
  }
@@ -14601,10 +14603,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14601
14603
  console.warn(`Invalid target: ${this.target}`);
14602
14604
  }
14603
14605
  if (params.external?.uri) {
14604
- const id = params.external.registry.get(schema)?.id;
14605
- if (!id)
14606
+ const id2 = params.external.registry.get(schema)?.id;
14607
+ if (!id2)
14606
14608
  throw new Error("Schema is missing an `id` property");
14607
- result.$id = params.external.uri(id);
14609
+ result.$id = params.external.uri(id2);
14608
14610
  }
14609
14611
  Object.assign(result, root.def);
14610
14612
  const defs = params.external?.defs ?? {};
@@ -17197,8 +17199,8 @@ var UrlElicitationRequiredError = class extends McpError {
17197
17199
  };
17198
17200
 
17199
17201
  // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
17200
- function isTerminal(status) {
17201
- return status === "completed" || status === "failed" || status === "cancelled";
17202
+ function isTerminal(status2) {
17203
+ return status2 === "completed" || status2 === "failed" || status2 === "cancelled";
17202
17204
  }
17203
17205
 
17204
17206
  // node_modules/zod-to-json-schema/dist/esm/Options.js
@@ -19417,8 +19419,8 @@ var Protocol = class {
19417
19419
  }
19418
19420
  return task;
19419
19421
  },
19420
- storeTaskResult: async (taskId, status, result) => {
19421
- await taskStore.storeTaskResult(taskId, status, result, sessionId);
19422
+ storeTaskResult: async (taskId, status2, result) => {
19423
+ await taskStore.storeTaskResult(taskId, status2, result, sessionId);
19422
19424
  const task = await taskStore.getTask(taskId, sessionId);
19423
19425
  if (task) {
19424
19426
  const notification = TaskStatusNotificationSchema.parse({
@@ -19434,15 +19436,15 @@ var Protocol = class {
19434
19436
  getTaskResult: (taskId) => {
19435
19437
  return taskStore.getTaskResult(taskId, sessionId);
19436
19438
  },
19437
- updateTaskStatus: async (taskId, status, statusMessage) => {
19439
+ updateTaskStatus: async (taskId, status2, statusMessage) => {
19438
19440
  const task = await taskStore.getTask(taskId, sessionId);
19439
19441
  if (!task) {
19440
19442
  throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
19441
19443
  }
19442
19444
  if (isTerminal(task.status)) {
19443
- throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
19445
+ throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status2}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
19444
19446
  }
19445
- await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId);
19447
+ await taskStore.updateTaskStatus(taskId, status2, statusMessage, sessionId);
19446
19448
  const updatedTask = await taskStore.getTask(taskId, sessionId);
19447
19449
  if (updatedTask) {
19448
19450
  const notification = TaskStatusNotificationSchema.parse({
@@ -19637,7 +19639,7 @@ var ExperimentalServerTasks = class {
19637
19639
  if (hasPreviousToolUse) {
19638
19640
  const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
19639
19641
  const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
19640
- if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
19642
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id2) => toolResultIds.has(id2))) {
19641
19643
  throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
19642
19644
  }
19643
19645
  }
@@ -20062,7 +20064,7 @@ var Server = class extends Protocol {
20062
20064
  if (hasPreviousToolUse) {
20063
20065
  const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
20064
20066
  const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
20065
- if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
20067
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id2) => toolResultIds.has(id2))) {
20066
20068
  throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
20067
20069
  }
20068
20070
  }
@@ -21072,16 +21074,16 @@ var ReadBuffer = class {
21072
21074
  if (index === -1) {
21073
21075
  return null;
21074
21076
  }
21075
- const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
21077
+ const line2 = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
21076
21078
  this._buffer = this._buffer.subarray(index + 1);
21077
- return deserializeMessage(line);
21079
+ return deserializeMessage(line2);
21078
21080
  }
21079
21081
  clear() {
21080
21082
  this._buffer = void 0;
21081
21083
  }
21082
21084
  };
21083
- function deserializeMessage(line) {
21084
- return JSONRPCMessageSchema.parse(JSON.parse(line));
21085
+ function deserializeMessage(line2) {
21086
+ return JSONRPCMessageSchema.parse(JSON.parse(line2));
21085
21087
  }
21086
21088
  function serializeMessage(message) {
21087
21089
  return JSON.stringify(message) + "\n";
@@ -21177,6 +21179,12 @@ function makeNodeKind(raw) {
21177
21179
  function makeSubmapRef(raw) {
21178
21180
  return ID_RULE.test(raw) ? ok(raw) : err({ kind: "invalid-id", raw, rule: ID_RULE_TEXT });
21179
21181
  }
21182
+ var RANK_MIN = 0;
21183
+ var RANK_MAX = 99;
21184
+ var RANK_RULE_TEXT = `an integer in ${RANK_MIN}..${RANK_MAX}, 0 = bottom / most primitive`;
21185
+ function makeRank(raw) {
21186
+ return Number.isInteger(raw) && raw >= RANK_MIN && raw <= RANK_MAX ? ok(raw) : err({ kind: "invalid-rank", raw, rule: RANK_RULE_TEXT });
21187
+ }
21180
21188
  var MAP_KINDS = ["dev", "architecture", "dataflow", "behavior-tree", "sequence"];
21181
21189
  function makeMapKind(raw) {
21182
21190
  return MAP_KINDS.includes(raw) ? ok(raw) : err({ kind: "invalid-map-kind", raw });
@@ -21190,6 +21198,8 @@ function describeMapError(e) {
21190
21198
  switch (e.kind) {
21191
21199
  case "invalid-id":
21192
21200
  return `invalid id "${e.raw}" (rule: ${e.rule})`;
21201
+ case "invalid-rank":
21202
+ return `invalid rank ${e.raw} (rule: ${e.rule})`;
21193
21203
  case "invalid-status":
21194
21204
  return `invalid status "${e.raw}" (expected: ${NODE_STATUSES.join(" | ")})`;
21195
21205
  case "duplicate-layer":
@@ -21210,6 +21220,8 @@ function describeMapError(e) {
21210
21220
  return `node "${e.id}" cannot depend on itself`;
21211
21221
  case "duplicate-group":
21212
21222
  return `group "${e.id}" already exists`;
21223
+ case "id-collision":
21224
+ return `id "${e.id}" already names a ${e.taken} on this map; nodes and groups share one id namespace (both render as boxes, so one id must mean one box) \u2014 rename "${e.id}"`;
21213
21225
  case "unknown-group":
21214
21226
  return `group "${e.id}" does not exist`;
21215
21227
  case "invalid-map-kind":
@@ -21221,23 +21233,23 @@ function describeMapError(e) {
21221
21233
  case "group-layer-mismatch":
21222
21234
  return `node "${e.node}" (layer ${e.nodeLayer}) cannot join group "${e.group}" (layer ${e.groupLayer}); groups cluster nodes within one band`;
21223
21235
  case "layer-not-empty":
21224
- return `layer "${e.id}" still holds node "${e.occupant}"; move or remove its nodes first`;
21236
+ return `layer "${e.id}" still holds node "${e.occupant}"; move its nodes to another band (moveNode) or remove them (removeNode) first`;
21225
21237
  case "layer-holds-group":
21226
- return `layer "${e.id}" still holds group "${e.occupant}"; remove its groups first`;
21238
+ return `layer "${e.id}" still holds group "${e.occupant}"; remove its groups (removeGroup) first`;
21227
21239
  case "edge-not-downward":
21228
21240
  return `edge ${e.from} (rank ${e.fromRank}) -> ${e.to} (rank ${e.toRank}) is not strictly downward; dependencies may only point to a lower layer`;
21229
21241
  }
21230
21242
  }
21231
21243
 
21232
21244
  // src/domain/ops.ts
21233
- function findLayer(map, id) {
21234
- return map.layers.find((l) => l.id === id);
21245
+ function findLayer(map, id2) {
21246
+ return map.layers.find((l) => l.id === id2);
21235
21247
  }
21236
- function findNode(map, id) {
21237
- return map.nodes.find((n) => n.id === id);
21248
+ function findNode(map, id2) {
21249
+ return map.nodes.find((n) => n.id === id2);
21238
21250
  }
21239
- function findGroup(map, id) {
21240
- return map.groups.find((g) => g.id === id);
21251
+ function findGroup(map, id2) {
21252
+ return map.groups.find((g) => g.id === id2);
21241
21253
  }
21242
21254
  function checkMembership(map, node, nodeLayer, group) {
21243
21255
  const g = findGroup(map, group);
@@ -21249,26 +21261,38 @@ function checkMembership(map, node, nodeLayer, group) {
21249
21261
  function hasEdge(map, from, to) {
21250
21262
  return map.edges.some((e) => e.from === from && e.to === to);
21251
21263
  }
21264
+ function checkIdSpace(map, id2, declaring) {
21265
+ const taken = declaring === "node" ? map.groups.some((g) => g.id === id2) : map.nodes.some((n) => n.id === id2);
21266
+ return taken ? { kind: "id-collision", id: id2, taken: declaring === "node" ? "group" : "node" } : void 0;
21267
+ }
21252
21268
  function setTitle(map, title) {
21269
+ if (title === null || title === void 0) {
21270
+ const { title: _dropped, ...rest } = map;
21271
+ return rest;
21272
+ }
21253
21273
  return { ...map, title };
21254
21274
  }
21255
21275
  function setKind(map, kind) {
21256
21276
  return { ...map, kind };
21257
21277
  }
21258
- function findLane(map, id) {
21259
- return map.lanes.find((l) => l.id === id);
21278
+ function findLane(map, id2) {
21279
+ return map.lanes.find((l) => l.id === id2);
21260
21280
  }
21261
21281
  function declareLane(map, input) {
21262
21282
  if (findLane(map, input.id)) return err({ kind: "duplicate-lane", id: input.id });
21263
21283
  return ok({ ...map, lanes: [...map.lanes, { id: input.id, label: input.label }] });
21264
21284
  }
21265
- function removeLane(map, id) {
21266
- if (!findLane(map, id)) return err({ kind: "unknown-lane", id });
21285
+ function updateLane(map, id2, label) {
21286
+ if (!findLane(map, id2)) return err({ kind: "unknown-lane", id: id2 });
21287
+ return ok({ ...map, lanes: map.lanes.map((l) => l.id === id2 ? { ...l, label } : l) });
21288
+ }
21289
+ function removeLane(map, id2) {
21290
+ if (!findLane(map, id2)) return err({ kind: "unknown-lane", id: id2 });
21267
21291
  return ok({
21268
21292
  ...map,
21269
- lanes: map.lanes.filter((l) => l.id !== id),
21293
+ lanes: map.lanes.filter((l) => l.id !== id2),
21270
21294
  nodes: map.nodes.map((n) => {
21271
- if (n.lane !== id) return n;
21295
+ if (n.lane !== id2) return n;
21272
21296
  const { lane: _dropped, ...rest } = n;
21273
21297
  return rest;
21274
21298
  })
@@ -21280,18 +21304,47 @@ function declareLayer(map, input) {
21280
21304
  if (rankHolder) return err({ kind: "duplicate-rank", rank: input.rank, existing: rankHolder.id });
21281
21305
  return ok({ ...map, layers: [...map.layers, { id: input.id, name: input.name, rank: input.rank }] });
21282
21306
  }
21307
+ function updateLayer(map, id2, input) {
21308
+ const layer = findLayer(map, id2);
21309
+ if (!layer) return err({ kind: "unknown-layer", id: id2 });
21310
+ if (input.rank !== void 0 && input.rank !== layer.rank) {
21311
+ const rankHolder = map.layers.find((l) => l.rank === input.rank && l.id !== id2);
21312
+ if (rankHolder) return err({ kind: "duplicate-rank", rank: input.rank, existing: rankHolder.id });
21313
+ const rankAfter = (nodeId) => {
21314
+ const nodeLayer = findNode(map, nodeId).layer;
21315
+ return nodeLayer === id2 ? input.rank : findLayer(map, nodeLayer).rank;
21316
+ };
21317
+ for (const e of map.edges) {
21318
+ const fromRank = rankAfter(e.from);
21319
+ const toRank = rankAfter(e.to);
21320
+ if (fromRank <= toRank) return err({ kind: "edge-not-downward", from: e.from, fromRank, to: e.to, toRank });
21321
+ }
21322
+ }
21323
+ const updated = {
21324
+ ...layer,
21325
+ ...input.name !== void 0 ? { name: input.name } : {},
21326
+ ...input.rank !== void 0 ? { rank: input.rank } : {}
21327
+ };
21328
+ return ok({ ...map, layers: map.layers.map((l) => l.id === id2 ? updated : l) });
21329
+ }
21283
21330
  function declareGroup(map, input) {
21284
21331
  if (findGroup(map, input.id)) return err({ kind: "duplicate-group", id: input.id });
21332
+ const collision = checkIdSpace(map, input.id, "group");
21333
+ if (collision) return err(collision);
21285
21334
  if (!findLayer(map, input.layer)) return err({ kind: "unknown-layer", id: input.layer });
21286
21335
  return ok({ ...map, groups: [...map.groups, { id: input.id, label: input.label, layer: input.layer }] });
21287
21336
  }
21288
- function removeGroup(map, id) {
21289
- if (!findGroup(map, id)) return err({ kind: "unknown-group", id });
21337
+ function updateGroup(map, id2, label) {
21338
+ if (!findGroup(map, id2)) return err({ kind: "unknown-group", id: id2 });
21339
+ return ok({ ...map, groups: map.groups.map((g) => g.id === id2 ? { ...g, label } : g) });
21340
+ }
21341
+ function removeGroup(map, id2) {
21342
+ if (!findGroup(map, id2)) return err({ kind: "unknown-group", id: id2 });
21290
21343
  return ok({
21291
21344
  ...map,
21292
- groups: map.groups.filter((g) => g.id !== id),
21345
+ groups: map.groups.filter((g) => g.id !== id2),
21293
21346
  nodes: map.nodes.map((n) => {
21294
- if (n.group !== id) return n;
21347
+ if (n.group !== id2) return n;
21295
21348
  const { group: _dropped, ...rest } = n;
21296
21349
  return rest;
21297
21350
  })
@@ -21303,11 +21356,13 @@ function aggregateStatus(nodes) {
21303
21356
  if (nodes.length > 0 && nodes.every((n) => n.status === "done")) return "done";
21304
21357
  return "planned";
21305
21358
  }
21306
- function groupStatus(map, id) {
21307
- return aggregateStatus(map.nodes.filter((n) => n.group === id));
21359
+ function groupStatus(map, id2) {
21360
+ return aggregateStatus(map.nodes.filter((n) => n.group === id2));
21308
21361
  }
21309
21362
  function declareNode(map, input) {
21310
21363
  if (findNode(map, input.id)) return err({ kind: "duplicate-node", id: input.id });
21364
+ const collision = checkIdSpace(map, input.id, "node");
21365
+ if (collision) return err(collision);
21311
21366
  if (!findLayer(map, input.layer)) return err({ kind: "unknown-layer", id: input.layer });
21312
21367
  if (input.group !== void 0) {
21313
21368
  const bad = checkMembership(map, input.id, input.layer, input.group);
@@ -21319,6 +21374,7 @@ function declareNode(map, input) {
21319
21374
  label: input.label,
21320
21375
  layer: input.layer,
21321
21376
  status: input.status ?? "planned",
21377
+ ...input.evidence !== void 0 ? { evidence: input.evidence } : {},
21322
21378
  ...input.detail !== void 0 ? { detail: input.detail } : {},
21323
21379
  ...input.group !== void 0 ? { group: input.group } : {},
21324
21380
  ...input.kind !== void 0 ? { kind: input.kind } : {},
@@ -21339,6 +21395,9 @@ function linkNodes(map, from, to, label) {
21339
21395
  if (fromRank <= toRank) return err({ kind: "edge-not-downward", from, fromRank, to, toRank });
21340
21396
  return ok({ ...map, edges: [...map.edges, { from, to, ...label !== void 0 ? { label } : {} }] });
21341
21397
  }
21398
+ function resolveOptional(input, current) {
21399
+ return input === void 0 ? current : input === null ? void 0 : input;
21400
+ }
21342
21401
  function updateNode(map, input) {
21343
21402
  const node = findNode(map, input.id);
21344
21403
  if (!node) return err({ kind: "unknown-node", id: input.id });
@@ -21349,55 +21408,225 @@ function updateNode(map, input) {
21349
21408
  if (input.lane !== void 0 && input.lane !== null && !findLane(map, input.lane)) {
21350
21409
  return err({ kind: "unknown-lane", id: input.lane });
21351
21410
  }
21352
- const { group: currentGroup, kind: currentKind, lane: currentLane, submap: currentSubmap, ...bare } = node;
21353
- const nextGroup = input.group === void 0 ? currentGroup : input.group === null ? void 0 : input.group;
21354
- const nextKind = input.kind === void 0 ? currentKind : input.kind === null ? void 0 : input.kind;
21355
- const nextLane = input.lane === void 0 ? currentLane : input.lane === null ? void 0 : input.lane;
21356
- const nextSubmap = input.submap === void 0 ? currentSubmap : input.submap === null ? void 0 : input.submap;
21411
+ const {
21412
+ group: currentGroup,
21413
+ kind: currentKind,
21414
+ lane: currentLane,
21415
+ submap: currentSubmap,
21416
+ evidence: currentEvidence,
21417
+ detail: currentDetail,
21418
+ ...bare
21419
+ } = node;
21420
+ const nextGroup = resolveOptional(input.group, currentGroup);
21421
+ const nextKind = resolveOptional(input.kind, currentKind);
21422
+ const nextLane = resolveOptional(input.lane, currentLane);
21423
+ const nextSubmap = resolveOptional(input.submap, currentSubmap);
21424
+ const nextEvidence = resolveOptional(input.evidence, currentEvidence);
21425
+ const nextDetail = resolveOptional(input.detail, currentDetail);
21357
21426
  const updated = {
21358
21427
  ...bare,
21428
+ ...nextEvidence !== void 0 ? { evidence: nextEvidence } : {},
21429
+ ...nextDetail !== void 0 ? { detail: nextDetail } : {},
21359
21430
  ...nextGroup !== void 0 ? { group: nextGroup } : {},
21360
21431
  ...nextKind !== void 0 ? { kind: nextKind } : {},
21361
21432
  ...nextLane !== void 0 ? { lane: nextLane } : {},
21362
21433
  ...nextSubmap !== void 0 ? { submap: nextSubmap } : {},
21363
21434
  ...input.status !== void 0 ? { status: input.status } : {},
21364
- ...input.label !== void 0 ? { label: input.label } : {},
21365
- ...input.evidence !== void 0 ? { evidence: input.evidence } : {},
21366
- ...input.detail !== void 0 ? { detail: input.detail } : {}
21435
+ ...input.label !== void 0 ? { label: input.label } : {}
21367
21436
  };
21368
21437
  return ok({ ...map, nodes: map.nodes.map((n) => n.id === input.id ? updated : n) });
21369
21438
  }
21370
- function removeNode(map, id) {
21371
- if (!findNode(map, id)) return err({ kind: "unknown-node", id });
21439
+ function moveNode(map, id2, layer) {
21440
+ const node = findNode(map, id2);
21441
+ if (!node) return err({ kind: "unknown-node", id: id2 });
21442
+ const target = findLayer(map, layer);
21443
+ if (!target) return err({ kind: "unknown-layer", id: layer });
21444
+ if (node.group !== void 0) {
21445
+ const bad = checkMembership(map, id2, layer, node.group);
21446
+ if (bad) return err(bad);
21447
+ }
21448
+ const rankAfter = (nodeId) => nodeId === id2 ? target.rank : findLayer(map, findNode(map, nodeId).layer).rank;
21449
+ for (const e of map.edges) {
21450
+ if (e.from !== id2 && e.to !== id2) continue;
21451
+ const fromRank = rankAfter(e.from);
21452
+ const toRank = rankAfter(e.to);
21453
+ if (fromRank <= toRank) return err({ kind: "edge-not-downward", from: e.from, fromRank, to: e.to, toRank });
21454
+ }
21455
+ return ok({ ...map, nodes: map.nodes.map((n) => n.id === id2 ? { ...n, layer } : n) });
21456
+ }
21457
+ function removeNode(map, id2) {
21458
+ if (!findNode(map, id2)) return err({ kind: "unknown-node", id: id2 });
21372
21459
  return ok({
21373
21460
  ...map,
21374
- nodes: map.nodes.filter((n) => n.id !== id),
21375
- edges: map.edges.filter((e) => e.from !== id && e.to !== id)
21461
+ nodes: map.nodes.filter((n) => n.id !== id2),
21462
+ edges: map.edges.filter((e) => e.from !== id2 && e.to !== id2)
21376
21463
  });
21377
21464
  }
21378
21465
  function removeEdge(map, from, to) {
21379
21466
  if (!hasEdge(map, from, to)) return err({ kind: "unknown-edge", from, to });
21380
21467
  return ok({ ...map, edges: map.edges.filter((e) => !(e.from === from && e.to === to)) });
21381
21468
  }
21382
- function removeLayer(map, id) {
21383
- if (!findLayer(map, id)) return err({ kind: "unknown-layer", id });
21384
- const occupant = map.nodes.find((n) => n.layer === id);
21385
- if (occupant) return err({ kind: "layer-not-empty", id, occupant: occupant.id });
21386
- const groupOccupant = map.groups.find((g) => g.layer === id);
21387
- if (groupOccupant) return err({ kind: "layer-holds-group", id, occupant: groupOccupant.id });
21388
- return ok({ ...map, layers: map.layers.filter((l) => l.id !== id) });
21469
+ function removeLayer(map, id2) {
21470
+ if (!findLayer(map, id2)) return err({ kind: "unknown-layer", id: id2 });
21471
+ const occupant = map.nodes.find((n) => n.layer === id2);
21472
+ if (occupant) return err({ kind: "layer-not-empty", id: id2, occupant: occupant.id });
21473
+ const groupOccupant = map.groups.find((g) => g.layer === id2);
21474
+ if (groupOccupant) return err({ kind: "layer-holds-group", id: id2, occupant: groupOccupant.id });
21475
+ return ok({ ...map, layers: map.layers.filter((l) => l.id !== id2) });
21389
21476
  }
21390
21477
 
21391
- // src/render/render.ts
21478
+ // src/semantics/vocabulary.ts
21479
+ var STATUS_GLYPHS = {
21480
+ planned: ["\xB7", "."],
21481
+ "in-progress": ["\u283F", "*"],
21482
+ done: ["\u25A0", "#"],
21483
+ regressed: ["\u2717", "X"]
21484
+ };
21485
+ function statusGlyph(status2, unicode) {
21486
+ const [uni, ascii] = STATUS_GLYPHS[status2];
21487
+ return unicode ? uni : ascii;
21488
+ }
21489
+ var UNVERIFIED_DONE_GLYPHS = ["\u25A1", "o"];
21490
+ function unverifiedDoneGlyph(unicode) {
21491
+ const [uni, ascii] = UNVERIFIED_DONE_GLYPHS;
21492
+ return unicode ? uni : ascii;
21493
+ }
21494
+ var SPINNER_FRAMES = {
21495
+ unicode: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"],
21496
+ ascii: ["|", "/", "-", "\\"]
21497
+ };
21498
+ function spinnerGlyph(frame, unicode) {
21499
+ const frames = SPINNER_FRAMES[unicode ? "unicode" : "ascii"];
21500
+ return frames[(frame % frames.length + frames.length) % frames.length];
21501
+ }
21502
+ var NODE_KIND_GLYPHS = {
21503
+ selector: ["?", "?"],
21504
+ sequence: ["\xBB", ">"],
21505
+ parallel: ["\u2016", "="],
21506
+ decorator: ["\u25CC", "o"],
21507
+ condition: ["\u25C7", "c"],
21508
+ action: ["\xB7", "."],
21509
+ source: ["\u25CB", "o"],
21510
+ transform: ["\u25D0", "%"],
21511
+ sink: ["\u25CF", "*"],
21512
+ service: ["\u25C6", "S"],
21513
+ db: ["\u25A4", "D"],
21514
+ queue: ["\u2263", "Q"],
21515
+ ui: ["\u25A3", "U"]
21516
+ };
21517
+ function kindGlyph(kind, unicode) {
21518
+ const pair = NODE_KIND_GLYPHS[kind];
21519
+ return pair === void 0 ? void 0 : unicode ? pair[0] : pair[1];
21520
+ }
21521
+
21522
+ // src/semantics/semantics.ts
21392
21523
  var ZOOM_MIN = -4;
21393
21524
  var ZOOM_MAX = 2;
21394
21525
  var ZOOM_DEFAULT = 0;
21395
21526
  function clampZoom(n) {
21396
21527
  return Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, Math.round(n)));
21397
21528
  }
21529
+ function zoomMode(zoom) {
21530
+ if (zoom >= 1) return "detail";
21531
+ if (zoom <= -4) return "overview";
21532
+ return "boxes";
21533
+ }
21534
+ function isNeutralKind(map) {
21535
+ return map.kind !== void 0 && map.kind !== "dev";
21536
+ }
21537
+ function aggregateMap(map) {
21538
+ if (map.groups.length === 0) return void 0;
21539
+ const representative = /* @__PURE__ */ new Map();
21540
+ for (const n of map.nodes) representative.set(n.id, n.group ?? n.id);
21541
+ const nodes = map.groups.map((g) => {
21542
+ const members = map.nodes.filter((n) => n.group === g.id);
21543
+ const done = members.filter((n) => n.status === "done").length;
21544
+ return {
21545
+ id: g.id,
21546
+ // neutral kinds document structure, not progress — no member counts
21547
+ label: isNeutralKind(map) ? g.label : `${g.label} ${done}/${members.length}`,
21548
+ layer: g.layer,
21549
+ status: groupStatus(map, g.id)
21550
+ };
21551
+ });
21552
+ for (const n of map.nodes) if (n.group === void 0) nodes.push(n);
21553
+ const seen = /* @__PURE__ */ new Set();
21554
+ const edges = [];
21555
+ for (const e of map.edges) {
21556
+ const from = representative.get(e.from);
21557
+ const to = representative.get(e.to);
21558
+ if (from === to || seen.has(`${from}->${to}`)) continue;
21559
+ seen.add(`${from}->${to}`);
21560
+ edges.push({ from, to });
21561
+ }
21562
+ return {
21563
+ ...map.title !== void 0 ? { title: map.title } : {},
21564
+ ...map.kind !== void 0 ? { kind: map.kind } : {},
21565
+ layers: map.layers,
21566
+ groups: [],
21567
+ lanes: map.lanes,
21568
+ nodes,
21569
+ edges
21570
+ };
21571
+ }
21572
+ function flipForSequence(map) {
21573
+ if (map.kind !== "sequence") return map;
21574
+ return {
21575
+ ...map,
21576
+ // VIOLATION: state-explicit-in-types - `-l.rank as Rank` produces a value
21577
+ // the Rank brand promises cannot exist: mirroring 0..99 gives -99..0, and
21578
+ // makeRank would refuse every one of them. The alternative is a second
21579
+ // ordered-position type (an unbranded `order` field) threaded through the
21580
+ // renderer's whole layout stage purely so this one derived map can be
21581
+ // typed — a large change to express "these ranks are an order, not a
21582
+ // stored value". What makes it safe is the same thing that makes it
21583
+ // wrong: this map only ever reaches a renderer, which compares ranks and
21584
+ // never writes them (same contract as aggregateMap).
21585
+ layers: map.layers.map((l) => ({ ...l, rank: -l.rank })),
21586
+ edges: map.edges.map((e) => ({ from: e.to, to: e.from, ...e.label !== void 0 ? { label: e.label } : {} }))
21587
+ };
21588
+ }
21589
+
21590
+ // src/render/width.ts
21398
21591
  var WIDE_RANGES = [
21399
21592
  [4352, 4447],
21400
21593
  // Hangul Jamo
21594
+ // Wide symbols scattered through the BMP — mostly emoji that predate the
21595
+ // emoji planes (⌚ ⏰ ⚡ ✅ ✨ ❌ ❓ ⭐ ⬛ …).
21596
+ [8986, 8987],
21597
+ [9001, 9002],
21598
+ [9193, 9196],
21599
+ [9200, 9200],
21600
+ [9203, 9203],
21601
+ [9725, 9726],
21602
+ [9748, 9749],
21603
+ [9800, 9811],
21604
+ [9855, 9855],
21605
+ [9875, 9875],
21606
+ [9889, 9889],
21607
+ [9898, 9899],
21608
+ [9917, 9918],
21609
+ [9924, 9925],
21610
+ [9934, 9934],
21611
+ [9940, 9940],
21612
+ [9962, 9962],
21613
+ [9970, 9971],
21614
+ [9973, 9973],
21615
+ [9978, 9978],
21616
+ [9981, 9981],
21617
+ [9989, 9989],
21618
+ [9994, 9995],
21619
+ [10024, 10024],
21620
+ [10060, 10060],
21621
+ [10062, 10062],
21622
+ [10067, 10069],
21623
+ [10071, 10071],
21624
+ [10133, 10135],
21625
+ [10160, 10160],
21626
+ [10175, 10175],
21627
+ [11035, 11036],
21628
+ [11088, 11088],
21629
+ [11093, 11093],
21401
21630
  [11904, 42191],
21402
21631
  // CJK radicals .. Yi (covers CJK Unified Ideographs)
21403
21632
  [43360, 43391],
@@ -21410,14 +21639,41 @@ var WIDE_RANGES = [
21410
21639
  [65280, 65376],
21411
21640
  // fullwidth forms
21412
21641
  [65504, 65510],
21642
+ [127744, 128591],
21643
+ // pictographs, transport, emoticons (🚀 🎯 😀 …)
21644
+ [128640, 128767],
21645
+ [129280, 129535],
21646
+ // supplemental symbols (🤖 🧱 …)
21647
+ [129648, 129791],
21648
+ // symbols extended-A
21413
21649
  [131072, 262141]
21414
21650
  // CJK extension planes
21415
21651
  ];
21416
- function charWidth(cp) {
21417
- for (const [lo, hi] of WIDE_RANGES) {
21418
- if (cp >= lo && cp <= hi) return 2;
21652
+ var ZERO_WIDTH_RANGES = [
21653
+ [768, 879],
21654
+ // combining diacritical marks (decomposed 'e' + ´)
21655
+ [6832, 6911],
21656
+ [7616, 7679],
21657
+ [8203, 8207],
21658
+ // zero-width space .. RLM, zero-width joiner among them
21659
+ [8400, 8432],
21660
+ // combining marks for symbols
21661
+ [65024, 65039],
21662
+ // variation selectors, VS16 (emoji presentation) included
21663
+ [65056, 65071],
21664
+ // combining half marks
21665
+ [127995, 127999]
21666
+ // emoji skin tone modifiers — always applied to a base
21667
+ ];
21668
+ function inRanges(cp, ranges) {
21669
+ for (const [lo, hi] of ranges) {
21670
+ if (cp >= lo && cp <= hi) return true;
21419
21671
  }
21420
- return 1;
21672
+ return false;
21673
+ }
21674
+ function charWidth(cp) {
21675
+ if (inRanges(cp, ZERO_WIDTH_RANGES)) return 0;
21676
+ return inRanges(cp, WIDE_RANGES) ? 2 : 1;
21421
21677
  }
21422
21678
  function displayWidth(text2) {
21423
21679
  let w = 0;
@@ -21438,27 +21694,40 @@ function fitWidth(s, width) {
21438
21694
  }
21439
21695
  function wrapWidth(s, width) {
21440
21696
  const lines = [];
21441
- let line = "";
21697
+ let line2 = "";
21442
21698
  let w = 0;
21443
21699
  for (const ch of s.replace(/\r/g, "")) {
21444
21700
  if (ch === "\n") {
21445
- lines.push(line);
21446
- line = "";
21701
+ lines.push(line2);
21702
+ line2 = "";
21447
21703
  w = 0;
21448
21704
  continue;
21449
21705
  }
21450
21706
  const cw = displayWidth(ch);
21451
21707
  if (w + cw > width) {
21452
- lines.push(line);
21453
- line = "";
21708
+ lines.push(line2);
21709
+ line2 = "";
21454
21710
  w = 0;
21455
21711
  }
21456
- line += ch;
21712
+ line2 += ch;
21457
21713
  w += cw;
21458
21714
  }
21459
- if (line !== "") lines.push(line);
21715
+ if (line2 !== "") lines.push(line2);
21460
21716
  return lines;
21461
21717
  }
21718
+
21719
+ // src/render/canvas.ts
21720
+ var SGR = {
21721
+ none: "",
21722
+ dim: "2",
21723
+ amber: "33",
21724
+ green: "32",
21725
+ greenDim: "32;2",
21726
+ // done, but nothing behind the claim: green, not fully lit
21727
+ red: "31",
21728
+ faint: "90"
21729
+ };
21730
+ var ANSI_RESET = "\x1B[0m";
21462
21731
  var UP = 1;
21463
21732
  var DOWN = 2;
21464
21733
  var LEFT = 4;
@@ -21493,15 +21762,6 @@ function maskChar(mask, heavyHorizontal, unicode) {
21493
21762
  }
21494
21763
  return LIGHT_BY_MASK[mask] ?? "\u253C";
21495
21764
  }
21496
- var SGR = {
21497
- none: "",
21498
- dim: "2",
21499
- amber: "33",
21500
- green: "32",
21501
- red: "31",
21502
- faint: "90"
21503
- };
21504
- var ANSI_RESET = "\x1B[0m";
21505
21765
  var BORDER_JUNCTION = {
21506
21766
  "\u2500": { down: "\u252C", up: "\u2534" },
21507
21767
  "\u254C": { down: "\u252C", up: "\u2534" },
@@ -21527,11 +21787,17 @@ var Canvas = class {
21527
21787
  text(x, y, s, style, bold = false) {
21528
21788
  let cx = x;
21529
21789
  for (const ch of s) {
21790
+ const w = charWidth(ch.codePointAt(0));
21791
+ if (w === 0) {
21792
+ const base = this.cell(Math.max(0, cx - 1), y);
21793
+ const target = base.literal === "" ? this.cell(Math.max(0, cx - 2), y) : base;
21794
+ target.literal = (target.literal ?? "") + ch;
21795
+ continue;
21796
+ }
21530
21797
  const c = this.cell(cx, y);
21531
21798
  c.literal = ch;
21532
21799
  c.style = style;
21533
21800
  c.bold = bold;
21534
- const w = charWidth(ch.codePointAt(0));
21535
21801
  if (w === 2) {
21536
21802
  const phantom = this.cell(cx + 1, y);
21537
21803
  phantom.literal = "";
@@ -21566,7 +21832,7 @@ var Canvas = class {
21566
21832
  const out = [];
21567
21833
  for (let y = vp.y; y < vp.y + vp.height; y++) {
21568
21834
  const row = this.rows[y] ?? [];
21569
- let line = "";
21835
+ let line2 = "";
21570
21836
  let open = "";
21571
21837
  const end = Math.min(vp.x + vp.width, row.length);
21572
21838
  for (let x = Math.max(0, vp.x); x < end; x++) {
@@ -21581,13 +21847,13 @@ var Canvas = class {
21581
21847
  }
21582
21848
  const params = ch === " " ? "" : isWire ? c.bright ? "1" : SGR.faint : [SGR[c.style], c.bold ? "1" : ""].filter(Boolean).join(";");
21583
21849
  if (opts.color && params !== open) {
21584
- line += (open !== "" ? ANSI_RESET : "") + (params !== "" ? `\x1B[${params}m` : "");
21850
+ line2 += (open !== "" ? ANSI_RESET : "") + (params !== "" ? `\x1B[${params}m` : "");
21585
21851
  open = params;
21586
21852
  }
21587
- line += ch;
21853
+ line2 += ch;
21588
21854
  }
21589
- if (opts.color && open !== "") line += ANSI_RESET;
21590
- out.push(line.replace(/ +$/, ""));
21855
+ if (opts.color && open !== "") line2 += ANSI_RESET;
21856
+ out.push(line2.replace(/ +$/, ""));
21591
21857
  }
21592
21858
  return out;
21593
21859
  }
@@ -21610,163 +21876,33 @@ function drawPath(canvas, points, bright = false) {
21610
21876
  }
21611
21877
  }
21612
21878
  }
21613
- var SPINNER_UNICODE = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
21614
- var SPINNER_ASCII = ["|", "/", "-", "\\"];
21615
- function skinFor(status, unicode) {
21616
- const style = status === "planned" ? "dim" : status === "in-progress" ? "amber" : status === "done" ? "green" : "red";
21617
- if (!unicode) {
21618
- return status === "planned" ? { h: ".", v: ":", corners: ["+", "+", "+", "+"], style } : { h: "-", v: "|", corners: ["+", "+", "+", "+"], style };
21619
- }
21620
- switch (status) {
21621
- case "planned":
21622
- return { h: "\u254C", v: "\u254E", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style };
21623
- case "in-progress":
21624
- return { h: "\u2500", v: "\u2502", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style };
21625
- case "done":
21626
- case "regressed":
21627
- return { h: "\u2501", v: "\u2503", corners: ["\u250F", "\u2513", "\u2517", "\u251B"], style };
21628
- }
21629
- }
21630
- function glyphFor(status, opts) {
21631
- const spinner = opts.unicode ? SPINNER_UNICODE : SPINNER_ASCII;
21632
- switch (status) {
21633
- case "planned":
21634
- return opts.unicode ? "\xB7" : ".";
21635
- case "in-progress":
21636
- return spinner[opts.spinnerFrame % spinner.length];
21637
- case "done":
21638
- return opts.unicode ? "\u25A0" : "#";
21639
- case "regressed":
21640
- return opts.unicode ? "\u2717" : "X";
21641
- }
21642
- }
21643
- var NODE_KIND_GLYPHS = {
21644
- selector: ["?", "?"],
21645
- sequence: ["\xBB", ">"],
21646
- parallel: ["\u2016", "="],
21647
- decorator: ["\u25CC", "o"],
21648
- condition: ["\u25C7", "c"],
21649
- action: ["\xB7", "."],
21650
- source: ["\u25CB", "o"],
21651
- transform: ["\u25D0", "%"],
21652
- sink: ["\u25CF", "*"],
21653
- service: ["\u25C6", "S"],
21654
- db: ["\u25A4", "D"],
21655
- queue: ["\u2263", "Q"],
21656
- ui: ["\u25A3", "U"]
21657
- };
21658
- function kindGlyph(kind, unicode) {
21659
- const pair = NODE_KIND_GLYPHS[kind];
21660
- return pair === void 0 ? void 0 : unicode ? pair[0] : pair[1];
21661
- }
21662
- function isNeutralKind(map) {
21663
- return map.kind !== void 0 && map.kind !== "dev";
21664
- }
21665
- function neutralSkin(unicode) {
21666
- return unicode ? { h: "\u2500", v: "\u2502", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style: "none" } : { h: "-", v: "|", corners: ["+", "+", "+", "+"], style: "none" };
21667
- }
21879
+
21880
+ // src/render/zoom-geometry.ts
21668
21881
  var BOX_H = 3;
21669
21882
  var BOX_GAP = 2;
21670
21883
  var LEFT_MARGIN = 2;
21884
+ var BAR_MIN_RUN = 7;
21671
21885
  var DETAIL_BUDGET = { innerMin: 22, innerMax: 32, noteRows: 3 };
21672
21886
  var DETAIL_PLUS_BUDGET = { innerMin: 30, innerMax: 48, noteRows: 12 };
21673
21887
  function zoomGeometry(zoom) {
21888
+ const m = zoomMode(zoom);
21889
+ const mode = m === "overview" ? "constellation" : m;
21674
21890
  switch (zoom) {
21675
21891
  case 2:
21676
- return { mode: "detail", scale: 1, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false, detail: DETAIL_PLUS_BUDGET };
21892
+ return { mode, scale: 1, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false, detail: DETAIL_PLUS_BUDGET };
21677
21893
  case 1:
21678
- return { mode: "detail", scale: 1, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false, detail: DETAIL_BUDGET };
21894
+ return { mode, scale: 1, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false, detail: DETAIL_BUDGET };
21679
21895
  case 0:
21680
- return { mode: "boxes", scale: 1, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false };
21896
+ return { mode, scale: 1, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false };
21681
21897
  case -1:
21682
- return { mode: "boxes", scale: 0.85, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false };
21898
+ return { mode, scale: 0.85, pad: 1, boxGap: BOX_GAP, breathe: 1, titleGap: 1, barGap: 1, bandCounts: false };
21683
21899
  case -2:
21684
- return { mode: "boxes", scale: 0.7, pad: 0, boxGap: BOX_GAP, breathe: 0, titleGap: 0, barGap: 1, bandCounts: false };
21900
+ return { mode, scale: 0.7, pad: 0, boxGap: BOX_GAP, breathe: 0, titleGap: 0, barGap: 1, bandCounts: false };
21685
21901
  case -3:
21686
- return { mode: "boxes", scale: 0.55, pad: 0, boxGap: 1, breathe: 0, titleGap: 0, barGap: 1, bandCounts: true };
21902
+ return { mode, scale: 0.55, pad: 0, boxGap: 1, breathe: 0, titleGap: 0, barGap: 1, bandCounts: true };
21687
21903
  case -4:
21688
- return { mode: "constellation", scale: 0, pad: 0, boxGap: BOX_GAP, breathe: 0, titleGap: 0, barGap: 1, bandCounts: true };
21689
- }
21690
- }
21691
- var LABEL_BUDGET_MIN = 4;
21692
- function boxSpec(node, geo, unicode, neutral) {
21693
- const glyph = node.kind !== void 0 ? kindGlyph(node.kind, unicode) : void 0;
21694
- const badge = node.submap !== void 0 ? unicode ? " \u229E" : " +" : "";
21695
- const badgeW = displayWidth(badge);
21696
- const text2 = !neutral && glyph !== void 0 ? `${glyph} ${node.label}` : node.label;
21697
- if (geo.mode === "constellation") {
21698
- return { w: 3, h: 1, label: "", pad: 0, borderless: true, extra: [] };
21699
- }
21700
- if (geo.mode === "detail" && geo.detail !== void 0) {
21701
- const budget2 = geo.detail;
21702
- const innerW = Math.min(Math.max(displayWidth(text2) + badgeW + 4, budget2.innerMin), budget2.innerMax);
21703
- const extra = [];
21704
- if (node.evidence !== void 0) extra.push({ text: fitWidth(` ${node.evidence}`, innerW), style: "faint" });
21705
- if (node.detail !== void 0) {
21706
- const wrapped = wrapWidth(node.detail, innerW - 2);
21707
- for (let i = 0; i < Math.min(wrapped.length, budget2.noteRows); i++) {
21708
- const cut = i === budget2.noteRows - 1 && wrapped.length > budget2.noteRows;
21709
- extra.push({ text: ` ${cut ? fitWidth(wrapped[i] + "\u2026", innerW - 2) : wrapped[i]}`, style: "none" });
21710
- }
21711
- }
21712
- return {
21713
- w: innerW + 2,
21714
- h: BOX_H + extra.length,
21715
- label: fitWidth(text2, innerW - 4 - badgeW) + badge,
21716
- pad: 1,
21717
- borderless: false,
21718
- extra
21719
- };
21720
- }
21721
- const budget = Math.max(LABEL_BUDGET_MIN, Math.ceil(displayWidth(text2) * geo.scale));
21722
- const label = fitWidth(text2, budget) + badge;
21723
- return {
21724
- w: displayWidth(label) + 4 + 2 * geo.pad,
21725
- h: BOX_H,
21726
- label,
21727
- pad: geo.pad,
21728
- borderless: false,
21729
- extra: []
21730
- };
21731
- }
21732
- function renderMap(map, opts) {
21733
- const built = buildCanvas(map, opts);
21734
- return built.canvas.emit(opts);
21735
- }
21736
- function aggregateMap(map) {
21737
- if (map.groups.length === 0) return void 0;
21738
- const representative = /* @__PURE__ */ new Map();
21739
- for (const n of map.nodes) representative.set(n.id, n.group ?? n.id);
21740
- const nodes = map.groups.map((g) => {
21741
- const members = map.nodes.filter((n) => n.group === g.id);
21742
- const done = members.filter((n) => n.status === "done").length;
21743
- return {
21744
- id: g.id,
21745
- // neutral kinds document structure, not progress — no member counts
21746
- label: isNeutralKind(map) ? g.label : `${g.label} ${done}/${members.length}`,
21747
- layer: g.layer,
21748
- status: groupStatus(map, g.id)
21749
- };
21750
- });
21751
- for (const n of map.nodes) if (n.group === void 0) nodes.push(n);
21752
- const seen = /* @__PURE__ */ new Set();
21753
- const edges = [];
21754
- for (const e of map.edges) {
21755
- const from = representative.get(e.from);
21756
- const to = representative.get(e.to);
21757
- if (from === to || seen.has(`${from}->${to}`)) continue;
21758
- seen.add(`${from}->${to}`);
21759
- edges.push({ from, to });
21904
+ return { mode, scale: 0, pad: 0, boxGap: BOX_GAP, breathe: 0, titleGap: 0, barGap: 1, bandCounts: true };
21760
21905
  }
21761
- return {
21762
- ...map.title !== void 0 ? { title: map.title } : {},
21763
- ...map.kind !== void 0 ? { kind: map.kind } : {},
21764
- layers: map.layers,
21765
- groups: [],
21766
- lanes: map.lanes,
21767
- nodes,
21768
- edges
21769
- };
21770
21906
  }
21771
21907
  var AGGREGATE_GEO = {
21772
21908
  mode: "boxes",
@@ -21778,107 +21914,31 @@ var AGGREGATE_GEO = {
21778
21914
  barGap: 1,
21779
21915
  bandCounts: false
21780
21916
  };
21781
- function flipForSequence(map) {
21782
- if (map.kind !== "sequence") return map;
21783
- return {
21784
- ...map,
21785
- layers: map.layers.map((l) => ({ ...l, rank: -l.rank })),
21786
- edges: map.edges.map((e) => ({ from: e.to, to: e.from, ...e.label !== void 0 ? { label: e.label } : {} }))
21917
+
21918
+ // src/render/routing.ts
21919
+ function routeEdges(map, columns) {
21920
+ const { bandIndexOf, bandBoxes, boxOf, contentWidth } = columns;
21921
+ const pending = map.edges.map((e) => {
21922
+ const from = boxOf.get(e.from);
21923
+ const to = boxOf.get(e.to);
21924
+ return {
21925
+ from,
21926
+ to,
21927
+ fromBand: bandIndexOf.get(from.node.layer),
21928
+ toBand: bandIndexOf.get(to.node.layer)
21929
+ };
21930
+ });
21931
+ const gapVerticals = Array.from(
21932
+ { length: Math.max(0, columns.bands.length - 1) },
21933
+ () => /* @__PURE__ */ new Map()
21934
+ );
21935
+ const verticalFree = (gap, x, edge) => {
21936
+ const owner = gapVerticals[gap]?.get(x);
21937
+ return owner === void 0 || owner === edge;
21938
+ };
21939
+ const takeVertical = (gap, x, edge) => {
21940
+ gapVerticals[gap]?.set(x, edge);
21787
21941
  };
21788
- }
21789
- function buildCanvas(map, opts) {
21790
- const oriented = flipForSequence(map);
21791
- const plainGeo = zoomGeometry(opts.zoom ?? ZOOM_DEFAULT);
21792
- const aggregated = plainGeo.mode === "constellation" ? aggregateMap(oriented) : void 0;
21793
- return buildCanvasWith(aggregated ?? oriented, opts, aggregated !== void 0 ? AGGREGATE_GEO : plainGeo);
21794
- }
21795
- function buildCanvasWith(map, opts, geo) {
21796
- const canvas = new Canvas();
21797
- const neutral = isNeutralKind(map);
21798
- const bands = [...map.layers].sort((a, b) => b.rank - a.rank);
21799
- if (bands.length === 0) {
21800
- canvas.text(0, 0, map.title ?? "mellos mapping", "none", true);
21801
- canvas.text(0, 2, "(empty map \u2014 declare layers and nodes to begin)", "dim");
21802
- return { canvas, hits: [] };
21803
- }
21804
- const bandIndexOf = new Map(bands.map((l, i) => [l.id, i]));
21805
- const boxes = /* @__PURE__ */ new Map();
21806
- const bandBoxes = bands.map(() => []);
21807
- for (const node of map.nodes) {
21808
- const band = bandIndexOf.get(node.layer);
21809
- const box = { node, ...boxSpec(node, geo, opts.unicode, neutral), x: LEFT_MARGIN, y: 0 };
21810
- bandBoxes[band].push(box);
21811
- boxes.set(node.id, box);
21812
- }
21813
- const laneCount = map.lanes.length;
21814
- const laneX = [];
21815
- const laneW = [];
21816
- if (laneCount === 0) {
21817
- for (const row of bandBoxes) {
21818
- let x = LEFT_MARGIN;
21819
- for (const box of row) {
21820
- box.x = x;
21821
- x += box.w + geo.boxGap;
21822
- }
21823
- }
21824
- } else {
21825
- const laneGap = geo.boxGap + 2;
21826
- const laneIndexOf = new Map(map.lanes.map((l, i) => [l.id, i]));
21827
- const regions = laneCount + 1;
21828
- const grouped = bandBoxes.map((row) => {
21829
- const cells = Array.from({ length: regions }, () => []);
21830
- for (const box of row) {
21831
- const lane = box.node.lane;
21832
- cells[lane !== void 0 ? laneIndexOf.get(lane) : regions - 1].push(box);
21833
- }
21834
- return cells;
21835
- });
21836
- const regionW = Array.from({ length: regions }, () => 0);
21837
- for (const cells of grouped) {
21838
- for (let i = 0; i < regions; i++) {
21839
- const rowW = cells[i].reduce((sum, b, k) => sum + b.w + (k > 0 ? geo.boxGap : 0), 0);
21840
- regionW[i] = Math.max(regionW[i], rowW);
21841
- }
21842
- }
21843
- for (let i = 0; i < laneCount; i++) regionW[i] = Math.max(regionW[i], displayWidth(map.lanes[i].label) + 2);
21844
- let x0 = LEFT_MARGIN;
21845
- for (let i = 0; i < regions; i++) {
21846
- laneX.push(x0);
21847
- laneW.push(regionW[i]);
21848
- x0 += regionW[i] + laneGap;
21849
- }
21850
- for (const cells of grouped) {
21851
- for (let i = 0; i < regions; i++) {
21852
- let x = laneX[i];
21853
- for (const box of cells[i]) {
21854
- box.x = x;
21855
- x += box.w + geo.boxGap;
21856
- }
21857
- }
21858
- }
21859
- }
21860
- const bandLabel = bands.map((l, i) => {
21861
- const row = bandBoxes[i];
21862
- const done = row.filter((b) => b.node.status === "done").length;
21863
- return geo.bandCounts && row.length > 0 && !neutral ? ` ${l.name} ${done}/${row.length}` : ` ${l.name}`;
21864
- });
21865
- let contentWidth = LEFT_MARGIN;
21866
- for (const row of bandBoxes) {
21867
- const last = row[row.length - 1];
21868
- if (last) contentWidth = Math.max(contentWidth, last.x + last.w);
21869
- }
21870
- for (let i = 0; i < laneCount; i++) contentWidth = Math.max(contentWidth, laneX[i] + laneW[i]);
21871
- for (const label of bandLabel) contentWidth = Math.max(contentWidth, LEFT_MARGIN + displayWidth(label) + 7);
21872
- const routes = map.edges.map((e) => {
21873
- const fromBox = boxes.get(e.from);
21874
- const toBox = boxes.get(e.to);
21875
- return {
21876
- fromBox,
21877
- toBox,
21878
- fromBand: bandIndexOf.get(fromBox.node.layer),
21879
- toBand: bandIndexOf.get(toBox.node.layer)
21880
- };
21881
- });
21882
21942
  const claimedColumns = /* @__PURE__ */ new Map();
21883
21943
  const isFree = (box, x) => !(claimedColumns.get(box)?.has(x) ?? false);
21884
21944
  const claim = (box, x) => {
@@ -21887,60 +21947,65 @@ function buildCanvasWith(map, opts, geo) {
21887
21947
  set.add(x);
21888
21948
  return x;
21889
21949
  };
21890
- const straightX = /* @__PURE__ */ new Map();
21891
- for (const r of routes) {
21950
+ for (const r of pending) {
21892
21951
  if (r.toBand - r.fromBand !== 1) continue;
21893
- const lo = Math.max(r.fromBox.x + 1, r.toBox.x + 1);
21894
- const hi = Math.min(r.fromBox.x + r.fromBox.w - 2, r.toBox.x + r.toBox.w - 2);
21952
+ const lo = Math.max(r.from.x + 1, r.to.x + 1);
21953
+ const hi = Math.min(r.from.x + r.from.w - 2, r.to.x + r.to.w - 2);
21895
21954
  if (lo > hi) continue;
21896
21955
  const mid = Math.floor((lo + hi) / 2);
21897
- for (let d = 0; d <= hi - lo && !straightX.has(r); d++) {
21956
+ for (let d = 0; d <= hi - lo && r.straightX === void 0; d++) {
21898
21957
  for (const x of d === 0 ? [mid] : [mid - d, mid + d]) {
21899
- if (x >= lo && x <= hi && isFree(r.fromBox, x) && isFree(r.toBox, x)) {
21900
- straightX.set(r, claim(r.toBox, claim(r.fromBox, x)));
21958
+ if (x >= lo && x <= hi && isFree(r.from, x) && isFree(r.to, x) && verticalFree(r.fromBand, x, r)) {
21959
+ r.straightX = claim(r.to, claim(r.from, x));
21960
+ takeVertical(r.fromBand, x, r);
21901
21961
  break;
21902
21962
  }
21903
21963
  }
21904
21964
  }
21905
21965
  }
21906
- const bent = routes.filter((r) => !straightX.has(r));
21966
+ const bent = pending.filter((r) => r.straightX === void 0);
21907
21967
  const outgoing = /* @__PURE__ */ new Map();
21908
21968
  const incoming = /* @__PURE__ */ new Map();
21909
21969
  for (const r of bent) {
21910
- outgoing.set(r.fromBox, [...outgoing.get(r.fromBox) ?? [], r]);
21911
- incoming.set(r.toBox, [...incoming.get(r.toBox) ?? [], r]);
21970
+ outgoing.set(r.from, [...outgoing.get(r.from) ?? [], r]);
21971
+ incoming.set(r.to, [...incoming.get(r.to) ?? [], r]);
21912
21972
  }
21913
- const freeSlot = (box, k, n) => {
21973
+ const freeSlot = (box, k, n, edge, gap) => {
21914
21974
  const lo = box.x + 1;
21915
21975
  const hi = box.x + box.w - 2;
21916
21976
  const ideal = box.x + Math.min(box.w - 2, Math.max(1, Math.round((k + 1) * (box.w - 1) / (n + 1))));
21917
21977
  for (let d = 0; d <= hi - lo; d++) {
21918
21978
  for (const x of d === 0 ? [ideal] : [ideal - d, ideal + d]) {
21919
- if (x >= lo && x <= hi && isFree(box, x)) return claim(box, x);
21979
+ if (x >= lo && x <= hi && isFree(box, x) && verticalFree(gap, x, edge)) {
21980
+ takeVertical(gap, x, edge);
21981
+ return claim(box, x);
21982
+ }
21920
21983
  }
21921
21984
  }
21922
21985
  return ideal;
21923
21986
  };
21924
- const attach = /* @__PURE__ */ new Map();
21925
21987
  for (const r of bent) {
21926
- const outs = outgoing.get(r.fromBox);
21927
- const ins = incoming.get(r.toBox);
21928
- attach.set(r, {
21929
- sx: freeSlot(r.fromBox, outs.indexOf(r), outs.length),
21930
- ex: freeSlot(r.toBox, ins.indexOf(r), ins.length)
21931
- });
21988
+ const outs = outgoing.get(r.from);
21989
+ const ins = incoming.get(r.to);
21990
+ r.exitX = freeSlot(r.from, outs.indexOf(r), outs.length, r, r.fromBand);
21991
+ r.entryX = freeSlot(r.to, ins.indexOf(r), ins.length, r, r.toBand - 1);
21932
21992
  }
21933
- const skipRoutes = bent.filter((r) => r.toBand - r.fromBand > 1);
21934
21993
  const usedDescent = /* @__PURE__ */ new Set();
21935
- const descentX = /* @__PURE__ */ new Map();
21936
21994
  let fallbackCount = 0;
21937
21995
  const blockedByBox = (band, x) => bandBoxes[band].some((b) => x >= b.x && x <= b.x + b.w - 1);
21938
- for (const r of skipRoutes) {
21939
- const { ex } = attach.get(r);
21996
+ const descentGapsFree = (r, c) => {
21997
+ for (let g = r.fromBand; g <= r.toBand - 1; g++) {
21998
+ if (!verticalFree(g, c, r)) return false;
21999
+ }
22000
+ return true;
22001
+ };
22002
+ for (const r of bent.filter((e) => e.toBand - e.fromBand > 1)) {
22003
+ const ex = r.entryX;
21940
22004
  let chosen;
21941
22005
  for (let d = 0; d <= contentWidth && chosen === void 0; d++) {
21942
22006
  for (const c of d === 0 ? [ex] : [ex - d, ex + d]) {
21943
22007
  if (c < LEFT_MARGIN || c > contentWidth + 1 || usedDescent.has(c)) continue;
22008
+ if (!descentGapsFree(r, c)) continue;
21944
22009
  let blocked = false;
21945
22010
  for (let b = r.fromBand + 1; b < r.toBand && !blocked; b++) blocked = blockedByBox(b, c);
21946
22011
  if (!blocked) {
@@ -21951,167 +22016,182 @@ function buildCanvasWith(map, opts, geo) {
21951
22016
  }
21952
22017
  if (chosen === void 0) chosen = contentWidth + 2 + fallbackCount++ * 2;
21953
22018
  usedDescent.add(chosen);
21954
- descentX.set(r, chosen);
22019
+ for (let g = r.fromBand; g <= r.toBand - 1; g++) takeVertical(g, chosen, r);
22020
+ r.descentX = chosen;
21955
22021
  }
21956
- const totalWidth = fallbackCount > 0 ? contentWidth + 2 + fallbackCount * 2 : contentWidth;
21957
- const gapCount = bands.length - 1;
21958
- const gapSegments = Array.from({ length: gapCount }, () => []);
21959
- const segmentOf = /* @__PURE__ */ new Map();
22022
+ const gapCount = Math.max(0, columns.bands.length - 1);
22023
+ const gapSegments = Array.from(
22024
+ { length: gapCount },
22025
+ () => []
22026
+ );
21960
22027
  for (const r of bent) {
21961
- const { sx, ex } = attach.get(r);
21962
- if (r.toBand - r.fromBand === 1) {
21963
- const landing = { route: r, kind: "landing", lo: Math.min(sx, ex), hi: Math.max(sx, ex) };
21964
- gapSegments[r.toBand - 1].push(landing);
21965
- segmentOf.set(r, { landing });
22028
+ const sx = r.exitX;
22029
+ const ex = r.entryX;
22030
+ if (r.descentX === void 0) {
22031
+ gapSegments[r.toBand - 1].push({
22032
+ edge: r,
22033
+ kind: "landing",
22034
+ segment: { lo: Math.min(sx, ex), hi: Math.max(sx, ex) }
22035
+ });
21966
22036
  } else {
21967
- const c = descentX.get(r);
21968
- const exit = { route: r, kind: "exit", lo: Math.min(sx, c), hi: Math.max(sx, c) };
21969
- const landing = { route: r, kind: "landing", lo: Math.min(c, ex), hi: Math.max(c, ex) };
21970
- gapSegments[r.fromBand].push(exit);
21971
- gapSegments[r.toBand - 1].push(landing);
21972
- segmentOf.set(r, { exit, landing });
22037
+ const c = r.descentX;
22038
+ gapSegments[r.fromBand].push({ edge: r, kind: "exit", segment: { lo: Math.min(sx, c), hi: Math.max(sx, c) } });
22039
+ gapSegments[r.toBand - 1].push({
22040
+ edge: r,
22041
+ kind: "landing",
22042
+ segment: { lo: Math.min(c, ex), hi: Math.max(c, ex) }
22043
+ });
21973
22044
  }
21974
22045
  }
21975
- const segmentRow = /* @__PURE__ */ new Map();
21976
- const gapRowCount = gapSegments.map((segments) => {
22046
+ const exitRow = /* @__PURE__ */ new Map();
22047
+ const landingRow = /* @__PURE__ */ new Map();
22048
+ const gapRowCount = gapSegments.map((entries) => {
21977
22049
  const rowEnds = [];
21978
- for (const s of [...segments].sort((a, b) => a.lo - b.lo)) {
21979
- let row = rowEnds.findIndex((end) => s.lo > end + 1);
22050
+ for (const e of [...entries].sort((a, b) => a.segment.lo - b.segment.lo)) {
22051
+ let row = rowEnds.findIndex((end) => e.segment.lo > end + 1);
21980
22052
  if (row === -1) {
21981
- rowEnds.push(s.hi);
22053
+ rowEnds.push(e.segment.hi);
21982
22054
  row = rowEnds.length - 1;
21983
22055
  } else {
21984
- rowEnds[row] = Math.max(rowEnds[row], s.hi);
22056
+ rowEnds[row] = Math.max(rowEnds[row], e.segment.hi);
21985
22057
  }
21986
- segmentRow.set(s, row);
22058
+ (e.kind === "exit" ? exitRow : landingRow).set(e.edge, row);
21987
22059
  }
21988
22060
  return rowEnds.length;
21989
22061
  });
21990
- let y = 0;
21991
- if (map.title !== void 0) y += 1 + geo.titleGap;
21992
- let laneHeaderY;
21993
- if (laneCount > 0) {
21994
- laneHeaderY = y;
21995
- y += 1 + geo.barGap;
21996
- }
21997
- const barY = [];
21998
- const gapTrackStartY = [];
21999
- for (let b = 0; b < bands.length; b++) {
22000
- barY.push(y);
22001
- y += 1 + geo.barGap;
22002
- const row = bandBoxes[b];
22003
- for (const box of row) box.y = y;
22004
- y += row.reduce((max, box) => Math.max(max, box.h), geo.mode === "constellation" ? 1 : BOX_H);
22005
- if (b < gapCount) {
22006
- y += geo.breathe;
22007
- gapTrackStartY.push(y);
22008
- y += gapRowCount[b];
22009
- y += geo.breathe;
22010
- }
22011
- }
22012
- const legendY = y + 1;
22013
- const rowYOf = (gap, s) => gapTrackStartY[gap] + segmentRow.get(s);
22014
- if (map.title !== void 0) canvas.text(LEFT_MARGIN, 0, map.title, "none", true);
22015
- if (laneHeaderY !== void 0) {
22016
- for (let i = 0; i < laneCount; i++) {
22017
- const label = fitWidth(map.lanes[i].label, laneW[i]);
22018
- const cx = laneX[i] + Math.max(0, Math.floor((laneW[i] - displayWidth(label)) / 2));
22019
- canvas.text(cx, laneHeaderY, label, "faint", true);
22020
- }
22021
- }
22022
- for (let b = 0; b < bands.length; b++) {
22023
- const label = bandLabel[b];
22024
- for (let x = 0; x < totalWidth; x++) canvas.line(x, barY[b], LEFT | RIGHT, true);
22025
- const labelStart = (fallbackCount > 0 ? contentWidth : totalWidth) - displayWidth(label);
22026
- canvas.text(labelStart, barY[b], label, "none", true);
22027
- }
22028
- for (const box of boxes.values()) {
22029
- drawBox(canvas, box, opts, neutral, opts.focus !== void 0 && box.node.id === opts.focus);
22030
- }
22031
- for (const r of routes) {
22032
- const sy = r.fromBox.y + r.fromBox.h - 1;
22033
- const ey = r.toBox.y;
22034
- const bright = opts.focus !== void 0 && (r.fromBox.node.id === opts.focus || r.toBox.node.id === opts.focus);
22035
- const direct = straightX.get(r);
22036
- if (direct !== void 0) {
22037
- drawPath(
22038
- canvas,
22039
- [
22040
- [direct, sy],
22041
- [direct, ey]
22042
- ],
22043
- bright
22044
- );
22045
- continue;
22046
- }
22047
- const { sx, ex } = attach.get(r);
22048
- const segments = segmentOf.get(r);
22049
- const landingY = rowYOf(r.toBand - 1, segments.landing);
22050
- if (r.toBand - r.fromBand === 1) {
22051
- drawPath(
22052
- canvas,
22053
- [
22054
- [sx, sy],
22055
- [sx, landingY],
22056
- [ex, landingY],
22057
- [ex, ey]
22058
- ],
22059
- bright
22060
- );
22061
- } else {
22062
- const c = descentX.get(r);
22063
- const exitY = rowYOf(r.fromBand, segments.exit);
22064
- drawPath(
22065
- canvas,
22066
- [
22067
- [sx, sy],
22068
- [sx, exitY],
22069
- [c, exitY],
22070
- [c, landingY],
22071
- [ex, landingY],
22072
- [ex, ey]
22073
- ],
22074
- bright
22075
- );
22062
+ const edges = pending.map((r) => {
22063
+ const common = { from: r.from, to: r.to, fromBand: r.fromBand, toBand: r.toBand };
22064
+ if (r.straightX !== void 0) return { ...common, kind: "straight", x: r.straightX };
22065
+ if (r.descentX === void 0) {
22066
+ return { ...common, kind: "dogleg", exitX: r.exitX, entryX: r.entryX, landingRow: landingRow.get(r) };
22076
22067
  }
22068
+ return {
22069
+ ...common,
22070
+ kind: "thread",
22071
+ exitX: r.exitX,
22072
+ entryX: r.entryX,
22073
+ descentX: r.descentX,
22074
+ exitRow: exitRow.get(r),
22075
+ landingRow: landingRow.get(r)
22076
+ };
22077
+ });
22078
+ return { edges, gapRowCount, fallbackCount };
22079
+ }
22080
+ function edgePolyline(edge, rows) {
22081
+ const from = rows.boxOf.get(edge.from.node.id);
22082
+ const to = rows.boxOf.get(edge.to.node.id);
22083
+ const sy = from.y + from.h - 1;
22084
+ const ey = to.y;
22085
+ if (edge.kind === "straight") {
22086
+ return [
22087
+ [edge.x, sy],
22088
+ [edge.x, ey]
22089
+ ];
22077
22090
  }
22078
- let lx = LEFT_MARGIN;
22079
- if (neutral) {
22080
- lx = canvas.text(lx, legendY, map.kind, "faint");
22081
- const seen = /* @__PURE__ */ new Set();
22082
- for (const n of map.nodes) {
22083
- const k = n.kind;
22084
- if (k === void 0 || seen.has(k) || kindGlyph(k, opts.unicode) === void 0) continue;
22085
- seen.add(k);
22086
- lx = canvas.text(lx, legendY, " ", "none");
22087
- lx = canvas.text(lx, legendY, `${kindGlyph(k, opts.unicode)} ${k}`, "none");
22088
- }
22089
- } else {
22090
- const legendOpts = { ...opts, spinnerFrame: 0 };
22091
- const legendEntries = [
22092
- ["planned", "dim"],
22093
- ["in-progress", "amber"],
22094
- ["done", "green"],
22095
- ["regressed", "red"]
22091
+ const landingY = rows.gapTrackStartY[edge.toBand - 1] + edge.landingRow;
22092
+ if (edge.kind === "dogleg") {
22093
+ return [
22094
+ [edge.exitX, sy],
22095
+ [edge.exitX, landingY],
22096
+ [edge.entryX, landingY],
22097
+ [edge.entryX, ey]
22096
22098
  ];
22097
- for (const [status, style] of legendEntries) {
22098
- if (lx > LEFT_MARGIN) lx = canvas.text(lx, legendY, " ", "none");
22099
- lx = canvas.text(lx, legendY, `${glyphFor(status, legendOpts)} ${status}`, style);
22099
+ }
22100
+ const exitY = rows.gapTrackStartY[edge.fromBand] + edge.exitRow;
22101
+ return [
22102
+ [edge.exitX, sy],
22103
+ [edge.exitX, exitY],
22104
+ [edge.descentX, exitY],
22105
+ [edge.descentX, landingY],
22106
+ [edge.entryX, landingY],
22107
+ [edge.entryX, ey]
22108
+ ];
22109
+ }
22110
+
22111
+ // src/render/skins.ts
22112
+ function styleFor(face) {
22113
+ switch (face) {
22114
+ case "planned":
22115
+ return "dim";
22116
+ case "in-progress":
22117
+ return "amber";
22118
+ case "done":
22119
+ return "green";
22120
+ case "done-unverified":
22121
+ return "greenDim";
22122
+ case "regressed":
22123
+ return "red";
22124
+ }
22125
+ }
22126
+ function skinFor(face, unicode) {
22127
+ const style = styleFor(face);
22128
+ if (!unicode) {
22129
+ return face === "planned" ? { h: ".", v: ":", corners: ["+", "+", "+", "+"], style } : { h: "-", v: "|", corners: ["+", "+", "+", "+"], style };
22130
+ }
22131
+ switch (face) {
22132
+ case "planned":
22133
+ return { h: "\u254C", v: "\u254E", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style };
22134
+ case "in-progress":
22135
+ return { h: "\u2500", v: "\u2502", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style };
22136
+ // an unverified done keeps the heavy border of done — it is the same
22137
+ // claim, told with a hollow glyph and a dimmer green
22138
+ case "done":
22139
+ case "done-unverified":
22140
+ case "regressed":
22141
+ return { h: "\u2501", v: "\u2503", corners: ["\u250F", "\u2513", "\u2517", "\u251B"], style };
22142
+ }
22143
+ }
22144
+ function glyphFor(face, opts) {
22145
+ if (face === "in-progress") return spinnerGlyph(opts.spinnerFrame, opts.unicode);
22146
+ return face === "done-unverified" ? unverifiedDoneGlyph(opts.unicode) : statusGlyph(face, opts.unicode);
22147
+ }
22148
+ function neutralSkin(unicode) {
22149
+ return unicode ? { h: "\u2500", v: "\u2502", corners: ["\u256D", "\u256E", "\u2570", "\u256F"], style: "none" } : { h: "-", v: "|", corners: ["+", "+", "+", "+"], style: "none" };
22150
+ }
22151
+ function neutralGlyph(node, unicode) {
22152
+ return (node.kind !== void 0 ? kindGlyph(node.kind, unicode) : void 0) ?? (unicode ? "\xB7" : ".");
22153
+ }
22154
+ function unverifiedDoneIds(declared, drawn) {
22155
+ const out = /* @__PURE__ */ new Set();
22156
+ const declaredById = new Map(declared.nodes.map((n) => [n.id, n]));
22157
+ for (const node of drawn.nodes) {
22158
+ if (node.status !== "done") continue;
22159
+ const own = declaredById.get(node.id);
22160
+ if (own !== void 0) {
22161
+ if (own.evidence === void 0) out.add(node.id);
22162
+ } else if (declared.nodes.some(
22163
+ (m) => m.group === node.id && m.status === "done" && m.evidence === void 0
22164
+ )) {
22165
+ out.add(node.id);
22100
22166
  }
22101
22167
  }
22102
- const hits = [...boxes.values()].map((b) => ({
22103
- id: b.node.id,
22104
- x: b.x,
22105
- y: b.y,
22106
- w: b.w,
22107
- h: b.h
22108
- }));
22109
- return { canvas, hits };
22168
+ return out;
22169
+ }
22170
+
22171
+ // src/render/draw.ts
22172
+ function drawTitle(canvas, title) {
22173
+ canvas.text(LEFT_MARGIN, 0, title, "none", true);
22174
+ }
22175
+ function drawLaneHeaders(canvas, map, columns, rows) {
22176
+ if (rows.laneHeaderY === void 0) return;
22177
+ for (let i = 0; i < map.lanes.length; i++) {
22178
+ const region = columns.lanes[i];
22179
+ const label = fitWidth(map.lanes[i].label, region.w);
22180
+ const cx = region.x + Math.max(0, Math.floor((region.w - displayWidth(label)) / 2));
22181
+ canvas.text(cx, rows.laneHeaderY, label, "faint", true);
22182
+ }
22183
+ }
22184
+ function drawBands(canvas, columns, rows, wiredWidth, totalWidth) {
22185
+ for (let b = 0; b < columns.bands.length; b++) {
22186
+ const label = columns.bandLabel[b];
22187
+ for (let x = 0; x < wiredWidth; x++) canvas.line(x, rows.barY[b], LEFT | RIGHT, true);
22188
+ canvas.text(totalWidth - displayWidth(label), rows.barY[b], label, "none", true);
22189
+ }
22110
22190
  }
22111
- function drawBox(canvas, box, opts, neutral, focused = false) {
22191
+ function drawBox(canvas, box, opts, neutral, face, focused = false) {
22112
22192
  const { node, x, y, w } = box;
22113
- const skin = neutral ? neutralSkin(opts.unicode) : skinFor(node.status, opts.unicode);
22114
- const slotGlyph = neutral ? (node.kind !== void 0 ? kindGlyph(node.kind, opts.unicode) : void 0) ?? (opts.unicode ? "\xB7" : ".") : glyphFor(node.status, opts);
22193
+ const skin = neutral ? neutralSkin(opts.unicode) : skinFor(face, opts.unicode);
22194
+ const slotGlyph = neutral ? neutralGlyph(node, opts.unicode) : glyphFor(face, opts);
22115
22195
  if (box.borderless) {
22116
22196
  canvas.text(x + 1, y, slotGlyph, skin.style, true);
22117
22197
  return;
@@ -22131,66 +22211,234 @@ function drawBox(canvas, box, opts, neutral, focused = false) {
22131
22211
  }
22132
22212
  canvas.text(x, y + box.h - 1, skin.corners[2] + skin.h.repeat(inner) + skin.corners[3], skin.style, focused);
22133
22213
  }
22134
-
22135
- // src/store/store.ts
22136
- import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
22137
- import { basename, dirname, join } from "node:path";
22138
- var STATE_FILE_VERSION = 1;
22139
- var STATE_FILE_RELATIVE_PATH = join(".claude", "mellos-mapping.json");
22140
- var PAGES_DIR_NAME = "mellos-mapping.pages";
22141
- function pageFilePath(defaultFile, page) {
22142
- return page === void 0 ? defaultFile : join(dirname(defaultFile), PAGES_DIR_NAME, `${page}.json`);
22214
+ function drawEdges(canvas, edges, rows, opts) {
22215
+ for (const edge of edges) {
22216
+ const bright = opts.focus !== void 0 && (edge.from.node.id === opts.focus || edge.to.node.id === opts.focus);
22217
+ drawPath(canvas, edgePolyline(edge, rows), bright);
22218
+ }
22143
22219
  }
22144
- var CONFIG_FILE_NAME = "mellos-mapping.config.json";
22145
- var CONFIG_FILE_VERSION = 1;
22146
- function configFilePath(defaultFile) {
22147
- return join(dirname(defaultFile), CONFIG_FILE_NAME);
22220
+ function drawLegend(canvas, map, opts, legendY, neutral, anyUnverified) {
22221
+ let lx = LEFT_MARGIN;
22222
+ if (neutral) {
22223
+ lx = canvas.text(lx, legendY, map.kind, "faint");
22224
+ const seen = /* @__PURE__ */ new Set();
22225
+ for (const n of map.nodes) {
22226
+ const k = n.kind;
22227
+ if (k === void 0 || seen.has(k) || kindGlyph(k, opts.unicode) === void 0) continue;
22228
+ seen.add(k);
22229
+ lx = canvas.text(lx, legendY, " ", "none");
22230
+ lx = canvas.text(lx, legendY, `${kindGlyph(k, opts.unicode)} ${k}`, "none");
22231
+ }
22232
+ return;
22233
+ }
22234
+ const legendOpts = { ...opts, spinnerFrame: 0 };
22235
+ const faces = ["planned", "in-progress", "done", "regressed"];
22236
+ if (anyUnverified) faces.push("done-unverified");
22237
+ for (const face of faces) {
22238
+ if (lx > LEFT_MARGIN) lx = canvas.text(lx, legendY, " ", "none");
22239
+ const word = face === "done-unverified" ? "done, no evidence" : face;
22240
+ lx = canvas.text(lx, legendY, `${glyphFor(face, legendOpts)} ${word}`, styleFor(face));
22241
+ }
22148
22242
  }
22149
- var MAPPING_POLICIES = ["always", "complex", "on-request"];
22150
- function makeMappingPolicy(raw) {
22151
- return MAPPING_POLICIES.includes(raw) ? ok(raw) : err({ kind: "invalid-policy", raw, allowed: MAPPING_POLICIES });
22243
+
22244
+ // src/render/layout.ts
22245
+ var LABEL_BUDGET_MIN = 4;
22246
+ function boxSpec(node, geo, unicode, neutral) {
22247
+ const glyph = node.kind !== void 0 ? kindGlyph(node.kind, unicode) : void 0;
22248
+ const badge = node.submap !== void 0 ? unicode ? " \u229E" : " +" : "";
22249
+ const badgeW = displayWidth(badge);
22250
+ const text2 = !neutral && glyph !== void 0 ? `${glyph} ${node.label}` : node.label;
22251
+ if (geo.mode === "constellation") {
22252
+ return { w: 3, h: 1, label: "", pad: 0, borderless: true, extra: [] };
22253
+ }
22254
+ if (geo.mode === "detail" && geo.detail !== void 0) {
22255
+ const budget2 = geo.detail;
22256
+ const innerW = Math.min(Math.max(displayWidth(text2) + badgeW + 4, budget2.innerMin), budget2.innerMax);
22257
+ const extra = [];
22258
+ if (node.evidence !== void 0) extra.push({ text: fitWidth(` ${node.evidence}`, innerW), style: "faint" });
22259
+ if (node.detail !== void 0) {
22260
+ const wrapped = wrapWidth(node.detail, innerW - 2);
22261
+ for (let i = 0; i < Math.min(wrapped.length, budget2.noteRows); i++) {
22262
+ const cut = i === budget2.noteRows - 1 && wrapped.length > budget2.noteRows;
22263
+ extra.push({ text: ` ${cut ? fitWidth(wrapped[i] + "\u2026", innerW - 2) : wrapped[i]}`, style: "none" });
22264
+ }
22265
+ }
22266
+ return {
22267
+ w: innerW + 2,
22268
+ h: BOX_H + extra.length,
22269
+ label: fitWidth(text2, innerW - 4 - badgeW) + badge,
22270
+ pad: 1,
22271
+ borderless: false,
22272
+ extra
22273
+ };
22274
+ }
22275
+ const budget = Math.max(LABEL_BUDGET_MIN, Math.ceil(displayWidth(text2) * geo.scale));
22276
+ const label = fitWidth(text2, budget) + badge;
22277
+ return {
22278
+ w: displayWidth(label) + 4 + 2 * geo.pad,
22279
+ h: BOX_H,
22280
+ label,
22281
+ pad: geo.pad,
22282
+ borderless: false,
22283
+ extra: []
22284
+ };
22152
22285
  }
22153
- function describeMappingPolicy(policy) {
22154
- switch (policy) {
22155
- case "always":
22156
- return "map every structured task \u2014 workflows, designs, architecture, technical dependencies";
22157
- case "complex":
22158
- return "map only medium or complex tasks \u2014 several modules, a new subsystem, roughly an hour or more";
22159
- case "on-request":
22160
- return "map only when the user explicitly asks";
22286
+ function layoutColumns(map, geo, unicode, neutral) {
22287
+ const bands = [...map.layers].sort((a, b) => b.rank - a.rank);
22288
+ const bandIndexOf = new Map(bands.map((l, i) => [l.id, i]));
22289
+ const sized = /* @__PURE__ */ new Map();
22290
+ const bandSized = bands.map(() => []);
22291
+ for (const node of map.nodes) {
22292
+ const spec = { node, ...boxSpec(node, geo, unicode, neutral) };
22293
+ bandSized[bandIndexOf.get(node.layer)].push(spec);
22294
+ sized.set(node.id, spec);
22295
+ }
22296
+ const columnOf = /* @__PURE__ */ new Map();
22297
+ const lanes = [];
22298
+ if (map.lanes.length === 0) {
22299
+ for (const row of bandSized) {
22300
+ let x = LEFT_MARGIN;
22301
+ for (const spec of row) {
22302
+ columnOf.set(spec, x);
22303
+ x += spec.w + geo.boxGap;
22304
+ }
22305
+ }
22306
+ } else {
22307
+ const laneCount = map.lanes.length;
22308
+ const laneGap = geo.boxGap + 2;
22309
+ const laneIndexOf = new Map(map.lanes.map((l, i) => [l.id, i]));
22310
+ const regions = laneCount + 1;
22311
+ const grouped = bandSized.map((row) => {
22312
+ const cells = Array.from({ length: regions }, () => []);
22313
+ for (const spec of row) {
22314
+ const lane = spec.node.lane;
22315
+ cells[lane !== void 0 ? laneIndexOf.get(lane) : regions - 1].push(spec);
22316
+ }
22317
+ return cells;
22318
+ });
22319
+ const regionW = Array.from({ length: regions }, () => 0);
22320
+ for (const cells of grouped) {
22321
+ for (let i = 0; i < regions; i++) {
22322
+ const rowW = cells[i].reduce((sum, b, k) => sum + b.w + (k > 0 ? geo.boxGap : 0), 0);
22323
+ regionW[i] = Math.max(regionW[i], rowW);
22324
+ }
22325
+ }
22326
+ for (let i = 0; i < laneCount; i++) regionW[i] = Math.max(regionW[i], displayWidth(map.lanes[i].label) + 2);
22327
+ let x0 = LEFT_MARGIN;
22328
+ for (let i = 0; i < regions; i++) {
22329
+ lanes.push({ x: x0, w: regionW[i] });
22330
+ x0 += regionW[i] + laneGap;
22331
+ }
22332
+ for (const cells of grouped) {
22333
+ for (let i = 0; i < regions; i++) {
22334
+ let x = lanes[i].x;
22335
+ for (const spec of cells[i]) {
22336
+ columnOf.set(spec, x);
22337
+ x += spec.w + geo.boxGap;
22338
+ }
22339
+ }
22340
+ }
22161
22341
  }
22342
+ const placed = /* @__PURE__ */ new Map();
22343
+ for (const [, spec] of sized) placed.set(spec, { ...spec, x: columnOf.get(spec) ?? LEFT_MARGIN });
22344
+ const bandBoxes = bandSized.map((row) => row.map((spec) => placed.get(spec)));
22345
+ const boxOf = /* @__PURE__ */ new Map();
22346
+ for (const node of map.nodes) boxOf.set(node.id, placed.get(sized.get(node.id)));
22347
+ const bandLabel = bands.map((l, i) => {
22348
+ const row = bandBoxes[i];
22349
+ const done = row.filter((b) => b.node.status === "done").length;
22350
+ return geo.bandCounts && row.length > 0 && !neutral ? ` ${l.name} ${done}/${row.length}` : ` ${l.name}`;
22351
+ });
22352
+ let contentWidth = LEFT_MARGIN + BAR_MIN_RUN;
22353
+ for (const row of bandBoxes) for (const box of row) contentWidth = Math.max(contentWidth, box.x + box.w);
22354
+ for (const lane of lanes) contentWidth = Math.max(contentWidth, lane.x + lane.w);
22355
+ return { bands, bandIndexOf, bandBoxes, boxOf, lanes, bandLabel, contentWidth };
22162
22356
  }
22163
- function loadMappingPolicy(defaultFile) {
22164
- const path = configFilePath(defaultFile);
22165
- let text2;
22166
- try {
22167
- text2 = readFileSync(path, "utf8");
22168
- } catch (e) {
22169
- if (e.code === "ENOENT") return ok(void 0);
22170
- throw e;
22357
+ function layoutRows(columns, geo, gapRowCount, hasTitle, hasLanes) {
22358
+ let y = 0;
22359
+ if (hasTitle) y += 1 + geo.titleGap;
22360
+ let laneHeaderY;
22361
+ if (hasLanes) {
22362
+ laneHeaderY = y;
22363
+ y += 1 + geo.barGap;
22171
22364
  }
22172
- let raw;
22173
- try {
22174
- raw = JSON.parse(text2);
22175
- } catch (e) {
22176
- return err({ kind: "malformed-json", path, detail: e.message });
22365
+ const barY = [];
22366
+ const gapTrackStartY = [];
22367
+ const bandBoxes = [];
22368
+ const placed = /* @__PURE__ */ new Map();
22369
+ const gapCount = columns.bands.length - 1;
22370
+ for (let b = 0; b < columns.bands.length; b++) {
22371
+ barY.push(y);
22372
+ y += 1 + geo.barGap;
22373
+ const row = columns.bandBoxes[b];
22374
+ for (const box of row) placed.set(box, { ...box, y });
22375
+ bandBoxes.push(row.map((box) => placed.get(box)));
22376
+ y += row.reduce((max, box) => Math.max(max, box.h), geo.mode === "constellation" ? 1 : BOX_H);
22377
+ if (b < gapCount) {
22378
+ y += geo.breathe;
22379
+ gapTrackStartY.push(y);
22380
+ y += gapRowCount[b];
22381
+ y += geo.breathe;
22382
+ }
22177
22383
  }
22178
- if (!isRecord(raw)) return err({ kind: "bad-shape", path, detail: "root is not an object" });
22179
- if (raw["version"] !== CONFIG_FILE_VERSION) {
22180
- return err({ kind: "bad-shape", path, detail: `version is ${String(raw["version"])}, expected ${CONFIG_FILE_VERSION}` });
22384
+ const boxOf = /* @__PURE__ */ new Map();
22385
+ for (const [id2, box] of columns.boxOf) boxOf.set(id2, placed.get(box));
22386
+ return { boxOf, bandBoxes, barY, gapTrackStartY, laneHeaderY, legendY: y + 1 };
22387
+ }
22388
+
22389
+ // src/render/render.ts
22390
+ function renderMap(map, opts) {
22391
+ const built = buildCanvas(map, opts);
22392
+ return built.canvas.emit(opts);
22393
+ }
22394
+ function buildCanvas(map, opts) {
22395
+ const oriented = flipForSequence(map);
22396
+ const plainGeo = zoomGeometry(opts.zoom ?? ZOOM_DEFAULT);
22397
+ const aggregated = plainGeo.mode === "constellation" ? aggregateMap(oriented) : void 0;
22398
+ const drawn = aggregated ?? oriented;
22399
+ return paint(drawn, opts, aggregated !== void 0 ? AGGREGATE_GEO : plainGeo, unverifiedDoneIds(oriented, drawn));
22400
+ }
22401
+ function paint(map, opts, geo, unverified) {
22402
+ const canvas = new Canvas();
22403
+ if (map.layers.length === 0) {
22404
+ canvas.text(0, 0, map.title ?? "mellos mapping", "none", true);
22405
+ canvas.text(0, 2, "(empty map \u2014 declare layers and nodes to begin)", "dim");
22406
+ return { canvas, hits: [] };
22181
22407
  }
22182
- const rawPolicy = raw["policy"];
22183
- if (rawPolicy === void 0) return ok(void 0);
22184
- if (typeof rawPolicy !== "string") return err({ kind: "bad-shape", path, detail: "policy is not a string" });
22185
- const policy = makeMappingPolicy(rawPolicy);
22186
- return policy.ok ? ok(policy.value) : err({ kind: "bad-shape", path, detail: `policy is "${rawPolicy}", expected one of: ${MAPPING_POLICIES.join(" | ")}` });
22408
+ const neutral = isNeutralKind(map);
22409
+ const columns = layoutColumns(map, geo, opts.unicode, neutral);
22410
+ const routing = routeEdges(map, columns);
22411
+ const rows = layoutRows(columns, geo, routing.gapRowCount, map.title !== void 0, map.lanes.length > 0);
22412
+ const wiredWidth = routing.fallbackCount > 0 ? columns.contentWidth + 2 + routing.fallbackCount * 2 : columns.contentWidth;
22413
+ const totalWidth = wiredWidth + Math.max(...columns.bandLabel.map(displayWidth));
22414
+ if (map.title !== void 0) drawTitle(canvas, map.title);
22415
+ drawLaneHeaders(canvas, map, columns, rows);
22416
+ drawBands(canvas, columns, rows, wiredWidth, totalWidth);
22417
+ const faceOf = (id2, status2) => unverified.has(id2) ? "done-unverified" : status2;
22418
+ for (const box of rows.boxOf.values()) {
22419
+ const id2 = box.node.id;
22420
+ drawBox(canvas, box, opts, neutral, faceOf(id2, box.node.status), opts.focus !== void 0 && id2 === opts.focus);
22421
+ }
22422
+ drawEdges(canvas, routing.edges, rows, opts);
22423
+ drawLegend(canvas, map, opts, rows.legendY, neutral, unverified.size > 0);
22424
+ const hits = [...rows.boxOf.values()].map((b) => ({
22425
+ id: b.node.id,
22426
+ x: b.x,
22427
+ y: b.y,
22428
+ w: b.w,
22429
+ h: b.h
22430
+ }));
22431
+ return { canvas, hits };
22187
22432
  }
22188
- function saveMappingPolicy(defaultFile, policy) {
22189
- const path = configFilePath(defaultFile);
22190
- mkdirSync(dirname(path), { recursive: true });
22191
- const tmp = path + ".tmp";
22192
- writeFileSync(tmp, JSON.stringify({ version: CONFIG_FILE_VERSION, policy }, null, 2) + "\n", "utf8");
22193
- renameSync(tmp, path);
22433
+
22434
+ // src/store/store.ts
22435
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
22436
+ import { basename, dirname, join } from "node:path";
22437
+
22438
+ // src/store/format.ts
22439
+ var STATE_FILE_VERSION = 1;
22440
+ function makePageId(raw) {
22441
+ return ID_RULE.test(raw) ? ok(raw) : err({ kind: "invalid-id", raw, rule: ID_RULE_TEXT });
22194
22442
  }
22195
22443
  function describeStoreError(e) {
22196
22444
  switch (e.kind) {
@@ -22202,132 +22450,198 @@ function describeStoreError(e) {
22202
22450
  return `map file ${e.path} has an unexpected shape: ${e.detail}`;
22203
22451
  case "invariant-violation":
22204
22452
  return `map file ${e.path} violates a structural invariant: ${describeMapError(e.violation)}`;
22453
+ case "save-failed":
22454
+ return `could not write ${e.path}: ${e.detail}`;
22455
+ case "delete-failed":
22456
+ return `could not delete ${e.path}: ${e.detail}`;
22205
22457
  }
22206
22458
  }
22207
22459
  function isRecord(v) {
22208
22460
  return typeof v === "object" && v !== null && !Array.isArray(v);
22209
22461
  }
22210
- function asArray(v) {
22211
- return Array.isArray(v) ? v : [];
22462
+ function describeValue(v) {
22463
+ if (v === void 0) return "missing";
22464
+ if (v === null) return "null";
22465
+ if (Array.isArray(v)) return "an array";
22466
+ return `a ${typeof v}`;
22467
+ }
22468
+ function badShape(path, where, expected, got) {
22469
+ return err({ kind: "bad-shape", path, detail: `${where} is ${describeValue(got)}, expected ${expected}` });
22212
22470
  }
22213
- function optionalString(v) {
22214
- return typeof v === "string" ? v : void 0;
22471
+ function arrayField(raw, key, path, presence) {
22472
+ const v = raw[key];
22473
+ if (Array.isArray(v)) return ok(v);
22474
+ if (v === void 0 && presence === "optional") return ok([]);
22475
+ return badShape(path, `"${key}"`, "an array", v);
22476
+ }
22477
+ function requiredString(rec, key, where, path) {
22478
+ const v = rec[key];
22479
+ return typeof v === "string" ? ok(v) : badShape(path, `${where}.${key}`, "a string", v);
22480
+ }
22481
+ function optionalString(rec, key, where, path) {
22482
+ const v = rec[key];
22483
+ if (v === void 0) return ok(void 0);
22484
+ return typeof v === "string" ? ok(v) : badShape(path, `${where}.${key}`, "a string", v);
22215
22485
  }
22216
22486
  function parseMap(raw, path) {
22217
22487
  if (!isRecord(raw)) return err({ kind: "bad-shape", path, detail: "root is not an object" });
22218
22488
  if (raw["version"] !== STATE_FILE_VERSION) {
22219
22489
  return err({ kind: "bad-shape", path, detail: `version is ${String(raw["version"])}, expected ${STATE_FILE_VERSION}` });
22220
22490
  }
22491
+ const layers = arrayField(raw, "layers", path, "required");
22492
+ if (!layers.ok) return layers;
22493
+ const nodes = arrayField(raw, "nodes", path, "required");
22494
+ if (!nodes.ok) return nodes;
22495
+ const edges = arrayField(raw, "edges", path, "required");
22496
+ if (!edges.ok) return edges;
22497
+ const lanes = arrayField(raw, "lanes", path, "optional");
22498
+ if (!lanes.ok) return lanes;
22499
+ const groups = arrayField(raw, "groups", path, "optional");
22500
+ if (!groups.ok) return groups;
22221
22501
  let map = EMPTY_MAP;
22222
- const title = optionalString(raw["title"]);
22223
- if (title !== void 0) map = setTitle(map, title);
22224
- const rawKind = optionalString(raw["kind"]);
22225
- if (rawKind !== void 0) {
22226
- const kind = makeMapKind(rawKind);
22502
+ const title = optionalString(raw, "title", "map", path);
22503
+ if (!title.ok) return title;
22504
+ if (title.value !== void 0) map = setTitle(map, title.value);
22505
+ const rawKind = optionalString(raw, "kind", "map", path);
22506
+ if (!rawKind.ok) return rawKind;
22507
+ if (rawKind.value !== void 0) {
22508
+ const kind = makeMapKind(rawKind.value);
22227
22509
  if (!kind.ok) return err({ kind: "invariant-violation", path, violation: kind.error });
22228
22510
  map = setKind(map, kind.value);
22229
22511
  }
22230
- for (const [i, rawLayer] of asArray(raw["layers"]).entries()) {
22231
- if (!isRecord(rawLayer)) return err({ kind: "bad-shape", path, detail: `layers[${i}] is not an object` });
22232
- const id = makeLayerId(String(rawLayer["id"] ?? ""));
22233
- if (!id.ok) return err({ kind: "invariant-violation", path, violation: id.error });
22234
- const name = optionalString(rawLayer["name"]);
22235
- const rank = rawLayer["rank"];
22236
- if (name === void 0 || typeof rank !== "number" || !Number.isInteger(rank)) {
22237
- return err({ kind: "bad-shape", path, detail: `layers[${i}] needs a string name and an integer rank` });
22238
- }
22239
- const next = declareLayer(map, { id: id.value, name, rank });
22512
+ for (const [i, rawLayer] of layers.value.entries()) {
22513
+ const where = `layers[${i}]`;
22514
+ if (!isRecord(rawLayer)) return badShape(path, where, "an object", rawLayer);
22515
+ const rawId = requiredString(rawLayer, "id", where, path);
22516
+ if (!rawId.ok) return rawId;
22517
+ const id2 = makeLayerId(rawId.value);
22518
+ if (!id2.ok) return err({ kind: "invariant-violation", path, violation: id2.error });
22519
+ const name = requiredString(rawLayer, "name", where, path);
22520
+ if (!name.ok) return name;
22521
+ const rawRank = rawLayer["rank"];
22522
+ if (typeof rawRank !== "number") return badShape(path, `${where}.rank`, "a number", rawRank);
22523
+ const rank2 = makeRank(rawRank);
22524
+ if (!rank2.ok) return err({ kind: "invariant-violation", path, violation: rank2.error });
22525
+ const next = declareLayer(map, { id: id2.value, name: name.value, rank: rank2.value });
22240
22526
  if (!next.ok) return err({ kind: "invariant-violation", path, violation: next.error });
22241
22527
  map = next.value;
22242
22528
  }
22243
- for (const [i, rawLane] of asArray(raw["lanes"]).entries()) {
22244
- if (!isRecord(rawLane)) return err({ kind: "bad-shape", path, detail: `lanes[${i}] is not an object` });
22245
- const id = makeLaneId(String(rawLane["id"] ?? ""));
22246
- if (!id.ok) return err({ kind: "invariant-violation", path, violation: id.error });
22247
- const label = optionalString(rawLane["label"]);
22248
- if (label === void 0) return err({ kind: "bad-shape", path, detail: `lanes[${i}] needs a string label` });
22249
- const declared = declareLane(map, { id: id.value, label });
22529
+ for (const [i, rawLane] of lanes.value.entries()) {
22530
+ const where = `lanes[${i}]`;
22531
+ if (!isRecord(rawLane)) return badShape(path, where, "an object", rawLane);
22532
+ const rawId = requiredString(rawLane, "id", where, path);
22533
+ if (!rawId.ok) return rawId;
22534
+ const id2 = makeLaneId(rawId.value);
22535
+ if (!id2.ok) return err({ kind: "invariant-violation", path, violation: id2.error });
22536
+ const label = requiredString(rawLane, "label", where, path);
22537
+ if (!label.ok) return label;
22538
+ const declared = declareLane(map, { id: id2.value, label: label.value });
22250
22539
  if (!declared.ok) return err({ kind: "invariant-violation", path, violation: declared.error });
22251
22540
  map = declared.value;
22252
22541
  }
22253
- for (const [i, rawGroup] of asArray(raw["groups"]).entries()) {
22254
- if (!isRecord(rawGroup)) return err({ kind: "bad-shape", path, detail: `groups[${i}] is not an object` });
22255
- const id = makeGroupId(String(rawGroup["id"] ?? ""));
22256
- if (!id.ok) return err({ kind: "invariant-violation", path, violation: id.error });
22257
- const layer = makeLayerId(String(rawGroup["layer"] ?? ""));
22542
+ for (const [i, rawGroup] of groups.value.entries()) {
22543
+ const where = `groups[${i}]`;
22544
+ if (!isRecord(rawGroup)) return badShape(path, where, "an object", rawGroup);
22545
+ const rawId = requiredString(rawGroup, "id", where, path);
22546
+ if (!rawId.ok) return rawId;
22547
+ const id2 = makeGroupId(rawId.value);
22548
+ if (!id2.ok) return err({ kind: "invariant-violation", path, violation: id2.error });
22549
+ const rawLayer = requiredString(rawGroup, "layer", where, path);
22550
+ if (!rawLayer.ok) return rawLayer;
22551
+ const layer = makeLayerId(rawLayer.value);
22258
22552
  if (!layer.ok) return err({ kind: "invariant-violation", path, violation: layer.error });
22259
- const label = optionalString(rawGroup["label"]);
22260
- if (label === void 0) return err({ kind: "bad-shape", path, detail: `groups[${i}] needs a string label` });
22261
- const declared = declareGroup(map, { id: id.value, label, layer: layer.value });
22553
+ const label = requiredString(rawGroup, "label", where, path);
22554
+ if (!label.ok) return label;
22555
+ const declared = declareGroup(map, { id: id2.value, label: label.value, layer: layer.value });
22262
22556
  if (!declared.ok) return err({ kind: "invariant-violation", path, violation: declared.error });
22263
22557
  map = declared.value;
22264
22558
  }
22265
- for (const [i, rawNode] of asArray(raw["nodes"]).entries()) {
22266
- if (!isRecord(rawNode)) return err({ kind: "bad-shape", path, detail: `nodes[${i}] is not an object` });
22267
- const id = makeNodeId(String(rawNode["id"] ?? ""));
22268
- if (!id.ok) return err({ kind: "invariant-violation", path, violation: id.error });
22269
- const layer = makeLayerId(String(rawNode["layer"] ?? ""));
22559
+ for (const [i, rawNode] of nodes.value.entries()) {
22560
+ const where = `nodes[${i}]`;
22561
+ if (!isRecord(rawNode)) return badShape(path, where, "an object", rawNode);
22562
+ const rawId = requiredString(rawNode, "id", where, path);
22563
+ if (!rawId.ok) return rawId;
22564
+ const id2 = makeNodeId(rawId.value);
22565
+ if (!id2.ok) return err({ kind: "invariant-violation", path, violation: id2.error });
22566
+ const rawLayer = requiredString(rawNode, "layer", where, path);
22567
+ if (!rawLayer.ok) return rawLayer;
22568
+ const layer = makeLayerId(rawLayer.value);
22270
22569
  if (!layer.ok) return err({ kind: "invariant-violation", path, violation: layer.error });
22271
- const status = makeNodeStatus(String(rawNode["status"] ?? ""));
22272
- if (!status.ok) return err({ kind: "invariant-violation", path, violation: status.error });
22273
- const label = optionalString(rawNode["label"]);
22274
- if (label === void 0) return err({ kind: "bad-shape", path, detail: `nodes[${i}] needs a string label` });
22275
- const detail = optionalString(rawNode["detail"]);
22276
- const rawGroup = optionalString(rawNode["group"]);
22570
+ const rawStatus = requiredString(rawNode, "status", where, path);
22571
+ if (!rawStatus.ok) return rawStatus;
22572
+ const status2 = makeNodeStatus(rawStatus.value);
22573
+ if (!status2.ok) return err({ kind: "invariant-violation", path, violation: status2.error });
22574
+ const label = requiredString(rawNode, "label", where, path);
22575
+ if (!label.ok) return label;
22576
+ const detail = optionalString(rawNode, "detail", where, path);
22577
+ if (!detail.ok) return detail;
22578
+ const rawGroup = optionalString(rawNode, "group", where, path);
22579
+ if (!rawGroup.ok) return rawGroup;
22277
22580
  let group;
22278
- if (rawGroup !== void 0) {
22279
- const made = makeGroupId(rawGroup);
22581
+ if (rawGroup.value !== void 0) {
22582
+ const made = makeGroupId(rawGroup.value);
22280
22583
  if (!made.ok) return err({ kind: "invariant-violation", path, violation: made.error });
22281
22584
  group = made.value;
22282
22585
  }
22283
- const rawNodeKind = optionalString(rawNode["kind"]);
22284
- let nodeKind;
22285
- if (rawNodeKind !== void 0) {
22286
- const made = makeNodeKind(rawNodeKind);
22586
+ const rawNodeKind = optionalString(rawNode, "kind", where, path);
22587
+ if (!rawNodeKind.ok) return rawNodeKind;
22588
+ let nodeKind2;
22589
+ if (rawNodeKind.value !== void 0) {
22590
+ const made = makeNodeKind(rawNodeKind.value);
22287
22591
  if (!made.ok) return err({ kind: "invariant-violation", path, violation: made.error });
22288
- nodeKind = made.value;
22592
+ nodeKind2 = made.value;
22289
22593
  }
22290
- const rawLane = optionalString(rawNode["lane"]);
22594
+ const rawLane = optionalString(rawNode, "lane", where, path);
22595
+ if (!rawLane.ok) return rawLane;
22291
22596
  let lane;
22292
- if (rawLane !== void 0) {
22293
- const made = makeLaneId(rawLane);
22597
+ if (rawLane.value !== void 0) {
22598
+ const made = makeLaneId(rawLane.value);
22294
22599
  if (!made.ok) return err({ kind: "invariant-violation", path, violation: made.error });
22295
22600
  lane = made.value;
22296
22601
  }
22297
- const rawSubmap = optionalString(rawNode["submap"]);
22602
+ const rawSubmap = optionalString(rawNode, "submap", where, path);
22603
+ if (!rawSubmap.ok) return rawSubmap;
22298
22604
  let submap;
22299
- if (rawSubmap !== void 0) {
22300
- const made = makeSubmapRef(rawSubmap);
22605
+ if (rawSubmap.value !== void 0) {
22606
+ const made = makeSubmapRef(rawSubmap.value);
22301
22607
  if (!made.ok) return err({ kind: "invariant-violation", path, violation: made.error });
22302
22608
  submap = made.value;
22303
22609
  }
22304
22610
  const declared = declareNode(map, {
22305
- id: id.value,
22306
- label,
22611
+ id: id2.value,
22612
+ label: label.value,
22307
22613
  layer: layer.value,
22308
- status: status.value,
22309
- ...detail !== void 0 ? { detail } : {},
22614
+ status: status2.value,
22615
+ ...detail.value !== void 0 ? { detail: detail.value } : {},
22310
22616
  ...group !== void 0 ? { group } : {},
22311
- ...nodeKind !== void 0 ? { kind: nodeKind } : {},
22617
+ ...nodeKind2 !== void 0 ? { kind: nodeKind2 } : {},
22312
22618
  ...lane !== void 0 ? { lane } : {},
22313
22619
  ...submap !== void 0 ? { submap } : {}
22314
22620
  });
22315
22621
  if (!declared.ok) return err({ kind: "invariant-violation", path, violation: declared.error });
22316
22622
  map = declared.value;
22317
- const evidence = optionalString(rawNode["evidence"]);
22318
- if (evidence !== void 0) {
22319
- const updated = updateNode(map, { id: id.value, evidence });
22623
+ const evidence = optionalString(rawNode, "evidence", where, path);
22624
+ if (!evidence.ok) return evidence;
22625
+ if (evidence.value !== void 0) {
22626
+ const updated = updateNode(map, { id: id2.value, evidence: evidence.value });
22320
22627
  if (!updated.ok) return err({ kind: "invariant-violation", path, violation: updated.error });
22321
22628
  map = updated.value;
22322
22629
  }
22323
22630
  }
22324
- for (const [i, rawEdge] of asArray(raw["edges"]).entries()) {
22325
- if (!isRecord(rawEdge)) return err({ kind: "bad-shape", path, detail: `edges[${i}] is not an object` });
22326
- const from = makeNodeId(String(rawEdge["from"] ?? ""));
22631
+ for (const [i, rawEdge] of edges.value.entries()) {
22632
+ const where = `edges[${i}]`;
22633
+ if (!isRecord(rawEdge)) return badShape(path, where, "an object", rawEdge);
22634
+ const rawFrom = requiredString(rawEdge, "from", where, path);
22635
+ if (!rawFrom.ok) return rawFrom;
22636
+ const from = makeNodeId(rawFrom.value);
22327
22637
  if (!from.ok) return err({ kind: "invariant-violation", path, violation: from.error });
22328
- const to = makeNodeId(String(rawEdge["to"] ?? ""));
22638
+ const rawTo = requiredString(rawEdge, "to", where, path);
22639
+ if (!rawTo.ok) return rawTo;
22640
+ const to = makeNodeId(rawTo.value);
22329
22641
  if (!to.ok) return err({ kind: "invariant-violation", path, violation: to.error });
22330
- const linked = linkNodes(map, from.value, to.value, optionalString(rawEdge["label"]));
22642
+ const label = optionalString(rawEdge, "label", where, path);
22643
+ if (!label.ok) return label;
22644
+ const linked = linkNodes(map, from.value, to.value, label.value);
22331
22645
  if (!linked.ok) return err({ kind: "invariant-violation", path, violation: linked.error });
22332
22646
  map = linked.value;
22333
22647
  }
@@ -22346,6 +22660,222 @@ function serializeMap(map) {
22346
22660
  };
22347
22661
  return JSON.stringify(body, null, 2) + "\n";
22348
22662
  }
22663
+
22664
+ // src/store/store.ts
22665
+ var RENAME_MAX_ATTEMPTS = 10;
22666
+ var RENAME_BACKOFF_STEP_MS = 10;
22667
+ var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOENT"]);
22668
+ function sleepSync(ms) {
22669
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
22670
+ }
22671
+ function discardTemp(tmp) {
22672
+ try {
22673
+ rmSync(tmp, { force: true });
22674
+ } catch {
22675
+ }
22676
+ }
22677
+ function errnoOf(e) {
22678
+ return e.code ?? e.message;
22679
+ }
22680
+ function writeFileAtomic(path, contents) {
22681
+ const tmp = `${path}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
22682
+ try {
22683
+ mkdirSync(dirname(path), { recursive: true });
22684
+ writeFileSync(tmp, contents, "utf8");
22685
+ } catch (e) {
22686
+ discardTemp(tmp);
22687
+ return err({ kind: "save-failed", path, detail: `writing the temp file failed: ${errnoOf(e)}` });
22688
+ }
22689
+ let attempt = 1;
22690
+ for (; ; ) {
22691
+ try {
22692
+ renameSync(tmp, path);
22693
+ return ok(void 0);
22694
+ } catch (e) {
22695
+ const code = errnoOf(e);
22696
+ if (!TRANSIENT_RENAME_CODES.has(code) || attempt >= RENAME_MAX_ATTEMPTS) {
22697
+ discardTemp(tmp);
22698
+ return err({ kind: "save-failed", path, detail: `${code} after ${attempt} attempt(s)` });
22699
+ }
22700
+ sleepSync(attempt * RENAME_BACKOFF_STEP_MS);
22701
+ attempt += 1;
22702
+ }
22703
+ }
22704
+ }
22705
+ function isRecord2(v) {
22706
+ return typeof v === "object" && v !== null && !Array.isArray(v);
22707
+ }
22708
+ function stripBom(text2) {
22709
+ return text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
22710
+ }
22711
+ var STORE_DIR_NAME = ".mellos";
22712
+ var STATE_FILE_RELATIVE_PATH = join(STORE_DIR_NAME, "map.json");
22713
+ var PAGES_DIR_NAME = "pages";
22714
+ function pageFilePath(defaultFile, page2) {
22715
+ return page2 === void 0 ? defaultFile : join(dirname(defaultFile), PAGES_DIR_NAME, `${page2}.json`);
22716
+ }
22717
+ function pageIdOfFile(defaultFile, path) {
22718
+ if (path === defaultFile) return void 0;
22719
+ const name = basename(path);
22720
+ return name.endsWith(".json") ? name.slice(0, -".json".length) : name;
22721
+ }
22722
+ function listPageFiles(defaultFile) {
22723
+ const out = [];
22724
+ if (existsSync(defaultFile)) out.push(defaultFile);
22725
+ let entries = [];
22726
+ try {
22727
+ entries = readdirSync(join(dirname(defaultFile), PAGES_DIR_NAME));
22728
+ } catch {
22729
+ }
22730
+ for (const e of entries.sort()) {
22731
+ if (e.endsWith(".json")) out.push(join(dirname(defaultFile), PAGES_DIR_NAME, e));
22732
+ }
22733
+ return out;
22734
+ }
22735
+ function deletePageFile(path) {
22736
+ try {
22737
+ rmSync(path, { force: true });
22738
+ return ok(void 0);
22739
+ } catch (e) {
22740
+ return err({ kind: "delete-failed", path, detail: errnoOf(e) });
22741
+ }
22742
+ }
22743
+ var VIEWERS_DIR_NAME = "viewers";
22744
+ var VIEWER_FILE_VERSION = 1;
22745
+ var VIEWER_STALE_MS = 5e3;
22746
+ var VIEWER_SWEEP_MS = 6e4;
22747
+ function viewersDirPath(defaultFile) {
22748
+ return join(dirname(defaultFile), VIEWERS_DIR_NAME);
22749
+ }
22750
+ function viewerPidOf(fileName) {
22751
+ const m = /^(\d+)\.json$/.exec(fileName);
22752
+ return m === null ? void 0 : Number(m[1]);
22753
+ }
22754
+ function parseViewerReport(raw) {
22755
+ let parsed;
22756
+ try {
22757
+ parsed = JSON.parse(stripBom(raw));
22758
+ } catch {
22759
+ return void 0;
22760
+ }
22761
+ if (!isRecord2(parsed)) return void 0;
22762
+ if (parsed["version"] !== VIEWER_FILE_VERSION) return void 0;
22763
+ const follow = parsed["follow"];
22764
+ if (typeof follow !== "boolean") return void 0;
22765
+ const page2 = parsed["page"];
22766
+ if (page2 === null || page2 === void 0) return { page: void 0, follow };
22767
+ if (typeof page2 !== "string") return void 0;
22768
+ const id2 = makePageId(page2);
22769
+ return id2.ok ? { page: id2.value, follow } : void 0;
22770
+ }
22771
+ function readLiveViewers(defaultFile, nowMs) {
22772
+ const dir = viewersDirPath(defaultFile);
22773
+ let names;
22774
+ try {
22775
+ names = readdirSync(dir);
22776
+ } catch {
22777
+ return [];
22778
+ }
22779
+ const live = [];
22780
+ for (const name of names) {
22781
+ const pid = viewerPidOf(name);
22782
+ if (pid === void 0) continue;
22783
+ const path = join(dir, name);
22784
+ let raw;
22785
+ let ageMs;
22786
+ try {
22787
+ ageMs = Math.max(0, nowMs - statSync(path).mtimeMs);
22788
+ if (ageMs > VIEWER_SWEEP_MS) {
22789
+ rmSync(path, { force: true });
22790
+ continue;
22791
+ }
22792
+ if (ageMs > VIEWER_STALE_MS) continue;
22793
+ raw = readFileSync(path, "utf8");
22794
+ } catch {
22795
+ continue;
22796
+ }
22797
+ const report = parseViewerReport(raw);
22798
+ if (report !== void 0) live.push({ ...report, pid, ageMs });
22799
+ }
22800
+ return live.sort((a, b) => a.ageMs - b.ageMs || a.pid - b.pid);
22801
+ }
22802
+ var CONFIG_FILE_NAME = "config.json";
22803
+ var CONFIG_FILE_VERSION = 1;
22804
+ function configFilePath(defaultFile) {
22805
+ return join(dirname(defaultFile), CONFIG_FILE_NAME);
22806
+ }
22807
+ function userConfigFilePath(userBase) {
22808
+ return join(userBase, STORE_DIR_NAME, CONFIG_FILE_NAME);
22809
+ }
22810
+ var MAPPING_POLICIES = ["always", "complex", "on-request"];
22811
+ function makeMappingPolicy(raw) {
22812
+ return MAPPING_POLICIES.includes(raw) ? ok(raw) : err({ kind: "invalid-policy", raw, allowed: MAPPING_POLICIES });
22813
+ }
22814
+ function describeMappingPolicy(policy) {
22815
+ switch (policy) {
22816
+ case "always":
22817
+ return "map every structured task \u2014 workflows, designs, architecture, technical dependencies";
22818
+ case "complex":
22819
+ return "map only medium or complex tasks \u2014 several modules, a new subsystem, roughly an hour or more";
22820
+ case "on-request":
22821
+ return "map only when the user explicitly asks";
22822
+ }
22823
+ }
22824
+ function loadMappingPolicy(path) {
22825
+ let text2;
22826
+ try {
22827
+ text2 = readFileSync(path, "utf8");
22828
+ } catch (e) {
22829
+ if (e.code === "ENOENT") return ok(void 0);
22830
+ throw e;
22831
+ }
22832
+ let raw;
22833
+ try {
22834
+ raw = JSON.parse(stripBom(text2));
22835
+ } catch (e) {
22836
+ return err({ kind: "malformed-json", path, detail: e.message });
22837
+ }
22838
+ if (!isRecord2(raw)) return err({ kind: "bad-shape", path, detail: "root is not an object" });
22839
+ if (raw["version"] !== CONFIG_FILE_VERSION) {
22840
+ return err({ kind: "bad-shape", path, detail: `version is ${String(raw["version"])}, expected ${CONFIG_FILE_VERSION}` });
22841
+ }
22842
+ const rawPolicy = raw["policy"];
22843
+ if (rawPolicy === void 0) return ok(void 0);
22844
+ if (typeof rawPolicy !== "string") return err({ kind: "bad-shape", path, detail: "policy is not a string" });
22845
+ const policy = makeMappingPolicy(rawPolicy);
22846
+ return policy.ok ? ok(policy.value) : err({ kind: "bad-shape", path, detail: `policy is "${rawPolicy}", expected one of: ${MAPPING_POLICIES.join(" | ")}` });
22847
+ }
22848
+ function saveMappingPolicy(path, policy) {
22849
+ const body = JSON.stringify({ version: CONFIG_FILE_VERSION, policy }, null, 2) + "\n";
22850
+ return writeFileAtomic(path, body);
22851
+ }
22852
+ var POLICY_SCOPES = ["user", "project"];
22853
+ function effectiveMappingPolicy(projectConfigFile, userConfigFile) {
22854
+ const project = loadMappingPolicy(projectConfigFile);
22855
+ if (!project.ok) return project;
22856
+ const user = loadMappingPolicy(userConfigFile);
22857
+ if (!user.ok) return user;
22858
+ const effective = project.value ?? user.value;
22859
+ const source = project.value !== void 0 ? "project" : user.value !== void 0 ? "user" : void 0;
22860
+ return ok({ project: project.value, user: user.value, effective, source });
22861
+ }
22862
+ var LEGACY_STATE_FILE_RELATIVE_PATH = join(".claude", "mellos-mapping.json");
22863
+ var LEGACY_PAGES_DIR_NAME = "mellos-mapping.pages";
22864
+ var LEGACY_CONFIG_FILE_NAME = "mellos-mapping.config.json";
22865
+ function migrateLegacyStore(defaultFile) {
22866
+ const projectRoot = dirname(dirname(defaultFile));
22867
+ const legacyDefault = join(projectRoot, LEGACY_STATE_FILE_RELATIVE_PATH);
22868
+ const legacyPages = join(dirname(legacyDefault), LEGACY_PAGES_DIR_NAME);
22869
+ const legacyConfig = join(dirname(legacyDefault), LEGACY_CONFIG_FILE_NAME);
22870
+ const hasLegacy = existsSync(legacyDefault) || existsSync(legacyPages) || existsSync(legacyConfig);
22871
+ const hasCurrent = existsSync(defaultFile) || existsSync(join(dirname(defaultFile), PAGES_DIR_NAME)) || existsSync(configFilePath(defaultFile));
22872
+ if (!hasLegacy || hasCurrent) return false;
22873
+ mkdirSync(dirname(defaultFile), { recursive: true });
22874
+ if (existsSync(legacyDefault)) renameSync(legacyDefault, defaultFile);
22875
+ if (existsSync(legacyPages)) renameSync(legacyPages, join(dirname(defaultFile), PAGES_DIR_NAME));
22876
+ if (existsSync(legacyConfig)) renameSync(legacyConfig, configFilePath(defaultFile));
22877
+ return true;
22878
+ }
22349
22879
  function loadMapFile(path) {
22350
22880
  let text2;
22351
22881
  try {
@@ -22357,20 +22887,20 @@ function loadMapFile(path) {
22357
22887
  }
22358
22888
  let raw;
22359
22889
  try {
22360
- raw = JSON.parse(text2);
22890
+ raw = JSON.parse(stripBom(text2));
22361
22891
  } catch (e) {
22362
22892
  return err({ kind: "malformed-json", path, detail: e.message });
22363
22893
  }
22364
22894
  return parseMap(raw, path);
22365
22895
  }
22366
22896
  function saveMapFile(path, map) {
22367
- mkdirSync(dirname(path), { recursive: true });
22368
- const tmp = path + ".tmp";
22369
- writeFileSync(tmp, serializeMap(map), "utf8");
22370
- renameSync(tmp, path);
22897
+ return writeFileAtomic(path, serializeMap(map));
22371
22898
  }
22372
22899
 
22373
22900
  // src/server/apply.ts
22901
+ function refuseSelfDive(where, submap, page2) {
22902
+ return submap === page2 ? `${where}: a node cannot dive into its own page ("${submap}"); a submap links a CHILD page` : void 0;
22903
+ }
22374
22904
  function applyDeclare(map, input) {
22375
22905
  let next = input.title !== void 0 ? setTitle(map, input.title) : map;
22376
22906
  if (input.kind !== void 0) {
@@ -22379,38 +22909,40 @@ function applyDeclare(map, input) {
22379
22909
  next = setKind(next, kind.value);
22380
22910
  }
22381
22911
  for (const [i, l] of (input.layers ?? []).entries()) {
22382
- const id = makeLayerId(l.id);
22383
- if (!id.ok) return err(`layers[${i}]: ${describeMapError(id.error)}`);
22384
- const declared = declareLayer(next, { id: id.value, name: l.name, rank: l.rank });
22912
+ const id2 = makeLayerId(l.id);
22913
+ if (!id2.ok) return err(`layers[${i}]: ${describeMapError(id2.error)}`);
22914
+ const rank2 = makeRank(l.rank);
22915
+ if (!rank2.ok) return err(`layers[${i}]: ${describeMapError(rank2.error)}`);
22916
+ const declared = declareLayer(next, { id: id2.value, name: l.name, rank: rank2.value });
22385
22917
  if (!declared.ok) return err(`layers[${i}]: ${describeMapError(declared.error)}`);
22386
22918
  next = declared.value;
22387
22919
  }
22388
22920
  for (const [i, l] of (input.lanes ?? []).entries()) {
22389
- const id = makeLaneId(l.id);
22390
- if (!id.ok) return err(`lanes[${i}]: ${describeMapError(id.error)}`);
22391
- const declared = declareLane(next, { id: id.value, label: l.label });
22921
+ const id2 = makeLaneId(l.id);
22922
+ if (!id2.ok) return err(`lanes[${i}]: ${describeMapError(id2.error)}`);
22923
+ const declared = declareLane(next, { id: id2.value, label: l.label });
22392
22924
  if (!declared.ok) return err(`lanes[${i}]: ${describeMapError(declared.error)}`);
22393
22925
  next = declared.value;
22394
22926
  }
22395
22927
  for (const [i, g] of (input.groups ?? []).entries()) {
22396
- const id = makeGroupId(g.id);
22397
- if (!id.ok) return err(`groups[${i}]: ${describeMapError(id.error)}`);
22928
+ const id2 = makeGroupId(g.id);
22929
+ if (!id2.ok) return err(`groups[${i}]: ${describeMapError(id2.error)}`);
22398
22930
  const layer = makeLayerId(g.layer);
22399
22931
  if (!layer.ok) return err(`groups[${i}]: ${describeMapError(layer.error)}`);
22400
- const declared = declareGroup(next, { id: id.value, label: g.label, layer: layer.value });
22932
+ const declared = declareGroup(next, { id: id2.value, label: g.label, layer: layer.value });
22401
22933
  if (!declared.ok) return err(`groups[${i}]: ${describeMapError(declared.error)}`);
22402
22934
  next = declared.value;
22403
22935
  }
22404
22936
  for (const [i, n] of (input.nodes ?? []).entries()) {
22405
- const id = makeNodeId(n.id);
22406
- if (!id.ok) return err(`nodes[${i}]: ${describeMapError(id.error)}`);
22937
+ const id2 = makeNodeId(n.id);
22938
+ if (!id2.ok) return err(`nodes[${i}]: ${describeMapError(id2.error)}`);
22407
22939
  const layer = makeLayerId(n.layer);
22408
22940
  if (!layer.ok) return err(`nodes[${i}]: ${describeMapError(layer.error)}`);
22409
- let status;
22941
+ let status2;
22410
22942
  if (n.status !== void 0) {
22411
22943
  const parsed = makeNodeStatus(n.status);
22412
22944
  if (!parsed.ok) return err(`nodes[${i}]: ${describeMapError(parsed.error)}`);
22413
- status = parsed.value;
22945
+ status2 = parsed.value;
22414
22946
  }
22415
22947
  let group;
22416
22948
  if (n.group !== void 0) {
@@ -22434,13 +22966,16 @@ function applyDeclare(map, input) {
22434
22966
  if (n.submap !== void 0) {
22435
22967
  const parsed = makeSubmapRef(n.submap);
22436
22968
  if (!parsed.ok) return err(`nodes[${i}]: ${describeMapError(parsed.error)}`);
22969
+ const selfDive = refuseSelfDive(`nodes[${i}]`, n.submap, input.page);
22970
+ if (selfDive !== void 0) return err(selfDive);
22437
22971
  submap = parsed.value;
22438
22972
  }
22439
22973
  const declared = declareNode(next, {
22440
- id: id.value,
22974
+ id: id2.value,
22441
22975
  label: n.label,
22442
22976
  layer: layer.value,
22443
- ...status !== void 0 ? { status } : {},
22977
+ ...status2 !== void 0 ? { status: status2 } : {},
22978
+ ...n.evidence !== void 0 ? { evidence: n.evidence } : {},
22444
22979
  ...n.detail !== void 0 ? { detail: n.detail } : {},
22445
22980
  ...group !== void 0 ? { group } : {},
22446
22981
  ...kind !== void 0 ? { kind } : {},
@@ -22463,14 +22998,56 @@ function applyDeclare(map, input) {
22463
22998
  }
22464
22999
  function applyUpdate(map, input) {
22465
23000
  let next = map;
22466
- for (const [i, u] of input.updates.entries()) {
22467
- const id = makeNodeId(u.id);
22468
- if (!id.ok) return err(`updates[${i}]: ${describeMapError(id.error)}`);
22469
- let status;
23001
+ const items = (input.updates?.length ?? 0) + (input.layers?.length ?? 0) + (input.groups?.length ?? 0) + (input.lanes?.length ?? 0);
23002
+ if (items === 0) return err("nothing to revise: pass updates, layers, groups or lanes");
23003
+ for (const [i, l] of (input.layers ?? []).entries()) {
23004
+ const id2 = makeLayerId(l.id);
23005
+ if (!id2.ok) return err(`layers[${i}]: ${describeMapError(id2.error)}`);
23006
+ if (l.name === void 0 && l.rank === void 0) {
23007
+ return err(`layers[${i}]: nothing to change; give a name, a rank, or both`);
23008
+ }
23009
+ let rank2;
23010
+ if (l.rank !== void 0) {
23011
+ const parsed = makeRank(l.rank);
23012
+ if (!parsed.ok) return err(`layers[${i}]: ${describeMapError(parsed.error)}`);
23013
+ rank2 = parsed.value;
23014
+ }
23015
+ const updated = updateLayer(next, id2.value, {
23016
+ ...l.name !== void 0 ? { name: l.name } : {},
23017
+ ...rank2 !== void 0 ? { rank: rank2 } : {}
23018
+ });
23019
+ if (!updated.ok) return err(`layers[${i}]: ${describeMapError(updated.error)}`);
23020
+ next = updated.value;
23021
+ }
23022
+ for (const [i, g] of (input.groups ?? []).entries()) {
23023
+ const id2 = makeGroupId(g.id);
23024
+ if (!id2.ok) return err(`groups[${i}]: ${describeMapError(id2.error)}`);
23025
+ const updated = updateGroup(next, id2.value, g.label);
23026
+ if (!updated.ok) return err(`groups[${i}]: ${describeMapError(updated.error)}`);
23027
+ next = updated.value;
23028
+ }
23029
+ for (const [i, l] of (input.lanes ?? []).entries()) {
23030
+ const id2 = makeLaneId(l.id);
23031
+ if (!id2.ok) return err(`lanes[${i}]: ${describeMapError(id2.error)}`);
23032
+ const updated = updateLane(next, id2.value, l.label);
23033
+ if (!updated.ok) return err(`lanes[${i}]: ${describeMapError(updated.error)}`);
23034
+ next = updated.value;
23035
+ }
23036
+ for (const [i, u] of (input.updates ?? []).entries()) {
23037
+ const id2 = makeNodeId(u.id);
23038
+ if (!id2.ok) return err(`updates[${i}]: ${describeMapError(id2.error)}`);
23039
+ if (u.layer !== void 0) {
23040
+ const layer = makeLayerId(u.layer);
23041
+ if (!layer.ok) return err(`updates[${i}]: ${describeMapError(layer.error)}`);
23042
+ const moved = moveNode(next, id2.value, layer.value);
23043
+ if (!moved.ok) return err(`updates[${i}]: ${describeMapError(moved.error)}`);
23044
+ next = moved.value;
23045
+ }
23046
+ let status2;
22470
23047
  if (u.status !== void 0) {
22471
23048
  const parsed = makeNodeStatus(u.status);
22472
23049
  if (!parsed.ok) return err(`updates[${i}]: ${describeMapError(parsed.error)}`);
22473
- status = parsed.value;
23050
+ status2 = parsed.value;
22474
23051
  }
22475
23052
  let group;
22476
23053
  if (u.group === null) group = null;
@@ -22498,11 +23075,13 @@ function applyUpdate(map, input) {
22498
23075
  else if (u.submap !== void 0) {
22499
23076
  const parsed = makeSubmapRef(u.submap);
22500
23077
  if (!parsed.ok) return err(`updates[${i}]: ${describeMapError(parsed.error)}`);
23078
+ const selfDive = refuseSelfDive(`updates[${i}]`, u.submap, input.page);
23079
+ if (selfDive !== void 0) return err(selfDive);
22501
23080
  submap = parsed.value;
22502
23081
  }
22503
23082
  const updated = updateNode(next, {
22504
- id: id.value,
22505
- ...status !== void 0 ? { status } : {},
23083
+ id: id2.value,
23084
+ ...status2 !== void 0 ? { status: status2 } : {},
22506
23085
  ...u.label !== void 0 ? { label: u.label } : {},
22507
23086
  ...u.evidence !== void 0 ? { evidence: u.evidence } : {},
22508
23087
  ...u.detail !== void 0 ? { detail: u.detail } : {},
@@ -22528,30 +23107,30 @@ function applyRemove(map, input) {
22528
23107
  next = removed.value;
22529
23108
  }
22530
23109
  for (const [i, rawId] of (input.nodes ?? []).entries()) {
22531
- const id = makeNodeId(rawId);
22532
- if (!id.ok) return err(`nodes[${i}]: ${describeMapError(id.error)}`);
22533
- const removed = removeNode(next, id.value);
23110
+ const id2 = makeNodeId(rawId);
23111
+ if (!id2.ok) return err(`nodes[${i}]: ${describeMapError(id2.error)}`);
23112
+ const removed = removeNode(next, id2.value);
22534
23113
  if (!removed.ok) return err(`nodes[${i}]: ${describeMapError(removed.error)}`);
22535
23114
  next = removed.value;
22536
23115
  }
22537
23116
  for (const [i, rawId] of (input.groups ?? []).entries()) {
22538
- const id = makeGroupId(rawId);
22539
- if (!id.ok) return err(`groups[${i}]: ${describeMapError(id.error)}`);
22540
- const removed = removeGroup(next, id.value);
23117
+ const id2 = makeGroupId(rawId);
23118
+ if (!id2.ok) return err(`groups[${i}]: ${describeMapError(id2.error)}`);
23119
+ const removed = removeGroup(next, id2.value);
22541
23120
  if (!removed.ok) return err(`groups[${i}]: ${describeMapError(removed.error)}`);
22542
23121
  next = removed.value;
22543
23122
  }
22544
23123
  for (const [i, rawId] of (input.lanes ?? []).entries()) {
22545
- const id = makeLaneId(rawId);
22546
- if (!id.ok) return err(`lanes[${i}]: ${describeMapError(id.error)}`);
22547
- const removed = removeLane(next, id.value);
23124
+ const id2 = makeLaneId(rawId);
23125
+ if (!id2.ok) return err(`lanes[${i}]: ${describeMapError(id2.error)}`);
23126
+ const removed = removeLane(next, id2.value);
22548
23127
  if (!removed.ok) return err(`lanes[${i}]: ${describeMapError(removed.error)}`);
22549
23128
  next = removed.value;
22550
23129
  }
22551
23130
  for (const [i, rawId] of (input.layers ?? []).entries()) {
22552
- const id = makeLayerId(rawId);
22553
- if (!id.ok) return err(`layers[${i}]: ${describeMapError(id.error)}`);
22554
- const removed = removeLayer(next, id.value);
23131
+ const id2 = makeLayerId(rawId);
23132
+ if (!id2.ok) return err(`layers[${i}]: ${describeMapError(id2.error)}`);
23133
+ const removed = removeLayer(next, id2.value);
22555
23134
  if (!removed.ok) return err(`layers[${i}]: ${describeMapError(removed.error)}`);
22556
23135
  next = removed.value;
22557
23136
  }
@@ -22560,105 +23139,280 @@ function applyRemove(map, input) {
22560
23139
  function summarize(map) {
22561
23140
  const byStatus = { planned: 0, "in-progress": 0, done: 0, regressed: 0 };
22562
23141
  for (const n of map.nodes) byStatus[n.status]++;
22563
- const statusPart = Object.entries(byStatus).filter(([, count]) => count > 0).map(([status, count]) => `${count} ${status}`).join(", ");
23142
+ const statusPart = Object.entries(byStatus).filter(([, count]) => count > 0).map(([status2, count]) => `${count} ${status2}`).join(", ");
22564
23143
  return `map now: ${map.layers.length} layer(s), ${map.nodes.length} node(s)` + (statusPart ? ` [${statusPart}]` : "") + (map.groups.length > 0 ? `, ${map.groups.length} group(s)` : "") + (map.lanes.length > 0 ? `, ${map.lanes.length} lane(s)` : "") + `, ${map.edges.length} edge(s)` + (map.kind !== void 0 && map.kind !== "dev" ? ` (${map.kind})` : "");
22565
23144
  }
22566
23145
 
22567
23146
  // src/server/server.ts
22568
23147
  var SERVER_NAME = "mellos-mapping";
22569
- var SERVER_VERSION = "0.19.0";
22570
- var ID = external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "lowercase letters, digits and dashes, 1-64 chars").describe("stable kebab-case identifier");
22571
- var PAGE = ID.optional().describe(
22572
- "page (parallel map) this call targets; omit for the default page. One effort = one page: start a NEW effort on its own page named after the effort, so concurrent sessions never write over each other and the pane can switch between pages."
22573
- );
22574
- var STATUS = external_exports.enum(["planned", "in-progress", "done", "regressed"]).describe("planned = ghost on the map; in-progress = spinner; done = verified; regressed = was done, now broken");
22575
- var EDGE = external_exports.object({
22576
- from: ID.describe("the node that USES the other (must live on a higher layer)"),
22577
- to: ID.describe("the node being used (must live on a strictly lower layer)")
22578
- });
22579
- var KIND = external_exports.enum(["dev", "architecture", "dataflow", "behavior-tree", "sequence"]).describe(
22580
- "diagram kind. dev (default) = the live progress ledger with status skins. The rest are documentation diagrams rendered neutrally: architecture (layered components; also fits call graphs and module dependencies), dataflow (source\u2192transform\u2192sink, stages as layers), behavior-tree (root on top, leaves at the bottom; also fits mind maps and WBS), sequence (classic call/return: rank = time step with rank 0 = EARLIEST, drawn top-down; declare lanes as participants and make every call AND every return its own event node in the acting participant's lane, edges labeled with the message). State machines are unsupported: cycles cannot enter a Mellos map."
22581
- );
22582
- var NODE_KIND = ID.describe(
22583
- "node kind rendered as a glyph prefix. Known: selector | sequence | parallel | decorator | condition | action (behavior trees); source | transform | sink (dataflow); service | db | queue | ui (architecture). Unknown kinds are kept and shown in the detail panel."
22584
- );
23148
+ var SERVER_VERSION = "0.20.2";
23149
+ var TITLE_MAX = 120;
23150
+ var LABEL_MAX = 60;
23151
+ var DETAIL_MAX = 600;
23152
+ var EVIDENCE_MAX = 200;
23153
+ var EDGE_LABEL_MAX = 80;
23154
+ function id(description) {
23155
+ return external_exports.string().regex(ID_RULE, ID_RULE_TEXT).describe(description);
23156
+ }
23157
+ var PAGE_DESCRIPTION = "page (parallel map) this call targets; omit for the default page. One effort = one page: start a NEW effort on its own page named after the effort, so concurrent sessions never write over each other and the pane can switch between pages.";
23158
+ function page() {
23159
+ return id(PAGE_DESCRIPTION).optional();
23160
+ }
23161
+ var STATUS_VOCABULARY = "planned = ghost on the map; in-progress = spinner; done = verified; regressed = was done, now broken";
23162
+ function status(note2) {
23163
+ return external_exports.enum(NODE_STATUSES).describe(`${note2}. ${STATUS_VOCABULARY}`);
23164
+ }
23165
+ function nodeKind() {
23166
+ return id(
23167
+ "node kind rendered as a glyph prefix. Known: selector | sequence | parallel | decorator | condition | action (behavior trees); source | transform | sink (dataflow); service | db | queue | ui (architecture). Unknown kinds are kept and shown in the detail panel."
23168
+ );
23169
+ }
23170
+ function mapKind() {
23171
+ return external_exports.enum(MAP_KINDS).describe(
23172
+ "diagram kind. dev (default) = the live progress ledger with status skins. The rest are documentation diagrams rendered neutrally: architecture (layered components; also fits call graphs and module dependencies), dataflow (source\u2192transform\u2192sink, stages as layers), behavior-tree (root on top, leaves at the bottom; also fits mind maps and WBS), sequence (classic call/return: rank = time step with rank 0 = EARLIEST, drawn top-down; declare lanes as participants and make every call AND every return its own event node in the acting participant's lane, edges labeled with the message). State machines are unsupported: cycles cannot enter a Mellos map."
23173
+ );
23174
+ }
23175
+ function rank() {
23176
+ return external_exports.number().int().min(RANK_MIN).max(RANK_MAX).describe(`${RANK_RULE_TEXT}; must be unique among the map's bands`);
23177
+ }
23178
+ function edgeEnds() {
23179
+ return {
23180
+ from: id("the node that USES the other (must live on a higher layer)"),
23181
+ to: id("the node being used (must live on a strictly lower layer)")
23182
+ };
23183
+ }
23184
+ var NO_CONTROLS = /^[^\u0000-\u001f\u007f-\u009f]*$/;
23185
+ var NO_CONTROLS_TEXT = "one line of text; control characters (ESC, newline, tab) are not allowed";
23186
+ var NO_CONTROLS_BUT_BREAKS = /^[^\u0000-\u0008\u000b-\u001f\u007f-\u009f]*$/;
23187
+ var NO_CONTROLS_BUT_BREAKS_TEXT = "text with optional newlines (\\n) and tabs; other control characters (ESC, BEL, CR) are not allowed";
23188
+ function line(max, description) {
23189
+ return external_exports.string().min(1).max(max).regex(NO_CONTROLS, NO_CONTROLS_TEXT).describe(description);
23190
+ }
23191
+ function note(max, description) {
23192
+ return external_exports.string().min(1).max(max).regex(NO_CONTROLS_BUT_BREAKS, NO_CONTROLS_BUT_BREAKS_TEXT).describe(description);
23193
+ }
23194
+ function closed(shape) {
23195
+ return external_exports.object(shape).strict();
23196
+ }
23197
+ var DEFAULT_PAGE_NAME = "(default)";
23198
+ var DEFAULT_PAGE_ABSENT = "(default: absent)";
23199
+ function pagesLine(stateFile, shown) {
23200
+ const files = listPageFiles(stateFile);
23201
+ const named = files.map((f) => pageIdOfFile(stateFile, f)).filter((p) => p !== void 0);
23202
+ const hasDefault = files.length > named.length;
23203
+ const known = [hasDefault ? DEFAULT_PAGE_NAME : DEFAULT_PAGE_ABSENT, ...named];
23204
+ return `pages: ${known.join(", ")} \u2014 this view: ${shown ?? DEFAULT_PAGE_NAME}`;
23205
+ }
23206
+ function pageName(page2) {
23207
+ return page2 ?? DEFAULT_PAGE_NAME;
23208
+ }
23209
+ function paneLine(stateFile, touched) {
23210
+ const viewers = readLiveViewers(stateFile, Date.now());
23211
+ if (viewers.length === 0) {
23212
+ return `pane: CLOSED \u2014 nobody is seeing this map. Open it with mmap_open {page: ${touched === void 0 ? "(omit for the default page)" : `"${touched}"`}} and do not ask first: a user with a mapping policy has already said they want the picture.`;
23213
+ }
23214
+ if (viewers.some((v) => v.page === touched)) return "pane: open on this page \u2014 the user is seeing this.";
23215
+ const elsewhere = [...new Set(viewers.map((v) => pageName(v.page)))].join(", ");
23216
+ if (viewers.some((v) => v.follow)) {
23217
+ return `pane: open on ${elsewhere}, auto-follow on \u2014 it lands on this page within a second.`;
23218
+ }
23219
+ return `pane: open on ${elsewhere}, auto-follow OFF \u2014 the user pinned that page, so this change is NOT on their screen. Tell them rather than switching it behind them; mmap_open {page} retargets the pane if they want it moved.`;
23220
+ }
23221
+ var LAUNCH_TIMEOUT_MS = 45e3;
23222
+ var PANE_REPORT_TIMEOUT_MS = 8e3;
23223
+ var PANE_REPORT_POLL_MS = 250;
23224
+ function launcherPath(moduleUrl) {
23225
+ return join2(dirname2(dirname2(fileURLToPath(moduleUrl))), "scripts", "open-pane.mjs");
23226
+ }
23227
+ function projectDirOf(stateFile) {
23228
+ return dirname2(dirname2(stateFile));
23229
+ }
23230
+ function launcherArgs(projectDir, page2, window) {
23231
+ const args = [projectDir];
23232
+ if (page2 !== void 0) args.push("--page", page2);
23233
+ if (window) args.push("--window");
23234
+ return args;
23235
+ }
23236
+ function runLauncher(script, args) {
23237
+ return new Promise((resolve) => {
23238
+ const child = spawn(process.execPath, [script, ...args], {
23239
+ windowsHide: true,
23240
+ stdio: ["ignore", "pipe", "pipe"]
23241
+ });
23242
+ let output = "";
23243
+ const collect = (chunk) => {
23244
+ output += chunk.toString("utf8");
23245
+ };
23246
+ child.stdout.on("data", collect);
23247
+ child.stderr.on("data", collect);
23248
+ const abandon = setTimeout(() => child.kill(), LAUNCH_TIMEOUT_MS);
23249
+ child.on("error", (e) => {
23250
+ clearTimeout(abandon);
23251
+ resolve({ ok: false, output: e.message });
23252
+ });
23253
+ child.on("close", (code) => {
23254
+ clearTimeout(abandon);
23255
+ resolve({ ok: code === 0, output: output.trim() });
23256
+ });
23257
+ });
23258
+ }
23259
+ function paneShows(viewers, page2) {
23260
+ return page2 === void 0 ? viewers.length > 0 : viewers.some((v) => v.page === page2);
23261
+ }
23262
+ async function awaitPane(stateFile, page2, deadlineMs) {
23263
+ for (; ; ) {
23264
+ const viewers = readLiveViewers(stateFile, Date.now());
23265
+ if (paneShows(viewers, page2)) return viewers;
23266
+ if (Date.now() >= deadlineMs) return viewers;
23267
+ await new Promise((r) => setTimeout(r, PANE_REPORT_POLL_MS));
23268
+ }
23269
+ }
23270
+ function openOutcome(run, viewers, page2) {
23271
+ if (!run.ok) {
23272
+ return `could not open the pane: ${run.output === "" ? "the launcher failed without saying why" : run.output}
23273
+ Relay this to the user \u2014 on a machine without Windows Terminal the map is opened by running the watcher in any second terminal or tmux split (see the plugin README).`;
23274
+ }
23275
+ if (paneShows(viewers, page2)) {
23276
+ return `pane: open and showing ${pageName(page2)} \u2014 the user can see the map now.
23277
+ ${run.output}`;
23278
+ }
23279
+ if (viewers.length > 0) {
23280
+ const elsewhere = [...new Set(viewers.map((v) => pageName(v.page)))].join(", ");
23281
+ return `pane: open, but it reports ${elsewhere} rather than ${pageName(page2)}. With auto-follow on it lands there on your next write; with follow off the user is holding that page on purpose.
23282
+ ` + run.output;
23283
+ }
23284
+ return `the launcher succeeded but no pane has reported in within ${PANE_REPORT_TIMEOUT_MS / 1e3}s. It may still be starting; the \`pane:\` line on your next write says whether it made it.
23285
+ ` + run.output;
23286
+ }
22585
23287
  function text(s, isError = false) {
22586
23288
  return { content: [{ type: "text", text: s }], ...isError ? { isError: true } : {} };
22587
23289
  }
23290
+ function saveFailed(error2) {
23291
+ return text(`save failed, nothing changed (retry): ${describeStoreError(error2)}`, true);
23292
+ }
22588
23293
  function loadOrEmpty(stateFile) {
22589
23294
  const loaded = loadMapFile(stateFile);
22590
23295
  if (loaded.ok) return { ok: true, value: loaded.value };
22591
23296
  if (loaded.error.kind === "not-found") return { ok: true, value: EMPTY_MAP };
22592
23297
  return { ok: false, error: describeStoreError(loaded.error) };
22593
23298
  }
22594
- function buildServer(stateFile) {
23299
+ function buildServer(stateFile, userConfigFile) {
22595
23300
  const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
22596
- const fileOf = (page) => pageFilePath(stateFile, page);
22597
- const mutate = (page, apply) => {
22598
- const file = fileOf(page);
23301
+ const projectConfigFile = configFilePath(stateFile);
23302
+ const fileOf = (page2) => pageFilePath(stateFile, page2);
23303
+ const mutate = (page2, apply) => {
23304
+ const file = fileOf(page2);
22599
23305
  const current = loadOrEmpty(file);
22600
23306
  if (!current.ok) return text(current.error, true);
22601
23307
  const applied = apply(current.value);
22602
23308
  if (!applied.ok) return text(`refused (nothing changed): ${applied.error}`, true);
22603
- saveMapFile(file, applied.value);
22604
- return text(summarize(applied.value) + (page !== void 0 ? ` [page: ${page}]` : ""));
23309
+ const saved = saveMapFile(file, applied.value);
23310
+ if (!saved.ok) return saveFailed(saved.error);
23311
+ return text(summarize(applied.value) + (page2 !== void 0 ? ` [page: ${page2}]` : ""));
23312
+ };
23313
+ const withPane = (result, page2) => result.isError === true ? result : text(`${result.content[0]?.text ?? ""}
23314
+ ${paneLine(stateFile, page2)}`);
23315
+ const knownPages = () => listPageFiles(stateFile).map((f) => pageIdOfFile(stateFile, f)).filter((p) => p !== void 0);
23316
+ const refusePageDeletion = (pages, target) => {
23317
+ if (target !== void 0 && pages.includes(target)) {
23318
+ return text(
23319
+ `refused (nothing changed): pages includes "${target}", the page this call targets \u2014 one call must not edit a map it is deleting. Delete it from a call that does not target it.`,
23320
+ true
23321
+ );
23322
+ }
23323
+ const known = knownPages();
23324
+ const unknown2 = pages.filter((p) => !known.includes(p));
23325
+ if (unknown2.length > 0) {
23326
+ return text(
23327
+ `refused (nothing changed): no page named ${unknown2.map((p) => `"${p}"`).join(", ")}. This project's named pages: ${known.length > 0 ? known.join(", ") : "(none)"}.`,
23328
+ true
23329
+ );
23330
+ }
23331
+ return void 0;
23332
+ };
23333
+ const deletePages = (pages, summary) => {
23334
+ const deleted = [];
23335
+ const failed = [];
23336
+ for (const p of pages) {
23337
+ const removed = deletePageFile(pageFilePath(stateFile, p));
23338
+ if (removed.ok) deleted.push(p);
23339
+ else failed.push(`${p} (${describeStoreError(removed.error)})`);
23340
+ }
23341
+ const gone = `deleted page(s): ${deleted.length > 0 ? deleted.join(", ") : "(none)"}`;
23342
+ if (failed.length === 0) return text(`${summary}${gone}`);
23343
+ return text(
23344
+ `${summary}${gone}; could NOT delete: ${failed.join("; ")}. Deleting files is not a transaction: what is named deleted above is gone for good, and only the failures are worth retrying.`,
23345
+ true
23346
+ );
22605
23347
  };
22606
23348
  const setupNudge = () => {
22607
- const policy = loadMappingPolicy(stateFile);
22608
- if (!policy.ok) return `
22609
- note: ${describeStoreError(policy.error)} \u2014 fix it or rerun setup (mmap_setup).`;
22610
- if (policy.value !== void 0) return "";
22611
- return "\nnote: mapping policy not set for this project. Ask the user when maps should open \u2014 " + MAPPING_POLICIES.map((p) => `${p} (${describeMappingPolicy(p)})`).join("; ") + " \u2014 then record the answer with mmap_setup.";
23349
+ const scopes = effectiveMappingPolicy(projectConfigFile, userConfigFile);
23350
+ if (!scopes.ok) return `
23351
+ note: ${describeStoreError(scopes.error)} \u2014 fix it or rerun setup (mmap_setup).`;
23352
+ if (scopes.value.effective !== void 0) return "";
23353
+ return "\nnote: no mapping policy has been chosen yet. Ask the user when maps should open \u2014 " + MAPPING_POLICIES.map((p) => `${p} (${describeMappingPolicy(p)})`).join("; ") + " \u2014 then record the answer with mmap_setup. It is asked once ever, not once per project.";
22612
23354
  };
22613
23355
  server.registerTool(
22614
23356
  "mmap_declare",
22615
23357
  {
22616
23358
  title: "Declare map structure",
22617
- description: "Grow the Mellos map: set the title and diagram kind, add layer bands, lanes and groups (labeled subsystems within ONE band \u2014 declare them when a single band grows crowded, roughly five or more nodes in that band; a group must be a strict subset of its band, and a map spread thin across many bands needs none), add nodes, add dependency edges. Declare the whole ghost design up front, then grow it as understanding deepens. Edges must point strictly downward (a node may only use nodes on lower layers); the batch is all-or-nothing.",
22618
- inputSchema: {
22619
- page: PAGE,
22620
- title: external_exports.string().max(120).optional().describe("map title, e.g. the feature being built"),
22621
- kind: KIND.optional(),
23359
+ description: "Grow the Mellos map: set the title and diagram kind, add layer bands, lanes and groups (labeled subsystems within ONE band \u2014 declare them when a single band grows crowded, roughly five or more nodes in that band; a group must be a strict subset of its band, and a map spread thin across many bands needs none), add nodes, add dependency edges. Declare the whole ghost design up front, then grow it as understanding deepens. Edges must point strictly downward (a node may only use nodes on lower layers); the batch is all-or-nothing. The title lives here and only here: pass it again to replace it, or null to remove it. Revising what already exists (moving, renaming, relabeling, clearing) is mmap_update.",
23360
+ inputSchema: closed({
23361
+ page: page(),
23362
+ title: line(TITLE_MAX, "map title, e.g. the feature being built; null removes it").nullable().optional(),
23363
+ kind: mapKind().optional(),
22622
23364
  lanes: external_exports.array(
22623
- external_exports.object({
22624
- id: ID,
22625
- label: external_exports.string().min(1).max(60).describe("column name, e.g. a sequence participant")
23365
+ closed({
23366
+ id: id("stable kebab-case identifier of the lane"),
23367
+ label: line(LABEL_MAX, "column name, e.g. a sequence participant")
22626
23368
  })
22627
23369
  ).optional().describe("vertical columns crossing all bands; declaration order = left-to-right"),
22628
23370
  layers: external_exports.array(
22629
- external_exports.object({
22630
- id: ID,
22631
- name: external_exports.string().min(1).max(60).describe("display name of the band"),
22632
- rank: external_exports.number().int().min(0).max(99).describe("0 = bottom / most primitive; must be unique")
23371
+ closed({
23372
+ id: id("stable kebab-case identifier of the band"),
23373
+ name: line(LABEL_MAX, "display name of the band"),
23374
+ rank: rank()
22633
23375
  })
22634
23376
  ).optional(),
22635
23377
  groups: external_exports.array(
22636
- external_exports.object({
22637
- id: ID,
22638
- label: external_exports.string().min(1).max(60).describe("subsystem name shown at the far zoom"),
22639
- layer: ID.describe("band this group clusters; members must live on the same band")
23378
+ closed({
23379
+ id: id("stable kebab-case identifier of the group"),
23380
+ label: line(LABEL_MAX, "subsystem name shown at the far zoom"),
23381
+ layer: id("band this group clusters; members must live on the same band")
22640
23382
  })
22641
23383
  ).optional(),
22642
23384
  nodes: external_exports.array(
22643
- external_exports.object({
22644
- id: ID,
22645
- label: external_exports.string().min(1).max(60).describe("display label inside the box"),
22646
- layer: ID.describe("id of the band this node lives in"),
22647
- status: STATUS.optional().describe("defaults to planned"),
22648
- detail: external_exports.string().max(600).optional().describe("design notes shown in the pane detail panel: responsibility, contract, key decisions"),
22649
- group: ID.optional().describe("same-band group this node belongs to"),
22650
- kind: NODE_KIND.optional(),
22651
- lane: ID.optional().describe("lane (column) this node belongs to"),
22652
- submap: ID.optional().describe(
23385
+ closed({
23386
+ id: id("stable kebab-case identifier of the node"),
23387
+ label: line(LABEL_MAX, "display label inside the box"),
23388
+ layer: id("id of the band this node lives in"),
23389
+ status: status("defaults to planned").optional(),
23390
+ evidence: line(
23391
+ EVIDENCE_MAX,
23392
+ "how an already-verified node was verified; for regressed: what broke. Declaring a node straight to done needs it as much as updating one does."
23393
+ ).optional(),
23394
+ detail: note(
23395
+ DETAIL_MAX,
23396
+ "design notes shown in the pane detail panel: responsibility, contract, key decisions"
23397
+ ).optional(),
23398
+ group: id("same-band group this node belongs to").optional(),
23399
+ kind: nodeKind().optional(),
23400
+ lane: id("lane (column) this node belongs to").optional(),
23401
+ submap: id(
22653
23402
  "page slug of this node's child map \u2014 the pane badges the node \u229E and double-click dives in. Declare the child page separately. Create a sub-map only when the node's internals genuinely deserve their own picture; most nodes need none."
22654
- )
23403
+ ).optional()
22655
23404
  })
22656
23405
  ).optional(),
22657
- edges: external_exports.array(EDGE.extend({ label: external_exports.string().max(80).optional().describe("what flows along the edge") })).optional()
22658
- }
23406
+ edges: external_exports.array(
23407
+ closed({
23408
+ ...edgeEnds(),
23409
+ label: line(EDGE_LABEL_MAX, "what flows along the edge").optional()
23410
+ })
23411
+ ).optional()
23412
+ })
22659
23413
  },
22660
23414
  (input) => {
22661
- const result = mutate(input.page, (map) => applyDeclare(map, input));
23415
+ const result = withPane(mutate(input.page, (map) => applyDeclare(map, input)), input.page);
22662
23416
  if (result.isError === true) return result;
22663
23417
  const nudge = setupNudge();
22664
23418
  return nudge === "" ? result : text((result.content[0]?.text ?? "") + nudge);
@@ -22667,83 +23421,158 @@ note: ${describeStoreError(policy.error)} \u2014 fix it or rerun setup (mmap_set
22667
23421
  server.registerTool(
22668
23422
  "mmap_update",
22669
23423
  {
22670
- title: "Record progress on nodes",
22671
- description: "Update node status/label/evidence. Set in-progress when starting a node (the pane spins), done with evidence when its verification passes, regressed with evidence when a done node breaks. The map is a ledger: report honestly, it never blocks you.",
22672
- inputSchema: {
22673
- page: PAGE,
23424
+ title: "Record progress and revise the map",
23425
+ description: "The revision tool, all-or-nothing. Record progress on nodes: in-progress when starting a node (the pane spins), done with evidence when its verification passes, regressed with evidence when a done node breaks. Revise what the ghost design got wrong: move a node to another band, join or leave a group or lane, rename a band (or re-rank it, which reorders the whole map), relabel a group or a lane. Every clearable field takes null to empty it \u2014 that is how a field is cleared, never an empty string. Bands, groups and lanes are applied before the node updates, and within one node update `layer` moves the node before its other fields. The map is a ledger: report honestly, it never blocks you.",
23426
+ inputSchema: closed({
23427
+ page: page(),
22674
23428
  updates: external_exports.array(
22675
- external_exports.object({
22676
- id: ID,
22677
- status: STATUS.optional(),
22678
- label: external_exports.string().min(1).max(60).optional(),
22679
- evidence: external_exports.string().max(200).optional().describe("for done: how it was verified; for regressed: what broke"),
22680
- detail: external_exports.string().max(600).optional().describe("design notes shown in the pane detail panel: responsibility, contract, key decisions"),
22681
- group: ID.nullable().optional().describe("join this same-band group; null leaves the current group"),
22682
- kind: NODE_KIND.nullable().optional().describe("set the node kind; null clears it"),
22683
- lane: ID.nullable().optional().describe("join this lane; null leaves the current lane"),
22684
- submap: ID.nullable().optional().describe("link a child map page; null unlinks it")
23429
+ closed({
23430
+ id: id("id of the node to update"),
23431
+ status: status("the status to record").optional(),
23432
+ label: line(LABEL_MAX, "new display label inside the box").optional(),
23433
+ evidence: line(EVIDENCE_MAX, "for done: how it was verified; for regressed: what broke; null clears it").nullable().optional(),
23434
+ detail: note(
23435
+ DETAIL_MAX,
23436
+ "design notes shown in the pane detail panel: responsibility, contract, key decisions; null clears them"
23437
+ ).nullable().optional(),
23438
+ layer: id(
23439
+ "move the node to this band; applied before this item's other fields, so a node can move and join a group on the new band in one item. Every edge touching it must still point strictly downward, and a grouped node may only move to its group's band."
23440
+ ).optional(),
23441
+ group: id("join this same-band group; null leaves the current group").nullable().optional(),
23442
+ kind: nodeKind().nullable().optional(),
23443
+ lane: id("join this lane; null leaves the current lane").nullable().optional(),
23444
+ submap: id("link a child map page by slug; null unlinks it").nullable().optional()
22685
23445
  })
22686
- ).min(1)
22687
- }
23446
+ ).min(1).optional(),
23447
+ layers: external_exports.array(
23448
+ closed({
23449
+ id: id("id of the band to revise"),
23450
+ name: line(LABEL_MAX, "new display name of the band").optional(),
23451
+ rank: rank().optional()
23452
+ })
23453
+ ).min(1).optional().describe("rename and/or re-rank existing bands; an item must carry a name, a rank, or both"),
23454
+ groups: external_exports.array(closed({ id: id("id of the group to relabel"), label: line(LABEL_MAX, "new subsystem name") })).min(1).optional().describe("relabel existing groups; membership and band are untouched"),
23455
+ lanes: external_exports.array(closed({ id: id("id of the lane to relabel"), label: line(LABEL_MAX, "new column name") })).min(1).optional().describe("relabel existing lanes; order and membership are untouched")
23456
+ })
22688
23457
  },
22689
- (input) => mutate(input.page, (map) => applyUpdate(map, input))
23458
+ (input) => withPane(mutate(input.page, (map) => applyUpdate(map, input)), input.page)
22690
23459
  );
22691
23460
  server.registerTool(
22692
23461
  "mmap_remove",
22693
23462
  {
22694
23463
  title: "Revise the map",
22695
- description: "Remove edges, nodes, groups and empty layer bands (in that order, all-or-nothing). Removing a node also removes every edge touching it; removing a group merely ungroups its members. Use when the ghost design turns out wrong \u2014 the map is a hypothesis, revising it is honest work.",
22696
- inputSchema: {
22697
- page: PAGE,
22698
- edges: external_exports.array(EDGE).optional(),
22699
- nodes: external_exports.array(ID).optional(),
22700
- groups: external_exports.array(ID).optional().describe("groups to remove; members stay, merely ungrouped"),
22701
- lanes: external_exports.array(ID).optional().describe("lanes to remove; members stay, merely off-lane"),
22702
- layers: external_exports.array(ID).optional().describe("bands to remove; must be empty of nodes and groups")
22703
- }
23464
+ description: 'Remove edges, nodes, groups and empty layer bands (in that order, all-or-nothing). Removing a node also removes every edge touching it; removing a group merely ungroups its members. Use when the ghost design turns out wrong \u2014 the map is a hypothesis, revising it is honest work. `pages` is the other scale: it DELETES whole page files, so a finished effort can be cleaned up instead of accumulating tabs forever. A bare `{pages: ["slug"]}` with no other field is the normal form; combined with map edits, the edits are applied first and the pages are deleted after. The deletion is permanent and cannot be undone, so delete only pages whose effort is over \u2014 and only ever with the user behind it. An unknown slug is refused with the project\'s real page list (naming a page that does not exist is a typo, not a request). The default page has no slug and is not deletable here. A node elsewhere still pointing at a deleted page with `submap` stays legal \u2014 a submap reference has no existence invariant \u2014 but it has nowhere to dive until the page comes back.',
23465
+ inputSchema: closed({
23466
+ page: page(),
23467
+ edges: external_exports.array(closed(edgeEnds())).optional(),
23468
+ nodes: external_exports.array(id("id of the node to remove, with every edge touching it")).optional(),
23469
+ groups: external_exports.array(id("id of the group to remove; members stay, merely ungrouped")).optional(),
23470
+ lanes: external_exports.array(id("id of the lane to remove; members stay, merely off-lane")).optional(),
23471
+ layers: external_exports.array(id("id of the band to remove; it must hold no nodes and no groups")).optional(),
23472
+ pages: external_exports.array(
23473
+ id(
23474
+ "slug of a page whose WHOLE map file is deleted \u2014 the page and everything drawn on it. Not the page this same call targets with `page`."
23475
+ )
23476
+ ).optional().describe("pages to delete entirely, after this call's map edits; permanent")
23477
+ })
22704
23478
  },
22705
- (input) => mutate(input.page, (map) => applyRemove(map, input))
23479
+ (input) => {
23480
+ if (input.pages === void 0) return withPane(mutate(input.page, (map) => applyRemove(map, input)), input.page);
23481
+ const refusal = refusePageDeletion(input.pages, input.page);
23482
+ if (refusal !== void 0) return refusal;
23483
+ const editsAnything = (input.edges?.length ?? 0) + (input.nodes?.length ?? 0) + (input.groups?.length ?? 0) + (input.lanes?.length ?? 0) + (input.layers?.length ?? 0) > 0;
23484
+ let summary = "";
23485
+ if (editsAnything) {
23486
+ const edited = mutate(input.page, (map) => applyRemove(map, input));
23487
+ if (edited.isError === true) return edited;
23488
+ summary = `${edited.content[0]?.text ?? ""}
23489
+ `;
23490
+ }
23491
+ return withPane(deletePages(input.pages, summary), input.page);
23492
+ }
22706
23493
  );
22707
23494
  server.registerTool(
22708
23495
  "mmap_setup",
22709
23496
  {
22710
23497
  title: "Configure when maps open",
22711
- description: `Get or set this project's mapping policy \u2014 WHEN the assistant opens a Mellos map. Call with no arguments to read it. If it reports "not set", ask the USER to choose (never pick for them): always = ` + describeMappingPolicy("always") + "; complex = " + describeMappingPolicy("complex") + "; on-request = " + describeMappingPolicy("on-request") + ". Then call again with their choice to persist it. The policy guides you; it never blocks the tools, and an explicit user request for a map always wins.",
22712
- inputSchema: {
22713
- policy: external_exports.enum(MAPPING_POLICIES).optional().describe("the user's choice to persist; omit to read the current policy")
22714
- }
23498
+ description: 'Get or set the mapping policy \u2014 WHEN the assistant opens a Mellos map. Call with no arguments to read it: the reply names the policy chosen for the USER (every project), the one this PROJECT overrides it with if any, and which of them is in effect. If it reports "not set", ask the USER to choose (never pick for them): always = ' + describeMappingPolicy("always") + "; complex = " + describeMappingPolicy("complex") + "; on-request = " + describeMappingPolicy("on-request") + '. Then call again with their choice to persist it. It defaults to user scope, which is the normal one \u2014 the question is about how someone works, so it is asked once ever, not once per repository. Pass scope: "project" only when the user wants THIS project to differ from that habit. The policy guides you; it never blocks the tools, and an explicit user request for a map always wins.',
23499
+ inputSchema: closed({
23500
+ policy: external_exports.enum(MAPPING_POLICIES).optional().describe("the user's choice to persist; omit to read the current policy"),
23501
+ scope: external_exports.enum(POLICY_SCOPES).optional().describe(
23502
+ 'where to record the choice: "user" (default) applies to every project this user opens; "project" overrides that for this project alone. Ignored when reading.'
23503
+ )
23504
+ })
22715
23505
  },
22716
23506
  (input) => {
23507
+ const scope = input.scope ?? "user";
23508
+ const configFile = scope === "project" ? projectConfigFile : userConfigFile;
22717
23509
  if (input.policy !== void 0) {
22718
23510
  const policy = input.policy;
22719
- saveMappingPolicy(stateFile, policy);
22720
- return text(`mapping policy set: ${policy} \u2014 ${describeMappingPolicy(policy)} [${configFilePath(stateFile)}]`);
23511
+ const saved = saveMappingPolicy(configFile, policy);
23512
+ if (!saved.ok) return saveFailed(saved.error);
23513
+ const reach = scope === "user" ? 'applies to EVERY project this user opens; a single project can still override it with mmap_setup {policy, scope: "project"}' : "applies to THIS project only, overriding the user-level choice";
23514
+ return text(
23515
+ `mapping policy set (${scope} scope): ${policy} \u2014 ${describeMappingPolicy(policy)}. ${reach}. [${configFile}]`
23516
+ );
22721
23517
  }
22722
- const loaded = loadMappingPolicy(stateFile);
22723
- if (!loaded.ok) return text(describeStoreError(loaded.error), true);
22724
- if (loaded.value === void 0) {
23518
+ const scopes = effectiveMappingPolicy(projectConfigFile, userConfigFile);
23519
+ if (!scopes.ok) return text(describeStoreError(scopes.error), true);
23520
+ const { user, project, effective, source } = scopes.value;
23521
+ const said = (p) => p === void 0 ? "not set" : p;
23522
+ const heading = `mapping policy \u2014 user: ${said(user)}; project: ${said(project)}`;
23523
+ if (effective === void 0) {
22725
23524
  return text(
22726
- "mapping policy not set. Ask the user to choose one of: " + MAPPING_POLICIES.map((p) => `${p} (${describeMappingPolicy(p)})`).join("; ") + " \u2014 then call mmap_setup with their choice. Until then act as complex."
23525
+ `${heading}. Nobody has chosen yet. Ask the user to choose one of: ` + MAPPING_POLICIES.map((p) => `${p} (${describeMappingPolicy(p)})`).join("; ") + " \u2014 then call mmap_setup with their choice (scope defaults to user, which is what you want: the question is asked once ever). Until then act as complex."
22727
23526
  );
22728
23527
  }
22729
- return text(`mapping policy: ${loaded.value} \u2014 ${describeMappingPolicy(loaded.value)}`);
23528
+ return text(`${heading}. In effect: ${effective} (${source} scope) \u2014 ${describeMappingPolicy(effective)}`);
22730
23529
  }
22731
23530
  );
22732
23531
  server.registerTool(
22733
23532
  "mmap_view",
22734
23533
  {
22735
23534
  title: "View the current map",
22736
- description: "Render the current Mellos map as monochrome text \u2014 the same picture the split-pane watcher shows live. Use it to check the map state or to show it inline in conversation.",
22737
- inputSchema: {
22738
- page: PAGE,
23535
+ description: "Render the current Mellos map as monochrome text \u2014 the same picture the split-pane watcher shows live. Use it to check the map state or to show it inline in conversation. Every response ends with a `pages:` line naming the pages this project actually has and which one you are looking at, so this is also how you discover whether a map exists at all and under which slugs \u2014 never probe the files.",
23536
+ inputSchema: closed({
23537
+ page: page(),
22739
23538
  zoom: external_exports.number().int().min(ZOOM_MIN).max(ZOOM_MAX).optional().describe("zoom ladder: 1 = detail (notes unfold), 0 = standard (default), -1..-3 = scaled down, -4 = overview glyphs")
22740
- }
23539
+ })
22741
23540
  },
22742
23541
  (input) => {
22743
23542
  const current = loadOrEmpty(fileOf(input.page));
22744
23543
  if (!current.ok) return text(current.error, true);
22745
23544
  const zoom = clampZoom(input.zoom ?? 0);
22746
- return text(renderMap(current.value, { color: false, unicode: true, spinnerFrame: 0, zoom }).join("\n"));
23545
+ const picture = renderMap(current.value, { color: false, unicode: true, spinnerFrame: 0, zoom }).join("\n");
23546
+ return text(`${picture}
23547
+ ${pagesLine(stateFile, input.page)}
23548
+ ${paneLine(stateFile, input.page)}`);
23549
+ }
23550
+ );
23551
+ server.registerTool(
23552
+ "mmap_open",
23553
+ {
23554
+ title: "Open the map pane",
23555
+ description: "Put the live map on the user's screen: a terminal pane beside this conversation that redraws on every write. Call it whenever a result says `pane: CLOSED` \u2014 and do NOT ask permission first, because a user who has set a mapping policy has already said they want to see the map. With a pane already open this RETARGETS it to `page` instead of opening a second one, so it is also how you show the user a particular page when they ask for one. It never closes a pane: taking the map off the screen belongs to the user (the `q` key in the pane, or typing `mmap` in a terminal). The reply says whether a pane actually reported itself in afterwards, not merely that a command was run. Windows Terminal is the supported route; anywhere else it says so and you relay the manual command from the README.",
23556
+ inputSchema: closed({
23557
+ page: id(
23558
+ "page to show first \u2014 the page THIS effort lives on, the same slug you pass to the other tools. Omit only for the default page: without it a fresh pane opens on whichever page was written last, which after a gap is rarely the one under discussion."
23559
+ ).optional(),
23560
+ window: external_exports.boolean().optional().describe(
23561
+ `open the map in its own "mellos-mapping" window instead of splitting this conversation's window. Pass it only when the user asked for the map separate (a second monitor, a small screen); the split is the default because the map is meant to sit beside what it describes.`
23562
+ )
23563
+ })
23564
+ },
23565
+ async (input) => {
23566
+ const script = launcherPath(import.meta.url);
23567
+ if (!existsSync2(script)) {
23568
+ return text(
23569
+ `cannot open the pane: the launcher is missing at ${script}. This install is incomplete \u2014 tell the user to reinstall the plugin (a source checkout needs "npm run build").`,
23570
+ true
23571
+ );
23572
+ }
23573
+ const run = await runLauncher(script, launcherArgs(projectDirOf(stateFile), input.page, input.window === true));
23574
+ const viewers = run.ok ? await awaitPane(stateFile, input.page, Date.now() + PANE_REPORT_TIMEOUT_MS) : [];
23575
+ return text(openOutcome(run, viewers, input.page), !run.ok);
22747
23576
  }
22748
23577
  );
22749
23578
  return server;
@@ -22752,8 +23581,14 @@ function resolveStateFile(env, cwd) {
22752
23581
  const projectDir = env["MELLOS_MAPPING_CWD"] ?? env["CLAUDE_PROJECT_DIR"] ?? cwd;
22753
23582
  return join2(projectDir, STATE_FILE_RELATIVE_PATH);
22754
23583
  }
23584
+ function resolveUserConfigFile(home) {
23585
+ return userConfigFilePath(home);
23586
+ }
22755
23587
  async function main() {
22756
- const server = buildServer(resolveStateFile(process.env, process.cwd()));
23588
+ const stateFile = resolveStateFile(process.env, process.cwd());
23589
+ const userConfigFile = resolveUserConfigFile(homedir());
23590
+ if (migrateLegacyStore(stateFile)) console.error("mellos-mapping: moved the legacy .claude map store to .mellos/ \u2014 commit the move.");
23591
+ const server = buildServer(stateFile, userConfigFile);
22757
23592
  await server.connect(new StdioServerTransport());
22758
23593
  }
22759
23594
  function launchedAsEntry(argv1, moduleUrl) {
@@ -22775,5 +23610,10 @@ export {
22775
23610
  SERVER_VERSION,
22776
23611
  buildServer,
22777
23612
  launchedAsEntry,
22778
- resolveStateFile
23613
+ launcherArgs,
23614
+ launcherPath,
23615
+ openOutcome,
23616
+ projectDirOf,
23617
+ resolveStateFile,
23618
+ resolveUserConfigFile
22779
23619
  };