thinkwell 0.5.5 → 0.5.7

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 (46) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +232 -278
  3. package/dist/agent.js.map +1 -1
  4. package/dist/build.js +44 -98
  5. package/dist/cli/build.js +92 -227
  6. package/dist/cli/bundle.js +570 -1136
  7. package/dist/cli/check.js +125 -214
  8. package/dist/cli/commands.js +63 -177
  9. package/dist/cli/compiler-host.js +81 -190
  10. package/dist/cli/dependency-check.js +125 -269
  11. package/dist/cli/dependency-errors.js +12 -84
  12. package/dist/cli/fmt.js +1 -13
  13. package/dist/cli/init-command.js +21 -68
  14. package/dist/cli/init.js +90 -220
  15. package/dist/cli/loader.js +95 -361
  16. package/dist/cli/new-command.js +25 -73
  17. package/dist/cli/package-manager.js +50 -117
  18. package/dist/cli/schema.d.ts.map +1 -1
  19. package/dist/cli/schema.js +91 -245
  20. package/dist/cli/schema.js.map +1 -1
  21. package/dist/cli/workspace.js +92 -226
  22. package/dist/connectors/index.js +1 -7
  23. package/dist/generated/features.d.ts +6 -0
  24. package/dist/generated/features.d.ts.map +1 -0
  25. package/dist/generated/features.js +5 -0
  26. package/dist/generated/features.js.map +1 -0
  27. package/dist/index.js +0 -5
  28. package/dist/schema.js +3 -36
  29. package/dist/session.js +50 -82
  30. package/dist/think-builder.d.ts.map +1 -1
  31. package/dist/think-builder.js +287 -368
  32. package/dist/think-builder.js.map +1 -1
  33. package/dist/thought-event.d.ts +1 -0
  34. package/dist/thought-event.d.ts.map +1 -1
  35. package/dist/thought-event.js +0 -1
  36. package/dist/thought-stream.js +60 -96
  37. package/dist-pkg/acp.cjs +13386 -1876
  38. package/dist-pkg/cli-build.cjs +264 -446
  39. package/dist-pkg/cli-bundle.cjs +433 -818
  40. package/dist-pkg/cli-check.cjs +302 -499
  41. package/dist-pkg/cli-dependency-check.cjs +39 -82
  42. package/dist-pkg/cli-dependency-errors.cjs +9 -41
  43. package/dist-pkg/cli-loader.cjs +91 -173
  44. package/dist-pkg/protocol.cjs +2 -8
  45. package/dist-pkg/thinkwell.cjs +927 -1846
  46. package/package.json +9 -7
package/dist/cli/check.js CHANGED
@@ -1,12 +1,3 @@
1
- /**
2
- * Check command for type-checking with @JSONSchema transformation.
3
- *
4
- * This module provides the `thinkwell check` command that runs TypeScript's
5
- * type checker without producing output files. It uses the same custom
6
- * CompilerHost as `thinkwell build`, but with `--noEmit` to skip emit.
7
- *
8
- * Supports single-package projects and workspace mode (pnpm/npm workspaces).
9
- */
10
1
  import ts from "typescript";
11
2
  import { existsSync } from "node:fs";
12
3
  import { resolve, join } from "node:path";
@@ -15,224 +6,144 @@ import { cyan, cyanBold, greenBold, whiteBold, dim } from "./fmt.js";
15
6
  import { detectWorkspace, resolvePackageName } from "./workspace.js";
16
7
  import { checkDependencies, hasPackageJson } from "./dependency-check.js";
17
8
  import { formatMissingDependencyError, hasMissingDependencies } from "./dependency-errors.js";
18
- // ============================================================================
19
- // Diagnostics Formatting
20
- // ============================================================================
21
9
  function createDiagnosticsHost(pretty) {
22
- return {
23
- getCanonicalFileName: (fileName) => fileName,
24
- getCurrentDirectory: ts.sys.getCurrentDirectory,
25
- getNewLine: () => ts.sys.newLine,
26
- };
10
+ return {
11
+ getCanonicalFileName: (fileName) => fileName,
12
+ getCurrentDirectory: ts.sys.getCurrentDirectory,
13
+ getNewLine: () => ts.sys.newLine
14
+ };
27
15
  }
28
16
  function formatDiagnostics(diagnostics, pretty) {
29
- if (diagnostics.length === 0)
30
- return "";
31
- const host = createDiagnosticsHost(pretty);
32
- return pretty
33
- ? ts.formatDiagnosticsWithColorAndContext(diagnostics, host)
34
- : ts.formatDiagnostics(diagnostics, host);
17
+ if (diagnostics.length === 0)
18
+ return "";
19
+ const host = createDiagnosticsHost(pretty);
20
+ return pretty ? ts.formatDiagnosticsWithColorAndContext(diagnostics, host) : ts.formatDiagnostics(diagnostics, host);
35
21
  }
36
- // ============================================================================
37
- // Single-Package Check
38
- // ============================================================================
39
- /**
40
- * Type-check a single package directory. Returns the number of errors found.
41
- */
42
22
  function checkPackage(dir, pretty, projectDir) {
43
- const configPath = resolve(dir, "tsconfig.json");
44
- const { program, configErrors } = projectDir
45
- ? createThinkwellProgram({ configPath, projectDir })
46
- : createThinkwellProgram(configPath);
47
- // Report config-level diagnostics
48
- if (configErrors.length > 0) {
49
- process.stderr.write(formatDiagnostics(configErrors, pretty));
50
- const hasFatal = configErrors.some((d) => d.category === ts.DiagnosticCategory.Error);
51
- if (hasFatal) {
52
- return configErrors.filter((d) => d.category === ts.DiagnosticCategory.Error).length;
53
- }
54
- }
55
- const diagnostics = ts.getPreEmitDiagnostics(program);
56
- const errors = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Error);
57
- if (diagnostics.length > 0) {
58
- process.stderr.write("\n");
59
- process.stderr.write(formatDiagnostics(diagnostics, pretty));
60
- }
61
- return errors.length;
23
+ const configPath = resolve(dir, "tsconfig.json"), { program, configErrors } = projectDir ? createThinkwellProgram({ configPath, projectDir }) : createThinkwellProgram(configPath);
24
+ if (configErrors.length > 0 && (process.stderr.write(formatDiagnostics(configErrors, pretty)), configErrors.some((d) => d.category === ts.DiagnosticCategory.Error)))
25
+ return configErrors.filter((d) => d.category === ts.DiagnosticCategory.Error).length;
26
+ const diagnostics = ts.getPreEmitDiagnostics(program), errors = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Error);
27
+ return diagnostics.length > 0 && (process.stderr.write(`
28
+ `), process.stderr.write(formatDiagnostics(diagnostics, pretty))), errors.length;
62
29
  }
63
- // ============================================================================
64
- // Check Command
65
- // ============================================================================
66
30
  export async function runCheck(options) {
67
- const cwd = process.cwd();
68
- const pretty = options.pretty ?? process.stdout.isTTY ?? false;
69
- const workspace = detectWorkspace(cwd);
70
- // --package requires a workspace
71
- if (options.packages && options.packages.length > 0 && !workspace) {
72
- process.stderr.write('Error: --package can only be used in a workspace.\n' +
73
- 'No pnpm-workspace.yaml or package.json "workspaces" found in current directory.\n');
74
- process.exit(2);
75
- }
76
- // Single-package mode (no workspace detected)
77
- if (!workspace) {
78
- // Check for required dependencies when a package.json exists
79
- if (hasPackageJson(cwd)) {
80
- const depCheck = await checkDependencies(cwd);
81
- if (hasMissingDependencies(depCheck)) {
82
- process.stderr.write(formatMissingDependencyError(depCheck) + "\n");
83
- process.exit(2);
84
- }
85
- }
86
- const configPath = resolve(cwd, "tsconfig.json");
87
- if (!existsSync(configPath)) {
88
- process.stderr.write("Error: Cannot find tsconfig.json\n");
89
- process.stderr.write("\n");
90
- process.stderr.write(" Run this command from a directory with a tsconfig.json.\n");
91
- process.exit(2);
92
- }
93
- // Read package name for display, fall back to directory name
94
- let pkgName = cwd.split("/").pop() ?? "project";
95
- try {
96
- const pkgPath = join(cwd, "package.json");
97
- if (existsSync(pkgPath)) {
98
- const pkg = JSON.parse((await import("node:fs")).readFileSync(pkgPath, "utf-8"));
99
- if (pkg.name)
100
- pkgName = pkg.name;
101
- }
102
- }
103
- catch {
104
- // Use directory name
105
- }
106
- process.stderr.write(` Checking ${pkgName}...\n`);
107
- const projectDir = hasPackageJson(cwd) ? cwd : undefined;
108
- const errorCount = checkPackage(cwd, pretty, projectDir);
109
- if (errorCount > 0) {
110
- process.exit(1);
111
- }
112
- process.stderr.write(" No type errors found.\n");
113
- return;
114
- }
115
- // Workspace mode: check dependencies at workspace root
116
- if (hasPackageJson(workspace.rootDir)) {
117
- const depCheck = await checkDependencies(workspace.rootDir);
118
- if (hasMissingDependencies(depCheck)) {
119
- process.stderr.write(formatMissingDependencyError(depCheck) + "\n");
120
- process.exit(2);
121
- }
122
- }
123
- // Workspace mode: determine which packages to check
124
- let packagesToCheck;
125
- if (options.packages && options.packages.length > 0) {
126
- // Resolve named packages
127
- packagesToCheck = [];
128
- for (const name of options.packages) {
129
- const result = resolvePackageName(name, workspace.members);
130
- switch (result.kind) {
131
- case "found":
132
- if (!result.member.hasTsConfig) {
133
- process.stderr.write(`Error: Package "${result.member.name}" has no tsconfig.json.\n`);
134
- process.exit(2);
135
- }
136
- packagesToCheck.push(result.member);
137
- break;
138
- case "ambiguous":
139
- process.stderr.write(`Error: Ambiguous package name "${result.name}". Matches:\n`);
140
- for (const m of result.matches) {
141
- process.stderr.write(` - ${m.name}\n`);
142
- }
143
- process.stderr.write("\nUse the full package name to disambiguate.\n");
144
- process.exit(2);
145
- break; // unreachable but satisfies control flow
146
- case "not-found":
147
- process.stderr.write(`Error: Package "${result.name}" not found in workspace.\n`);
148
- process.stderr.write("\nAvailable packages:\n");
149
- for (const available of result.available) {
150
- process.stderr.write(` - ${available}\n`);
151
- }
152
- process.exit(2);
153
- break; // unreachable but satisfies control flow
154
- }
155
- }
31
+ const cwd = process.cwd(), pretty = options.pretty ?? process.stdout.isTTY ?? !1, workspace = detectWorkspace(cwd);
32
+ if (options.packages && options.packages.length > 0 && !workspace && (process.stderr.write(`Error: --package can only be used in a workspace.
33
+ No pnpm-workspace.yaml or package.json "workspaces" found in current directory.
34
+ `), process.exit(2)), !workspace) {
35
+ if (hasPackageJson(cwd)) {
36
+ const depCheck = await checkDependencies(cwd);
37
+ hasMissingDependencies(depCheck) && (process.stderr.write(formatMissingDependencyError(depCheck) + `
38
+ `), process.exit(2));
156
39
  }
157
- else {
158
- // Check all packages that have tsconfig.json
159
- packagesToCheck = workspace.members.filter((m) => m.hasTsConfig);
40
+ const configPath = resolve(cwd, "tsconfig.json");
41
+ existsSync(configPath) || (process.stderr.write(`Error: Cannot find tsconfig.json
42
+ `), process.stderr.write(`
43
+ `), process.stderr.write(` Run this command from a directory with a tsconfig.json.
44
+ `), process.exit(2));
45
+ let pkgName = cwd.split("/").pop() ?? "project";
46
+ try {
47
+ const pkgPath = join(cwd, "package.json");
48
+ if (existsSync(pkgPath)) {
49
+ const pkg = JSON.parse((await import("node:fs")).readFileSync(pkgPath, "utf-8"));
50
+ pkg.name && (pkgName = pkg.name);
51
+ }
52
+ } catch {
160
53
  }
161
- if (packagesToCheck.length === 0) {
162
- process.stderr.write("No TypeScript packages found in workspace.\n");
163
- process.exit(2);
54
+ process.stderr.write(` Checking ${pkgName}...
55
+ `);
56
+ const projectDir = hasPackageJson(cwd) ? cwd : void 0;
57
+ checkPackage(cwd, pretty, projectDir) > 0 && process.exit(1), process.stderr.write(` No type errors found.
58
+ `);
59
+ return;
60
+ }
61
+ if (hasPackageJson(workspace.rootDir)) {
62
+ const depCheck = await checkDependencies(workspace.rootDir);
63
+ hasMissingDependencies(depCheck) && (process.stderr.write(formatMissingDependencyError(depCheck) + `
64
+ `), process.exit(2));
65
+ }
66
+ let packagesToCheck;
67
+ if (options.packages && options.packages.length > 0) {
68
+ packagesToCheck = [];
69
+ for (const name of options.packages) {
70
+ const result = resolvePackageName(name, workspace.members);
71
+ switch (result.kind) {
72
+ case "found":
73
+ result.member.hasTsConfig || (process.stderr.write(`Error: Package "${result.member.name}" has no tsconfig.json.
74
+ `), process.exit(2)), packagesToCheck.push(result.member);
75
+ break;
76
+ case "ambiguous":
77
+ process.stderr.write(`Error: Ambiguous package name "${result.name}". Matches:
78
+ `);
79
+ for (const m of result.matches)
80
+ process.stderr.write(` - ${m.name}
81
+ `);
82
+ process.stderr.write(`
83
+ Use the full package name to disambiguate.
84
+ `), process.exit(2);
85
+ break;
86
+ // unreachable but satisfies control flow
87
+ case "not-found":
88
+ process.stderr.write(`Error: Package "${result.name}" not found in workspace.
89
+ `), process.stderr.write(`
90
+ Available packages:
91
+ `);
92
+ for (const available of result.available)
93
+ process.stderr.write(` - ${available}
94
+ `);
95
+ process.exit(2);
96
+ break;
97
+ }
164
98
  }
165
- // Check each package
166
- let totalErrors = 0;
167
- let packagesWithErrors = 0;
168
- const total = packagesToCheck.length;
169
- for (const member of packagesToCheck) {
170
- process.stderr.write(` Checking ${member.name}...`);
171
- const originalCwd = process.cwd();
172
- process.chdir(member.dir);
173
- try {
174
- const errorCount = checkPackage(member.dir, pretty, member.dir);
175
- if (errorCount > 0) {
176
- totalErrors += errorCount;
177
- packagesWithErrors++;
178
- }
179
- else {
180
- process.stderr.write(" ok\n");
181
- }
182
- }
183
- finally {
184
- process.chdir(originalCwd);
185
- }
99
+ } else
100
+ packagesToCheck = workspace.members.filter((m) => m.hasTsConfig);
101
+ packagesToCheck.length === 0 && (process.stderr.write(`No TypeScript packages found in workspace.
102
+ `), process.exit(2));
103
+ let totalErrors = 0, packagesWithErrors = 0;
104
+ const total = packagesToCheck.length;
105
+ for (const member of packagesToCheck) {
106
+ process.stderr.write(` Checking ${member.name}...`);
107
+ const originalCwd = process.cwd();
108
+ process.chdir(member.dir);
109
+ try {
110
+ const errorCount = checkPackage(member.dir, pretty, member.dir);
111
+ errorCount > 0 ? (totalErrors += errorCount, packagesWithErrors++) : process.stderr.write(` ok
112
+ `);
113
+ } finally {
114
+ process.chdir(originalCwd);
186
115
  }
187
- // Summary
188
- process.stderr.write("\n");
189
- if (packagesWithErrors > 0) {
190
- process.stderr.write(` ${packagesWithErrors} of ${total} package${total === 1 ? "" : "s"} had errors.\n`);
191
- process.exit(1);
192
- }
193
- process.stderr.write(` All ${total} package${total === 1 ? "" : "s"} passed.\n`);
116
+ }
117
+ process.stderr.write(`
118
+ `), packagesWithErrors > 0 && (process.stderr.write(` ${packagesWithErrors} of ${total} package${total === 1 ? "" : "s"} had errors.
119
+ `), process.exit(1)), process.stderr.write(` All ${total} package${total === 1 ? "" : "s"} passed.
120
+ `);
194
121
  }
195
- // ============================================================================
196
- // Argument Parsing
197
- // ============================================================================
198
122
  export function parseCheckArgs(args) {
199
- const options = {};
200
- let i = 0;
201
- while (i < args.length) {
202
- const arg = args[i];
203
- if (arg === "-p" || arg === "--package") {
204
- i++;
205
- if (i >= args.length) {
206
- throw new Error("Missing value for --package");
207
- }
208
- if (!options.packages)
209
- options.packages = [];
210
- options.packages.push(args[i]);
211
- }
212
- else if (arg === "--pretty") {
213
- options.pretty = true;
214
- }
215
- else if (arg === "--no-pretty") {
216
- options.pretty = false;
217
- }
218
- else if (arg.startsWith("-")) {
219
- throw new Error(`Unknown option: ${arg}`);
220
- }
221
- else {
222
- throw new Error(`Unexpected argument: ${arg}\n\n` +
223
- ` "thinkwell check" type-checks the project without producing output.\n` +
224
- ` It does not take positional arguments.\n\n` +
225
- ` To check a specific workspace package, use: thinkwell check -p ${arg}`);
226
- }
227
- i++;
228
- }
229
- return options;
123
+ const options = {};
124
+ let i = 0;
125
+ for (; i < args.length; ) {
126
+ const arg = args[i];
127
+ if (arg === "-p" || arg === "--package") {
128
+ if (i++, i >= args.length)
129
+ throw new Error("Missing value for --package");
130
+ options.packages || (options.packages = []), options.packages.push(args[i]);
131
+ } else if (arg === "--pretty")
132
+ options.pretty = !0;
133
+ else if (arg === "--no-pretty")
134
+ options.pretty = !1;
135
+ else throw arg.startsWith("-") ? new Error(`Unknown option: ${arg}`) : new Error(`Unexpected argument: ${arg}
136
+
137
+ "thinkwell check" type-checks the project without producing output.
138
+ It does not take positional arguments.
139
+
140
+ To check a specific workspace package, use: thinkwell check -p ${arg}`);
141
+ i++;
142
+ }
143
+ return options;
230
144
  }
231
- // ============================================================================
232
- // Help
233
- // ============================================================================
234
145
  export function showCheckHelp() {
235
- console.log(`
146
+ console.log(`
236
147
  ${cyanBold("thinkwell check")} - ${whiteBold("Type-check TypeScript with @JSONSchema support")}
237
148
 
238
149
  ${greenBold("Usage:")}
@@ -264,6 +175,6 @@ ${greenBold("Examples:")}
264
175
  ${cyanBold("thinkwell check")} ${cyan("-p acp")} Check a specific workspace package
265
176
  ${cyanBold("thinkwell check")} ${cyan("-p acp -p protocol")} Check multiple packages
266
177
  ${cyanBold("thinkwell check")} ${cyan("--no-pretty")} Disable colorized output ${dim("(for CI)")}
267
- `.trim() + "\n");
178
+ `.trim() + `
179
+ `);
268
180
  }
269
- //# sourceMappingURL=check.js.map
@@ -1,67 +1,31 @@
1
- /**
2
- * Shared CLI commands and help text.
3
- *
4
- * This module is the single source of truth for help screens and
5
- * utility functions shared between the two CLI entry points:
6
- * - src/cli/main.cjs (compiled binary, CommonJS)
7
- * - bin/thinkwell (npm distribution, ESM)
8
- *
9
- * IMPORTANT: This module must remain self-contained (no local imports)
10
- * because main.cjs loads it via require() inside the pkg snapshot,
11
- * where ESM import resolution for sibling modules fails.
12
- */
13
1
  import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
14
2
  import { join, dirname } from "node:path";
15
3
  import { homedir } from "node:os";
16
4
  import { styleText } from "node:util";
17
- const cyan = (t) => styleText("cyan", t);
18
- const cyanBold = (t) => styleText(["cyan", "bold"], t);
19
- const greenBold = (t) => styleText(["green", "bold"], t);
20
- const whiteBold = (t) => styleText(["white", "bold"], t);
21
- const redBold = (t) => styleText(["red", "bold"], t);
22
- const dim = (t) => styleText("dim", t);
5
+ const cyan = (t) => styleText("cyan", t), cyanBold = (t) => styleText(["cyan", "bold"], t), greenBold = (t) => styleText(["green", "bold"], t), whiteBold = (t) => styleText(["white", "bold"], t), redBold = (t) => styleText(["red", "bold"], t), dim = (t) => styleText("dim", t);
23
6
  function getWelcomeMarkerPath() {
24
- const cacheDir = process.env.THINKWELL_CACHE_DIR || join(homedir(), ".cache", "thinkwell");
25
- return join(cacheDir, "welcome-version");
7
+ const cacheDir = process.env.THINKWELL_CACHE_DIR || join(homedir(), ".cache", "thinkwell");
8
+ return join(cacheDir, "welcome-version");
26
9
  }
27
10
  function shouldAnimate(version, forceWelcome) {
28
- if (forceWelcome)
29
- return true;
30
- try {
31
- const stored = readFileSync(getWelcomeMarkerPath(), "utf-8").trim();
32
- return stored !== version;
33
- }
34
- catch {
35
- return true;
36
- }
11
+ if (forceWelcome)
12
+ return !0;
13
+ try {
14
+ return readFileSync(getWelcomeMarkerPath(), "utf-8").trim() !== version;
15
+ } catch {
16
+ return !0;
17
+ }
37
18
  }
38
19
  function recordWelcomeVersion(version) {
39
- try {
40
- const markerPath = getWelcomeMarkerPath();
41
- mkdirSync(dirname(markerPath), { recursive: true });
42
- writeFileSync(markerPath, version + "\n", "utf-8");
43
- }
44
- catch {
45
- // Best-effort — if we can't write, the animation plays again next time.
46
- }
20
+ try {
21
+ const markerPath = getWelcomeMarkerPath();
22
+ mkdirSync(dirname(markerPath), { recursive: !0 }), writeFileSync(markerPath, version + `
23
+ `, "utf-8");
24
+ } catch {
25
+ }
47
26
  }
48
- /**
49
- * Print the main help screen to stdout, with an optional typewriter
50
- * animation on the tagline when stdout is an interactive TTY.
51
- *
52
- * The animation is gated to run once per installed version. Pass
53
- * `forceWelcome: true` (via the undocumented `--welcome` flag) to
54
- * bypass version gating.
55
- */
56
27
  export async function showMainHelp(options) {
57
- const anchor = cyanBold("thinkwell");
58
- const revealedText = " - agent scripting made easy";
59
- const pen = " āœļø ";
60
- const penWithSparkle = " āœØāœļø ";
61
- const { version, forceWelcome = false } = options;
62
- const ttyOk = process.stdout.isTTY && !process.env.CI;
63
- const animate = ttyOk && shouldAnimate(version, forceWelcome);
64
- const middleHelp = `
28
+ const anchor = cyanBold("thinkwell"), revealedText = " - agent scripting made easy", pen = " \u270D\uFE0F ", penWithSparkle = " \u2728\u270D\uFE0F ", { version, forceWelcome = !1 } = options, animate = process.stdout.isTTY && !process.env.CI && shouldAnimate(version, forceWelcome), middleHelp = `
65
29
 
66
30
  ${greenBold("Usage:")}
67
31
  ${cyanBold("thinkwell")} ${dim("<script.ts> [args...]")} Run a TypeScript script
@@ -76,136 +40,58 @@ ${greenBold("Usage:")}
76
40
 
77
41
  ${greenBold("Example:")}
78
42
  ${cyanBold("thinkwell")} ${cyan("my-agent.ts")}
79
- `;
80
- const infoLineText = "For more information, visit: ";
81
- const infoLineUrl = "https://thinkwell.sh";
82
- const infoLine = `${infoLineText}${cyanBold(infoLineUrl)}`;
83
- if (!animate) {
84
- console.log(anchor + whiteBold(revealedText + penWithSparkle) + middleHelp + "\n" + infoLine);
85
- return;
86
- }
87
- // Helper: typewriter with āœļø trailing cursor
88
- const typewrite = (chars, duration, styleFn) => new Promise((resolve) => {
89
- const interval = duration / chars.length;
90
- let i = 0;
91
- const timer = setInterval(() => {
92
- i++;
93
- const revealed = chars.slice(0, i).join("");
94
- process.stdout.write(`\r\x1b[K${styleFn(revealed)}${pen}`);
95
- if (i >= chars.length) {
96
- clearInterval(timer);
97
- resolve();
98
- }
99
- }, interval);
100
- });
101
- // Hide cursor during animation
102
- process.stdout.write("\x1b[?25l");
103
- // Stage 1: Typewriter the tagline over 1s
104
- process.stdout.write(pen);
105
- const fullText = "thinkwell" + revealedText;
106
- const fullChars = [...fullText];
107
- await typewrite(fullChars, 1000, (revealed) => revealed.length <= 9
108
- ? cyanBold(revealed)
109
- : cyanBold("thinkwell") + whiteBold(revealed.slice(9)));
110
- // Print the middle help text statically (remove āœļø from tagline first)
111
- process.stdout.write(`\r\x1b[K${anchor}${whiteBold(revealedText)}`);
112
- process.stdout.write(middleHelp + "\n");
113
- // Stage 2: Typewriter the "For more information" line with šŸ“– prefix over 1s
114
- const infoChars = [...(infoLineText + infoLineUrl)];
115
- await new Promise((resolve) => {
116
- const interval = 1000 / infoChars.length;
117
- let i = 0;
118
- const timer = setInterval(() => {
119
- i++;
120
- const revealed = infoChars.slice(0, i).join("");
121
- const styled = revealed.length <= infoLineText.length
122
- ? revealed
123
- : infoLineText + cyanBold(revealed.slice(infoLineText.length));
124
- process.stdout.write(`\r\x1b[KšŸ“– ${styled}${pen}`);
125
- if (i >= infoChars.length) {
126
- clearInterval(timer);
127
- resolve();
128
- }
129
- }, interval);
130
- });
131
- // Remove pen, keep šŸ“–
132
- process.stdout.write(`\r\x1b[KšŸ“– ${infoLine}`);
133
- // Stage 3: Pen travels up vertically in the same column to the tagline
134
- //
135
- // The pen sits just after "šŸ“– For more information, visit: https://thinkwell.sh"
136
- // "šŸ“– " = 3 visible cols, + infoLineText (29) + infoLineUrl (20) = 52 cols.
137
- // The pen emoji occupies col 53+. On each intermediate line, we save the
138
- // plain character at col 53 (all description text at that column is unstyled
139
- // ASCII), write the pen over it, then restore it before moving up.
140
- const penCol = 53;
141
- const goCol = `\x1b[${penCol}G`;
142
- // Strip ANSI escape codes to get visible text
143
- const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
144
- // Build array of plain chars at penCol for each line (bottom to top)
145
- const helpLines = (middleHelp + "\n").split("\n");
146
- // helpLines[0] corresponds to the line just below the tagline (bottom of array = closest to info line)
147
- const charAtCol = helpLines.map((line) => {
148
- const plain = stripAnsi(line);
149
- // penCol is 1-based, string index is 0-based
150
- return plain[penCol - 1] ?? " ";
151
- });
152
- // Number of \n characters between tagline and info line
153
- const linesFromInfoToTagline = helpLines.length - 1;
154
- const stepDelay = 40; // ms per line
155
- // Animate pen traveling upward — save/restore single char at fixed col
156
- for (let linesUp = 1; linesUp < linesFromInfoToTagline; linesUp++) {
157
- // Draw pen at fixed column on this line
158
- process.stdout.write(`\x1b[${linesUp}A${goCol}āœļø\x1b[${linesUp}B\r`);
159
- await new Promise((r) => setTimeout(r, stepDelay));
160
- // Restore original character at that column
161
- const originalChar = charAtCol[helpLines.length - 1 - linesUp];
162
- process.stdout.write(`\x1b[${linesUp}A${goCol}${originalChar}\x1b[${linesUp}B\r`);
163
- }
164
- // Pen arrives at tagline — slide left from col 53 to final position
165
- // The tagline "thinkwell - agent scripting made easy" is 38 visible chars.
166
- // The sparkle goes at col 39, pen (āœļø) at col 41 (after "✨").
167
- // But first the pen slides from col 53 to col 40 (one past the sparkle slot).
168
- const taglineRow = linesFromInfoToTagline;
169
- const sparkleCol = 39;
170
- for (let col = penCol; col > sparkleCol; col--) {
171
- process.stdout.write(`\x1b[${taglineRow}A\x1b[${col}Gāœļø\x1b[${taglineRow}B\r`);
172
- await new Promise((r) => setTimeout(r, stepDelay));
173
- // Erase pen (it's over spaces on the tagline)
174
- process.stdout.write(`\x1b[${taglineRow}A\x1b[${col}G \x1b[${taglineRow}B\r`);
175
- }
176
- // Write sparkle + pen at final position
177
- process.stdout.write(`\x1b[${taglineRow}A\r\x1b[K${anchor}${whiteBold(revealedText + penWithSparkle)}\x1b[${taglineRow}B\r`);
178
- // Record that this version's welcome animation has been shown
179
- recordWelcomeVersion(version);
180
- // Show cursor
181
- process.stdout.write("\x1b[?25h\n");
43
+ `, infoLineText = "For more information, visit: ", infoLineUrl = "https://thinkwell.sh", infoLine = `${infoLineText}${cyanBold(infoLineUrl)}`;
44
+ if (!animate) {
45
+ console.log(anchor + whiteBold(revealedText + penWithSparkle) + middleHelp + `
46
+ ` + infoLine);
47
+ return;
48
+ }
49
+ const typewrite = (chars, duration, styleFn) => new Promise((resolve) => {
50
+ const interval = duration / chars.length;
51
+ let i = 0;
52
+ const timer = setInterval(() => {
53
+ i++;
54
+ const revealed = chars.slice(0, i).join("");
55
+ process.stdout.write(`\r\x1B[K${styleFn(revealed)}${pen}`), i >= chars.length && (clearInterval(timer), resolve());
56
+ }, interval);
57
+ });
58
+ process.stdout.write("\x1B[?25l"), process.stdout.write(pen);
59
+ const fullChars = [..."thinkwell" + revealedText];
60
+ await typewrite(fullChars, 1e3, (revealed) => revealed.length <= 9 ? cyanBold(revealed) : cyanBold("thinkwell") + whiteBold(revealed.slice(9))), process.stdout.write(`\r\x1B[K${anchor}${whiteBold(revealedText)}`), process.stdout.write(middleHelp + `
61
+ `);
62
+ const infoChars = [...infoLineText + infoLineUrl];
63
+ await new Promise((resolve) => {
64
+ const interval = 1e3 / infoChars.length;
65
+ let i = 0;
66
+ const timer = setInterval(() => {
67
+ i++;
68
+ const revealed = infoChars.slice(0, i).join(""), styled = revealed.length <= infoLineText.length ? revealed : infoLineText + cyanBold(revealed.slice(infoLineText.length));
69
+ process.stdout.write(`\r\x1B[K\u{1F4D6} ${styled}${pen}`), i >= infoChars.length && (clearInterval(timer), resolve());
70
+ }, interval);
71
+ }), process.stdout.write(`\r\x1B[K\u{1F4D6} ${infoLine}`);
72
+ const penCol = 53, goCol = `\x1B[${penCol}G`, stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, ""), helpLines = (middleHelp + `
73
+ `).split(`
74
+ `), charAtCol = helpLines.map((line) => stripAnsi(line)[penCol - 1] ?? " "), linesFromInfoToTagline = helpLines.length - 1, stepDelay = 40;
75
+ for (let linesUp = 1; linesUp < linesFromInfoToTagline; linesUp++) {
76
+ process.stdout.write(`\x1B[${linesUp}A${goCol}\u270D\uFE0F\x1B[${linesUp}B\r`), await new Promise((r) => setTimeout(r, stepDelay));
77
+ const originalChar = charAtCol[helpLines.length - 1 - linesUp];
78
+ process.stdout.write(`\x1B[${linesUp}A${goCol}${originalChar}\x1B[${linesUp}B\r`);
79
+ }
80
+ const taglineRow = linesFromInfoToTagline, sparkleCol = 39;
81
+ for (let col = penCol; col > sparkleCol; col--)
82
+ process.stdout.write(`\x1B[${taglineRow}A\x1B[${col}G\u270D\uFE0F\x1B[${taglineRow}B\r`), await new Promise((r) => setTimeout(r, stepDelay)), process.stdout.write(`\x1B[${taglineRow}A\x1B[${col}G \x1B[${taglineRow}B\r`);
83
+ process.stdout.write(`\x1B[${taglineRow}A\r\x1B[K${anchor}${whiteBold(revealedText + penWithSparkle)}\x1B[${taglineRow}B\r`), recordWelcomeVersion(version), process.stdout.write(`\x1B[?25h
84
+ `);
182
85
  }
183
- /**
184
- * Print the "no script provided" error and exit.
185
- */
186
86
  export function showNoScriptError() {
187
- console.error(`${redBold("Error:")} No script provided.`);
188
- console.error("");
189
- console.error("Usage: thinkwell <script.ts> [args...]");
190
- process.exit(1);
87
+ console.error(`${redBold("Error:")} No script provided.`), console.error(""), console.error("Usage: thinkwell <script.ts> [args...]"), process.exit(1);
191
88
  }
192
- /**
193
- * Check if args contain a help flag (--help or -h).
194
- */
195
89
  export function hasHelpFlag(args) {
196
- return args.includes("--help") || args.includes("-h");
90
+ return args.includes("--help") || args.includes("-h");
197
91
  }
198
- /**
199
- * Check if args contain the undocumented --welcome flag.
200
- */
201
92
  export function hasWelcomeFlag(args) {
202
- return args.includes("--welcome");
93
+ return args.includes("--welcome");
203
94
  }
204
- /**
205
- * Format an error message with a red bold "Error:" prefix.
206
- * Use with console.error(): `console.error(fmtError("something went wrong"))`
207
- */
208
95
  export function fmtError(message) {
209
- return `${redBold("Error:")} ${message}`;
96
+ return `${redBold("Error:")} ${message}`;
210
97
  }
211
- //# sourceMappingURL=commands.js.map