@selesai/code 0.8.10 → 0.8.12

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
@@ -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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selesai/code",
3
- "version": "0.8.10",
3
+ "version": "0.8.12",
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",