@north-light/crouter 0.3.245 → 0.3.247

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.
@@ -248,7 +248,7 @@ export function entryText(entry) {
248
248
  * its checks — the first failing one is the answer an engineer acts on. */
249
249
  export function exclusionReason(record, subject) {
250
250
  if (!record.docGate.pass) {
251
- return `document gate: ${gateReason(record.docGate.reason, explainRecordGate(record, 'doc', subject))}`;
251
+ return 'document gate';
252
252
  }
253
253
  if (record.shadowedBy !== null)
254
254
  return `shadowed by ${record.shadowedBy.scope}:${record.shadowedBy.path}`;
@@ -208,8 +208,9 @@ function commaList(arg) {
208
208
  function shortPath(value) {
209
209
  return value.startsWith('/') ? tildify(value) : value;
210
210
  }
211
- // Regex reduction — every real `cwd` gate is an anchored literal path, and it
212
- // should read as the directory it means.
211
+ // Regex reduction — every real `cwd` gate is an anchored literal path, whole or
212
+ // followed by one alternation of the projects it covers, and it should read as
213
+ // the directory or directories it means.
213
214
  const META = new Set(['\\', '^', '$', '.', '|', '?', '*', '+', '(', ')', '[', ']', '{', '}']);
214
215
  /** The pattern as the thing it demands: an anchored literal path collapsed to
215
216
  * the directory it names, else the longest literal run it insists on, else an
@@ -225,6 +226,9 @@ export function readableRegex(source) {
225
226
  const literal = unescapeLiteral(body);
226
227
  if (literal !== null && literal !== '')
227
228
  return shortPath(literal);
229
+ const branches = distributeAlternation(body);
230
+ if (branches !== null)
231
+ return branches.map((branch) => shortPath(branch)).join(' or ');
228
232
  const run = longestLiteralRun(source);
229
233
  return run === null ? 'a pattern' : `contains "${shortPath(run)}"`;
230
234
  }
@@ -249,6 +253,73 @@ function unescapeLiteral(source) {
249
253
  }
250
254
  return out;
251
255
  }
256
+ /** The paths a literal prefix followed by ONE alternation group means: each
257
+ * branch appended to the prefix, so the reader is handed directories they can
258
+ * actually be in rather than the prefix they all share. Null when the body is
259
+ * any other shape — a second group, text after the group, or a branch holding
260
+ * a metacharacter outside the glob vocabulary. */
261
+ function distributeAlternation(body) {
262
+ const open = body.indexOf('(');
263
+ if (open === -1 || !body.endsWith(')'))
264
+ return null;
265
+ const prefix = unescapeLiteral(body.slice(0, open));
266
+ if (prefix === null)
267
+ return null;
268
+ let inner = body.slice(open + 1, -1);
269
+ if (inner.startsWith('?:'))
270
+ inner = inner.slice(2);
271
+ // One group only: a nested group is a shape this reduction does not read.
272
+ if (inner.includes('(') || inner.includes(')'))
273
+ return null;
274
+ const branches = inner.split('|');
275
+ if (branches.length < 2)
276
+ return null;
277
+ const paths = [];
278
+ for (const branch of branches) {
279
+ const text = branchText(branch);
280
+ if (text === null)
281
+ return null;
282
+ paths.push(prefix + text);
283
+ }
284
+ return paths;
285
+ }
286
+ /** One branch as plain text, with a whole-segment wildcard rendered as the path
287
+ * glob it means. The vocabulary is exactly `[^/]+` and `.*`; any other
288
+ * metacharacter means the branch is not a path and the whole shape is
289
+ * abandoned. */
290
+ function branchText(branch) {
291
+ let out = '';
292
+ for (let i = 0; i < branch.length; i++) {
293
+ if (branch.startsWith('[^/]+', i)) {
294
+ out += '*';
295
+ i += 4;
296
+ continue;
297
+ }
298
+ if (branch.startsWith('[^\\/]+', i)) {
299
+ out += '*';
300
+ i += 5;
301
+ continue;
302
+ }
303
+ if (branch.startsWith('.*', i)) {
304
+ out += '*';
305
+ i += 1;
306
+ continue;
307
+ }
308
+ const ch = branch[i];
309
+ if (ch === '\\') {
310
+ const next = branch[i + 1];
311
+ if (next === undefined || (!META.has(next) && next !== '/' && next !== '-'))
312
+ return null;
313
+ out += next;
314
+ i++;
315
+ continue;
316
+ }
317
+ if (META.has(ch))
318
+ return null;
319
+ out += ch;
320
+ }
321
+ return out;
322
+ }
252
323
  /** The longest stretch of characters EVERY string the pattern accepts contains,
253
324
  * or null when it guarantees nothing longer than a single character. Only text
254
325
  * outside every group and character class counts: a group may be alternated or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.245",
3
+ "version": "0.3.247",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.245",
3
+ "version": "0.3.247",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/crouter",
9
- "version": "0.3.245",
9
+ "version": "0.3.247",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {