@sayknow-cli/coding-agent 0.6.1 → 0.6.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.6.3] - 2026-09-25
6
+
7
+ ## [0.6.2] - 2026-09-24
8
+
9
+ ### Fixed
10
+
11
+ - `/model` list search now matches vendor-style version punctuation. Typing `opus 5.5` (or just `5.5`) in the ALL or provider tab returned "No matching models" even though `anthropic/claude-opus-5-5` was in the list, because those tabs fuzzy-matched only `${id} ${provider}` and `5.5` cannot be a subsequence of `5-5`. The ALL/provider tabs now use the same punctuation-insensitive pipeline as the CANONICAL tab (id + provider + display name, alpha-token and compact-substring prefilters, fuzzy only for ranking/typo rescue), so `opus 5.5`, `5.5`, `opus55` and `opus-5-5` all land on Opus 5.5.
12
+
5
13
  ## [0.6.1] - 2026-09-24
6
14
 
7
15
  ### Added
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@sayknow-cli/coding-agent",
4
- "version": "0.6.1",
4
+ "version": "0.6.3",
5
5
  "description": "Sayknow-CLI CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://sayknow-cli.com",
7
7
  "author": "jaybeyond",
@@ -54,12 +54,12 @@
54
54
  "@agentclientprotocol/sdk": "1.3.0",
55
55
  "@babel/parser": "^7.29.3",
56
56
  "@mozilla/readability": "^0.6.0",
57
- "@sayknow-cli/stats": "0.6.1",
58
- "@sayknow-cli/agent-core": "0.6.1",
59
- "@sayknow-cli/ai": "0.6.1",
60
- "@sayknow-cli/natives": "0.6.1",
61
- "@sayknow-cli/tui": "0.6.1",
62
- "@sayknow-cli/utils": "0.6.1",
57
+ "@sayknow-cli/stats": "0.6.3",
58
+ "@sayknow-cli/agent-core": "0.6.3",
59
+ "@sayknow-cli/ai": "0.6.3",
60
+ "@sayknow-cli/natives": "0.6.3",
61
+ "@sayknow-cli/tui": "0.6.3",
62
+ "@sayknow-cli/utils": "0.6.3",
63
63
  "@puppeteer/browsers": "^2.13.0",
64
64
  "@types/turndown": "5.0.6",
65
65
  "@xterm/headless": "^6.0.0",
@@ -64,11 +64,49 @@ function getAlphaSearchTokens(query: string): string[] {
64
64
  return [...normalizeSearchText(query).matchAll(/[a-z]+/g)].map(match => match[0]).filter(token => token.length > 0);
65
65
  }
66
66
 
67
+ interface SearchableItem {
68
+ searchText: string;
69
+ normalizedSearchText: string;
70
+ compactSearchText: string;
71
+ }
72
+
73
+ function buildSearchFields(parts: readonly string[]): SearchableItem {
74
+ const searchText = parts.join(" ");
75
+ return {
76
+ searchText,
77
+ normalizedSearchText: normalizeSearchText(searchText),
78
+ compactSearchText: compactSearchText(searchText),
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Punctuation-insensitive search shared by every list view. Users type model
84
+ * names the way vendors print them ("opus 5.5", "gpt 5.4"), while ids spell
85
+ * the same thing with dashes ("claude-opus-5-5"). The alpha-token and compact
86
+ * substring passes bridge that gap; the fuzzy pass only ranks (or, when both
87
+ * prefilters are empty, rescues typos) over the display text.
88
+ */
89
+ function filterSearchableItems<T extends SearchableItem>(items: T[], query: string): T[] {
90
+ const alphaTokens = getAlphaSearchTokens(query);
91
+ const alphaFiltered =
92
+ alphaTokens.length === 0
93
+ ? items
94
+ : items.filter(item => alphaTokens.every(token => item.normalizedSearchText.includes(token)));
95
+ const compactQuery = compactSearchText(query);
96
+ const substringFiltered =
97
+ compactQuery.length === 0
98
+ ? alphaFiltered
99
+ : alphaFiltered.filter(item => item.compactSearchText.includes(compactQuery));
100
+ const fuzzySource =
101
+ substringFiltered.length > 0 ? substringFiltered : alphaFiltered.length > 0 ? alphaFiltered : items;
102
+ return fuzzyFilter(fuzzySource, query, ({ searchText }) => searchText);
103
+ }
104
+
67
105
  function computeModelRank(model: Model, roles: Record<string, RoleAssignment | undefined>): number {
68
106
  return roles.default && modelsAreEqual(roles.default.model, model) ? 0 : 1;
69
107
  }
70
108
 
71
- interface ModelItem {
109
+ interface ModelItem extends SearchableItem {
72
110
  kind: "provider";
73
111
  provider: string;
74
112
  id: string;
@@ -78,15 +116,12 @@ interface ModelItem {
78
116
  explicitThinkingLevel?: boolean;
79
117
  }
80
118
 
81
- interface CanonicalModelItem {
119
+ interface CanonicalModelItem extends SearchableItem {
82
120
  kind: "canonical";
83
121
  id: string;
84
122
  model: Model;
85
123
  selector: string;
86
124
  variantCount: number;
87
- searchText: string;
88
- normalizedSearchText: string;
89
- compactSearchText: string;
90
125
  thinkingLevel?: ThinkingLevel;
91
126
  explicitThinkingLevel?: boolean;
92
127
  }
@@ -732,6 +767,7 @@ export class ModelSelectorComponent extends Container {
732
767
  selector: `${scoped.model.provider}/${scoped.model.id}`,
733
768
  thinkingLevel: scoped.thinkingLevel,
734
769
  explicitThinkingLevel: scoped.explicitThinkingLevel,
770
+ ...buildSearchFields([scoped.model.id, scoped.model.provider, scoped.model.name]),
735
771
  }));
736
772
  } else {
737
773
  // Reload config and cached discovery state without blocking on live provider refresh
@@ -754,6 +790,7 @@ export class ModelSelectorComponent extends Container {
754
790
  id: model.id,
755
791
  model,
756
792
  selector: `${model.provider}/${model.id}`,
793
+ ...buildSearchFields([model.id, model.provider, model.name]),
757
794
  }));
758
795
  } catch (error) {
759
796
  this.#allModels = [];
@@ -779,23 +816,20 @@ export class ModelSelectorComponent extends Container {
779
816
  });
780
817
  if (!selectedModel) return undefined;
781
818
  const selectedSelector = `${selectedModel.provider}/${selectedModel.id}`;
782
- const searchText = [
783
- record.id,
784
- record.name,
785
- selectedModel.provider,
786
- selectedModel.id,
787
- selectedModel.name,
788
- ...record.variants.flatMap(variant => [variant.selector, variant.model.name]),
789
- ].join(" ");
790
819
  const item: CanonicalModelItem = {
791
820
  kind: "canonical",
792
821
  id: record.id,
793
822
  model: selectedModel,
794
823
  selector: record.id,
795
824
  variantCount: record.variants.length,
796
- searchText,
797
- normalizedSearchText: normalizeSearchText(searchText),
798
- compactSearchText: compactSearchText(searchText),
825
+ ...buildSearchFields([
826
+ record.id,
827
+ record.name,
828
+ selectedModel.provider,
829
+ selectedModel.id,
830
+ selectedModel.name,
831
+ ...record.variants.flatMap(variant => [variant.selector, variant.model.name]),
832
+ ]),
799
833
  };
800
834
  const scopedThinkingLevel = scopedThinkingBySelector.get(selectedSelector);
801
835
  if (scopedThinkingLevel !== undefined) {
@@ -925,29 +959,11 @@ export class ModelSelectorComponent extends Container {
925
959
  }
926
960
 
927
961
  if (isCanonicalTab) {
928
- const alphaTokens = getAlphaSearchTokens(query);
929
- const alphaFiltered =
930
- alphaTokens.length === 0
931
- ? baseCanonicalModels
932
- : baseCanonicalModels.filter(item =>
933
- alphaTokens.every(token => item.normalizedSearchText.includes(token)),
934
- );
935
- const compactQuery = compactSearchText(query);
936
- const substringFiltered =
937
- compactQuery.length === 0
938
- ? alphaFiltered
939
- : alphaFiltered.filter(item => item.compactSearchText.includes(compactQuery));
940
- const fuzzySource =
941
- substringFiltered.length > 0
942
- ? substringFiltered
943
- : alphaFiltered.length > 0
944
- ? alphaFiltered
945
- : baseCanonicalModels;
946
- const fuzzyMatches = fuzzyFilter(fuzzySource, query, ({ searchText }) => searchText);
962
+ const fuzzyMatches = filterSearchableItems(baseCanonicalModels, query);
947
963
  this.#sortCanonicalModels(fuzzyMatches);
948
964
  this.#filteredCanonicalModels = fuzzyMatches;
949
965
  } else {
950
- const fuzzyMatches = fuzzyFilter(baseModels, query, ({ id, provider }) => `${id} ${provider}`);
966
+ const fuzzyMatches = filterSearchableItems(baseModels, query);
951
967
  this.#sortModels(fuzzyMatches);
952
968
  this.#filteredModels = fuzzyMatches;
953
969
  }