@selesai/code 0.8.7 → 0.8.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/dist/cli/args.d.ts +1 -0
  3. package/dist/cli/args.js +11 -0
  4. package/dist/cli/args.test.d.ts +1 -0
  5. package/dist/cli/args.test.js +24 -0
  6. package/dist/core/agent-session.d.ts +4 -1
  7. package/dist/core/agent-session.js +5 -6
  8. package/dist/core/experimental.d.ts +4 -0
  9. package/dist/core/experimental.js +4 -0
  10. package/dist/core/extensions/types.d.ts +4 -0
  11. package/dist/core/keybindings.d.ts +24 -0
  12. package/dist/core/model-config.d.ts +5 -0
  13. package/dist/core/model-config.js +1 -0
  14. package/dist/core/project-trust.js +2 -2
  15. package/dist/core/sdk.d.ts +5 -3
  16. package/dist/core/sdk.js +2 -1
  17. package/dist/core/session-manager.js +2 -2
  18. package/dist/core/settings-manager.d.ts +6 -0
  19. package/dist/core/settings-manager.js +12 -0
  20. package/dist/core/system-prompt.js +1 -1
  21. package/dist/core/tools/bash.js +2 -0
  22. package/dist/core/tools/edit.js +2 -0
  23. package/dist/core/tools/find.js +1 -1
  24. package/dist/core/tools/grep.js +1 -1
  25. package/dist/core/tools/read.js +2 -0
  26. package/dist/core/tools/write.js +2 -0
  27. package/dist/defaults/models.json +21 -0
  28. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +0 -18
  29. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +26 -0
  30. package/dist/extensions/tokenin-onboarding.ts +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/main.js +4 -0
  33. package/dist/modes/interactive/components/model-selector.js +2 -1
  34. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  35. package/dist/modes/interactive/components/settings-selector.js +10 -0
  36. package/dist/modes/interactive/components/tool-execution.js +10 -1
  37. package/dist/modes/interactive/interactive-mode.d.ts +8 -2
  38. package/dist/modes/interactive/interactive-mode.js +81 -25
  39. package/dist/modes/interactive/model-catalog-refresh.d.ts +6 -0
  40. package/dist/modes/interactive/model-catalog-refresh.js +33 -0
  41. package/dist/modes/interactive/theme/dark.json +2 -0
  42. package/dist/modes/interactive/theme/light.json +2 -0
  43. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  44. package/dist/modes/interactive/theme/theme-controller.js +25 -11
  45. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  46. package/dist/modes/interactive/theme/theme.d.ts +5 -3
  47. package/dist/modes/interactive/theme/theme.js +12 -2
  48. package/dist/utils/abort.d.ts +4 -0
  49. package/dist/utils/abort.js +47 -0
  50. package/dist/utils/tools-manager.d.ts +5 -1
  51. package/dist/utils/tools-manager.js +12 -21
  52. package/package.json +6 -6
@@ -1,8 +1,10 @@
1
1
  import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
2
2
  import { type EditorTheme, type MarkdownTheme, type RgbColor, type SelectListTheme, type SettingsListTheme } from "@earendil-works/pi-tui";
3
3
  import type { SourceInfo } from "../../../core/source-info.ts";
4
- export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "thinkingMax" | "bashMode";
5
- export type ThemeBg = "selectedBg" | "scrollbarThumb" | "userMessageBg" | "customMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
4
+ export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "thinkingMax" | "searchMatchText" | "bashMode";
5
+ export type ThemeBg = "selectedBg" | "scrollbarThumb" | "searchMatchBg" | "userMessageBg" | "customMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
6
+ type OptionalThemeColor = "thinkingMax" | "searchMatchText";
7
+ type OptionalThemeBg = "scrollbarThumb" | "searchMatchBg";
6
8
  type ColorMode = "truecolor" | "256color";
7
9
  export declare class Theme {
8
10
  readonly name?: string;
@@ -11,7 +13,7 @@ export declare class Theme {
11
13
  private fgColors;
12
14
  private bgColors;
13
15
  private mode;
14
- constructor(fgColors: Record<ThemeColor, string | number>, bgColors: Record<Exclude<ThemeBg, "scrollbarThumb">, string | number> & Partial<Record<"scrollbarThumb", string | number>>, mode: ColorMode, options?: {
16
+ constructor(fgColors: Record<Exclude<ThemeColor, OptionalThemeColor>, string | number> & Partial<Record<OptionalThemeColor, string | number>>, bgColors: Record<Exclude<ThemeBg, OptionalThemeBg>, string | number> & Partial<Record<OptionalThemeBg, string | number>>, mode: ColorMode, options?: {
15
17
  name?: string;
16
18
  sourcePath?: string;
17
19
  sourceInfo?: SourceInfo;
@@ -31,9 +31,11 @@ const ThemeJsonSchema = Type.Object({
31
31
  dim: ColorValueSchema,
32
32
  text: ColorValueSchema,
33
33
  thinkingText: ColorValueSchema,
34
- // Backgrounds & Content Text (11 required, 1 optional)
34
+ // Backgrounds & Content Text (11 required, 3 optional)
35
35
  selectedBg: ColorValueSchema,
36
36
  scrollbarThumb: Type.Optional(ColorValueSchema),
37
+ searchMatchBg: Type.Optional(ColorValueSchema),
38
+ searchMatchText: Type.Optional(ColorValueSchema),
37
39
  userMessageBg: ColorValueSchema,
38
40
  userMessageText: ColorValueSchema,
39
41
  customMessageBg: ColorValueSchema,
@@ -229,6 +231,8 @@ function withThemeColorFallbacks(colors) {
229
231
  ...colors,
230
232
  thinkingMax: colors.thinkingMax ?? colors.thinkingXhigh,
231
233
  scrollbarThumb: colors.scrollbarThumb ?? colors.selectedBg,
234
+ searchMatchBg: colors.searchMatchBg ?? colors.selectedBg,
235
+ searchMatchText: colors.searchMatchText ?? colors.text,
232
236
  };
233
237
  }
234
238
  // ============================================================================
@@ -247,7 +251,11 @@ export class Theme {
247
251
  this.sourceInfo = options.sourceInfo;
248
252
  this.mode = mode;
249
253
  this.fgColors = new Map();
250
- const colors = { ...fgColors, thinkingMax: fgColors.thinkingMax ?? fgColors.thinkingXhigh };
254
+ const colors = {
255
+ ...fgColors,
256
+ thinkingMax: fgColors.thinkingMax ?? fgColors.thinkingXhigh,
257
+ searchMatchText: fgColors.searchMatchText ?? fgColors.text,
258
+ };
251
259
  for (const [key, value] of Object.entries(colors)) {
252
260
  this.fgColors.set(key, fgAnsi(value, mode));
253
261
  }
@@ -255,6 +263,7 @@ export class Theme {
255
263
  const backgrounds = {
256
264
  ...bgColors,
257
265
  scrollbarThumb: bgColors.scrollbarThumb ?? bgColors.selectedBg,
266
+ searchMatchBg: bgColors.searchMatchBg ?? bgColors.selectedBg,
258
267
  };
259
268
  for (const [key, value] of Object.entries(backgrounds)) {
260
269
  this.bgColors.set(key, bgAnsi(value, mode));
@@ -473,6 +482,7 @@ function createTheme(themeJson, mode, sourcePath) {
473
482
  const bgColorKeys = new Set([
474
483
  "selectedBg",
475
484
  "scrollbarThumb",
485
+ "searchMatchBg",
476
486
  "userMessageBg",
477
487
  "customMessageBg",
478
488
  "toolPendingBg",
@@ -0,0 +1,4 @@
1
+ /** Normalize an optional public signal without imposing a deadline. */
2
+ export declare function operationSignal(signal?: AbortSignal): AbortSignal;
3
+ /** Stop waiting on abort while observing the abandoned operation through settlement. */
4
+ export declare function raceWithAbortSignal<T>(operation: Promise<T>, signal: AbortSignal | undefined): Promise<T>;
@@ -0,0 +1,47 @@
1
+ function abortReason(signal) {
2
+ if (signal.reason !== undefined)
3
+ return signal.reason;
4
+ const error = new Error("The operation was aborted");
5
+ error.name = "AbortError";
6
+ return error;
7
+ }
8
+ /** Normalize an optional public signal without imposing a deadline. */
9
+ export function operationSignal(signal) {
10
+ return signal ?? new AbortController().signal;
11
+ }
12
+ /** Stop waiting on abort while observing the abandoned operation through settlement. */
13
+ export function raceWithAbortSignal(operation, signal) {
14
+ if (!signal)
15
+ return operation;
16
+ if (signal.aborted) {
17
+ void operation.catch(() => { });
18
+ return Promise.reject(abortReason(signal));
19
+ }
20
+ return new Promise((resolve, reject) => {
21
+ let settled = false;
22
+ const cleanup = () => signal.removeEventListener("abort", onAbort);
23
+ const onAbort = () => {
24
+ if (settled)
25
+ return;
26
+ settled = true;
27
+ cleanup();
28
+ reject(abortReason(signal));
29
+ };
30
+ signal.addEventListener("abort", onAbort, { once: true });
31
+ void operation.then((value) => {
32
+ if (settled)
33
+ return;
34
+ settled = true;
35
+ cleanup();
36
+ resolve(value);
37
+ }, (error) => {
38
+ if (settled)
39
+ return;
40
+ settled = true;
41
+ cleanup();
42
+ reject(error);
43
+ });
44
+ if (signal.aborted)
45
+ onAbort();
46
+ });
47
+ }
@@ -1,3 +1,7 @@
1
1
  export type ManagedTool = "fd" | "rg" | "rtk";
2
2
  export declare function getToolPath(tool: ManagedTool): string | null;
3
- export declare function ensureTool(tool: ManagedTool, silent?: boolean): Promise<string | undefined>;
3
+ export interface ToolStatus {
4
+ type: "info" | "warning";
5
+ message: string;
6
+ }
7
+ export declare function ensureTool(tool: ManagedTool, onStatus?: (status: ToolStatus) => void): Promise<string | undefined>;
@@ -1,5 +1,4 @@
1
1
  import { createHash } from "node:crypto";
2
- import chalk from "chalk";
3
2
  import { spawnSync } from "child_process";
4
3
  import { chmodSync, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync } from "fs";
5
4
  import { arch, platform } from "os";
@@ -359,8 +358,9 @@ const TERMUX_PACKAGES = {
359
358
  rg: "ripgrep",
360
359
  };
361
360
  // Ensure a tool is available, downloading if necessary
362
- // Returns the path to the tool, or null if unavailable
363
- export async function ensureTool(tool, silent = false) {
361
+ // Reports progress through `onStatus`; status messages are otherwise silent.
362
+ // Returns the tool path, or undefined if unavailable.
363
+ export async function ensureTool(tool, onStatus) {
364
364
  const config = TOOLS[tool];
365
365
  if (!config)
366
366
  return undefined;
@@ -372,40 +372,31 @@ export async function ensureTool(tool, silent = false) {
372
372
  if (!config.verify || config.verify(existingPath)) {
373
373
  return existingPath;
374
374
  }
375
- if (!silent) {
376
- console.log(chalk.yellow(`${config.name} found but failed verification; installing managed binary.`));
377
- }
375
+ onStatus?.({ type: "warning", message: `${config.name} found but failed verification; installing managed binary.` });
378
376
  }
379
377
  if (isOfflineModeEnabled()) {
380
- if (!silent) {
381
- console.log(chalk.yellow(`${config.name} not found. Offline mode enabled, skipping download.`));
382
- }
378
+ onStatus?.({ type: "warning", message: `${config.name} not found. Offline mode enabled, skipping download.` });
383
379
  return undefined;
384
380
  }
385
381
  // On Android/Termux, Linux binaries don't work due to Bionic libc incompatibility.
386
382
  // Users must install via pkg.
387
383
  if (platform() === "android") {
388
384
  const pkgName = TERMUX_PACKAGES[tool] ?? tool;
389
- if (!silent) {
390
- console.log(chalk.yellow(`${config.name} not found. Install with: pkg install ${pkgName}`));
391
- }
385
+ onStatus?.({ type: "warning", message: `${config.name} not found. Install with: pkg install ${pkgName}` });
392
386
  return undefined;
393
387
  }
394
388
  // Tool not found - download it
395
- if (!silent) {
396
- console.log(chalk.dim(`${config.name} not found. Downloading...`));
397
- }
389
+ onStatus?.({ type: "info", message: `${config.name} not found. Downloading...` });
398
390
  try {
399
391
  const path = await downloadTool(tool);
400
- if (!silent) {
401
- console.log(chalk.dim(`${config.name} installed to ${path}`));
402
- }
392
+ onStatus?.({ type: "info", message: `${config.name} installed to ${path}` });
403
393
  return path;
404
394
  }
405
395
  catch (e) {
406
- if (!silent) {
407
- console.log(chalk.yellow(`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`));
408
- }
396
+ onStatus?.({
397
+ type: "warning",
398
+ message: `Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`,
399
+ });
409
400
  return undefined;
410
401
  }
411
402
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selesai/code",
3
- "version": "0.8.7",
3
+ "version": "0.8.9",
4
4
  "description": "Maintained, extension-first Pi coding agent with built-in workflows, subagents, web research, questions, skills, and an enhanced terminal UI.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -50,17 +50,17 @@
50
50
  ],
51
51
  "scripts": {
52
52
  "clean": "shx rm -rf dist",
53
- "test": "vitest run src/extensions/undo.test.ts src/extensions/agent-browser.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/model-prompt-injector/index.test.ts src/extensions/web-agent-onboarding.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/workflow-modes.test.ts",
54
- "test:coverage": "vitest run --coverage src/extensions/undo.test.ts src/extensions/agent-browser.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/model-prompt-injector/index.test.ts src/extensions/web-agent-onboarding.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/workflow-modes.test.ts",
53
+ "test": "vitest run src/extensions/undo.test.ts src/extensions/agent-browser.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/model-prompt-injector/index.test.ts src/extensions/web-agent-onboarding.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/workflow-modes.test.ts src/__tests__/model-registry-defaults.test.ts src/cli/args.test.ts",
54
+ "test:coverage": "vitest run --coverage src/extensions/undo.test.ts src/extensions/agent-browser.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/model-prompt-injector/index.test.ts src/extensions/web-agent-onboarding.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/workflow-modes.test.ts src/__tests__/model-registry-defaults.test.ts src/cli/args.test.ts",
55
55
  "prepare": "npm run build",
56
56
  "build": "npm run clean && tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js dist/rpc-entry.js && npm run copy-assets",
57
57
  "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/ && shx mkdir -p dist/defaults && shx cp src/defaults/* dist/defaults/ && shx mkdir -p dist/extensions && node scripts/copy-extensions.mjs && shx mkdir -p dist/themes && shx cp -r src/themes/. dist/themes/ && shx mkdir -p dist/skills && shx cp -r src/skills/. dist/skills/"
58
58
  },
59
59
  "dependencies": {
60
60
  "@ast-grep/cli": "0.45.0",
61
- "@earendil-works/pi-agent-core": "0.84.1",
62
- "@earendil-works/pi-ai": "0.84.1",
63
- "@earendil-works/pi-tui": "0.84.1",
61
+ "@earendil-works/pi-agent-core": "0.84.2",
62
+ "@earendil-works/pi-ai": "0.84.2",
63
+ "@earendil-works/pi-tui": "0.84.2",
64
64
  "@mozilla/readability": "^0.6.0",
65
65
  "@silvia-odwyer/photon-node": "0.3.4",
66
66
  "chalk": "5.6.2",