@mrclrchtr/supi-review 1.4.0 → 1.5.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.
Files changed (42) hide show
  1. package/README.md +53 -59
  2. package/package.json +2 -9
  3. package/src/git.ts +107 -1
  4. package/src/history/collect.ts +210 -0
  5. package/src/history/synthesize.ts +109 -0
  6. package/src/model.ts +100 -0
  7. package/src/review.ts +204 -342
  8. package/src/target/packet.ts +216 -0
  9. package/src/tool/brief-runner.ts +269 -0
  10. package/src/tool/review-runner.ts +434 -0
  11. package/src/tool/runner-types.ts +29 -12
  12. package/src/tool/schemas.ts +31 -0
  13. package/src/types.ts +87 -48
  14. package/src/ui/flow.ts +216 -0
  15. package/src/ui/format-content.ts +22 -16
  16. package/src/ui/progress-widget.ts +40 -40
  17. package/src/ui/renderer.ts +38 -21
  18. package/node_modules/@mrclrchtr/supi-core/README.md +0 -107
  19. package/node_modules/@mrclrchtr/supi-core/package.json +0 -44
  20. package/node_modules/@mrclrchtr/supi-core/src/api.ts +0 -83
  21. package/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +0 -76
  22. package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +0 -186
  23. package/node_modules/@mrclrchtr/supi-core/src/context/context-messages.ts +0 -119
  24. package/node_modules/@mrclrchtr/supi-core/src/context/context-provider-registry.ts +0 -36
  25. package/node_modules/@mrclrchtr/supi-core/src/context/context-tag.ts +0 -31
  26. package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -255
  27. package/node_modules/@mrclrchtr/supi-core/src/extension.ts +0 -1
  28. package/node_modules/@mrclrchtr/supi-core/src/index.ts +0 -83
  29. package/node_modules/@mrclrchtr/supi-core/src/project-roots.ts +0 -170
  30. package/node_modules/@mrclrchtr/supi-core/src/registry-utils.ts +0 -54
  31. package/node_modules/@mrclrchtr/supi-core/src/session-utils.ts +0 -29
  32. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-command.ts +0 -15
  33. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +0 -41
  34. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +0 -226
  35. package/node_modules/@mrclrchtr/supi-core/src/terminal.ts +0 -60
  36. package/src/briefs.ts +0 -101
  37. package/src/profiles.ts +0 -52
  38. package/src/prompts.ts +0 -116
  39. package/src/settings.ts +0 -246
  40. package/src/tool/runner.ts +0 -432
  41. package/src/tool/target-resolution.ts +0 -102
  42. package/src/ui/ui.ts +0 -208
package/src/model.ts ADDED
@@ -0,0 +1,100 @@
1
+ import type { Model } from "@earendil-works/pi-ai";
2
+ import { type ExtensionContext, SettingsManager } from "@earendil-works/pi-coding-agent";
3
+ import type { ReviewModelSelection } from "./types.ts";
4
+
5
+ /** Build the canonical `provider/modelId` string used throughout the review flow. */
6
+ export function toCanonicalModelId(
7
+ model: Pick<NonNullable<ExtensionContext["model"]>, "provider" | "id">,
8
+ ): string {
9
+ return `${model.provider}/${model.id}`;
10
+ }
11
+
12
+ /**
13
+ * List review models using Pi's scoped model configuration only.
14
+ *
15
+ * If no scoped model patterns are configured, the review picker is intentionally empty.
16
+ */
17
+ export function getSelectableReviewModels(
18
+ ctx: Pick<ExtensionContext, "cwd" | "modelRegistry" | "model">,
19
+ enabledModelPatterns = SettingsManager.create(ctx.cwd).getEnabledModels(),
20
+ ): ReviewModelSelection[] {
21
+ if (!enabledModelPatterns || enabledModelPatterns.length === 0) {
22
+ return [];
23
+ }
24
+
25
+ const byCanonicalId = new Map<string, ReviewModelSelection>();
26
+ const availableModels = filterByEnabledModels(
27
+ enabledModelPatterns,
28
+ ctx.modelRegistry.getAvailable(),
29
+ );
30
+
31
+ const addModel = (
32
+ // biome-ignore lint/suspicious/noExplicitAny: Model<any> is pi's canonical type
33
+ model: Model<any>,
34
+ isCurrent: boolean,
35
+ ) => {
36
+ const canonicalId = toCanonicalModelId(model);
37
+ const existing = byCanonicalId.get(canonicalId);
38
+ if (existing) {
39
+ if (isCurrent) existing.isCurrent = true;
40
+ return;
41
+ }
42
+
43
+ byCanonicalId.set(canonicalId, {
44
+ canonicalId,
45
+ provider: model.provider,
46
+ id: model.id,
47
+ model,
48
+ label: model.name ?? canonicalId,
49
+ description: canonicalId,
50
+ isCurrent,
51
+ });
52
+ };
53
+
54
+ if (ctx.model && matchModelPatterns(ctx.model, enabledModelPatterns)) {
55
+ addModel(ctx.model, true);
56
+ }
57
+
58
+ for (const model of availableModels) {
59
+ addModel(
60
+ model,
61
+ ctx.model ? toCanonicalModelId(model) === toCanonicalModelId(ctx.model) : false,
62
+ );
63
+ }
64
+
65
+ return Array.from(byCanonicalId.values()).sort((a, b) => {
66
+ if (a.isCurrent !== b.isCurrent) return a.isCurrent ? -1 : 1;
67
+ return a.canonicalId.localeCompare(b.canonicalId);
68
+ });
69
+ }
70
+
71
+ function filterByEnabledModels<T extends { provider: string; id: string }>(
72
+ patterns: string[],
73
+ models: T[],
74
+ ): T[] {
75
+ return models.filter((model) => matchModelPatterns(model, patterns));
76
+ }
77
+
78
+ function matchModelPatterns(model: { provider: string; id: string }, patterns: string[]): boolean {
79
+ return patterns.some((pattern) => matchModelPattern(model, pattern));
80
+ }
81
+
82
+ function matchModelPattern(model: { provider: string; id: string }, pattern: string): boolean {
83
+ const canonicalId = `${model.provider}/${model.id}`;
84
+ if (pattern.includes("/")) {
85
+ return simpleGlobMatch(canonicalId, pattern);
86
+ }
87
+ return simpleGlobMatch(model.id, pattern) || simpleGlobMatch(canonicalId, pattern);
88
+ }
89
+
90
+ function simpleGlobMatch(text: string, pattern: string): boolean {
91
+ if (!pattern.includes("*") && !pattern.includes("?")) {
92
+ return text.toLowerCase() === pattern.toLowerCase();
93
+ }
94
+
95
+ const regex = pattern
96
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
97
+ .replace(/\*/g, ".*")
98
+ .replace(/\?/g, ".");
99
+ return new RegExp(`^${regex}$`, "i").test(text);
100
+ }