@cortexkit/aft-opencode 0.15.4 → 0.16.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.
package/dist/bridge.d.ts CHANGED
@@ -3,6 +3,12 @@
3
3
  * Returns: negative if a < b, 0 if equal, positive if a > b.
4
4
  */
5
5
  export declare function compareSemver(a: string, b: string): number;
6
+ interface ConfigureWarningsContext {
7
+ projectRoot: string;
8
+ sessionId?: string;
9
+ client?: unknown;
10
+ warnings: unknown[];
11
+ }
6
12
  export interface BridgeOptions {
7
13
  /** Request timeout in milliseconds. Default: 30000 */
8
14
  timeoutMs?: number;
@@ -12,6 +18,12 @@ export interface BridgeOptions {
12
18
  minVersion?: string;
13
19
  /** Called when binary version is older than minVersion. Receives (binaryVersion, minVersion). */
14
20
  onVersionMismatch?: (binaryVersion: string, minVersion: string) => void;
21
+ /** Called after the first successful configure returns user-visible warnings. */
22
+ onConfigureWarnings?: (context: ConfigureWarningsContext) => void | Promise<void>;
23
+ }
24
+ interface SendOptions {
25
+ timeoutMs?: number;
26
+ configureWarningClient?: unknown;
15
27
  }
16
28
  /**
17
29
  * Manages a persistent `aft` child process, communicating via NDJSON over
@@ -39,6 +51,7 @@ export declare class BinaryBridge {
39
51
  private configOverrides;
40
52
  private minVersion;
41
53
  private onVersionMismatch;
54
+ private onConfigureWarnings;
42
55
  private restartResetTimer;
43
56
  constructor(binaryPath: string, cwd: string, options?: BridgeOptions, configOverrides?: Record<string, unknown>);
44
57
  /** Number of times the binary has been restarted after a crash. */
@@ -49,9 +62,8 @@ export declare class BinaryBridge {
49
62
  * Send a command to the binary and return the parsed response.
50
63
  * Lazy-spawns the binary on first call.
51
64
  */
52
- send(command: string, params?: Record<string, unknown>, options?: {
53
- timeoutMs?: number;
54
- }): Promise<Record<string, unknown>>;
65
+ send(command: string, params?: Record<string, unknown>, options?: SendOptions): Promise<Record<string, unknown>>;
66
+ private deliverConfigureWarnings;
55
67
  /** Kill the child process and reject all pending requests. */
56
68
  shutdown(): Promise<void>;
57
69
  /** Query binary version and compare against minVersion. Calls onVersionMismatch if outdated. */
@@ -72,4 +84,5 @@ export declare class BinaryBridge {
72
84
  private scheduleRestartCountReset;
73
85
  private clearRestartResetTimer;
74
86
  }
87
+ export {};
75
88
  //# sourceMappingURL=bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAkC1D;AA4CD,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qGAAqG;IACrG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAiB;IACzD,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAM;IAE7C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,YAAY,CAAM;IAC1B,sEAAsE;IACtE,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,iBAAiB,CAA8B;IACvD,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,iBAAiB,CAAoE;IAC7F,OAAO,CAAC,iBAAiB,CAA8C;gBAGrE,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAW3C,mEAAmE;IACnE,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,oDAAoD;IACpD,OAAO,IAAI,OAAO;IAIlB;;;OAGG;IACG,IAAI,CACR,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC/B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAgGnC,8DAA8D;IACxD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B/B,gGAAgG;YAClF,YAAY;IAoB1B,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,YAAY;IAkHpB,OAAO,CAAC,cAAc;IAOtB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,YAAY;IAkCpB,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,WAAW;IAgDnB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,sBAAsB;CAM/B"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAkC1D;AA4CD,UAAU,wBAAwB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qGAAqG;IACrG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnF;AAED,UAAU,WAAW;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAiB;IACzD,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAM;IAE7C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,YAAY,CAAM;IAC1B,sEAAsE;IACtE,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,iBAAiB,CAA8B;IACvD,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,iBAAiB,CAAoE;IAC7F,OAAO,CAAC,mBAAmB,CAEb;IACd,OAAO,CAAC,iBAAiB,CAA8C;gBAGrE,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAY3C,mEAAmE;IACnE,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,oDAAoD;IACpD,OAAO,IAAI,OAAO;IAIlB;;;OAGG;IACG,IAAI,CACR,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAiGrB,wBAAwB;IAuBtC,8DAA8D;IACxD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B/B,gGAAgG;YAClF,YAAY;IAoB1B,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,YAAY;IAkHpB,OAAO,CAAC,cAAc;IAOtB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,YAAY;IAkCpB,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,WAAW;IAgDnB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,sBAAsB;CAM/B"}
package/dist/config.d.ts CHANGED
@@ -1,4 +1,14 @@
1
1
  import { z } from "zod";
2
+ export declare const LspServerSchema: z.ZodObject<{
3
+ extensions: z.ZodArray<z.ZodString>;
4
+ binary: z.ZodString;
5
+ args: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
6
+ root_markers: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
7
+ disabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
8
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
9
+ initialization_options: z.ZodOptional<z.ZodUnknown>;
10
+ id: z.ZodString;
11
+ }, z.core.$strip>;
2
12
  export declare const AftConfigSchema: z.ZodObject<{
3
13
  format_on_edit: z.ZodOptional<z.ZodBoolean>;
4
14
  validate_on_edit: z.ZodOptional<z.ZodEnum<{
@@ -36,6 +46,24 @@ export declare const AftConfigSchema: z.ZodObject<{
36
46
  restrict_to_project_root: z.ZodOptional<z.ZodBoolean>;
37
47
  experimental_search_index: z.ZodOptional<z.ZodBoolean>;
38
48
  experimental_semantic_search: z.ZodOptional<z.ZodBoolean>;
49
+ experimental_lsp_ty: z.ZodOptional<z.ZodBoolean>;
50
+ lsp: z.ZodOptional<z.ZodObject<{
51
+ servers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
52
+ extensions: z.ZodArray<z.ZodString>;
53
+ binary: z.ZodString;
54
+ args: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
55
+ root_markers: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
56
+ disabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
57
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
58
+ initialization_options: z.ZodOptional<z.ZodUnknown>;
59
+ }, z.core.$strip>>>;
60
+ disabled: z.ZodOptional<z.ZodArray<z.ZodString>>;
61
+ python: z.ZodOptional<z.ZodEnum<{
62
+ pyright: "pyright";
63
+ ty: "ty";
64
+ auto: "auto";
65
+ }>>;
66
+ }, z.core.$strip>>;
39
67
  url_fetch_allow_private: z.ZodOptional<z.ZodBoolean>;
40
68
  semantic: z.ZodOptional<z.ZodObject<{
41
69
  backend: z.ZodOptional<z.ZodEnum<{
@@ -52,6 +80,23 @@ export declare const AftConfigSchema: z.ZodObject<{
52
80
  max_callgraph_files: z.ZodOptional<z.ZodNumber>;
53
81
  }, z.core.$strip>;
54
82
  export type AftConfig = z.infer<typeof AftConfigSchema>;
83
+ export type LspServerConfig = z.infer<typeof LspServerSchema>;
84
+ export interface ConfigureLspServer {
85
+ id: string;
86
+ extensions: string[];
87
+ binary: string;
88
+ args: string[];
89
+ root_markers: string[];
90
+ disabled: boolean;
91
+ env?: Record<string, string>;
92
+ initialization_options?: unknown;
93
+ }
94
+ export interface ConfigureLspOverrides {
95
+ experimental_lsp_ty?: boolean;
96
+ lsp_servers?: ConfigureLspServer[];
97
+ disabled_lsp?: string[];
98
+ }
99
+ export declare function resolveLspConfigForConfigure(config: AftConfig): ConfigureLspOverrides;
55
100
  /**
56
101
  * Load AFT config using the same two-level pattern as oh-my-opencode:
57
102
  *
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA+CxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmD1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AA2KxD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAiCjE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmExB,eAAO,MAAM,eAAe;;;;;;;;;iBAE1B,CAAC;AAQH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuD1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAMD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,GAAG,qBAAqB,CAgDrF;AAoLD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAiCjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA8GlD;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAA,MAAM,MAAM,EAAE,MAwXb,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA+HlD;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAA,MAAM,MAAM,EAAE,MAwYb,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js CHANGED
@@ -21443,6 +21443,26 @@ var SemanticConfigSchema = exports_external.object({
21443
21443
  timeout_ms: exports_external.number().int().positive().optional(),
21444
21444
  max_batch_size: exports_external.number().int().positive().optional()
21445
21445
  });
21446
+ var LspExtensionSchema = exports_external.string().trim().min(1).refine((value) => value.replace(/^\.+/, "").length > 0, {
21447
+ message: "Extension must include characters other than leading dots"
21448
+ });
21449
+ var LspServerEntrySchema = exports_external.object({
21450
+ extensions: exports_external.array(LspExtensionSchema).min(1),
21451
+ binary: exports_external.string().trim().min(1),
21452
+ args: exports_external.array(exports_external.string()).optional().default([]),
21453
+ root_markers: exports_external.array(exports_external.string().trim().min(1)).optional().default([".git"]),
21454
+ disabled: exports_external.boolean().optional().default(false),
21455
+ env: exports_external.record(exports_external.string().min(1), exports_external.string()).optional(),
21456
+ initialization_options: exports_external.unknown().optional()
21457
+ });
21458
+ var LspServerSchema = LspServerEntrySchema.extend({
21459
+ id: exports_external.string().trim().min(1)
21460
+ });
21461
+ var LspConfigSchema = exports_external.object({
21462
+ servers: exports_external.record(exports_external.string().trim().min(1), LspServerEntrySchema).optional(),
21463
+ disabled: exports_external.array(exports_external.string().trim().min(1)).optional(),
21464
+ python: exports_external.enum(["pyright", "ty", "auto"]).optional()
21465
+ });
21446
21466
  var AftConfigSchema = exports_external.object({
21447
21467
  format_on_edit: exports_external.boolean().optional(),
21448
21468
  validate_on_edit: exports_external.enum(["syntax", "full"]).optional(),
@@ -21454,10 +21474,59 @@ var AftConfigSchema = exports_external.object({
21454
21474
  restrict_to_project_root: exports_external.boolean().optional(),
21455
21475
  experimental_search_index: exports_external.boolean().optional(),
21456
21476
  experimental_semantic_search: exports_external.boolean().optional(),
21477
+ experimental_lsp_ty: exports_external.boolean().optional(),
21478
+ lsp: LspConfigSchema.optional(),
21457
21479
  url_fetch_allow_private: exports_external.boolean().optional(),
21458
21480
  semantic: SemanticConfigSchema.optional(),
21459
21481
  max_callgraph_files: exports_external.number().int().positive().optional()
21460
21482
  });
21483
+ function normalizeLspExtension(extension) {
21484
+ return extension.trim().replace(/^\.+/, "");
21485
+ }
21486
+ function resolveLspConfigForConfigure(config2) {
21487
+ const overrides = {};
21488
+ const disabled = new Set(config2.lsp?.disabled ?? []);
21489
+ let experimentalTy = config2.experimental_lsp_ty;
21490
+ switch (config2.lsp?.python ?? "auto") {
21491
+ case "ty":
21492
+ experimentalTy = true;
21493
+ disabled.add("pyright");
21494
+ break;
21495
+ case "pyright":
21496
+ experimentalTy = false;
21497
+ disabled.add("ty");
21498
+ break;
21499
+ case "auto":
21500
+ break;
21501
+ }
21502
+ if (experimentalTy !== undefined) {
21503
+ overrides.experimental_lsp_ty = experimentalTy;
21504
+ }
21505
+ const servers = Object.entries(config2.lsp?.servers ?? {}).map(([id, server]) => {
21506
+ const entry = {
21507
+ id,
21508
+ extensions: server.extensions.map(normalizeLspExtension),
21509
+ binary: server.binary,
21510
+ args: server.args,
21511
+ root_markers: server.root_markers,
21512
+ disabled: server.disabled
21513
+ };
21514
+ if (server.env && Object.keys(server.env).length > 0) {
21515
+ entry.env = server.env;
21516
+ }
21517
+ if (server.initialization_options !== undefined) {
21518
+ entry.initialization_options = server.initialization_options;
21519
+ }
21520
+ return entry;
21521
+ });
21522
+ if (servers.length > 0) {
21523
+ overrides.lsp_servers = servers;
21524
+ }
21525
+ if (disabled.size > 0) {
21526
+ overrides.disabled_lsp = [...disabled];
21527
+ }
21528
+ return overrides;
21529
+ }
21461
21530
  function detectConfigFile(basePath) {
21462
21531
  const jsoncPath = `${basePath}.jsonc`;
21463
21532
  const jsonPath = `${basePath}.json`;
@@ -21540,12 +21609,21 @@ function mergeConfigs(base, override) {
21540
21609
  const formatter = { ...base.formatter, ...override.formatter };
21541
21610
  const checker = { ...base.checker, ...override.checker };
21542
21611
  const semantic = mergeSemanticConfig(base.semantic, override.semantic);
21612
+ const lspServers = { ...base.lsp?.servers, ...override.lsp?.servers };
21613
+ const disabledLsp = [...base.lsp?.disabled ?? [], ...override.lsp?.disabled ?? []];
21614
+ const lsp = {
21615
+ ...base.lsp,
21616
+ ...override.lsp,
21617
+ ...Object.keys(lspServers).length > 0 ? { servers: lspServers } : {},
21618
+ ...disabledLsp.length > 0 ? { disabled: [...new Set(disabledLsp)] } : {}
21619
+ };
21543
21620
  const { semantic: _stripSemantic, ...safeOverride } = override;
21544
21621
  return {
21545
21622
  ...base,
21546
21623
  ...safeOverride,
21547
21624
  ...Object.keys(formatter).length > 0 ? { formatter } : {},
21548
21625
  ...Object.keys(checker).length > 0 ? { checker } : {},
21626
+ ...Object.values(lsp).some((value) => value !== undefined) ? { lsp } : {},
21549
21627
  semantic,
21550
21628
  ...disabledTools.length > 0 ? { disabled_tools: [...new Set(disabledTools)] } : {}
21551
21629
  };
@@ -21814,6 +21892,7 @@ var AFT_MARKER = "\uD83D\uDD27 AFT:";
21814
21892
  var FEATURE_MARKER = `${AFT_MARKER} New in`;
21815
21893
  var WARNING_MARKER = `${AFT_MARKER} \u26A0\uFE0F`;
21816
21894
  var STATUS_MARKER = `${AFT_MARKER} \u2705`;
21895
+ var WARNED_TOOLS_FILE = "warned_tools.json";
21817
21896
  function getDesktopStatePath() {
21818
21897
  const os2 = platform();
21819
21898
  const home = homedir3();
@@ -21958,6 +22037,86 @@ async function sendFeatureAnnouncement(opts, version2, features, storageDir) {
21958
22037
  } catch {}
21959
22038
  }
21960
22039
  }
22040
+ function readWarnedTools(storageDir) {
22041
+ try {
22042
+ const warnedToolsPath = join4(storageDir, WARNED_TOOLS_FILE);
22043
+ if (!existsSync3(warnedToolsPath))
22044
+ return {};
22045
+ const parsed = JSON.parse(readFileSync2(warnedToolsPath, "utf-8"));
22046
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
22047
+ return {};
22048
+ const warned = {};
22049
+ for (const [key, version2] of Object.entries(parsed)) {
22050
+ if (typeof version2 === "string") {
22051
+ warned[key] = version2;
22052
+ }
22053
+ }
22054
+ return warned;
22055
+ } catch {
22056
+ return {};
22057
+ }
22058
+ }
22059
+ function writeWarnedTools(storageDir, warned) {
22060
+ try {
22061
+ mkdirSync2(storageDir, { recursive: true });
22062
+ const warnedToolsPath = join4(storageDir, WARNED_TOOLS_FILE);
22063
+ writeFileSync(warnedToolsPath, `${JSON.stringify(warned, null, 2)}
22064
+ `);
22065
+ } catch {}
22066
+ }
22067
+ function warningKey(warning, projectRoot) {
22068
+ return [
22069
+ projectRoot ?? "_",
22070
+ warning.kind,
22071
+ warning.language ?? warning.server ?? "_",
22072
+ warning.tool ?? warning.binary ?? "_",
22073
+ warning.hint
22074
+ ].map((part) => encodeURIComponent(part)).join(":");
22075
+ }
22076
+ function warningTitle(warning) {
22077
+ switch (warning.kind) {
22078
+ case "formatter_not_installed":
22079
+ return "Formatter is not installed";
22080
+ case "checker_not_installed":
22081
+ return "Checker is not installed";
22082
+ case "lsp_binary_missing":
22083
+ return "LSP binary is missing";
22084
+ }
22085
+ }
22086
+ function formatConfigureWarning(warning) {
22087
+ const details = [];
22088
+ if (warning.language)
22089
+ details.push(`language: ${warning.language}`);
22090
+ if (warning.server)
22091
+ details.push(`server: ${warning.server}`);
22092
+ if (warning.tool)
22093
+ details.push(`tool: ${warning.tool}`);
22094
+ if (warning.binary && warning.binary !== warning.tool) {
22095
+ details.push(`binary: ${warning.binary}`);
22096
+ }
22097
+ const suffix = details.length > 0 ? ` (${details.join(", ")})` : "";
22098
+ return `${WARNING_MARKER} ${warningTitle(warning)}${suffix}
22099
+ ${warning.hint}`;
22100
+ }
22101
+ async function deliverConfigureWarnings(opts, warnings) {
22102
+ if (warnings.length === 0)
22103
+ return;
22104
+ const warned = readWarnedTools(opts.storageDir);
22105
+ let changed = false;
22106
+ for (const warning of warnings) {
22107
+ const key = warningKey(warning, opts.projectRoot);
22108
+ if (Object.hasOwn(warned, key))
22109
+ continue;
22110
+ const delivered = await sendIgnoredMessage(opts.client, opts.sessionId, formatConfigureWarning(warning));
22111
+ if (!delivered)
22112
+ continue;
22113
+ warned[key] = opts.pluginVersion;
22114
+ changed = true;
22115
+ }
22116
+ if (changed) {
22117
+ writeWarnedTools(opts.storageDir, warned);
22118
+ }
22119
+ }
21961
22120
  async function cleanupWarnings(opts) {
21962
22121
  if (isTuiMode())
21963
22122
  return;
@@ -22279,6 +22438,7 @@ class BinaryBridge {
22279
22438
  configOverrides;
22280
22439
  minVersion;
22281
22440
  onVersionMismatch;
22441
+ onConfigureWarnings;
22282
22442
  restartResetTimer = null;
22283
22443
  constructor(binaryPath, cwd, options, configOverrides) {
22284
22444
  this.binaryPath = binaryPath;
@@ -22288,6 +22448,7 @@ class BinaryBridge {
22288
22448
  this.configOverrides = clampSemanticTimeout(configOverrides ?? {}, this.timeoutMs);
22289
22449
  this.minVersion = options?.minVersion;
22290
22450
  this.onVersionMismatch = options?.onVersionMismatch;
22451
+ this.onConfigureWarnings = options?.onConfigureWarnings;
22291
22452
  }
22292
22453
  get restartCount() {
22293
22454
  return this._restartCount;
@@ -22312,6 +22473,7 @@ class BinaryBridge {
22312
22473
  if (configResult.success === false) {
22313
22474
  throw new Error(`[aft-plugin] Configure failed: ${configResult.message ?? "unknown error"}`);
22314
22475
  }
22476
+ await this.deliverConfigureWarnings(configResult, params, options);
22315
22477
  await this.checkVersion();
22316
22478
  if (!this.isAlive()) {
22317
22479
  throw new Error(`[aft-plugin] Bridge died during version check. Check logs: ${getLogFilePath()}`);
@@ -22356,6 +22518,23 @@ class BinaryBridge {
22356
22518
  });
22357
22519
  });
22358
22520
  }
22521
+ async deliverConfigureWarnings(configResult, params, options) {
22522
+ if (!this.onConfigureWarnings || !Array.isArray(configResult.warnings))
22523
+ return;
22524
+ if (configResult.warnings.length === 0)
22525
+ return;
22526
+ try {
22527
+ const sessionId = typeof params.session_id === "string" ? params.session_id : undefined;
22528
+ await this.onConfigureWarnings({
22529
+ projectRoot: this.cwd,
22530
+ sessionId,
22531
+ client: options?.configureWarningClient,
22532
+ warnings: configResult.warnings
22533
+ });
22534
+ } catch (err) {
22535
+ warn(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
22536
+ }
22537
+ }
22359
22538
  async shutdown() {
22360
22539
  this._shuttingDown = true;
22361
22540
  this.clearRestartResetTimer();
@@ -22598,7 +22777,8 @@ class BridgePool {
22598
22777
  timeoutMs: options.timeoutMs,
22599
22778
  maxRestarts: options.maxRestarts,
22600
22779
  minVersion: options.minVersion,
22601
- onVersionMismatch: options.onVersionMismatch
22780
+ onVersionMismatch: options.onVersionMismatch,
22781
+ onConfigureWarnings: options.onConfigureWarnings
22602
22782
  };
22603
22783
  this.configOverrides = configOverrides;
22604
22784
  if (Number.isFinite(this.idleTimeoutMs)) {
@@ -23538,7 +23718,11 @@ function callBridge(ctx, runtime, command, params = {}) {
23538
23718
  merged.session_id = runtime.sessionID;
23539
23719
  }
23540
23720
  const timeoutMs = timeoutForCommand(command);
23541
- return bridgeFor(ctx, runtime).send(command, merged, timeoutMs !== undefined ? { timeoutMs } : undefined);
23721
+ const sendOptions = {
23722
+ ...timeoutMs !== undefined ? { timeoutMs } : {},
23723
+ configureWarningClient: ctx.client
23724
+ };
23725
+ return bridgeFor(ctx, runtime).send(command, merged, Object.keys(sendOptions).length > 0 ? sendOptions : undefined);
23542
23726
  }
23543
23727
 
23544
23728
  // src/tools/permissions.ts
@@ -23654,8 +23838,24 @@ function astTools(ctx) {
23654
23838
  const filesSearched = data.files_searched ?? 0;
23655
23839
  const filesWithMatches = data.files_with_matches ?? filesSearched;
23656
23840
  let output;
23657
- if (matchCount === 0) {
23841
+ if (data.no_files_matched_scope) {
23842
+ output = "No files matched the scope (paths/globs resolved to zero files)";
23843
+ if (data.scope_warnings && data.scope_warnings.length > 0) {
23844
+ output += `
23845
+
23846
+ Scope warnings:
23847
+ ${data.scope_warnings.map((w) => ` ${w}`).join(`
23848
+ `)}`;
23849
+ }
23850
+ } else if (matchCount === 0) {
23658
23851
  output = `No matches found (searched ${filesSearched} files)`;
23852
+ if (data.scope_warnings && data.scope_warnings.length > 0) {
23853
+ output += `
23854
+
23855
+ Scope warnings:
23856
+ ${data.scope_warnings.map((w) => ` ${w}`).join(`
23857
+ `)}`;
23858
+ }
23659
23859
  const hint = getEmptyResultHint(args.pattern, args.lang);
23660
23860
  if (hint) {
23661
23861
  output += `
@@ -23743,8 +23943,24 @@ ${hint}`;
23743
23943
  const filesSearched = data.files_searched ?? data.total_files ?? 0;
23744
23944
  const filesWithMatches = data.files_with_matches ?? data.total_files ?? filesSearched;
23745
23945
  let output;
23746
- if (matchCount === 0) {
23946
+ if (data.no_files_matched_scope) {
23947
+ output = "No files matched the scope (paths/globs resolved to zero files)";
23948
+ if (data.scope_warnings && data.scope_warnings.length > 0) {
23949
+ output += `
23950
+
23951
+ Scope warnings:
23952
+ ${data.scope_warnings.map((w) => ` ${w}`).join(`
23953
+ `)}`;
23954
+ }
23955
+ } else if (matchCount === 0) {
23747
23956
  output = `No matches found (searched ${filesSearched} files)`;
23957
+ if (data.scope_warnings && data.scope_warnings.length > 0) {
23958
+ output += `
23959
+
23960
+ Scope warnings:
23961
+ ${data.scope_warnings.map((w) => ` ${w}`).join(`
23962
+ `)}`;
23963
+ }
23748
23964
  } else {
23749
23965
  output = isDryRun ? `[DRY RUN] Would replace ${matchCount} match(es) in ${filesWithMatches} file(s) (${filesSearched} searched)
23750
23966
 
@@ -24048,44 +24264,181 @@ function buildUnifiedDiff(fp, before, after) {
24048
24264
  `);
24049
24265
  const afterLines = after.split(`
24050
24266
  `);
24051
- let diff = `Index: ${fp}
24267
+ const ops = diffLines(beforeLines, afterLines);
24268
+ if (ops.every((op) => op.tag === "eq")) {
24269
+ return `Index: ${fp}
24052
24270
  ===================================================================
24053
24271
  --- ${fp}
24054
24272
  +++ ${fp}
24055
24273
  `;
24056
- let firstChange = -1;
24057
- let lastChange = -1;
24058
- const maxLen = Math.max(beforeLines.length, afterLines.length);
24059
- for (let i = 0;i < maxLen; i++) {
24060
- if ((beforeLines[i] ?? "") !== (afterLines[i] ?? "")) {
24061
- if (firstChange === -1)
24062
- firstChange = i;
24063
- lastChange = i;
24064
- }
24065
- }
24066
- if (firstChange === -1)
24067
- return diff;
24068
- const ctxStart = Math.max(0, firstChange - 2);
24069
- const ctxEnd = Math.min(maxLen - 1, lastChange + 2);
24070
- diff += `@@ -${ctxStart + 1},${Math.min(beforeLines.length, ctxEnd + 1) - ctxStart} +${ctxStart + 1},${Math.min(afterLines.length, ctxEnd + 1) - ctxStart} @@
24071
- `;
24072
- for (let i = ctxStart;i <= ctxEnd; i++) {
24073
- const bl = i < beforeLines.length ? beforeLines[i] : undefined;
24074
- const al = i < afterLines.length ? afterLines[i] : undefined;
24075
- if (bl === al) {
24076
- diff += ` ${bl}
24274
+ }
24275
+ const CONTEXT = 3;
24276
+ const HUNK_GAP = CONTEXT * 2;
24277
+ const hunks = groupIntoHunks(ops, CONTEXT, HUNK_GAP, beforeLines.length, afterLines.length);
24278
+ let diff = `Index: ${fp}
24279
+ ===================================================================
24280
+ --- ${fp}
24281
+ +++ ${fp}
24077
24282
  `;
24078
- } else {
24079
- if (bl !== undefined)
24080
- diff += `-${bl}
24283
+ for (const hunk of hunks) {
24284
+ diff += `@@ -${hunk.beforeStart},${hunk.beforeCount} +${hunk.afterStart},${hunk.afterCount} @@
24081
24285
  `;
24082
- if (al !== undefined)
24083
- diff += `+${al}
24286
+ for (const line of hunk.lines) {
24287
+ diff += `${line}
24084
24288
  `;
24085
24289
  }
24086
24290
  }
24087
24291
  return diff;
24088
24292
  }
24293
+ function lineCount(content) {
24294
+ if (content.length === 0)
24295
+ return 0;
24296
+ const parts = content.split(`
24297
+ `);
24298
+ if (parts[parts.length - 1] === "")
24299
+ parts.pop();
24300
+ return parts.length;
24301
+ }
24302
+ function countDiffLines(before, after) {
24303
+ const beforeLines = before.split(`
24304
+ `);
24305
+ const afterLines = after.split(`
24306
+ `);
24307
+ const ops = diffLines(beforeLines, afterLines);
24308
+ let additions = 0;
24309
+ let deletions = 0;
24310
+ for (const op of ops) {
24311
+ if (op.tag === "ins")
24312
+ additions++;
24313
+ else if (op.tag === "del")
24314
+ deletions++;
24315
+ }
24316
+ return { additions, deletions };
24317
+ }
24318
+ function diffLines(a, b) {
24319
+ const n = a.length;
24320
+ const m = b.length;
24321
+ const dp = new Uint32Array((n + 1) * (m + 1));
24322
+ const w = m + 1;
24323
+ for (let i2 = 1;i2 <= n; i2++) {
24324
+ for (let j2 = 1;j2 <= m; j2++) {
24325
+ if (a[i2 - 1] === b[j2 - 1]) {
24326
+ dp[i2 * w + j2] = dp[(i2 - 1) * w + (j2 - 1)] + 1;
24327
+ } else {
24328
+ const up = dp[(i2 - 1) * w + j2];
24329
+ const left = dp[i2 * w + (j2 - 1)];
24330
+ dp[i2 * w + j2] = up >= left ? up : left;
24331
+ }
24332
+ }
24333
+ }
24334
+ const ops = [];
24335
+ let i = n;
24336
+ let j = m;
24337
+ while (i > 0 && j > 0) {
24338
+ if (a[i - 1] === b[j - 1]) {
24339
+ ops.push({ tag: "eq", beforeIdx: i - 1, afterIdx: j - 1, line: a[i - 1] });
24340
+ i--;
24341
+ j--;
24342
+ } else if (dp[(i - 1) * w + j] >= dp[i * w + (j - 1)]) {
24343
+ ops.push({ tag: "del", beforeIdx: i - 1, line: a[i - 1] });
24344
+ i--;
24345
+ } else {
24346
+ ops.push({ tag: "ins", afterIdx: j - 1, line: b[j - 1] });
24347
+ j--;
24348
+ }
24349
+ }
24350
+ while (i > 0) {
24351
+ ops.push({ tag: "del", beforeIdx: i - 1, line: a[i - 1] });
24352
+ i--;
24353
+ }
24354
+ while (j > 0) {
24355
+ ops.push({ tag: "ins", afterIdx: j - 1, line: b[j - 1] });
24356
+ j--;
24357
+ }
24358
+ ops.reverse();
24359
+ return ops;
24360
+ }
24361
+ function groupIntoHunks(ops, context, gap, beforeLen, afterLen) {
24362
+ const changeIdx = [];
24363
+ for (let k = 0;k < ops.length; k++) {
24364
+ if (ops[k].tag !== "eq")
24365
+ changeIdx.push(k);
24366
+ }
24367
+ if (changeIdx.length === 0)
24368
+ return [];
24369
+ const ranges = [];
24370
+ for (const idx of changeIdx) {
24371
+ const start = Math.max(0, idx - context);
24372
+ const end = Math.min(ops.length - 1, idx + context);
24373
+ if (ranges.length > 0 && start <= ranges[ranges.length - 1][1] + gap) {
24374
+ ranges[ranges.length - 1][1] = Math.max(ranges[ranges.length - 1][1], end);
24375
+ } else {
24376
+ ranges.push([start, end]);
24377
+ }
24378
+ }
24379
+ const hunks = [];
24380
+ for (const [start, end] of ranges) {
24381
+ let beforeStart = -1;
24382
+ let afterStart = -1;
24383
+ let beforeCount = 0;
24384
+ let afterCount = 0;
24385
+ const lines = [];
24386
+ for (let k = start;k <= end; k++) {
24387
+ const op = ops[k];
24388
+ if (op.tag === "eq") {
24389
+ if (beforeStart === -1)
24390
+ beforeStart = op.beforeIdx + 1;
24391
+ if (afterStart === -1)
24392
+ afterStart = op.afterIdx + 1;
24393
+ beforeCount++;
24394
+ afterCount++;
24395
+ lines.push(` ${op.line}`);
24396
+ } else if (op.tag === "del") {
24397
+ if (beforeStart === -1)
24398
+ beforeStart = op.beforeIdx + 1;
24399
+ if (afterStart === -1) {
24400
+ afterStart = inferAfterStart(ops, k, afterLen);
24401
+ }
24402
+ beforeCount++;
24403
+ lines.push(`-${op.line}`);
24404
+ } else {
24405
+ if (afterStart === -1)
24406
+ afterStart = op.afterIdx + 1;
24407
+ if (beforeStart === -1) {
24408
+ beforeStart = inferBeforeStart(ops, k, beforeLen);
24409
+ }
24410
+ afterCount++;
24411
+ lines.push(`+${op.line}`);
24412
+ }
24413
+ }
24414
+ if (beforeCount === 0)
24415
+ beforeStart = 0;
24416
+ if (afterCount === 0)
24417
+ afterStart = 0;
24418
+ hunks.push({ beforeStart, beforeCount, afterStart, afterCount, lines });
24419
+ }
24420
+ return hunks;
24421
+ }
24422
+ function inferAfterStart(ops, from, afterLen) {
24423
+ for (let k = from;k < ops.length; k++) {
24424
+ const op = ops[k];
24425
+ if (op.tag === "eq")
24426
+ return op.afterIdx + 1;
24427
+ if (op.tag === "ins")
24428
+ return op.afterIdx + 1;
24429
+ }
24430
+ return afterLen;
24431
+ }
24432
+ function inferBeforeStart(ops, from, beforeLen) {
24433
+ for (let k = from;k < ops.length; k++) {
24434
+ const op = ops[k];
24435
+ if (op.tag === "eq")
24436
+ return op.beforeIdx + 1;
24437
+ if (op.tag === "del")
24438
+ return op.beforeIdx + 1;
24439
+ }
24440
+ return beforeLen;
24441
+ }
24089
24442
  var z3 = tool3.schema;
24090
24443
  var READ_DESCRIPTION = `Read file contents or list directory entries.
24091
24444
 
@@ -24592,15 +24945,24 @@ function createApplyPatchTool(ctx) {
24592
24945
  switch (hunk.type) {
24593
24946
  case "add": {
24594
24947
  try {
24595
- await callBridge(ctx, context, "write", {
24596
- file: filePath,
24597
- content: hunk.contents.endsWith(`
24948
+ const content = hunk.contents.endsWith(`
24598
24949
  `) ? hunk.contents : `${hunk.contents}
24599
- `,
24950
+ `;
24951
+ const writeResult = await callBridge(ctx, context, "write", {
24952
+ file: filePath,
24953
+ content,
24600
24954
  create_dirs: true,
24601
- diagnostics: true
24955
+ diagnostics: true,
24956
+ include_diff: true
24957
+ });
24958
+ const wrDiff = writeResult.diff;
24959
+ perFileDiffs.push({
24960
+ filePath,
24961
+ before: "",
24962
+ after: hunk.contents,
24963
+ additions: wrDiff?.additions ?? lineCount(content),
24964
+ deletions: wrDiff?.deletions ?? 0
24602
24965
  });
24603
- perFileDiffs.push({ filePath, before: "", after: hunk.contents });
24604
24966
  results.push(`Created ${hunk.path}`);
24605
24967
  } catch (e) {
24606
24968
  patchFailed = true;
@@ -24612,7 +24974,13 @@ function createApplyPatchTool(ctx) {
24612
24974
  try {
24613
24975
  const before = await fs3.promises.readFile(filePath, "utf-8").catch(() => "");
24614
24976
  await callBridge(ctx, context, "delete_file", { file: filePath });
24615
- perFileDiffs.push({ filePath, before, after: "" });
24977
+ perFileDiffs.push({
24978
+ filePath,
24979
+ before,
24980
+ after: "",
24981
+ additions: 0,
24982
+ deletions: lineCount(before)
24983
+ });
24616
24984
  results.push(`Deleted ${hunk.path}`);
24617
24985
  } catch (e) {
24618
24986
  patchFailed = true;
@@ -24629,7 +24997,8 @@ function createApplyPatchTool(ctx) {
24629
24997
  file: targetPath,
24630
24998
  content: newContent,
24631
24999
  create_dirs: true,
24632
- diagnostics: true
25000
+ diagnostics: true,
25001
+ include_diff: true
24633
25002
  });
24634
25003
  const diags = writeResult.lsp_diagnostics;
24635
25004
  if (diags && diags.length > 0) {
@@ -24643,7 +25012,19 @@ LSP errors detected in ${relPath}, please fix:
24643
25012
  ${diagLines}`);
24644
25013
  }
24645
25014
  }
24646
- perFileDiffs.push({ filePath, before: original, after: newContent });
25015
+ const wrDiff = writeResult.diff;
25016
+ const isMove = Boolean(hunk.move_path);
25017
+ const { additions, deletions } = isMove || wrDiff?.additions === undefined || wrDiff.deletions === undefined ? countDiffLines(original, newContent) : {
25018
+ additions: wrDiff.additions,
25019
+ deletions: wrDiff.deletions
25020
+ };
25021
+ perFileDiffs.push({
25022
+ filePath,
25023
+ before: original,
25024
+ after: newContent,
25025
+ additions,
25026
+ deletions
25027
+ });
24647
25028
  if (hunk.move_path) {
24648
25029
  await callBridge(ctx, context, "delete_file", { file: filePath });
24649
25030
  results.push(`Updated and moved ${hunk.path} \u2192 ${hunk.move_path}`);
@@ -24692,20 +25073,6 @@ ${diagLines}`);
24692
25073
  const callID = getCallID(context);
24693
25074
  if (callID) {
24694
25075
  const diffByPath = new Map(perFileDiffs.map((d) => [d.filePath, d]));
24695
- const countAddDel = (patch) => {
24696
- let additions = 0;
24697
- let deletions = 0;
24698
- for (const line of patch.split(`
24699
- `)) {
24700
- if (line.startsWith("---") || line.startsWith("+++"))
24701
- continue;
24702
- if (line.startsWith("+"))
24703
- additions++;
24704
- else if (line.startsWith("-"))
24705
- deletions++;
24706
- }
24707
- return { additions, deletions };
24708
- };
24709
25076
  const files = hunks.map((h) => {
24710
25077
  const filePath = path4.resolve(context.directory, h.path);
24711
25078
  const rawMovePath = h.type === "update" ? h.move_path : undefined;
@@ -24714,7 +25081,8 @@ ${diagLines}`);
24714
25081
  const relPath = path4.relative(context.worktree, displayPath);
24715
25082
  const diffEntry = diffByPath.get(filePath);
24716
25083
  const patch = diffEntry ? buildUnifiedDiff(displayPath, diffEntry.before, diffEntry.after) : "";
24717
- const { additions, deletions } = countAddDel(patch);
25084
+ const additions = diffEntry?.additions ?? 0;
25085
+ const deletions = diffEntry?.deletions ?? 0;
24718
25086
  const uiType = h.type === "update" && rawMovePath ? "move" : h.type;
24719
25087
  return {
24720
25088
  filePath,
@@ -24907,12 +25275,28 @@ import { tool as tool5 } from "@opencode-ai/plugin";
24907
25275
  var z5 = tool5.schema;
24908
25276
  function lspTools(ctx) {
24909
25277
  const diagnosticsTool = {
24910
- description: "Get errors, warnings, hints from a language server. " + "Returns: { diagnostics: Array<{ file, line, column, end_line, end_column, severity, message, code }>, total: number, files_with_errors: number }.",
25278
+ description: "On-demand LSP file/scope check. Spawns the relevant language server (if " + "registered for the file's extension), opens the document, prefers LSP 3.17 " + "pull diagnostics when supported, and falls back to push + waitMs otherwise. " + "NOT a project-wide type checker \u2014 for full coverage run `tsc --noEmit`, " + "`cargo check`, `pyright`, etc.\n" + `
25279
+ ` + `Returns: {
25280
+ ` + ` diagnostics: Array<{file, line, column, end_line, end_column, severity, message, code, source}>,
25281
+ ` + ` total: number,
25282
+ ` + ` files_with_errors: number,
25283
+ ` + ` complete: boolean, // true = trustable absence; false = partial
25284
+ ` + ` lsp_servers_used: Array<{ // honest per-server status
25285
+ ` + ` server_id, scope: 'file'|'workspace', status: 'pull_ok'|'pull_unchanged'|'push_only'|'no_root_marker (...)' |
25286
+ ` + ` 'binary_not_installed: <name>'|'spawn_failed: ...'|'pull_failed: ...'|'workspace_pull_unsupported'|...
25287
+ ` + ` }>,
25288
+ ` + ` unchecked_files?: string[], // directory mode only \u2014 files we have no info for
25289
+ ` + ` walk_truncated?: boolean, // directory walk hit the 200-file cap
25290
+ ` + ` note?: string // present when no LSP server is registered for the file's extension
25291
+ ` + `}
25292
+ ` + `
25293
+ ` + `**Reading the response honestly:**
25294
+ ` + "- `total: 0, complete: true, lsp_servers_used: [{status: 'pull_ok'}]` \u2192 file is genuinely clean.\n" + "- `total: 0, lsp_servers_used: []` \u2192 no server registered for this extension; nothing was checked.\n" + "- `lsp_servers_used: [{status: 'binary_not_installed: bash-language-server'}]` \u2192 install the server to get diagnostics.\n" + "- `complete: false` (directory mode) \u2192 some files in the directory weren't checked; see `unchecked_files`.",
24911
25295
  args: {
24912
- filePath: z5.string().optional().describe("Path to file to get diagnostics for. Provide 'filePath' for a single file, 'directory' for all files under a path, or omit both for all tracked files"),
24913
- directory: z5.string().optional().describe("Path to directory to get diagnostics for all files under it. Mutually exclusive with 'filePath'"),
24914
- severity: z5.enum(["error", "warning", "information", "hint", "all"]).optional().describe("Filter by severity \u2014 'error', 'warning', 'information', 'hint', 'all' (default: 'all')"),
24915
- waitMs: z5.number().optional().describe("Wait N ms for fresh diagnostics before returning (max 10000, default: 0). Use after edits to let the server re-analyze.")
25296
+ filePath: z5.string().optional().describe("Path to a file to check. Mutually exclusive with 'directory'. Omit both to dump all cached diagnostics."),
25297
+ directory: z5.string().optional().describe("Path to a directory. Returns cached diagnostics + workspace pull from active servers; lists files we have no info for in 'unchecked_files'. Capped at 200 walked files."),
25298
+ severity: z5.enum(["error", "warning", "information", "hint", "all"]).optional().describe("Filter by severity (default: 'all')."),
25299
+ waitMs: z5.number().optional().describe("Wait up to N ms (max 10000, default 0) for push diagnostics to arrive. Only matters for servers that don't support LSP 3.17 pull (bash-language-server, yaml-language-server). Use after an edit to let the server re-analyze.")
24916
25300
  },
24917
25301
  execute: async (args, context) => {
24918
25302
  const filePath = args.filePath || undefined;
@@ -25089,20 +25473,20 @@ function readingTools(ctx) {
25089
25473
  if (response2.success === false) {
25090
25474
  throw new Error(response2.message || "outline failed");
25091
25475
  }
25092
- return response2.text;
25476
+ return formatOutlineText(response2);
25093
25477
  }
25094
25478
  if (Array.isArray(args.files) && args.files.length > 0) {
25095
25479
  const response2 = await callBridge(ctx, context, "outline", { files: args.files });
25096
25480
  if (response2.success === false) {
25097
25481
  throw new Error(response2.message || "outline failed");
25098
25482
  }
25099
- return response2.text;
25483
+ return formatOutlineText(response2);
25100
25484
  }
25101
25485
  const response = await callBridge(ctx, context, "outline", { file: args.filePath });
25102
25486
  if (response.success === false) {
25103
25487
  throw new Error(response.message || "outline failed");
25104
25488
  }
25105
- return response.text;
25489
+ return formatOutlineText(response);
25106
25490
  }
25107
25491
  },
25108
25492
  aft_zoom: {
@@ -25199,6 +25583,20 @@ async function discoverSourceFiles(dir, maxFiles = 200) {
25199
25583
  files.sort();
25200
25584
  return files;
25201
25585
  }
25586
+ function formatOutlineText(response) {
25587
+ const text = response.text ?? "";
25588
+ const skipped = response.skipped_files;
25589
+ if (!skipped || skipped.length === 0) {
25590
+ return text;
25591
+ }
25592
+ const lines = skipped.map(({ file: file2, reason }) => ` ${file2} \u2014 ${reason}`).join(`
25593
+ `);
25594
+ const header = text.length > 0 ? `${text}
25595
+
25596
+ ` : "";
25597
+ return `${header}Skipped ${skipped.length} file(s):
25598
+ ${lines}`;
25599
+ }
25202
25600
 
25203
25601
  // src/tools/refactoring.ts
25204
25602
  import { tool as tool8 } from "@opencode-ai/plugin";
@@ -25539,10 +25937,27 @@ function arg2(schema) {
25539
25937
  }
25540
25938
  function semanticTools(ctx) {
25541
25939
  const searchTool = {
25542
- description: "Search code by meaning using semantic similarity. Use when you don't know the exact name or text \u2014 describe what you're looking for in natural language and get the most relevant symbols, functions, and types.",
25940
+ description: [
25941
+ "Find symbols by concept when grep keywords fall short. Returns ranked code matches with similarity scores.",
25942
+ "",
25943
+ "When to reach for it:",
25944
+ "- Exploring an unfamiliar area: 'where is rate limiting handled', 'how does auth flow work'",
25945
+ "- Concept doesn't appear as a literal string: 'retry logic', 'cache invalidation', 'graceful shutdown'",
25946
+ "- After 2+ grep attempts that came back empty or noisy",
25947
+ "- You know roughly what the function does but not what it's named",
25948
+ "",
25949
+ "When NOT to use:",
25950
+ "- You have a specific symbol name \u2192 use grep",
25951
+ "- You have an error message or stack trace \u2192 use grep",
25952
+ "- You want the file/module structure \u2192 use aft_outline",
25953
+ "- You're following a call chain \u2192 use aft_navigate",
25954
+ "",
25955
+ "Scores below ~0.4 are usually weak matches; treat them as 'maybe relevant' and verify with read."
25956
+ ].join(`
25957
+ `),
25543
25958
  args: {
25544
- query: arg2(exports_external.string().describe("Natural language search query")),
25545
- topK: arg2(exports_external.number().optional().describe("Number of results (default: 10)"))
25959
+ query: arg2(exports_external.string().describe("Concept or capability to find, phrased as a programmer would describe the code. Examples: 'fuzzy match with whitespace tolerance', 'undo backup before edit', 'retry failed network request'.")),
25960
+ topK: arg2(exports_external.number().optional().describe("Number of results (default: 10, max: 100)"))
25546
25961
  },
25547
25962
  execute: async (args, context) => {
25548
25963
  const response = await callBridge(ctx, context, "semantic_search", {
@@ -25691,6 +26106,15 @@ function isTuiMode2() {
25691
26106
  function throwSentinel(command) {
25692
26107
  throw new Error(`${SENTINEL_PREFIX}${command.toUpperCase().replace(/-/g, "_")}_HANDLED__`);
25693
26108
  }
26109
+ function isConfigureWarning(value) {
26110
+ if (!value || typeof value !== "object" || Array.isArray(value))
26111
+ return false;
26112
+ const warning = value;
26113
+ return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing") && typeof warning.hint === "string";
26114
+ }
26115
+ function coerceConfigureWarnings(warnings) {
26116
+ return warnings.filter(isConfigureWarning);
26117
+ }
25694
26118
  async function sendIgnoredMessage2(client, sessionID, text) {
25695
26119
  const typedClient = client;
25696
26120
  const promptInput = {
@@ -25737,6 +26161,7 @@ var plugin = async (input) => {
25737
26161
  configOverrides.experimental_search_index = aftConfig.experimental_search_index;
25738
26162
  if (aftConfig.experimental_semantic_search !== undefined)
25739
26163
  configOverrides.experimental_semantic_search = aftConfig.experimental_semantic_search;
26164
+ Object.assign(configOverrides, resolveLspConfigForConfigure(aftConfig));
25740
26165
  if (aftConfig.semantic !== undefined)
25741
26166
  configOverrides.semantic = aftConfig.semantic;
25742
26167
  if (aftConfig.max_callgraph_files !== undefined)
@@ -25778,6 +26203,20 @@ var plugin = async (input) => {
25778
26203
  }, (err) => {
25779
26204
  error48(`Auto-download failed: ${err.message}. Install manually: cargo install agent-file-tools@${minVersion}`);
25780
26205
  });
26206
+ },
26207
+ onConfigureWarnings: async ({ projectRoot, sessionId, client, warnings }) => {
26208
+ if (!sessionId)
26209
+ return;
26210
+ const validWarnings = coerceConfigureWarnings(warnings);
26211
+ if (validWarnings.length === 0)
26212
+ return;
26213
+ await deliverConfigureWarnings({
26214
+ client: client ?? input.client,
26215
+ sessionId,
26216
+ storageDir: configOverrides.storage_dir,
26217
+ pluginVersion: PLUGIN_VERSION,
26218
+ projectRoot
26219
+ }, validWarnings);
25781
26220
  }
25782
26221
  }, configOverrides);
25783
26222
  const ctx = {
@@ -21,6 +21,21 @@ export interface NotificationOptions {
21
21
  /** Server URL for message deletion (optional — from ctx.serverUrl) */
22
22
  serverUrl?: string;
23
23
  }
24
+ export interface ConfigureWarning {
25
+ kind: "formatter_not_installed" | "checker_not_installed" | "lsp_binary_missing";
26
+ language?: string;
27
+ server?: string;
28
+ tool?: string;
29
+ binary?: string;
30
+ hint: string;
31
+ }
32
+ export interface ConfigureWarningOptions {
33
+ client: unknown;
34
+ sessionId: string;
35
+ storageDir: string;
36
+ pluginVersion: string;
37
+ projectRoot?: string;
38
+ }
24
39
  /**
25
40
  * Send a persistent warning notification.
26
41
  * Desktop: ignored message, cleaned up on next startup when resolved.
@@ -40,6 +55,7 @@ export declare function sendStatus(opts: NotificationOptions, message: string):
40
55
  * TUI: toast with info variant.
41
56
  */
42
57
  export declare function sendFeatureAnnouncement(opts: NotificationOptions, version: string, features: string[], storageDir?: string): Promise<void>;
58
+ export declare function deliverConfigureWarnings(opts: ConfigureWarningOptions, warnings: ConfigureWarning[]): Promise<void>;
43
59
  /**
44
60
  * Clean up stale AFT warning messages from previous runs.
45
61
  * Desktop only — TUI toasts are inherently transient and don't need cleanup.
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA+NH,MAAM,WAAW,mBAAmB;IAClC,8BAA8B;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW3F;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C1F;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C9E"}
1
+ {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiOH,MAAM,WAAW,mBAAmB;IAClC,8BAA8B;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,yBAAyB,GAAG,uBAAuB,GAAG,oBAAoB,CAAC;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW3F;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C1F;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAqCf;AAuED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,uBAAuB,EAC7B,QAAQ,EAAE,gBAAgB,EAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C9E"}
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAY5D,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAC1D,OAAO,CAAC,YAAY,CAA+C;gBAGjE,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,WAAgB,EACzB,eAAe,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAmB/C;;;;;;;OAOG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAgB5D;;;;;;;OAOG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY;IAmB5C,wEAAwE;IACxE,OAAO,CAAC,OAAO;IAUf,yDAAyD;IACzD,OAAO,CAAC,QAAQ;IAgBhB,wDAAwD;IAClD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAY5D,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAC1D,OAAO,CAAC,YAAY,CAA+C;gBAGjE,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,WAAgB,EACzB,eAAe,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAoB/C;;;;;;;OAOG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAgB5D;;;;;;;OAOG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY;IAmB5C,wEAAwE;IACxE,OAAO,CAAC,OAAO;IAUf,yDAAyD;IACzD,OAAO,CAAC,QAAQ;IAgBhB,wDAAwD;IAClD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../src/tools/_shared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQlE,CAAC;AAEF,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAa3D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,YAAY,CAEhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAWlC"}
1
+ {"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../src/tools/_shared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQlE,CAAC;AAEF,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAa3D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,YAAY,CAEhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAelC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/tools/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAyCjD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAqM3E"}
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/tools/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAyCjD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA2N3E"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Test-only re-exports of internal helpers from hoisted.ts.
3
+ *
4
+ * Kept in a separate file so the production import surface of hoisted.ts
5
+ * stays focused. Bun's test runner imports this directly; the bundled
6
+ * plugin output never references this module.
7
+ */
8
+ export { _buildUnifiedDiffForTest } from "./hoisted.js";
9
+ //# sourceMappingURL=hoisted-internals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hoisted-internals.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted-internals.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC"}
@@ -10,6 +10,8 @@
10
10
  */
11
11
  import type { ToolDefinition } from "@opencode-ai/plugin";
12
12
  import type { PluginContext } from "../types.js";
13
+ /** Test-only export. Production code uses buildUnifiedDiff directly. */
14
+ export declare const _buildUnifiedDiffForTest: (fp: string, before: string, after: string) => string;
13
15
  /**
14
16
  * Creates the simple read tool. Registers as "read" when hoisted, "aft_read" when not.
15
17
  */
@@ -1 +1 @@
1
- {"version":3,"file":"hoisted.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA6EjD;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CA4IjE;AAkxBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAS/E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAuBnF"}
1
+ {"version":3,"file":"hoisted.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAcjD,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,GAAI,IAAI,MAAM,EAAE,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,MAChD,CAAC;AA+QtC;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CA4IjE;AA00BD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAS/E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAuBnF"}
@@ -1 +1 @@
1
- {"version":3,"file":"lsp.d.ts","sourceRoot":"","sources":["../../src/tools/lsp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA0D3E"}
1
+ {"version":3,"file":"lsp.d.ts","sourceRoot":"","sources":["../../src/tools/lsp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA+E3E"}
@@ -1 +1 @@
1
- {"version":3,"file":"semantic.d.ts","sourceRoot":"","sources":["../../src/tools/semantic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASjD,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAoChF"}
1
+ {"version":3,"file":"semantic.d.ts","sourceRoot":"","sources":["../../src/tools/semantic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASjD,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAyDhF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/aft-opencode",
3
- "version": "0.15.4",
3
+ "version": "0.16.0",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis",
6
6
  "main": "dist/index.js",
@@ -32,11 +32,11 @@
32
32
  "zod": "^4.1.8"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@cortexkit/aft-darwin-arm64": "0.15.4",
36
- "@cortexkit/aft-darwin-x64": "0.15.4",
37
- "@cortexkit/aft-linux-arm64": "0.15.4",
38
- "@cortexkit/aft-linux-x64": "0.15.4",
39
- "@cortexkit/aft-win32-x64": "0.15.4"
35
+ "@cortexkit/aft-darwin-arm64": "0.16.0",
36
+ "@cortexkit/aft-darwin-x64": "0.16.0",
37
+ "@cortexkit/aft-linux-arm64": "0.16.0",
38
+ "@cortexkit/aft-linux-x64": "0.16.0",
39
+ "@cortexkit/aft-win32-x64": "0.16.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^22.0.0",