thinkwell 0.5.4 → 0.5.6

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 (44) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +207 -279
  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.js +89 -245
  19. package/dist/cli/workspace.js +92 -226
  20. package/dist/connectors/index.js +1 -7
  21. package/dist/generated/features.d.ts +5 -0
  22. package/dist/generated/features.d.ts.map +1 -0
  23. package/dist/generated/features.js +4 -0
  24. package/dist/generated/features.js.map +1 -0
  25. package/dist/index.js +0 -5
  26. package/dist/schema.js +3 -36
  27. package/dist/session.js +50 -82
  28. package/dist/think-builder.d.ts.map +1 -1
  29. package/dist/think-builder.js +269 -370
  30. package/dist/think-builder.js.map +1 -1
  31. package/dist/thought-event.d.ts +1 -0
  32. package/dist/thought-event.d.ts.map +1 -1
  33. package/dist/thought-event.js +0 -1
  34. package/dist/thought-stream.js +60 -96
  35. package/dist-pkg/acp.cjs +13385 -1876
  36. package/dist-pkg/cli-build.cjs +171 -369
  37. package/dist-pkg/cli-bundle.cjs +289 -690
  38. package/dist-pkg/cli-check.cjs +202 -415
  39. package/dist-pkg/cli-dependency-check.cjs +39 -82
  40. package/dist-pkg/cli-dependency-errors.cjs +9 -41
  41. package/dist-pkg/cli-loader.cjs +90 -173
  42. package/dist-pkg/protocol.cjs +2 -8
  43. package/dist-pkg/thinkwell.cjs +876 -1842
  44. package/package.json +7 -6
@@ -1,296 +1,152 @@
1
- /**
2
- * Dependency checking for `thinkwell build` and `thinkwell check`.
3
- *
4
- * Implements a hybrid detection strategy:
5
- * - Fast path: Check package.json directly for dependencies
6
- * - Slow path: Fall back to `<pm> why --json` for workspace-hoisted dependencies
7
- *
8
- * @see doc/rfd/explicit-config.md for the full design
9
- */
10
1
  import { existsSync, readFileSync } from "node:fs";
11
2
  import { dirname, join } from "node:path";
12
3
  import { spawn } from "node:child_process";
13
- import { detectPackageManager, } from "./package-manager.js";
14
- /**
15
- * Read and parse package.json from a directory.
16
- * Returns null if the file doesn't exist or is invalid JSON.
17
- */
4
+ import { detectPackageManager } from "./package-manager.js";
18
5
  function readPackageJson(dir) {
19
- const pkgPath = join(dir, "package.json");
20
- if (!existsSync(pkgPath)) {
21
- return null;
22
- }
23
- try {
24
- const content = readFileSync(pkgPath, "utf-8");
25
- return JSON.parse(content);
26
- }
27
- catch {
28
- return null;
29
- }
6
+ const pkgPath = join(dir, "package.json");
7
+ if (!existsSync(pkgPath))
8
+ return null;
9
+ try {
10
+ const content = readFileSync(pkgPath, "utf-8");
11
+ return JSON.parse(content);
12
+ } catch {
13
+ return null;
14
+ }
30
15
  }
31
- /**
32
- * Check if a package is declared in package.json (fast path).
33
- *
34
- * Checks dependencies, devDependencies, peerDependencies, and optionalDependencies.
35
- * Returns the dependency status if found, or null if not found.
36
- */
37
16
  function checkPackageJsonDirect(pkg, packageName) {
38
- // Check each dependency type
39
- const depTypes = [
40
- pkg.dependencies,
41
- pkg.devDependencies,
42
- pkg.peerDependencies,
43
- pkg.optionalDependencies,
44
- ];
45
- for (const deps of depTypes) {
46
- if (deps && packageName in deps) {
47
- return {
48
- found: true,
49
- version: deps[packageName],
50
- source: "package.json",
51
- };
52
- }
53
- }
54
- return null;
17
+ const depTypes = [
18
+ pkg.dependencies,
19
+ pkg.devDependencies,
20
+ pkg.peerDependencies,
21
+ pkg.optionalDependencies
22
+ ];
23
+ for (const deps of depTypes)
24
+ if (deps && packageName in deps)
25
+ return {
26
+ found: !0,
27
+ version: deps[packageName],
28
+ source: "package.json"
29
+ };
30
+ return null;
55
31
  }
56
- // ============================================================================
57
- // Slow Path: Package Manager CLI Inspection
58
- // ============================================================================
59
- /**
60
- * Execute a command and return its stdout.
61
- * Rejects if the command fails or returns non-zero exit code.
62
- */
63
32
  function execCommand(cmd, cwd) {
64
- return new Promise((resolve, reject) => {
65
- const [command, ...args] = cmd;
66
- const proc = spawn(command, args, {
67
- cwd,
68
- stdio: ["ignore", "pipe", "pipe"],
69
- });
70
- let stdout = "";
71
- let stderr = "";
72
- proc.stdout.on("data", (data) => {
73
- stdout += data.toString();
74
- });
75
- proc.stderr.on("data", (data) => {
76
- stderr += data.toString();
77
- });
78
- proc.on("error", (err) => {
79
- reject(err);
80
- });
81
- proc.on("close", (code) => {
82
- if (code === 0) {
83
- resolve({ stdout, stderr });
84
- }
85
- else {
86
- reject(new Error(`Command failed with exit code ${code}: ${stderr}`));
87
- }
88
- });
33
+ return new Promise((resolve, reject) => {
34
+ const [command, ...args] = cmd, proc = spawn(command, args, {
35
+ cwd,
36
+ stdio: ["ignore", "pipe", "pipe"]
89
37
  });
38
+ let stdout = "", stderr = "";
39
+ proc.stdout.on("data", (data) => {
40
+ stdout += data.toString();
41
+ }), proc.stderr.on("data", (data) => {
42
+ stderr += data.toString();
43
+ }), proc.on("error", (err) => {
44
+ reject(err);
45
+ }), proc.on("close", (code) => {
46
+ code === 0 ? resolve({ stdout, stderr }) : reject(new Error(`Command failed with exit code ${code}: ${stderr}`));
47
+ });
48
+ });
90
49
  }
91
- /**
92
- * Parse pnpm why --json output to determine if a package is installed.
93
- *
94
- * pnpm why outputs JSON like:
95
- * {
96
- * "packageName": {
97
- * "version": "1.0.0",
98
- * "dependents": [...]
99
- * }
100
- * }
101
- *
102
- * For workspace-hoisted packages, the package appears with its version.
103
- */
104
50
  function parsePnpmWhyOutput(stdout, packageName) {
105
- try {
106
- const result = JSON.parse(stdout);
107
- // pnpm why returns an object with the package name as key
108
- if (result && typeof result === "object" && packageName in result) {
109
- const info = result[packageName];
110
- return {
111
- found: true,
112
- version: info?.version,
113
- source: "workspace",
114
- };
115
- }
116
- return null;
117
- }
118
- catch {
119
- return null;
120
- }
51
+ try {
52
+ const result = JSON.parse(stdout);
53
+ return result && typeof result == "object" && packageName in result ? {
54
+ found: !0,
55
+ version: result[packageName]?.version,
56
+ source: "workspace"
57
+ } : null;
58
+ } catch {
59
+ return null;
60
+ }
121
61
  }
122
- /**
123
- * Parse npm why --json output to determine if a package is installed.
124
- *
125
- * npm why (npm explain) outputs JSON array like:
126
- * [
127
- * {
128
- * "name": "packageName",
129
- * "version": "1.0.0",
130
- * "dependents": [...]
131
- * }
132
- * ]
133
- *
134
- * An empty array or error object means the package is not found.
135
- */
136
62
  function parseNpmWhyOutput(stdout, _packageName) {
137
- try {
138
- const result = JSON.parse(stdout);
139
- // npm why returns an array of explanations
140
- if (Array.isArray(result) && result.length > 0) {
141
- const first = result[0];
142
- return {
143
- found: true,
144
- version: first?.version,
145
- source: "workspace",
146
- };
147
- }
148
- return null;
149
- }
150
- catch {
151
- return null;
152
- }
63
+ try {
64
+ const result = JSON.parse(stdout);
65
+ return Array.isArray(result) && result.length > 0 ? {
66
+ found: !0,
67
+ version: result[0]?.version,
68
+ source: "workspace"
69
+ } : null;
70
+ } catch {
71
+ return null;
72
+ }
153
73
  }
154
- /**
155
- * Parse yarn why --json output to determine if a package is installed.
156
- *
157
- * Yarn Classic (v1) outputs newline-delimited JSON objects:
158
- * {"type":"info","data":"Found \"package@version\"..."}
159
- *
160
- * Yarn Berry (v2+) outputs similar but structured differently.
161
- * Exit code 0 with output indicates the package was found.
162
- */
163
74
  function parseYarnWhyOutput(stdout, _packageName) {
164
- // If there's any output and the command succeeded (which it did if we got here),
165
- // the package was found
166
- if (stdout.trim().length > 0) {
167
- // Try to extract version from the output
168
- // Yarn classic: {"type":"info","data":"Found \"pkg@1.0.0\"..."}
169
- let version;
170
- try {
171
- // Parse each line as JSON (yarn outputs newline-delimited JSON)
172
- const lines = stdout.trim().split("\n");
173
- for (const line of lines) {
174
- const obj = JSON.parse(line);
175
- if (obj.type === "info" && obj.data) {
176
- // Extract version from "Found \"pkg@1.0.0\"" pattern
177
- const match = obj.data.match(/"[^@]+@([^"]+)"/);
178
- if (match) {
179
- version = match[1];
180
- break;
181
- }
182
- }
183
- }
184
- }
185
- catch {
186
- // Couldn't parse version, but package was found
75
+ if (stdout.trim().length > 0) {
76
+ let version;
77
+ try {
78
+ const lines = stdout.trim().split(`
79
+ `);
80
+ for (const line of lines) {
81
+ const obj = JSON.parse(line);
82
+ if (obj.type === "info" && obj.data) {
83
+ const match = obj.data.match(/"[^@]+@([^"]+)"/);
84
+ if (match) {
85
+ version = match[1];
86
+ break;
87
+ }
187
88
  }
188
- return {
189
- found: true,
190
- version,
191
- source: "workspace",
192
- };
89
+ }
90
+ } catch {
193
91
  }
194
- return null;
92
+ return {
93
+ found: !0,
94
+ version,
95
+ source: "workspace"
96
+ };
97
+ }
98
+ return null;
195
99
  }
196
- /**
197
- * Check for a package using the package manager's `why` command (slow path).
198
- *
199
- * This handles workspace-hoisted dependencies that aren't declared directly
200
- * in the current package's package.json.
201
- */
202
100
  async function checkViaPackageManager(pm, packageName, cwd) {
203
- const cmd = pm.whyCommand(packageName);
204
- try {
205
- const { stdout } = await execCommand(cmd, cwd);
206
- switch (pm.name) {
207
- case "pnpm":
208
- return parsePnpmWhyOutput(stdout, packageName);
209
- case "npm":
210
- return parseNpmWhyOutput(stdout, packageName);
211
- case "yarn":
212
- return parseYarnWhyOutput(stdout, packageName);
213
- }
214
- }
215
- catch {
216
- // Command failed — package not found
217
- return null;
101
+ const cmd = pm.whyCommand(packageName);
102
+ try {
103
+ const { stdout } = await execCommand(cmd, cwd);
104
+ switch (pm.name) {
105
+ case "pnpm":
106
+ return parsePnpmWhyOutput(stdout, packageName);
107
+ case "npm":
108
+ return parseNpmWhyOutput(stdout, packageName);
109
+ case "yarn":
110
+ return parseYarnWhyOutput(stdout, packageName);
218
111
  }
112
+ } catch {
113
+ return null;
114
+ }
219
115
  }
220
- // ============================================================================
221
- // Main Check Function
222
- // ============================================================================
223
- /**
224
- * Check whether required dependencies (thinkwell, typescript) are available.
225
- *
226
- * Uses a hybrid strategy:
227
- * 1. Fast path: Check package.json directly
228
- * 2. Slow path: If not found, check via package manager CLI for workspace-hoisted deps
229
- *
230
- * @param projectDir - The project directory to check
231
- * @returns Status of both dependencies and package manager info
232
- */
233
116
  export async function checkDependencies(projectDir) {
234
- const pm = detectPackageManager(projectDir);
235
- const pkg = readPackageJson(projectDir);
236
- // Initialize results
237
- let thinkwellStatus = { found: false };
238
- let typescriptStatus = { found: false };
239
- // Fast path: check package.json directly
240
- if (pkg) {
241
- const thinkwellDirect = checkPackageJsonDirect(pkg, "thinkwell");
242
- if (thinkwellDirect) {
243
- thinkwellStatus = thinkwellDirect;
244
- }
245
- const typescriptDirect = checkPackageJsonDirect(pkg, "typescript");
246
- if (typescriptDirect) {
247
- typescriptStatus = typescriptDirect;
248
- }
249
- }
250
- // Slow path: if not found in package.json, check via package manager
251
- // This handles workspace-hoisted dependencies
252
- if (!thinkwellStatus.found) {
253
- const result = await checkViaPackageManager(pm, "thinkwell", projectDir);
254
- if (result) {
255
- thinkwellStatus = result;
256
- }
257
- }
258
- if (!typescriptStatus.found) {
259
- const result = await checkViaPackageManager(pm, "typescript", projectDir);
260
- if (result) {
261
- typescriptStatus = result;
262
- }
263
- }
264
- return {
265
- thinkwell: thinkwellStatus,
266
- typescript: typescriptStatus,
267
- packageManager: pm,
268
- };
117
+ const pm = detectPackageManager(projectDir), pkg = readPackageJson(projectDir);
118
+ let thinkwellStatus = { found: !1 }, typescriptStatus = { found: !1 };
119
+ if (pkg) {
120
+ const thinkwellDirect = checkPackageJsonDirect(pkg, "thinkwell");
121
+ thinkwellDirect && (thinkwellStatus = thinkwellDirect);
122
+ const typescriptDirect = checkPackageJsonDirect(pkg, "typescript");
123
+ typescriptDirect && (typescriptStatus = typescriptDirect);
124
+ }
125
+ if (!thinkwellStatus.found) {
126
+ const result = await checkViaPackageManager(pm, "thinkwell", projectDir);
127
+ result && (thinkwellStatus = result);
128
+ }
129
+ if (!typescriptStatus.found) {
130
+ const result = await checkViaPackageManager(pm, "typescript", projectDir);
131
+ result && (typescriptStatus = result);
132
+ }
133
+ return {
134
+ thinkwell: thinkwellStatus,
135
+ typescript: typescriptStatus,
136
+ packageManager: pm
137
+ };
269
138
  }
270
- /**
271
- * Check if a project has a package.json.
272
- * Used to determine if dependency checking should be performed at all.
273
- */
274
139
  export function hasPackageJson(projectDir) {
275
- return existsSync(join(projectDir, "package.json"));
140
+ return existsSync(join(projectDir, "package.json"));
276
141
  }
277
- /**
278
- * Find the nearest project root by walking up from `startDir`.
279
- *
280
- * A project root is a directory containing a `package.json`.
281
- * Returns the directory path, or undefined if none is found.
282
- */
283
142
  export function findProjectRoot(startDir) {
284
- let dir = startDir;
285
- while (true) {
286
- if (existsSync(join(dir, "package.json"))) {
287
- return dir;
288
- }
289
- const parent = dirname(dir);
290
- if (parent === dir) {
291
- return undefined;
292
- }
293
- dir = parent;
294
- }
143
+ let dir = startDir;
144
+ for (; ; ) {
145
+ if (existsSync(join(dir, "package.json")))
146
+ return dir;
147
+ const parent = dirname(dir);
148
+ if (parent === dir)
149
+ return;
150
+ dir = parent;
151
+ }
295
152
  }
296
- //# sourceMappingURL=dependency-check.js.map
@@ -1,92 +1,20 @@
1
- /**
2
- * Error message templates for missing dependencies.
3
- *
4
- * When a project has a package.json but is missing required dependencies
5
- * (thinkwell, typescript), we fail fast with clear remediation guidance.
6
- *
7
- * @see doc/rfd/explicit-config.md for the design rationale
8
- */
9
- // ============================================================================
10
- // Error Message Formatting
11
- // ============================================================================
12
- /**
13
- * Format an error message for missing dependencies.
14
- *
15
- * The message explains why explicit dependencies are required and provides
16
- * package-manager-specific commands for remediation.
17
- */
18
1
  export function formatMissingDependencyError(result) {
19
- const { thinkwell, typescript, packageManager } = result;
20
- const pm = packageManager;
21
- // Collect missing dependencies
22
- const missing = [];
23
- if (!thinkwell.found)
24
- missing.push("thinkwell");
25
- if (!typescript.found)
26
- missing.push("typescript");
27
- if (missing.length === 0) {
28
- return ""; // No error to format
29
- }
30
- // Build the error message
31
- const lines = [];
32
- // Primary error
33
- if (missing.length === 1) {
34
- lines.push(`Error: This project has a package.json but no dependency on '${missing[0]}'.`);
35
- }
36
- else {
37
- lines.push(`Error: This project has a package.json but is missing required dependencies.`);
38
- }
39
- lines.push("");
40
- // Explanation
41
- lines.push("When a project has explicit configuration, thinkwell expects explicit dependencies.");
42
- lines.push("This ensures you get the versions you expect, not versions bundled with the CLI.");
43
- lines.push("");
44
- // Remediation guidance
45
- lines.push("Run 'thinkwell init' to add the required dependencies, or add them manually:");
46
- // Generate package-manager-specific commands
47
- if (!thinkwell.found) {
48
- lines.push(` ${pm.addCommand("thinkwell")}`);
49
- }
50
- if (!typescript.found) {
51
- lines.push(` ${pm.addCommand("typescript", true)}`);
52
- }
53
- return lines.join("\n");
2
+ const { thinkwell, typescript, packageManager } = result, pm = packageManager, missing = [];
3
+ if (thinkwell.found || missing.push("thinkwell"), typescript.found || missing.push("typescript"), missing.length === 0)
4
+ return "";
5
+ const lines = [];
6
+ return missing.length === 1 ? lines.push(`Error: This project has a package.json but no dependency on '${missing[0]}'.`) : lines.push("Error: This project has a package.json but is missing required dependencies."), lines.push(""), lines.push("When a project has explicit configuration, thinkwell expects explicit dependencies."), lines.push("This ensures you get the versions you expect, not versions bundled with the CLI."), lines.push(""), lines.push("Run 'thinkwell init' to add the required dependencies, or add them manually:"), thinkwell.found || lines.push(` ${pm.addCommand("thinkwell")}`), typescript.found || lines.push(` ${pm.addCommand("typescript", !0)}`), lines.join(`
7
+ `);
54
8
  }
55
- /**
56
- * Check if any required dependencies are missing.
57
- */
58
9
  export function hasMissingDependencies(result) {
59
- return !result.thinkwell.found || !result.typescript.found;
10
+ return !result.thinkwell.found || !result.typescript.found;
60
11
  }
61
- /**
62
- * Check if required dependencies are missing, with optional typescript check.
63
- *
64
- * For `run` and `bundle`, typescript is only required when the script
65
- * uses `@JSONSchema` markers.
66
- */
67
12
  export function hasMissingDeps(result, options) {
68
- if (!result.thinkwell.found)
69
- return true;
70
- if (options.requireTypescript && !result.typescript.found)
71
- return true;
72
- return false;
13
+ return !!(!result.thinkwell.found || options.requireTypescript && !result.typescript.found);
73
14
  }
74
- /**
75
- * Format an error message for missing dependencies, with optional typescript check.
76
- *
77
- * Like `formatMissingDependencyError` but respects whether typescript is required.
78
- */
79
15
  export function formatMissingDepsError(result, options) {
80
- if (!options.requireTypescript && result.thinkwell.found) {
81
- return "";
82
- }
83
- // When typescript is not required, mask it as found to suppress its error line
84
- if (!options.requireTypescript) {
85
- return formatMissingDependencyError({
86
- ...result,
87
- typescript: { found: true },
88
- });
89
- }
90
- return formatMissingDependencyError(result);
16
+ return !options.requireTypescript && result.thinkwell.found ? "" : options.requireTypescript ? formatMissingDependencyError(result) : formatMissingDependencyError({
17
+ ...result,
18
+ typescript: { found: !0 }
19
+ });
91
20
  }
92
- //# sourceMappingURL=dependency-errors.js.map
package/dist/cli/fmt.js CHANGED
@@ -1,14 +1,2 @@
1
- /**
2
- * Shared ANSI formatting helpers for CLI help screens.
3
- *
4
- * Uses node:util styleText for consistent coloring across all
5
- * `thinkwell` subcommand help output.
6
- */
7
1
  import { styleText } from "node:util";
8
- export const cyan = (t) => styleText("cyan", t);
9
- export const cyanBold = (t) => styleText(["cyan", "bold"], t);
10
- export const greenBold = (t) => styleText(["green", "bold"], t);
11
- export const whiteBold = (t) => styleText(["white", "bold"], t);
12
- export const dim = (t) => styleText("dim", t);
13
- export const redBold = (t) => styleText(["red", "bold"], t);
14
- //# sourceMappingURL=fmt.js.map
2
+ export const cyan = (t) => styleText("cyan", t), cyanBold = (t) => styleText(["cyan", "bold"], t), greenBold = (t) => styleText(["green", "bold"], t), whiteBold = (t) => styleText(["white", "bold"], t), dim = (t) => styleText("dim", t), redBold = (t) => styleText(["red", "bold"], t);