@cortexkit/aft 0.28.2 → 0.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,qBAAqB,CAAC;AAMvE,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,WAAW,GAAG,cAAc,CAAC;AAE9E,eAAO,MAAM,2BAA2B,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,EAapF,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,iBAAiB,EAAqB,CAAC;AAEhF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,MAAM,WAAW,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA2GvE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAiBnE;AA4BD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,cAAc,EAAE,EAC1B,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CAgD5B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,gBAAgB,IAAI,sBAAsB,CAsDzD;AA0HD,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhF"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,qBAAqB,CAAC;AAMvE,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,WAAW,GAAG,cAAc,CAAC;AAE9E,eAAO,MAAM,2BAA2B,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,EAapF,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,iBAAiB,EAAqB,CAAC;AAEhF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,MAAM,WAAW,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA2GvE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAiBnE;AA4BD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,cAAc,EAAE,EAC1B,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CAgD5B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,gBAAgB,IAAI,sBAAsB,CAsDzD;AA0HD,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhF"}
package/dist/index.js CHANGED
@@ -11131,6 +11131,98 @@ function createGitHubIssue(repo, title, body) {
11131
11131
  }
11132
11132
  var init_github = () => {};
11133
11133
 
11134
+ // src/lib/issue-body.ts
11135
+ function isErrorLogLine(line) {
11136
+ return ERROR_LOG_PATTERNS.some((rx) => rx.test(line));
11137
+ }
11138
+ function extractRecentErrors(sanitized, limit = 20) {
11139
+ const matches = [];
11140
+ const lines = sanitized.split(/\r?\n/);
11141
+ for (let i = lines.length - 1;i >= 0 && matches.length < limit; i -= 1) {
11142
+ if (isErrorLogLine(lines[i])) {
11143
+ matches.push(lines[i]);
11144
+ }
11145
+ }
11146
+ return matches.reverse();
11147
+ }
11148
+ function capBodyToGithubLimit(body, maxBytes = MAX_GITHUB_BODY_BYTES) {
11149
+ if (Buffer.byteLength(body, "utf8") <= maxBytes)
11150
+ return body;
11151
+ const heading = "## Logs (last";
11152
+ const headingIdx = body.indexOf(heading);
11153
+ if (headingIdx === -1) {
11154
+ const marker = `
11155
+
11156
+ [truncated for GitHub 64KB limit]
11157
+ `;
11158
+ const markerBytes2 = Buffer.byteLength(marker, "utf8");
11159
+ return truncateToByteBudget(body, maxBytes - markerBytes2) + marker;
11160
+ }
11161
+ const headingEol = body.indexOf(`
11162
+ `, headingIdx);
11163
+ if (headingEol === -1)
11164
+ return body;
11165
+ const nextSectionIdx = findNextTopLevelHeading(body, headingEol + 1);
11166
+ const logBlockStart = headingEol + 1;
11167
+ const logBlockEnd = nextSectionIdx === -1 ? body.length : nextSectionIdx;
11168
+ const head = body.slice(0, logBlockStart);
11169
+ const log = body.slice(logBlockStart, logBlockEnd);
11170
+ const tail = body.slice(logBlockEnd);
11171
+ const overheadBytes = Buffer.byteLength(head, "utf8") + Buffer.byteLength(tail, "utf8");
11172
+ const truncationMarker = `[truncated for GitHub 64KB limit — older log lines dropped]
11173
+ `;
11174
+ const markerBytes = Buffer.byteLength(truncationMarker, "utf8");
11175
+ const logBudget = maxBytes - overheadBytes - markerBytes;
11176
+ if (logBudget <= 0) {
11177
+ return `${head}${truncationMarker}${tail}`;
11178
+ }
11179
+ const lines = log.split(`
11180
+ `);
11181
+ let keepLines = lines;
11182
+ let kept = keepLines.join(`
11183
+ `);
11184
+ while (Buffer.byteLength(kept, "utf8") > logBudget && keepLines.length > 1) {
11185
+ const dropCount = Math.max(1, Math.floor(keepLines.length * 0.05));
11186
+ keepLines = keepLines.slice(dropCount);
11187
+ kept = keepLines.join(`
11188
+ `);
11189
+ }
11190
+ if (Buffer.byteLength(kept, "utf8") > logBudget) {
11191
+ kept = truncateToByteBudget(kept, logBudget);
11192
+ }
11193
+ return `${head}${truncationMarker}${kept}${tail}`;
11194
+ }
11195
+ function findNextTopLevelHeading(body, startIdx) {
11196
+ const idx = body.indexOf(`
11197
+ ## `, startIdx);
11198
+ return idx === -1 ? -1 : idx + 1;
11199
+ }
11200
+ function truncateToByteBudget(input, maxBytes) {
11201
+ if (maxBytes <= 0)
11202
+ return "";
11203
+ const buf = Buffer.from(input, "utf8");
11204
+ if (buf.length <= maxBytes)
11205
+ return input;
11206
+ let end = maxBytes;
11207
+ while (end > 0 && (buf[end] & 192) === 128) {
11208
+ end -= 1;
11209
+ }
11210
+ return buf.subarray(0, end).toString("utf8");
11211
+ }
11212
+ var MAX_GITHUB_BODY_BYTES = 60000, ERROR_LOG_PATTERNS;
11213
+ var init_issue_body = __esm(() => {
11214
+ ERROR_LOG_PATTERNS = [
11215
+ /\bcrashed:/i,
11216
+ /\bfailed:/i,
11217
+ /\b(?:[A-Z][a-zA-Z]*)?Error:\s/,
11218
+ /\bpanicked at\b/,
11219
+ /\bEMERGENCY\b/,
11220
+ /\bexception\b/i,
11221
+ /^\s+at\s+[\w.<>$]+\s+\(/,
11222
+ /^\s+at\s+(?:file:|node_modules\/|[^/\s]+:\d+)/
11223
+ ];
11224
+ });
11225
+
11134
11226
  // src/lib/onnx-fix.ts
11135
11227
  import { existsSync as existsSync12, rmSync as rmSync3 } from "node:fs";
11136
11228
  import { join as join10 } from "node:path";
@@ -12413,7 +12505,7 @@ async function findBinary(expectedVersion) {
12413
12505
  return syncResult;
12414
12506
  }
12415
12507
  log("Binary not found locally, attempting auto-download...");
12416
- const downloaded = await ensureBinary(expectedVersion);
12508
+ const downloaded = await ensureBinaryForResolver(expectedVersion);
12417
12509
  if (downloaded)
12418
12510
  return downloaded;
12419
12511
  throw new Error([
@@ -12435,10 +12527,12 @@ async function findBinary(expectedVersion) {
12435
12527
  ].join(`
12436
12528
  `));
12437
12529
  }
12530
+ var ensureBinaryForResolver;
12438
12531
  var init_resolver = __esm(() => {
12439
12532
  init_active_logger();
12440
12533
  init_downloader();
12441
12534
  init_platform();
12535
+ ensureBinaryForResolver = ensureBinary;
12442
12536
  });
12443
12537
 
12444
12538
  // ../aft-bridge/dist/migration.js
@@ -12515,7 +12609,7 @@ async function ensureStorageMigrated(opts) {
12515
12609
 
12516
12610
  `);
12517
12611
  } catch {}
12518
- const result = spawnSync5(binaryPath, [
12612
+ const result = spawnSyncForMigration(binaryPath, [
12519
12613
  "migrate-storage",
12520
12614
  "--from",
12521
12615
  legacyRoot,
@@ -12548,7 +12642,7 @@ async function ensureStorageMigrated(opts) {
12548
12642
  async function getMigrationStatus(opts) {
12549
12643
  const newRoot = resolveCortexKitStorageRoot();
12550
12644
  const binaryPath = opts.binaryPath ?? await findBinary();
12551
- const result = spawnSync5(binaryPath, ["migrate-storage", "--status", "--to", newRoot, "--harness", opts.harness], {
12645
+ const result = spawnSyncForMigration(binaryPath, ["migrate-storage", "--status", "--to", newRoot, "--harness", opts.harness], {
12552
12646
  encoding: "utf8",
12553
12647
  stdio: ["ignore", "pipe", "pipe"],
12554
12648
  timeout: DEFAULT_TIMEOUT_MS
@@ -12565,9 +12659,10 @@ async function getMigrationStatus(opts) {
12565
12659
  throw new Error(`AFT storage migration status returned invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
12566
12660
  }
12567
12661
  }
12568
- var TARGET_MARKER = ".migrated_from_legacy", DEFAULT_TIMEOUT_MS;
12662
+ var spawnSyncForMigration, TARGET_MARKER = ".migrated_from_legacy", DEFAULT_TIMEOUT_MS;
12569
12663
  var init_migration = __esm(() => {
12570
12664
  init_resolver();
12665
+ spawnSyncForMigration = spawnSync5;
12571
12666
  DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
12572
12667
  });
12573
12668
 
@@ -36393,6 +36488,15 @@ ${tail2 || "<no log output>"}
36393
36488
  \`\`\`
36394
36489
  `;
36395
36490
  }).join(`
36491
+ `);
36492
+ const errorScanWindow = adapters.map((adapter) => {
36493
+ const path = adapter.getLogFile();
36494
+ return sanitizeContent(tailLogFile(path, 4000));
36495
+ }).join(`
36496
+ `);
36497
+ const recentErrorLines = extractRecentErrors(errorScanWindow, 20);
36498
+ const recentErrorsSection = recentErrorLines.length === 0 ? "_No error-shaped log lines found in recent history._" : ["```", recentErrorLines.join(`
36499
+ `), "```"].join(`
36396
36500
  `);
36397
36501
  const rawBody = [
36398
36502
  "## Description",
@@ -36407,12 +36511,15 @@ ${tail2 || "<no log output>"}
36407
36511
  "## Diagnostics",
36408
36512
  renderDiagnosticsMarkdown(report),
36409
36513
  "",
36514
+ "## Recent errors (last 20, sanitized)",
36515
+ recentErrorsSection,
36516
+ "",
36410
36517
  "## Logs (last 200 lines per harness)",
36411
36518
  logSections,
36412
36519
  "_Usernames and home paths have been stripped from this report._"
36413
36520
  ].join(`
36414
36521
  `);
36415
- const body = sanitizeContent(rawBody);
36522
+ const body = capBodyToGithubLimit(sanitizeContent(rawBody));
36416
36523
  const title = sanitizeContent(`AFT issue: ${description.slice(0, 72)}`);
36417
36524
  const outPath = join17(process.cwd(), `aft-issue-${Date.now()}.md`);
36418
36525
  writeFileSync4(outPath, `${body}
@@ -36443,6 +36550,7 @@ var init_doctor = __esm(() => {
36443
36550
  init_fs_util();
36444
36551
  init_github();
36445
36552
  init_harness_select();
36553
+ init_issue_body();
36446
36554
  init_lsp_cache();
36447
36555
  init_onnx_fix();
36448
36556
  init_prompts();
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Helpers for shaping `aft doctor --issue` GitHub issue bodies within
3
+ * GitHub's hard ~64KB issue-body limit.
4
+ *
5
+ * Two responsibilities live here:
6
+ *
7
+ * 1. Error-line extraction — pull the most-recent ERROR-shaped lines from
8
+ * a sanitized log so the issue body has a dedicated `## Recent errors`
9
+ * section that survives even when the main log tail needs aggressive
10
+ * truncation.
11
+ *
12
+ * 2. GitHub byte-budget capping — when a rendered report exceeds the
13
+ * budget, shrink the main log block (the noise-heavy section) from
14
+ * the top, preserving the diagnostics / configuration / error sections
15
+ * that matter most.
16
+ *
17
+ * Both helpers operate on already-sanitized markdown so they're harness-
18
+ * agnostic — OpenCode and Pi share the same byte budget, the same
19
+ * truncation marker text, and the same precision/false-positive tradeoff
20
+ * on what counts as an "error" line.
21
+ */
22
+ /**
23
+ * GitHub issue body byte budget. GitHub enforces ~64KB (65536 bytes); we
24
+ * leave 4KB of headroom for: GH's own URL encoding when opening the
25
+ * "Submit new issue" tab via `gh issue create --web`, future minor
26
+ * markdown growth from new sections, and a safety margin against any
27
+ * single-line entry crossing the cap.
28
+ */
29
+ export declare const MAX_GITHUB_BODY_BYTES = 60000;
30
+ /**
31
+ * Extract the most-recent error-shaped log lines from a sanitized log.
32
+ * Returns them in chronological order (oldest match first → newest match
33
+ * last) so the issue body reads naturally top-to-bottom.
34
+ *
35
+ * **Why this exists**: GitHub issue bodies have a hard ~64KB limit and a
36
+ * busy session's tail can easily blow past that. If the body needs
37
+ * truncation, the error section MUST survive because the whole point of
38
+ * the issue is the error. This extractor pulls them into a separate
39
+ * section that the body-cap is careful not to drop.
40
+ */
41
+ export declare function extractRecentErrors(sanitized: string, limit?: number): string[];
42
+ /**
43
+ * Apply a byte budget to a rendered issue body. If the body is already
44
+ * within budget, returns it unchanged. Otherwise rewrites the main
45
+ * fenced log block (whose heading must start with `## Logs (last`) to
46
+ * drop oldest log lines until the body fits, leaving a clear
47
+ * `[truncated for GitHub 64KB limit — older log lines dropped]` marker
48
+ * at the top of the kept slice.
49
+ *
50
+ * This deliberately ONLY touches the main-log section — the description,
51
+ * environment, diagnostics, and recent-errors sections are preserved
52
+ * intact because they're the most useful parts of the report. The main
53
+ * log is the noise-heavy one and the right thing to shrink first.
54
+ *
55
+ * AFT's main log section contains per-harness sub-sections (`#### opencode
56
+ * log (path)\n```...```\n#### pi log (path)\n```...```\n`). The cap walks
57
+ * the entire `## Logs (last ...` block as a single shrinkable region
58
+ * because precise per-harness allocation is fiddly and the alternative —
59
+ * dropping the whole log block on overflow — is strictly worse for
60
+ * debugging.
61
+ *
62
+ * Returns the (possibly-shrunk) body. UTF-8 byte length is the budget,
63
+ * matching how GitHub measures issue bodies (the issue API rejects
64
+ * `body` payloads above the limit).
65
+ */
66
+ export declare function capBodyToGithubLimit(body: string, maxBytes?: number): string;
67
+ //# sourceMappingURL=issue-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-body.d.ts","sourceRoot":"","sources":["../../src/lib/issue-body.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,QAAS,CAAC;AA2C5C;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,EAAE,CAU3E;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAA8B,GACvC,MAAM,CAqER"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/aft",
3
- "version": "0.28.2",
3
+ "version": "0.29.1",
4
4
  "type": "module",
5
5
  "description": "Unified CLI for Agent File Tools (AFT) — setup, doctor, and diagnostics across supported agent harnesses (OpenCode, Pi)",
6
6
  "license": "MIT",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@clack/prompts": "^1.2.0",
26
- "@cortexkit/aft-bridge": "0.28.2",
26
+ "@cortexkit/aft-bridge": "0.29.1",
27
27
  "comment-json": "^4.6.2"
28
28
  },
29
29
  "devDependencies": {