@selesai/code 0.8.9 → 0.8.11

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.
@@ -61,7 +61,7 @@ import { ScopedModelsSelectorComponent } from "./components/scoped-models-select
61
61
  import { SessionSelectorComponent } from "./components/session-selector.js";
62
62
  import { SettingsSelectorComponent } from "./components/settings-selector.js";
63
63
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
64
- import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
64
+ import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, ImageCaptioningStatusIndicator, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
65
65
  import { ToolExecutionComponent } from "./components/tool-execution.js";
66
66
  import { TreeSelectorComponent } from "./components/tree-selector.js";
67
67
  import { TrustSelectorComponent } from "./components/trust-selector.js";
@@ -2723,6 +2723,17 @@ export class InteractiveMode {
2723
2723
  case "agent_settled":
2724
2724
  await this.checkShutdownRequested();
2725
2725
  break;
2726
+ case "image_captioning_start":
2727
+ this.showStatusIndicator(new ImageCaptioningStatusIndicator(this.ui));
2728
+ this.ui.requestRender();
2729
+ break;
2730
+ case "image_captioning_end":
2731
+ this.clearStatusIndicator("imageCaptioning");
2732
+ if (!event.ok) {
2733
+ this.showStatus("Image captioning failed; image omitted");
2734
+ }
2735
+ this.ui.requestRender();
2736
+ break;
2726
2737
  case "compaction_start": {
2727
2738
  if (this.settingsManager.getShowTerminalProgress()) {
2728
2739
  this.ui.terminal.setProgress(true);
@@ -3668,6 +3679,9 @@ export class InteractiveMode {
3668
3679
  imageWidthCells: this.settingsManager.getImageWidthCells(),
3669
3680
  autoResizeImages: this.settingsManager.getImageAutoResize(),
3670
3681
  blockImages: this.settingsManager.getBlockImages(),
3682
+ imageCaptionModel: this.settingsManager.getImageCaptionModel(),
3683
+ imageCaptionContextTokens: this.settingsManager.getImageCaptionContextTokens(),
3684
+ visionModels: this.getVisionModels(),
3671
3685
  steeringMode: this.session.steeringMode,
3672
3686
  followUpMode: this.session.followUpMode,
3673
3687
  transport: this.settingsManager.getTransport(),
@@ -3695,6 +3709,7 @@ export class InteractiveMode {
3695
3709
  fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
3696
3710
  fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
3697
3711
  warnings: this.settingsManager.getWarnings(),
3712
+ skills: this.buildSkillToggleItems(),
3698
3713
  }, {
3699
3714
  onAutoCompactChange: (enabled) => {
3700
3715
  this.session.setAutoCompactionEnabled(enabled);
@@ -3728,6 +3743,14 @@ export class InteractiveMode {
3728
3743
  onBlockImagesChange: (blocked) => {
3729
3744
  this.settingsManager.setBlockImages(blocked);
3730
3745
  },
3746
+ onImageCaptionModelChange: (modelId) => {
3747
+ this.settingsManager.setImageCaptionModel(modelId);
3748
+ this.showStatus(modelId ? `Vision caption model: ${modelId}` : "Vision captioning disabled");
3749
+ },
3750
+ onImageCaptionContextTokensChange: (tokens) => {
3751
+ this.settingsManager.setImageCaptionContextTokens(tokens);
3752
+ this.showStatus(`Vision caption context: ${tokens} tokens`);
3753
+ },
3731
3754
  onSteeringModeChange: (mode) => {
3732
3755
  this.session.setSteeringMode(mode);
3733
3756
  },
@@ -3855,6 +3878,12 @@ export class InteractiveMode {
3855
3878
  onWarningsChange: (warnings) => {
3856
3879
  this.settingsManager.setWarnings(warnings);
3857
3880
  },
3881
+ onSkillsToggle: (item, enabled) => {
3882
+ this.toggleSkillSetting(item, enabled);
3883
+ },
3884
+ onSkillsReload: () => {
3885
+ void this.reloadAfterSkillChange();
3886
+ },
3858
3887
  onCancel: () => {
3859
3888
  done();
3860
3889
  this.ui.requestRender();
@@ -3863,6 +3892,73 @@ export class InteractiveMode {
3863
3892
  return { component: selector, focus: selector.getSettingsList() };
3864
3893
  });
3865
3894
  }
3895
+ getVisionModels() {
3896
+ try {
3897
+ const models = this.session.scopedModels.length > 0
3898
+ ? this.session.scopedModels.map((s) => s.model)
3899
+ : this.session.modelRuntime.getAvailableSnapshot();
3900
+ return models
3901
+ .filter((m) => m.input.includes("image"))
3902
+ .map((m) => `${m.provider}/${m.id}`)
3903
+ .sort();
3904
+ }
3905
+ catch {
3906
+ return [];
3907
+ }
3908
+ }
3909
+ buildSkillToggleItems() {
3910
+ const resolvedSkills = this.session.resourceLoader.getResolvedSkills();
3911
+ const agentDir = getAgentDir();
3912
+ const items = [];
3913
+ for (const resource of resolvedSkills) {
3914
+ const pathStr = resource.path;
3915
+ const isSkillFile = path.basename(pathStr) === "SKILL.md";
3916
+ const name = isSkillFile ? path.basename(path.dirname(pathStr)) : path.basename(pathStr);
3917
+ const scope = resource.metadata.scope === "project" ? "project" : "user";
3918
+ const baseDir = resource.metadata.baseDir ??
3919
+ (scope === "project" ? path.join(this.sessionManager.getCwd(), CONFIG_DIR_NAME) : agentDir);
3920
+ const pattern = path.relative(baseDir, pathStr) || name;
3921
+ items.push({
3922
+ name,
3923
+ pattern,
3924
+ scope,
3925
+ id: `${scope}:${pattern}`,
3926
+ enabled: resource.enabled,
3927
+ });
3928
+ }
3929
+ items.sort((a, b) => a.name.localeCompare(b.name));
3930
+ return items;
3931
+ }
3932
+ toggleSkillSetting(item, enabled) {
3933
+ if (item.scope === "project" && !this.settingsManager.isProjectTrusted()) {
3934
+ this.showError("Project is not trusted; cannot change project skills. Trust the project first.");
3935
+ return;
3936
+ }
3937
+ const settings = item.scope === "project"
3938
+ ? this.settingsManager.getProjectSettings()
3939
+ : this.settingsManager.getGlobalSettings();
3940
+ const current = [...(settings.skills ?? [])];
3941
+ const updated = current.filter((p) => {
3942
+ const stripped = p.startsWith("!") || p.startsWith("+") || p.startsWith("-") ? p.slice(1) : p;
3943
+ return stripped !== item.pattern;
3944
+ });
3945
+ updated.push(enabled ? `+${item.pattern}` : `-${item.pattern}`);
3946
+ if (item.scope === "project") {
3947
+ this.settingsManager.setProjectSkillPaths(updated);
3948
+ }
3949
+ else {
3950
+ this.settingsManager.setSkillPaths(updated);
3951
+ }
3952
+ }
3953
+ async reloadAfterSkillChange() {
3954
+ try {
3955
+ await this.session.reload();
3956
+ this.ui.requestRender();
3957
+ }
3958
+ catch (error) {
3959
+ this.showError(`Failed to reload after skill change: ${error instanceof Error ? error.message : String(error)}`);
3960
+ }
3961
+ }
3866
3962
  async handleModelCommand(searchTerm) {
3867
3963
  if (!searchTerm) {
3868
3964
  this.showModelSelector();
package/docs/settings.md CHANGED
@@ -127,7 +127,7 @@ Set `PI_SKIP_VERSION_CHECK=1` to disable the Pi version update check. Use `--off
127
127
  | Setting | Type | Default | Description |
128
128
  |---------|------|---------|-------------|
129
129
  | `retry.enabled` | boolean | `true` | Enable automatic agent-level retry on transient errors |
130
- | `retry.maxRetries` | number | `3` | Maximum agent-level retry attempts |
130
+ | `retry.maxRetries` | number | `10` | Maximum agent-level retry attempts |
131
131
  | `retry.baseDelayMs` | number | `2000` | Base delay for agent-level exponential backoff (2s, 4s, 8s) |
132
132
  | `retry.provider.timeoutMs` | number | SDK default | Provider/SDK request timeout in milliseconds |
133
133
  | `retry.provider.maxRetries` | number | `0` | Provider/SDK retry attempts |
@@ -141,7 +141,7 @@ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explic
141
141
  {
142
142
  "retry": {
143
143
  "enabled": true,
144
- "maxRetries": 3,
144
+ "maxRetries": 10,
145
145
  "baseDelayMs": 2000,
146
146
  "provider": {
147
147
  "timeoutMs": 3600000,
@@ -171,6 +171,8 @@ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explic
171
171
  | `terminal.clearOnShrink` | boolean | `false` | Clear empty rows when content shrinks (can cause flicker) |
172
172
  | `images.autoResize` | boolean | `true` | Resize images to 2000x2000 max |
173
173
  | `images.blockImages` | boolean | `false` | Block all images from being sent to LLM |
174
+ | `images.imageCaptionModel` | string | unset | Vision model (`provider/model`) to describe images when the active model cannot accept images; the caption text is used in place of the image (e.g. `tokenin/gemma-4`, `tokenin/kimi-k3`) |
175
+ | `images.imageCaptionContextTokens` | number | `16384` | Max token budget (approx) for the recent-conversation context sent to the caption model. Messages are kept whole (cut at message boundaries only); the user's current prompt is always included |
174
176
 
175
177
  ### Shell
176
178
 
@@ -296,7 +298,7 @@ Keys are top-level entry names (dir name for packaged extensions, file name for
296
298
  },
297
299
  "retry": {
298
300
  "enabled": true,
299
- "maxRetries": 3
301
+ "maxRetries": 10
300
302
  },
301
303
  "enabledModels": ["claude-*", "gpt-4o"],
302
304
  "warnings": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selesai/code",
3
- "version": "0.8.9",
3
+ "version": "0.8.11",
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,6 +50,8 @@
50
50
  ],
51
51
  "scripts": {
52
52
  "clean": "shx rm -rf dist",
53
+ "dev": "tsx src/cli.ts",
54
+ "dev:print": "tsx src/cli.ts --print",
53
55
  "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
56
  "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
57
  "prepare": "npm run build",