@intentic/claude-plugin 1.325.0 → 1.327.0

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.
@@ -29,9 +29,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
29
  mod
30
30
  ));
31
31
 
32
- // ../../node_modules/.pnpm/ignore@7.0.10/node_modules/ignore/index.js
32
+ // ../../node_modules/.pnpm/ignore@7.0.12/node_modules/ignore/index.js
33
33
  var require_ignore = __commonJS({
34
- "../../node_modules/.pnpm/ignore@7.0.10/node_modules/ignore/index.js"(exports, module) {
34
+ "../../node_modules/.pnpm/ignore@7.0.12/node_modules/ignore/index.js"(exports, module) {
35
35
  function makeArray(subject) {
36
36
  return Array.isArray(subject) ? subject : [subject];
37
37
  }
@@ -40,7 +40,7 @@ var require_ignore = __commonJS({
40
40
  var SPACE = " ";
41
41
  var ESCAPE = "\\";
42
42
  var REGEX_LITERAL_SPECIAL = /[.*+?()[\]{}^$|\\/]/;
43
- var REGEX_TEST_BLANK_LINE = /^\uFEFF? *$/;
43
+ var BOM = "\uFEFF";
44
44
  var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
45
45
  var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
46
46
  var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
@@ -165,6 +165,7 @@ var require_ignore = __commonJS({
165
165
  "g"
166
166
  );
167
167
  var TRAILING_WILDCARD = "\uE000";
168
+ var TRAILING_DOUBLESTAR = "\uE001";
168
169
  var extractBrackets = (pattern) => {
169
170
  const sources = [];
170
171
  const hold = (source) => `${PLACEHOLDER}${sources.push(source) - 1}${PLACEHOLDER}`;
@@ -175,7 +176,9 @@ var require_ignore = __commonJS({
175
176
  const char = pattern[index];
176
177
  if (char === ESCAPE) {
177
178
  const escaped = pattern[index + 1];
178
- if (escaped === "*" || escaped === "[" || escaped === SPACE || escaped === ESCAPE) {
179
+ if (escaped === UNDEFINED || escaped === SLASH && index + 2 === length) {
180
+ out += hold(NEVER_MATCH);
181
+ } else if (escaped === "*" || escaped === "[" || escaped === SPACE || escaped === ESCAPE) {
179
182
  out += pattern.slice(index, index + 2);
180
183
  } else {
181
184
  out += hold(
@@ -208,35 +211,6 @@ var require_ignore = __commonJS({
208
211
  var DIRECT = null;
209
212
  var REGEX_INNER_SLASH = /\/(?!$)/;
210
213
  var REPLACERS = [
211
- [
212
- // Remove BOM
213
- // TODO:
214
- // Other similar zero-width characters?
215
- /^\uFEFF/,
216
- () => EMPTY,
217
- "\uFEFF"
218
- ],
219
- [
220
- // A trailing line terminator, left on when a whole file's contents are
221
- // added as one pattern rather than split into lines. git never sees one
222
- // -- it reads a `.gitignore` line by line -- so it is not part of the
223
- // pattern and is dropped here, apart from the trailing-space trimming,
224
- // which follows git in touching spaces and nothing else.
225
- /[\r\n]+$/,
226
- () => EMPTY
227
- ],
228
- // > Trailing spaces are ignored unless they are quoted with backslash ("\")
229
- [
230
- // Only spaces, never tabs or other whitespace: git trims a trailing run
231
- // of `' '` and nothing else (dir.c, `trim_trailing_spaces`, a single
232
- // `case ' '`), so a pattern ending in a tab keeps it as a literal.
233
- // (a\ ) -> (a )
234
- // (a ) -> (a)
235
- // (a ) -> (a)
236
- // (a \ ) -> (a )
237
- /((?:\\\\)*?)(\\? +)$/,
238
- (_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
239
- ],
240
214
  // Replace (\ ) with ' '
241
215
  // Only a space: an escaped tab or other whitespace is already a literal by
242
216
  // the time it reaches here, and a bare tab must be left as one, not turned
@@ -245,11 +219,12 @@ var require_ignore = __commonJS({
245
219
  // (\\ ) -> '\\ '
246
220
  // (\\\ ) -> '\\ '
247
221
  [
248
- /(\\+?) /g,
249
- (_, m1) => {
250
- const { length } = m1;
251
- return m1.slice(0, length - length % 2) + SPACE;
252
- }
222
+ // A run of backslashes is taken whole, from its first one, so no match
223
+ // ever starts again inside it -- `(\\+?) ` did, which made a long run
224
+ // quadratic.
225
+ /(\\+)( ?)/g,
226
+ (_, run, space) => space ? run.slice(0, run.length - run.length % 2) + SPACE : run,
227
+ ESCAPE + SPACE
253
228
  ],
254
229
  // Escape metacharacters
255
230
  // which is written down by users but means special for regular expressions.
@@ -325,7 +300,7 @@ var require_ignore = __commonJS({
325
300
  // Zero, one or several directories
326
301
  // should not use '*', or it will be replaced by the next replacer
327
302
  // Check if it is not the last `'/**'`
328
- (_, index, str) => index + 6 < str.length ? str.slice(index + 6) === "\\/" ? "(?:\\/[^\\/]+)+" : "(?:\\/[^\\/]+)*" : "\\/.+",
303
+ (_, index, str) => index + 6 < str.length ? str.slice(index + 6) === "\\/" ? "(?:\\/[^\\/]+)+" : "(?:\\/[^\\/]+)*" : `\\/${TRAILING_DOUBLESTAR}`,
329
304
  "*"
330
305
  ],
331
306
  // normal intermediate wildcards
@@ -403,7 +378,7 @@ var require_ignore = __commonJS({
403
378
  // 'js' will match 'a.js' and 'a.js/'
404
379
  (source) => {
405
380
  const last = source[source.length - 1];
406
- if (!last || last === TRAILING_WILDCARD) {
381
+ if (!last || last === TRAILING_WILDCARD || last === TRAILING_DOUBLESTAR) {
407
382
  return source;
408
383
  }
409
384
  return last === SLASH ? `${source}$` : `${source}(?=$|\\/$)`;
@@ -414,16 +389,12 @@ var require_ignore = __commonJS({
414
389
  var MODE_IGNORE = "regex";
415
390
  var MODE_CHECK_IGNORE = "checkRegex";
416
391
  var UNDERSCORE = "_";
417
- var TRAILING_WILD_CARD_REPLACERS = {
418
- [MODE_IGNORE](_, p1) {
419
- const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
420
- return `${prefix}(?=$|\\/$)`;
421
- },
422
- [MODE_CHECK_IGNORE](_, p1) {
423
- const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
424
- return `${prefix}(?=$|\\/$)`;
425
- }
392
+ var replaceTrailingWildcard = (_, p1) => {
393
+ const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
394
+ return `${prefix}(?=$|\\/$)`;
426
395
  };
396
+ var REGEX_REPLACE_TRAILING_DOUBLESTAR = /\uE001$/;
397
+ var replaceTrailingDoublestar = () => ".+(?=$|\\/$)";
427
398
  var WILDCARD = "[^\\/]*";
428
399
  var separatorAfter = (run, at) => {
429
400
  let separator = EMPTY;
@@ -539,6 +510,14 @@ var require_ignore = __commonJS({
539
510
  (match, index) => sources[index]
540
511
  ) : replaced;
541
512
  };
513
+ var checkSourceOf = (body, prefix) => {
514
+ if (body[body.length - 1] === SLASH) {
515
+ prefix = makeRegexPrefix(body.slice(0, -1));
516
+ }
517
+ const head = prefix.slice(0, -1);
518
+ const last = prefix[prefix.length - 1];
519
+ return last === TRAILING_WILDCARD ? `${head}$` : last === TRAILING_DOUBLESTAR ? `${head}.*$` : NEVER_MATCH;
520
+ };
542
521
  var matchesBasename = (body) => {
543
522
  const index = body.indexOf(SLASH);
544
523
  return index < 0 || index === body.length - 1;
@@ -565,7 +544,7 @@ var require_ignore = __commonJS({
565
544
  return cut < 0 ? EMPTY : path.slice(0, cut + 1);
566
545
  };
567
546
  var isString = (subject) => typeof subject === "string";
568
- var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
547
+ var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern);
569
548
  var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
570
549
  var IgnoreRule = class {
571
550
  constructor(pattern, mark, body, ignoreCase, negative, prefix) {
@@ -599,24 +578,51 @@ var require_ignore = __commonJS({
599
578
  return this._make(MODE_CHECK_IGNORE, key);
600
579
  }
601
580
  _make(mode, key) {
602
- const str = pinWildcards(this.regexPrefix.replace(
603
- REGEX_REPLACE_TRAILING_WILDCARD,
604
- // It does not need to bind pattern
605
- TRAILING_WILD_CARD_REPLACERS[mode]
606
- ));
581
+ const str = pinWildcards(
582
+ mode === MODE_IGNORE ? this.regexPrefix.replace(REGEX_REPLACE_TRAILING_WILDCARD, replaceTrailingWildcard).replace(REGEX_REPLACE_TRAILING_DOUBLESTAR, replaceTrailingDoublestar) : checkSourceOf(this.body, this.regexPrefix)
583
+ );
607
584
  const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
608
585
  return define(this, key, regex);
609
586
  }
610
587
  };
588
+ var isLineEnd = (char) => char === "\r" || char === "\n";
589
+ var trimEnd = (body) => {
590
+ let end = body.length;
591
+ while (end && isLineEnd(body[end - 1])) {
592
+ end--;
593
+ }
594
+ const lineEnd = end;
595
+ while (end && body[end - 1] === SPACE) {
596
+ end--;
597
+ }
598
+ if (end === lineEnd) {
599
+ return body.slice(0, end);
600
+ }
601
+ let backslashes = 0;
602
+ while (backslashes < end && body[end - backslashes - 1] === ESCAPE) {
603
+ backslashes++;
604
+ }
605
+ return backslashes % 2 ? body.slice(0, end - 1) + SPACE : body.slice(0, end);
606
+ };
611
607
  var createRule = ({
612
608
  pattern,
613
609
  mark
614
610
  }, ignoreCase) => {
611
+ let body = pattern[0] === BOM ? pattern.slice(1) : pattern;
612
+ if (body[0] === "#") {
613
+ return;
614
+ }
615
615
  let negative = false;
616
- let body = pattern;
617
- if (body.indexOf("!") === 0) {
616
+ if (body[0] === "!") {
618
617
  negative = true;
619
- body = body.substr(1);
618
+ body = body.slice(1);
619
+ }
620
+ const last = body[body.length - 1];
621
+ if (last === SPACE || last === "\r" || last === "\n") {
622
+ body = trimEnd(body);
623
+ }
624
+ if (!body) {
625
+ return;
620
626
  }
621
627
  body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
622
628
  const regexPrefix = makeRegexPrefix(body);
@@ -647,8 +653,8 @@ var require_ignore = __commonJS({
647
653
  pattern
648
654
  };
649
655
  }
650
- if (checkPattern(pattern.pattern)) {
651
- const rule = createRule(pattern, this._ignoreCase);
656
+ const rule = checkPattern(pattern.pattern) && createRule(pattern, this._ignoreCase);
657
+ if (rule) {
652
658
  this._added = true;
653
659
  this._rules.push(rule);
654
660
  if (matchesBasename(rule.body)) {
@@ -662,8 +668,39 @@ var require_ignore = __commonJS({
662
668
  makeArray(
663
669
  isString(pattern) ? splitPattern(pattern) : pattern
664
670
  ).forEach(this._add, this);
671
+ if (this._added) {
672
+ this._literalRules = UNDEFINED;
673
+ }
665
674
  return this._added;
666
675
  }
676
+ // Match the literal 'abc/' for `checkIgnore`, last rule wins. Only a rule
677
+ // ending in a wildcard can match it (see `checkSourceOf`), so the rest
678
+ // are left out once, rather than tested or compiled for every path.
679
+ testLiteral(path) {
680
+ const rules = this._literalRules || (this._literalRules = this._rules.filter(
681
+ ({ body }) => body[body.length - (body[body.length - 1] === SLASH ? 2 : 1)] === "*"
682
+ ));
683
+ let ignored = false;
684
+ let unignored = false;
685
+ let matchedRule;
686
+ for (let index = rules.length - 1; index >= 0; index--) {
687
+ const rule = rules[index];
688
+ if (rule.checkRegex.test(path)) {
689
+ ignored = !rule.negative;
690
+ unignored = rule.negative;
691
+ matchedRule = rule.negative ? UNDEFINED : rule;
692
+ break;
693
+ }
694
+ }
695
+ const ret = {
696
+ ignored,
697
+ unignored
698
+ };
699
+ if (matchedRule) {
700
+ ret.rule = matchedRule;
701
+ }
702
+ return ret;
703
+ }
667
704
  // Test one single path without recursively checking parent directories
668
705
  //
669
706
  // - checkUnignored `boolean` whether should check if the path is unignored,
@@ -784,14 +821,12 @@ var require_ignore = __commonJS({
784
821
  if (path.charCodeAt(path.length - 1) !== SLASH_CODE) {
785
822
  return this.test(path);
786
823
  }
787
- const parentPath = parentOf(path);
788
- if (parentPath) {
789
- const parent = this._t(parentPath, this._testCache, true);
790
- if (parent.ignored) {
791
- return parent;
792
- }
824
+ const dir = this._t(path, this._testCache, true);
825
+ if (dir.ignored) {
826
+ return dir;
793
827
  }
794
- return this._rules.test(path, false, MODE_CHECK_IGNORE);
828
+ const literal = this._rules.testLiteral(path);
829
+ return literal.ignored || literal.unignored ? literal : dir;
795
830
  }
796
831
  _t(path, cache2, checkUnignored) {
797
832
  if (path in cache2) {
@@ -838,7 +873,7 @@ var require_ignore = __commonJS({
838
873
 
839
874
  // src/session-start.ts
840
875
  import { appendFileSync as appendFileSync2 } from "node:fs";
841
- import { join as join6 } from "node:path";
876
+ import { join as join7 } from "node:path";
842
877
 
843
878
  // ../agent-context/src/field-notes.ts
844
879
  import { createHash } from "node:crypto";
@@ -847,7 +882,7 @@ var FIELD_NOTES_NOTE_TITLE = "Field notes for this sandbox";
847
882
  var FIELD_NOTES_NOTE_HEADER = `## ${FIELD_NOTES_NOTE_TITLE}`;
848
883
  var ALWAYS = ["meta"];
849
884
  var ROW = /^(\d+),([a-z][a-z0-9_-]*),/;
850
- var TOP_LEVEL = /^([a-z][a-z0-9_]*)(\[|:)/;
885
+ var TOP_LEVEL = /^([a-z][a-z0-9_-]*)(\[|:)/;
851
886
  var blocksOf = (lines) => {
852
887
  const opens = [];
853
888
  for (const [at, line] of lines.entries()) {
@@ -943,8 +978,8 @@ var fieldNotes = ({ file: path, budget, onUnreadable, title = FIELD_NOTES_NOTE_T
943
978
  };
944
979
 
945
980
  // ../agent-context/src/workspace-map.ts
946
- import { readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
947
- import { basename, dirname, join, relative, sep } from "node:path";
981
+ import { readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
982
+ import { basename, dirname, join as join2, relative, sep } from "node:path";
948
983
 
949
984
  // ../../_shared/workspace-ignore/src/index.ts
950
985
  var import_ignore = __toESM(require_ignore(), 1);
@@ -1000,6 +1035,96 @@ var firstSegment = (relPath) => {
1000
1035
  var isReferencePath = (relPath) => firstSegment(relPath) === REFERENCE_DIR;
1001
1036
  var isPublicPath = (relPath) => firstSegment(relPath) === PUBLIC_DIR;
1002
1037
 
1038
+ // ../agent-context/src/workspace-activity.ts
1039
+ import { execFileSync } from "node:child_process";
1040
+ import { existsSync, readdirSync } from "node:fs";
1041
+ import { join } from "node:path";
1042
+ var RECENT_DAYS = 21;
1043
+ var RECENT_COMMITS = 400;
1044
+ var GIT_TIMEOUT_MS = 3e3;
1045
+ var QUIET_DIRS = /* @__PURE__ */ new Set(["generated", "dist", "locales", "baselines", "fixtures", "__snapshots__"]);
1046
+ var QUIET_FILE = /\.(md|lock|snap)$|(^|[-.])lock\.(json|ya?ml)$/;
1047
+ var SHELF_MIN_DIRS = 4;
1048
+ var changedFiles = (repoDir) => {
1049
+ try {
1050
+ const out = execFileSync(
1051
+ "git",
1052
+ ["-C", repoDir, "log", `--since=${RECENT_DAYS}.days`, "-n", String(RECENT_COMMITS), "--no-merges", "--relative", "--name-only", "--format=%x1e"],
1053
+ { encoding: "utf8", timeout: GIT_TIMEOUT_MS, stdio: ["ignore", "pipe", "ignore"], maxBuffer: 64 * 1024 * 1024 }
1054
+ );
1055
+ return out.split("").slice(1).map((commit) => commit.split("\n").filter((line) => line !== ""));
1056
+ } catch {
1057
+ return [];
1058
+ }
1059
+ };
1060
+ var isRepo = (dir) => existsSync(join(dir, ".git"));
1061
+ var isQuiet = (path) => {
1062
+ const segments = path.split("/");
1063
+ return QUIET_FILE.test(segments.at(-1) ?? "") || segments.slice(0, -1).some((segment) => segment.startsWith(".") || IGNORED_DIRS.has(segment) || QUIET_DIRS.has(segment));
1064
+ };
1065
+ var workspaceActivityOf = ({ projectRoot, areas }) => {
1066
+ const repos = [
1067
+ ...isRepo(projectRoot) ? [{ dir: projectRoot, prefix: "" }] : [],
1068
+ ...areas.filter((area) => isRepo(join(projectRoot, area))).map((area) => ({ dir: join(projectRoot, area), prefix: `${area}/` }))
1069
+ ];
1070
+ const commits = repos.flatMap(({ dir, prefix }) => changedFiles(dir).map((files) => files.map((file) => `${prefix}${file}`)));
1071
+ const shelves = /* @__PURE__ */ new Map();
1072
+ const isShelf = (folder) => {
1073
+ const known = shelves.get(folder);
1074
+ if (known !== void 0) {
1075
+ return known;
1076
+ }
1077
+ let shelf = false;
1078
+ try {
1079
+ const entries = readdirSync(join(projectRoot, folder), { withFileTypes: true });
1080
+ const dirs = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).length;
1081
+ const files = entries.filter((entry) => entry.isFile()).length;
1082
+ shelf = dirs >= SHELF_MIN_DIRS && files < dirs;
1083
+ } catch {
1084
+ }
1085
+ shelves.set(folder, shelf);
1086
+ return shelf;
1087
+ };
1088
+ const featureOf = (path) => {
1089
+ const dirs = path.split("/").slice(0, -1);
1090
+ if (dirs.length === 0) {
1091
+ return void 0;
1092
+ }
1093
+ const src = dirs.indexOf("src");
1094
+ const start = src >= 0 ? src + 2 : 3;
1095
+ let depth = Math.min(start, dirs.length);
1096
+ if (depth < dirs.length && isShelf(dirs.slice(0, depth).join("/"))) {
1097
+ depth += 1;
1098
+ }
1099
+ return dirs.slice(0, depth).join("/");
1100
+ };
1101
+ const byFolder = /* @__PURE__ */ new Map();
1102
+ const byFeature = /* @__PURE__ */ new Map();
1103
+ for (const files of commits) {
1104
+ const kept = files.filter((file) => !isQuiet(file));
1105
+ const folders = new Set(kept.flatMap((file) => file.split("/").slice(0, -1).map((_, at, dirs) => dirs.slice(0, at + 1).join("/"))));
1106
+ for (const folder of folders) {
1107
+ byFolder.set(folder, (byFolder.get(folder) ?? 0) + 1);
1108
+ }
1109
+ for (const feature of new Set(kept.map(featureOf).filter((folder) => folder !== void 0))) {
1110
+ byFeature.set(feature, (byFeature.get(feature) ?? 0) + 1);
1111
+ }
1112
+ }
1113
+ const hot = [...byFeature.entries()].map(([folder, count]) => ({ folder, commits: count })).toSorted((left, right) => right.commits - left.commits || left.folder.localeCompare(right.folder));
1114
+ return { commits: commits.length, byFolder, hot };
1115
+ };
1116
+ var groupedFolders = (folders) => {
1117
+ const groups = /* @__PURE__ */ new Map();
1118
+ for (const folder of folders) {
1119
+ const cut = folder.lastIndexOf("/");
1120
+ const parent = cut === -1 ? "" : folder.slice(0, cut);
1121
+ groups.set(parent, [...groups.get(parent) ?? [], cut === -1 ? folder : folder.slice(cut + 1)]);
1122
+ }
1123
+ return [...groups.entries()].flatMap(
1124
+ ([parent, names]) => parent === "" ? names : [`${parent}/${names.length === 1 ? names[0] ?? "" : `{${names.join(",")}}`}`]
1125
+ );
1126
+ };
1127
+
1003
1128
  // ../agent-context/src/workspace-map.ts
1004
1129
  var WORKSPACE_MAP_NOTE_HEADER = "## Map of this project";
1005
1130
  var MAX_NOTE_CHARS = 2800;
@@ -1016,11 +1141,11 @@ var isBrowsable = (name, relPath) => !name.startsWith(".") && !IGNORED_DIRS.has(
1016
1141
  var childDirs = (dir, root) => {
1017
1142
  let entries;
1018
1143
  try {
1019
- entries = readdirSync(dir, { withFileTypes: true });
1144
+ entries = readdirSync2(dir, { withFileTypes: true });
1020
1145
  } catch {
1021
1146
  return [];
1022
1147
  }
1023
- return entries.filter((entry) => entry.isDirectory() && isBrowsable(entry.name, posix(relative(root, join(dir, entry.name))))).map((entry) => entry.name).toSorted();
1148
+ return entries.filter((entry) => entry.isDirectory() && isBrowsable(entry.name, posix(relative(root, join2(dir, entry.name))))).map((entry) => entry.name).toSorted();
1024
1149
  };
1025
1150
  var readIfPresent = (path) => {
1026
1151
  try {
@@ -1029,9 +1154,9 @@ var readIfPresent = (path) => {
1029
1154
  return void 0;
1030
1155
  }
1031
1156
  };
1032
- var hasManifest = (dir) => MANIFESTS.some((name) => readIfPresent(join(dir, name)) !== void 0);
1157
+ var hasManifest = (dir) => MANIFESTS.some((name) => readIfPresent(join2(dir, name)) !== void 0);
1033
1158
  var describeManifest = (dir, name) => {
1034
- const source = readIfPresent(join(dir, name));
1159
+ const source = readIfPresent(join2(dir, name));
1035
1160
  if (source === void 0) {
1036
1161
  return void 0;
1037
1162
  }
@@ -1053,7 +1178,7 @@ var describeManifest = (dir, name) => {
1053
1178
  return found === void 0 || found === "" ? void 0 : found;
1054
1179
  };
1055
1180
  var describeReadme = (dir) => {
1056
- const source = readIfPresent(join(dir, "README.md")) ?? readIfPresent(join(dir, "readme.md"));
1181
+ const source = readIfPresent(join2(dir, "README.md")) ?? readIfPresent(join2(dir, "readme.md"));
1057
1182
  if (source === void 0) {
1058
1183
  return void 0;
1059
1184
  }
@@ -1071,12 +1196,20 @@ var describeReadme = (dir) => {
1071
1196
  }
1072
1197
  return void 0;
1073
1198
  };
1199
+ var firstSentence = (text) => {
1200
+ const plain = text.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
1201
+ return /^(.+?[.!?])(?:\s|$)/.exec(plain)?.[1] ?? plain;
1202
+ };
1074
1203
  var clip = (text, max) => {
1075
- const oneLine = text.replace(/\s+/g, " ").trim();
1204
+ const oneLine = firstSentence(text);
1076
1205
  if (oneLine.length <= max) {
1077
1206
  return oneLine;
1078
1207
  }
1079
1208
  const head = oneLine.slice(0, max);
1209
+ const clause = Math.max(...[", ", "; ", ": ", " \u2014 "].map((mark) => head.lastIndexOf(mark)));
1210
+ if (clause > max / 2) {
1211
+ return head.slice(0, clause);
1212
+ }
1080
1213
  const lastSpace = head.lastIndexOf(" ");
1081
1214
  return `${(lastSpace > max / 2 ? head.slice(0, lastSpace) : head).replace(/[.,;:]$/, "")}\u2026`;
1082
1215
  };
@@ -1100,7 +1233,7 @@ var measure = (dir, root) => {
1100
1233
  }
1101
1234
  let entries;
1102
1235
  try {
1103
- entries = readdirSync(current, { withFileTypes: true });
1236
+ entries = readdirSync2(current, { withFileTypes: true });
1104
1237
  } catch {
1105
1238
  return;
1106
1239
  }
@@ -1109,7 +1242,7 @@ var measure = (dir, root) => {
1109
1242
  return;
1110
1243
  }
1111
1244
  visited += 1;
1112
- const child = join(current, entry.name);
1245
+ const child = join2(current, entry.name);
1113
1246
  if (entry.isDirectory()) {
1114
1247
  if (isBrowsable(entry.name, posix(relative(root, child)))) {
1115
1248
  walk(child, depth + 1);
@@ -1137,7 +1270,7 @@ var projectRootOf = (root, cwd) => {
1137
1270
  let current = cwd;
1138
1271
  for (; ; ) {
1139
1272
  try {
1140
- statSync2(join(current, ".git"));
1273
+ statSync2(join2(current, ".git"));
1141
1274
  return current;
1142
1275
  } catch {
1143
1276
  }
@@ -1156,13 +1289,13 @@ var packagesIn = (dir, root) => {
1156
1289
  return [];
1157
1290
  }
1158
1291
  const children = childDirs(dir, root);
1159
- const bearing = children.filter((child) => hasManifest(join(dir, child)));
1160
- return children.length >= CONTAINER_MIN_CHILDREN && bearing.length >= CONTAINER_MIN_CHILDREN ? bearing.map((child) => ({ name: child, dir: join(dir, child) })) : [];
1292
+ const bearing = children.filter((child) => hasManifest(join2(dir, child)));
1293
+ return children.length >= CONTAINER_MIN_CHILDREN && bearing.length >= CONTAINER_MIN_CHILDREN ? bearing.map((child) => ({ name: child, dir: join2(dir, child) })) : [];
1161
1294
  };
1162
1295
  var areaDirsOf = (projectRoot, root) => {
1163
1296
  const names = childDirs(projectRoot, root);
1164
1297
  const areas = names.map((name) => {
1165
- const dir = join(projectRoot, name);
1298
+ const dir = join2(projectRoot, name);
1166
1299
  return { name, dir, packages: packagesIn(dir, root) };
1167
1300
  });
1168
1301
  const shelf = areas.length <= 2 ? areas.find((area) => area.packages.length > 0) : void 0;
@@ -1262,11 +1395,41 @@ var render = (map, exactPaths) => {
1262
1395
  const total = map.omitted + dropped;
1263
1396
  return [...head, ...lines, ...total > 0 ? [` \u2026 and ${total} more`] : [], ...tail].join("\n");
1264
1397
  };
1398
+ var COMPACT_AREAS = 6;
1399
+ var COMPACT_HOT = 6;
1400
+ var COMPACT_ACTIVE_SHARE = 0.03;
1401
+ var COMPACT_HOT_MIN = 2;
1402
+ var renderCompact = (map, exactPaths, activity) => {
1403
+ const project = map.project === "" ? "the workspace" : `\`${map.project}\``;
1404
+ const leaves = map.areas.flatMap((area) => area.children.length > 1 ? area.children : [area]);
1405
+ const busy = (area) => activity.byFolder.get(area.name) ?? 0;
1406
+ const ranked = activity.commits > 0 ? leaves.toSorted((left, right) => busy(right) - busy(left) || right.files - left.files || left.name.localeCompare(right.name)) : leaves.toSorted((left, right) => right.files - left.files || left.name.localeCompare(right.name));
1407
+ const active = (area, at) => at < COMPACT_AREAS && (activity.commits === 0 || busy(area) >= Math.max(1, activity.commits * COMPACT_ACTIVE_SHARE));
1408
+ const listed = ranked.filter((area, at) => area.here || active(area, at));
1409
+ const rest = leaves.filter((area) => !listed.includes(area)).map((area) => area.name);
1410
+ const unlisted = map.omitted + map.areas.reduce((sum, area) => sum + area.childrenOmitted, 0);
1411
+ const width = Math.min(Math.max(...listed.map((area) => area.name.length)) + 2, 34);
1412
+ const hot = groupedFolders(
1413
+ activity.hot.filter((entry) => entry.commits >= COMPACT_HOT_MIN).slice(0, COMPACT_HOT).map((entry) => entry.folder)
1414
+ );
1415
+ const where = map.cwd === map.project ? `You are at the top of ${project}.` : `You are here: \`${map.cwd}\`, in ${project}.`;
1416
+ return [
1417
+ WORKSPACE_MAP_NOTE_HEADER,
1418
+ "",
1419
+ `${where} This is its layout, so do not \`ls\` or \`tree\` the root to orient yourself; use ${exactPaths} for exact paths.`,
1420
+ "",
1421
+ ...listed.map((area) => `${area.name.padEnd(width)}${area.purpose}${area.here ? " \u2190 you are here" : ""}`.trimEnd()),
1422
+ ...rest.length > 0 || unlisted > 0 ? [`Also: ${groupedFolders(rest).join(", ")}${unlisted > 0 ? `${rest.length > 0 ? ", " : ""}and ${unlisted} smaller` : ""}`] : [],
1423
+ ...map.siblings.length > 0 ? [`Also under the workspace root: ${map.siblings.map((name) => `${name}/`).join(", ")}`] : [],
1424
+ ...hot.length > 0 ? ["", `Recent work (last 3 weeks) landed in: ${hot.join(" \xB7 ")}`] : []
1425
+ ].join("\n");
1426
+ };
1265
1427
  var TTL_MS = 6e4;
1266
1428
  var cache = /* @__PURE__ */ new Map();
1267
1429
  var workspaceMapNote = (input) => {
1268
1430
  const exactPaths = input.exactPaths ?? "`iq files` or Read";
1269
- const key = `${input.root}\0${input.cwd}\0${exactPaths}`;
1431
+ const form = input.form ?? "full";
1432
+ const key = `${input.root}\0${input.cwd}\0${exactPaths}\0${form}`;
1270
1433
  const hit = cache.get(key);
1271
1434
  if (hit !== void 0 && Date.now() - hit.at < TTL_MS) {
1272
1435
  return hit.note;
@@ -1274,7 +1437,11 @@ var workspaceMapNote = (input) => {
1274
1437
  let note;
1275
1438
  try {
1276
1439
  const map = workspaceMapOf(input);
1277
- note = map === void 0 ? void 0 : render(map, exactPaths);
1440
+ note = map === void 0 ? void 0 : form === "compact" ? renderCompact(
1441
+ map,
1442
+ exactPaths,
1443
+ workspaceActivityOf({ projectRoot: join2(input.root, map.project), areas: map.areas.map((area) => area.name) })
1444
+ ) : render(map, exactPaths);
1278
1445
  } catch {
1279
1446
  note = void 0;
1280
1447
  }
@@ -1284,12 +1451,12 @@ var workspaceMapNote = (input) => {
1284
1451
 
1285
1452
  // src/features.ts
1286
1453
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
1287
- import { join as join3 } from "node:path";
1454
+ import { join as join4 } from "node:path";
1288
1455
 
1289
1456
  // src/runtime.ts
1290
1457
  import { spawn } from "node:child_process";
1291
1458
  import { accessSync, closeSync, constants, mkdirSync, openSync, readFileSync as readFileSync3, statSync as statSync3, writeFileSync } from "node:fs";
1292
- import { delimiter, dirname as dirname2, join as join2 } from "node:path";
1459
+ import { delimiter, dirname as dirname2, join as join3 } from "node:path";
1293
1460
  var pluginRoot = (env = process.env) => env["CLAUDE_PLUGIN_ROOT"] ?? dirname2(import.meta.dirname);
1294
1461
  var isMissing = (error) => {
1295
1462
  const code = typeof error === "object" && error !== null ? error.code : void 0;
@@ -1297,7 +1464,7 @@ var isMissing = (error) => {
1297
1464
  };
1298
1465
  var pluginFile = (parts, env = process.env) => {
1299
1466
  try {
1300
- return readFileSync3(join2(pluginRoot(env), ...parts), "utf8");
1467
+ return readFileSync3(join3(pluginRoot(env), ...parts), "utf8");
1301
1468
  } catch (error) {
1302
1469
  if (isMissing(error)) {
1303
1470
  return void 0;
@@ -1310,7 +1477,7 @@ var onPath = (name, env = process.env) => {
1310
1477
  for (const dir of (env["PATH"] ?? "").split(delimiter)) {
1311
1478
  for (const extension of extensions) {
1312
1479
  try {
1313
- accessSync(join2(dir, `${name}${extension}`), constants.X_OK);
1480
+ accessSync(join3(dir, `${name}${extension}`), constants.X_OK);
1314
1481
  return true;
1315
1482
  } catch {
1316
1483
  }
@@ -1433,7 +1600,7 @@ var readOptions = (env = process.env) => {
1433
1600
  });
1434
1601
  return Object.fromEntries(entries);
1435
1602
  };
1436
- var optionsFile = (data) => join3(data, "options.json");
1603
+ var optionsFile = (data) => join4(data, "options.json");
1437
1604
  var saveOptions = (data, options) => {
1438
1605
  mkdirSync2(data, { recursive: true });
1439
1606
  writeFileSync2(optionsFile(data), `${JSON.stringify(options)}
@@ -1442,7 +1609,7 @@ var saveOptions = (data, options) => {
1442
1609
 
1443
1610
  // src/hook-io.ts
1444
1611
  import { homedir } from "node:os";
1445
- import { join as join4 } from "node:path";
1612
+ import { join as join5 } from "node:path";
1446
1613
  var readStdin = async () => {
1447
1614
  const chunks = [];
1448
1615
  for await (const chunk of process.stdin) {
@@ -1477,13 +1644,13 @@ var runHook = async (handle) => {
1477
1644
  `);
1478
1645
  }
1479
1646
  };
1480
- var dataDir = (env = process.env) => env["CLAUDE_PLUGIN_DATA"] ?? join4(env["CLAUDE_CONFIG_DIR"] ?? join4(homedir(), ".claude"), "plugins", "data", "intentic-intentic");
1647
+ var dataDir = (env = process.env) => env["CLAUDE_PLUGIN_DATA"] ?? join5(env["CLAUDE_CONFIG_DIR"] ?? join5(homedir(), ".claude"), "plugins", "data", "intentic-intentic");
1481
1648
  var projectDir = (input, env = process.env) => env["CLAUDE_PROJECT_DIR"] ?? input.cwd ?? process.cwd();
1482
- var fieldNotesFile = (project) => join4(project, ".claude", "intentic", "field-notes.toon");
1649
+ var fieldNotesFile = (project) => join5(project, ".claude", "intentic", "field-notes.toon");
1483
1650
 
1484
1651
  // src/sessions.ts
1485
1652
  import { appendFileSync, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "node:fs";
1486
- import { dirname as dirname3, join as join5 } from "node:path";
1653
+ import { dirname as dirname3, join as join6 } from "node:path";
1487
1654
 
1488
1655
  // ../agent-context/src/experiments.ts
1489
1656
  import { createHash as createHash2 } from "node:crypto";
@@ -1513,7 +1680,7 @@ var armsOf = (options, session) => {
1513
1680
  };
1514
1681
  var sends = (options, arms, mechanism) => MECHANISM_ON[mechanism](options) && arms[mechanism] !== false;
1515
1682
  var measuredArms = (arms, offered) => Object.fromEntries(Object.entries(arms).filter(([mechanism]) => offered[mechanism] === true));
1516
- var sessionsFile = (data) => join5(data, "sessions.jsonl");
1683
+ var sessionsFile = (data) => join6(data, "sessions.jsonl");
1517
1684
  var recordSession = (data, row) => {
1518
1685
  const file = sessionsFile(data);
1519
1686
  mkdirSync3(dirname3(file), { recursive: true });
@@ -1530,7 +1697,7 @@ var compose = (options, input, project, arms, data, env) => {
1530
1697
  const context = [];
1531
1698
  const notices = [];
1532
1699
  const notice = (id, text) => {
1533
- if (claimIfDue(join6(data, "notices", `${id}.stamp`), NOTICE_EVERY_MS)) {
1700
+ if (claimIfDue(join7(data, "notices", `${id}.stamp`), NOTICE_EVERY_MS)) {
1534
1701
  notices.push(text);
1535
1702
  }
1536
1703
  };
@@ -1570,7 +1737,7 @@ var compose = (options, input, project, arms, data, env) => {
1570
1737
  ${nudge.trim()}`);
1571
1738
  sent.iq = true;
1572
1739
  }
1573
- startDetached("iq", ["sessions", "ingest"], { cwd: project, log: join6(data, "iq", `${projectSlug(project)}.ingest.log`), env });
1740
+ startDetached("iq", ["sessions", "ingest"], { cwd: project, log: join7(data, "iq", `${projectSlug(project)}.ingest.log`), env });
1574
1741
  } else {
1575
1742
  notice("iq-missing", "intentic: iq search is on but the `iq` CLI is not installed (`npm i -g @intentic/iq`), so nothing was taught.");
1576
1743
  }