@stupify/cli 0.0.8 → 0.0.10

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/dist/analysis.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { cachedJson, fingerprint } from "./cache.js";
2
2
  import { searchPrompt } from "./prompts.js";
3
+ import { diagnostic, diagnosticError } from "./ui.js";
3
4
  export async function runSearch(model, request) {
4
5
  const raw = await runJsonPrompt(model, request.prompt, request.schema, 0);
5
6
  return uncheckedSearchMatches(raw, request.contexts);
@@ -100,8 +101,8 @@ Your previous response was not valid JSON. Return the requested JSON object only
100
101
  const retryParsed = parseJson(retry);
101
102
  if (retryParsed.ok)
102
103
  return retryParsed.value;
103
- console.error("Raw model output:");
104
- console.error(retry);
104
+ diagnosticError("Raw model output:");
105
+ diagnostic(retry);
105
106
  throw new Error("Model returned invalid JSON.");
106
107
  }
107
108
  async function complete(model, prompt, schema, temperature) {
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "0.0.8";
1
+ export declare const VERSION = "0.0.10";
2
2
  import type { ModelConfig, ModelId } from "./types.ts";
3
3
  export declare const DEFAULT_MODEL_ID: ModelId;
4
4
  export declare const MODEL_REGISTRY: Record<ModelId, ModelConfig>;
package/dist/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = "0.0.8";
1
+ export const VERSION = "0.0.10";
2
2
  export const DEFAULT_MODEL_ID = "gemma-4-e2b";
3
3
  export const MODEL_REGISTRY = {
4
4
  "gemma-4-e2b": {
package/dist/git.d.ts CHANGED
@@ -9,3 +9,4 @@ export declare function netDiffFromStdin(text: string): Promise<NetDiff>;
9
9
  export declare function stagedDiff(): Promise<StagedDiff>;
10
10
  export declare function gitRoot(): Promise<string>;
11
11
  export declare function gitPath(pathspec: string): Promise<string>;
12
+ export declare function gitUserLabel(): Promise<string>;
package/dist/git.js CHANGED
@@ -87,6 +87,15 @@ export async function gitPath(pathspec) {
87
87
  throw new Error(`Could not resolve git path: ${pathspec}`);
88
88
  }
89
89
  }
90
+ export async function gitUserLabel() {
91
+ const [name, email] = await Promise.all([
92
+ gitConfig("user.name"),
93
+ gitConfig("user.email"),
94
+ ]);
95
+ if (name && email)
96
+ return `${name} <${email}>`;
97
+ return name || email || "working tree";
98
+ }
90
99
  async function netDiff(base, target, label, id) {
91
100
  const [text, stats, shortBase, shortTarget] = await Promise.all([
92
101
  diff(base, target),
@@ -104,19 +113,53 @@ async function netDiff(base, target, label, id) {
104
113
  };
105
114
  }
106
115
  async function sourceRange(base, target, label, id) {
107
- const [stats, shortBase, shortTarget] = await Promise.all([
116
+ const [stats, shortBase, shortTarget, committers] = await Promise.all([
108
117
  diffStats(base, target),
109
118
  shortCommit(base),
110
119
  shortCommit(target),
120
+ committersForRange(base, target),
111
121
  ]);
112
122
  return {
113
123
  id: id ?? sourceId(`net:${shortBase}..${shortTarget}`),
114
124
  label,
115
125
  base,
116
126
  target,
127
+ committers,
117
128
  stats,
118
129
  };
119
130
  }
131
+ async function gitConfig(key) {
132
+ try {
133
+ const { stdout } = await execFileAsync("git", ["config", "--get", key]);
134
+ return stdout.trim();
135
+ }
136
+ catch {
137
+ return "";
138
+ }
139
+ }
140
+ async function committersForRange(base, target) {
141
+ try {
142
+ const { stdout } = await execFileAsync("git", ["log", "--format=%cn <%ce>", `${base}..${target}`], {
143
+ maxBuffer: 4 * 1024 * 1024,
144
+ });
145
+ return uniqueLines(stdout);
146
+ }
147
+ catch {
148
+ return [];
149
+ }
150
+ }
151
+ function uniqueLines(value) {
152
+ const seen = new Set();
153
+ const lines = [];
154
+ for (const line of value.split(/\r?\n/)) {
155
+ const trimmed = line.trim();
156
+ if (!trimmed || seen.has(trimmed))
157
+ continue;
158
+ seen.add(trimmed);
159
+ lines.push(trimmed);
160
+ }
161
+ return lines;
162
+ }
120
163
  async function baseBefore(since) {
121
164
  try {
122
165
  const { stdout } = await execFileAsync("git", [
package/dist/model.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ModelId } from "./types.ts";
2
+ import type { CliUi } from "./ui.ts";
2
3
  export type ModelProfile = "scout";
3
4
  export type LocalModel = Readonly<{
4
5
  id: ModelId;
@@ -6,5 +7,5 @@ export type LocalModel = Readonly<{
6
7
  baseUrl: string;
7
8
  profile: ModelProfile;
8
9
  }>;
9
- export declare function firstRunModelBootstrap(modelId: ModelId): Promise<string>;
10
- export declare function loadLocalModel(modelPath: string, modelId: ModelId, profile?: ModelProfile): Promise<LocalModel>;
10
+ export declare function firstRunModelBootstrap(modelId: ModelId, ui: CliUi): Promise<string>;
11
+ export declare function loadLocalModel(modelPath: string, modelId: ModelId, profile: ModelProfile, ui: CliUi): Promise<LocalModel>;
package/dist/model.js CHANGED
@@ -1,42 +1,39 @@
1
1
  import { execFile, spawn } from "node:child_process";
2
- import { createReadStream, createWriteStream } from "node:fs";
3
2
  import { mkdir, open, readFile, rename, rm, stat, writeFile, } from "node:fs/promises";
4
3
  import { homedir, platform } from "node:os";
5
- import { stdin as input, stderr as statusOutput, stdout as output, } from "node:process";
6
- import { createInterface } from "node:readline/promises";
7
4
  import path from "node:path";
8
5
  import { promisify } from "node:util";
9
6
  import { MODEL_REGISTRY } from "./constants.js";
10
7
  const execFileAsync = promisify(execFile);
11
8
  const LLAMA_SERVER_HOST = "127.0.0.1";
12
- export async function firstRunModelBootstrap(modelId) {
9
+ export async function firstRunModelBootstrap(modelId, ui) {
13
10
  const selectedModel = MODEL_REGISTRY[modelId];
14
11
  const modelDir = path.join(cacheDir(), "models");
15
12
  const modelPath = path.join(modelDir, selectedModel.file);
16
13
  if (await exists(modelPath))
17
14
  return modelPath;
18
- console.error(`No local Stupify model found.
15
+ ui.note(`No local Stupify model found.
19
16
  Stupify runs locally.
20
17
  Download this model now?
21
18
  Model: ${selectedModel.name}
22
- Size: ${selectedModel.size}`);
23
- if (!(await confirm("Continue? y/N ")))
19
+ Size: ${selectedModel.size}`, "Setup", { force: true });
20
+ if (!(await ui.confirm("Continue?")))
24
21
  throw new Error("Setup cancelled.");
25
22
  await mkdir(modelDir, { recursive: true });
26
- await downloadModel(modelPath, selectedModel.url);
23
+ await downloadModel(modelPath, selectedModel.url, ui);
27
24
  if (!(await exists(modelPath)))
28
25
  throw new Error("Model download failed: file was not created.");
29
26
  return modelPath;
30
27
  }
31
- export async function loadLocalModel(modelPath, modelId, profile = "scout") {
28
+ export async function loadLocalModel(modelPath, modelId, profile, ui) {
32
29
  const selectedModel = MODEL_REGISTRY[modelId];
33
30
  const runtime = modelRuntime(profile);
34
31
  const runningModel = await runningServerModel(runtime.baseUrl);
35
32
  if (runningModel) {
36
33
  if (runningModel !== modelId)
37
- await stopManagedServer(runtime);
34
+ await stopManagedServer(runtime, ui);
38
35
  if (runningModel === modelId) {
39
- console.error(`Using local model: ${selectedModel.name}`);
36
+ ui.info(`Using local model: ${selectedModel.name}`);
40
37
  return {
41
38
  id: modelId,
42
39
  name: selectedModel.name,
@@ -46,8 +43,16 @@ export async function loadLocalModel(modelPath, modelId, profile = "scout") {
46
43
  }
47
44
  }
48
45
  await ensureLlamaServerBinary();
49
- await startLlamaServer(modelPath, modelId, selectedModel.name, runtime);
50
- await waitForServer(runtime.baseUrl, modelId);
46
+ await startLlamaServer(modelPath, modelId, selectedModel.name, runtime, ui);
47
+ const ready = ui.spinner(`Waiting for local ${profile} model`);
48
+ try {
49
+ await waitForServer(runtime.baseUrl, modelId);
50
+ ready.stop(`Local ${profile} model ready`);
51
+ }
52
+ catch (error) {
53
+ ready.error(`Local ${profile} model failed to start`);
54
+ throw error;
55
+ }
51
56
  return {
52
57
  id: modelId,
53
58
  name: selectedModel.name,
@@ -101,14 +106,14 @@ Install llama.cpp first:
101
106
  brew install llama.cpp`);
102
107
  }
103
108
  }
104
- async function startLlamaServer(modelPath, modelId, modelName, runtime) {
109
+ async function startLlamaServer(modelPath, modelId, modelName, runtime, ui) {
105
110
  const logDir = path.join(cacheDir(), "logs");
106
111
  await mkdir(logDir, { recursive: true });
107
112
  const logPath = path.join(logDir, "llama-server.log");
108
113
  const out = await open(logPath, "a");
109
114
  const err = await open(logPath, "a");
110
- console.error(`Starting local model server: ${modelName}`);
111
- console.error(`llama-server log: ${logPath}`);
115
+ ui.step(`Starting local model server: ${modelName}`);
116
+ ui.info(`llama-server log: ${logPath}`);
112
117
  const args = [
113
118
  "-m",
114
119
  modelPath,
@@ -151,14 +156,14 @@ async function startLlamaServer(modelPath, modelId, modelName, runtime) {
151
156
  await out.close();
152
157
  await err.close();
153
158
  }
154
- async function stopManagedServer(runtime) {
159
+ async function stopManagedServer(runtime, ui) {
155
160
  const pid = await managedServerPid(runtime);
156
161
  if (!pid) {
157
162
  const runningModel = await runningServerModel(runtime.baseUrl);
158
163
  throw new Error(`A llama-server is already running with ${runningModel ?? "another model"}.
159
164
  Stop it before switching models, or use STUPIFY_LLAMA_SERVER_URL for that server.`);
160
165
  }
161
- console.error("Restarting local model server for selected model.");
166
+ ui.step("Restarting local model server for selected model.");
162
167
  try {
163
168
  process.kill(pid, "SIGTERM");
164
169
  }
@@ -214,10 +219,11 @@ async function waitForServer(baseUrl, modelId) {
214
219
  function sleep(ms) {
215
220
  return new Promise((resolve) => setTimeout(resolve, ms));
216
221
  }
217
- async function downloadModel(modelPath, modelUrl) {
222
+ async function downloadModel(modelPath, modelUrl, ui) {
218
223
  const tempPath = `${modelPath}.download`;
219
224
  await rm(tempPath, { force: true });
220
- console.error("Downloading model...");
225
+ const downloadSpinner = ui.spinner("Downloading model");
226
+ let downloadProgress = null;
221
227
  try {
222
228
  const response = await fetch(modelUrl);
223
229
  if (!response.ok || !response.body)
@@ -225,8 +231,13 @@ async function downloadModel(modelPath, modelUrl) {
225
231
  const total = Number(response.headers.get("content-length") ?? 0);
226
232
  let received = 0;
227
233
  let lastPrint = 0;
234
+ let lastProgressBytes = 0;
228
235
  const reader = response.body.getReader();
229
236
  const file = await open(tempPath, "wx");
237
+ if (total > 0) {
238
+ downloadSpinner.clear();
239
+ downloadProgress = ui.progress("Downloading model", total);
240
+ }
230
241
  try {
231
242
  while (true) {
232
243
  const { done, value } = await reader.read();
@@ -237,42 +248,30 @@ async function downloadModel(modelPath, modelUrl) {
237
248
  const now = Date.now();
238
249
  if (total > 0 && now - lastPrint > 500) {
239
250
  lastPrint = now;
240
- statusOutput.write(`\r${formatBytes(received)} / ${formatBytes(total)}`);
251
+ downloadProgress?.advance(received - lastProgressBytes, `${formatBytes(received)} / ${formatBytes(total)}`);
252
+ lastProgressBytes = received;
241
253
  }
242
254
  }
243
255
  }
244
256
  finally {
245
257
  await file.close();
246
258
  }
247
- if (total > 0)
248
- statusOutput.write(`\r${formatBytes(received)} / ${formatBytes(total)}\n`);
259
+ if (downloadProgress && received > lastProgressBytes) {
260
+ downloadProgress.advance(received - lastProgressBytes, `${formatBytes(received)} / ${formatBytes(total)}`);
261
+ }
262
+ const activeProgress = downloadProgress ?? downloadSpinner;
263
+ activeProgress.stop(total > 0
264
+ ? `Downloaded ${formatBytes(received)} / ${formatBytes(total)}`
265
+ : "Downloaded model");
249
266
  await rename(tempPath, modelPath);
250
267
  }
251
268
  catch (error) {
269
+ const activeProgress = downloadProgress ?? downloadSpinner;
270
+ activeProgress.error("Model download failed");
252
271
  await rm(tempPath, { force: true });
253
272
  throw error;
254
273
  }
255
274
  }
256
- async function confirm(question) {
257
- const rl = createInterface(terminalIo());
258
- try {
259
- const answer = (await rl.question(question)).trim().toLowerCase();
260
- return answer === "y" || answer === "yes";
261
- }
262
- finally {
263
- rl.close();
264
- }
265
- }
266
- function terminalIo() {
267
- if (input.isTTY)
268
- return { input, output };
269
- if (platform() !== "win32")
270
- return {
271
- input: createReadStream("/dev/tty"),
272
- output: createWriteStream("/dev/tty"),
273
- };
274
- throw new Error("No local Stupify model found. Run `stupify` once in an interactive terminal to set up the model.");
275
- }
276
275
  function cacheDir() {
277
276
  if (process.env.STUPIFY_CACHE_DIR)
278
277
  return process.env.STUPIFY_CACHE_DIR;
package/dist/render.js CHANGED
@@ -1,38 +1,37 @@
1
1
  import { VERSION } from "./constants.js";
2
+ import { format } from "./ui.js";
2
3
  export function renderSearchRun(run, command) {
3
4
  if (command.json)
4
5
  return JSON.stringify(run, null, 2);
5
6
  if (run.stats.skipped && run.stats.skipReason === "input_too_large") {
6
- return `🧙 stupify 🪄
7
- Search input is too large for precise local search.
8
- Size:
7
+ return `${format.heading("Search input is too large for precise local search.")}
8
+ ${format.heading("Size:")}
9
9
  ~${run.stats.inputTokens ?? "unknown"} tokens
10
- Limit:
10
+ ${format.heading("Limit:")}
11
11
  ${run.stats.inputTokenCap ?? "unknown"} tokens
12
12
  Stupify skipped the search rather than review truncated context.
13
13
  Nothing was blocked.
14
- Try:
14
+ ${format.heading("Try:")}
15
15
  rerun with ${sourceHint(command)} --max-search-input-tokens ${Math.max((run.stats.inputTokens ?? 12_000) + 1, (run.stats.inputTokenCap ?? 12_000) * 2)}`;
16
16
  }
17
17
  if (run.stats.skipped && run.stats.skipReason === "no_candidates") {
18
- return `🧙 stupify 🪄
19
- Search complete.
20
- Patterns: ${run.patterns.join(", ")}
21
- No search targets found.`;
18
+ return `${format.heading("Search complete.")}
19
+ ${format.label("Patterns:")} ${run.patterns.join(", ")}
20
+ ${format.success("No search targets found.")}`;
22
21
  }
23
22
  if (run.matches.length === 0) {
24
- return `🧙 stupify 🪄
25
- Search complete.
26
- Patterns: ${run.patterns.join(", ")}
27
- No judgment-offload signals found.`;
23
+ return `${format.heading("Search complete.")}
24
+ ${format.label("Patterns:")} ${run.patterns.join(", ")}
25
+ ${format.success("No judgment-offload signals found.")}`;
28
26
  }
29
- return `🧙 stupify 🪄
30
- Possible judgment-offload detected:
31
- ${run.matches.map((match, index) => `${index + 1}. ${match.patternId}
32
- Why: ${match.checkWhy ?? "This pattern may indicate judgment-offload."}
33
- Match: ${match.reason}
34
- Proof: ${match.proof}`).join("\n")}
35
- Search mode is warn-only.`;
27
+ return `${format.warn("AI SLOP DETECTED")}
28
+ ${run.matches.map((match, index) => `${index + 1}.
29
+ ${format.muted("who:")} ${committerLabel(run)}
30
+ ${format.muted("what:")} ${match.patternId} - ${match.reason}
31
+ ${format.muted("when:")} ${sourceLabel(command)}
32
+ ${format.muted("where:")} ${match.proof}
33
+ ${format.muted("why:")} ${match.checkWhy ?? "This pattern may indicate judgment-offload."}`).join("\n")}
34
+ ${format.muted("Search mode is warn-only.")}`;
36
35
  }
37
36
  export function helpText() {
38
37
  return `Stupify ${VERSION}
@@ -92,3 +91,22 @@ function sourceHint(command) {
92
91
  return `--commits ${command.count}`;
93
92
  return "--stdin";
94
93
  }
94
+ function sourceLabel(command) {
95
+ if (command.kind === "staged")
96
+ return "staged changes";
97
+ if (command.kind === "since")
98
+ return `since ${command.since}`;
99
+ if (command.kind === "commit")
100
+ return `commit ${command.commit}`;
101
+ if (command.kind === "commits")
102
+ return `last ${command.count} commits`;
103
+ return "stdin diff";
104
+ }
105
+ function committerLabel(run) {
106
+ const committers = (run.stats.committers ?? []).filter(Boolean);
107
+ if (committers.length === 0)
108
+ return "unknown committer";
109
+ if (committers.length <= 3)
110
+ return committers.join(", ");
111
+ return `${committers.slice(0, 3).join(", ")} +${committers.length - 3} more`;
112
+ }
@@ -6,17 +6,19 @@ import path from "node:path";
6
6
  import { promisify } from "node:util";
7
7
  import { cachedJson, fingerprint } from "./cache.js";
8
8
  import { readDiffFromStdin } from "./diff.js";
9
- import { sourceRangeForCommit, sourceRangeForRecentCommits, sourceRangeSince, stagedDiff, } from "./git.js";
9
+ import { gitUserLabel, sourceRangeForCommit, sourceRangeForRecentCommits, sourceRangeSince, stagedDiff, } from "./git.js";
10
+ import { diagnostic } from "./ui.js";
10
11
  import { sourceId } from "./types.js";
11
12
  const execFileAsync = promisify(execFile);
12
13
  export async function semChangeSetForCommand(command) {
13
14
  if (command.kind === "stdin")
14
- return semChangeSetFromPatch(await readDiffFromStdin(), command.debugSem);
15
+ return semChangeSetFromPatch(await readDiffFromStdin(), command.debugSem, "stdin", ["stdin"]);
15
16
  if (command.kind === "staged") {
16
- const diff = await stagedDiff();
17
+ const [diff, committer] = await Promise.all([stagedDiff(), gitUserLabel()]);
18
+ const committers = [committer];
17
19
  if (!diff.text.trim())
18
- return emptyChangeSet("staged", diff.stats);
19
- return semChangeSetFromPatch(diff.text, command.debugSem, "staged");
20
+ return emptyChangeSet("staged", diff.stats, committers);
21
+ return semChangeSetFromPatch(diff.text, command.debugSem, "staged", committers);
20
22
  }
21
23
  if (command.kind === "commit") {
22
24
  const range = await sourceRangeForCommit(command.commit);
@@ -27,12 +29,13 @@ export async function semChangeSetForCommand(command) {
27
29
  const raw = await cachedSemDiff(["diff", "--from", range.base, "--to", range.target, "--format", "json"], range, command.debugSem);
28
30
  return withContextWorkspace(normalizeSemDiff(raw, range), command.debugSem);
29
31
  }
30
- function emptyChangeSet(label, stats) {
32
+ function emptyChangeSet(label, stats, committers) {
31
33
  return {
32
34
  id: sourceId(label),
33
35
  label,
34
36
  base: label,
35
37
  target: label,
38
+ committers,
36
39
  contextCwd: process.cwd(),
37
40
  cleanup: async () => undefined,
38
41
  changes: [],
@@ -56,7 +59,7 @@ async function semRangeForCommand(command) {
56
59
  return sourceRangeForRecentCommits(command.count);
57
60
  throw new Error("sem cannot resolve stdin as a git range.");
58
61
  }
59
- async function semChangeSetFromPatch(patch, debugSem, label = "stdin") {
62
+ async function semChangeSetFromPatch(patch, debugSem, label = "stdin", committers) {
60
63
  if (!patch.trim())
61
64
  throw new Error("No diff received on stdin.");
62
65
  const raw = await cachedJson("sem-diff", fingerprint({
@@ -71,6 +74,7 @@ async function semChangeSetFromPatch(patch, debugSem, label = "stdin") {
71
74
  label,
72
75
  base: label,
73
76
  target: label,
77
+ committers,
74
78
  stats: { filesChanged: 0, additions: 0, deletions: 0 },
75
79
  }),
76
80
  contextCwd: process.cwd(),
@@ -105,7 +109,7 @@ async function withContextWorkspace(changeSet, debugSem) {
105
109
  }
106
110
  async function runSem(args, debugSem, cwd = process.cwd()) {
107
111
  if (debugSem)
108
- console.error(`sem ${args.join(" ")}`);
112
+ diagnostic(`sem ${args.join(" ")}`);
109
113
  const { command, commandArgs } = resolveSemCommand(args);
110
114
  try {
111
115
  const { stdout, stderr } = await execFileAsync(command, commandArgs, {
@@ -113,7 +117,7 @@ async function runSem(args, debugSem, cwd = process.cwd()) {
113
117
  maxBuffer: 128 * 1024 * 1024,
114
118
  });
115
119
  if (debugSem && stderr.trim())
116
- console.error(stderr.trim());
120
+ diagnostic(stderr.trim());
117
121
  return JSON.parse(stdout);
118
122
  }
119
123
  catch (error) {
@@ -123,7 +127,7 @@ async function runSem(args, debugSem, cwd = process.cwd()) {
123
127
  }
124
128
  async function runSemWithInput(args, stdin, debugSem) {
125
129
  if (debugSem)
126
- console.error(`sem ${args.join(" ")}`);
130
+ diagnostic(`sem ${args.join(" ")}`);
127
131
  const { command, commandArgs } = resolveSemCommand(args);
128
132
  return new Promise((resolve, reject) => {
129
133
  const child = spawn(command, commandArgs, { stdio: ["pipe", "pipe", "pipe"] });
@@ -135,7 +139,7 @@ async function runSemWithInput(args, stdin, debugSem) {
135
139
  child.on("close", (code) => {
136
140
  const stderrText = Buffer.concat(stderr).toString("utf8");
137
141
  if (debugSem && stderrText.trim())
138
- console.error(stderrText.trim());
142
+ diagnostic(stderrText.trim());
139
143
  if (code !== 0) {
140
144
  reject(new Error(`sem failed with exit code ${code}${stderrText ? `: ${stderrText.trim()}` : ""}`));
141
145
  return;
@@ -152,7 +156,7 @@ async function runSemWithInput(args, stdin, debugSem) {
152
156
  }
153
157
  async function git(args, debugSem) {
154
158
  if (debugSem)
155
- console.error(`git ${args.join(" ")}`);
159
+ diagnostic(`git ${args.join(" ")}`);
156
160
  await execFileAsync("git", [...args], { maxBuffer: 128 * 1024 * 1024 });
157
161
  }
158
162
  async function cleanupWorktree(tempDir, worktreeAdded, debugSem) {
@@ -194,6 +198,7 @@ function normalizeSemDiff(value, range) {
194
198
  label: range.label,
195
199
  base: range.base,
196
200
  target: range.target,
201
+ committers: range.committers,
197
202
  contextCwd: process.cwd(),
198
203
  cleanup: async () => undefined,
199
204
  changes,
package/dist/stupify.d.ts CHANGED
@@ -1,4 +1,38 @@
1
1
  #!/usr/bin/env node
2
2
  import type { SearchCommand, SearchRunJson } from "./types.ts";
3
3
  export declare function main(argv?: string[]): Promise<number>;
4
- export declare function runSearchCommand(command: SearchCommand, startedAt: number): Promise<SearchRunJson>;
4
+ export declare function runSearchCommand(command: SearchCommand, startedAt: number, ui?: {
5
+ intro(title: string, logOptions?: Readonly<{
6
+ force?: boolean;
7
+ }>): void;
8
+ outro(message: string, logOptions?: Readonly<{
9
+ force?: boolean;
10
+ }>): void;
11
+ note(message: string, title?: string, logOptions?: Readonly<{
12
+ force?: boolean;
13
+ }>): void;
14
+ info(message: string, logOptions?: Readonly<{
15
+ force?: boolean;
16
+ }>): void;
17
+ step(message: string, logOptions?: Readonly<{
18
+ force?: boolean;
19
+ }>): void;
20
+ success(message: string, logOptions?: Readonly<{
21
+ force?: boolean;
22
+ }>): void;
23
+ warn(message: string, logOptions?: Readonly<{
24
+ force?: boolean;
25
+ }>): void;
26
+ error(message: string, logOptions?: Readonly<{
27
+ force?: boolean;
28
+ }>): void;
29
+ debug(message: string): void;
30
+ confirm(message: string): Promise<boolean>;
31
+ spinner(message: string, logOptions?: Readonly<{
32
+ force?: boolean;
33
+ }>): import("@clack/prompts").SpinnerResult;
34
+ progress(message: string, max: number, logOptions?: Readonly<{
35
+ force?: boolean;
36
+ }>): import("@clack/prompts").ProgressResult;
37
+ writeStdout(text: string): void;
38
+ }): Promise<SearchRunJson>;