From 5f8a64e6959bff708a630415c8b43b75f8ef3650 Mon Sep 17 00:00:00 2001 From: Vignesh Natarajan Date: Wed, 21 Jan 2026 21:42:06 -0800 Subject: [PATCH] feat: migrate lobster to TypeScript and oxlint --- .oxlintrc.json | 2 +- package.json | 2 +- pnpm-lock.yaml | 122 ++++++++++++++++++ src/{cli.js => cli.ts} | 13 +- src/commands/{registry.js => registry.ts} | 0 .../stdlib/{approve.js => approve.ts} | 0 .../{clawd_invoke.js => clawd_invoke.ts} | 6 +- .../stdlib/{diff_last.js => diff_last.ts} | 0 src/commands/stdlib/{exec.js => exec.ts} | 2 +- src/commands/stdlib/{head.js => head.ts} | 0 src/commands/stdlib/{json.js => json.ts} | 0 src/commands/stdlib/{pick.js => pick.ts} | 0 src/commands/stdlib/{state.js => state.ts} | 0 src/commands/stdlib/{table.js => table.ts} | 0 src/commands/stdlib/{where.js => where.ts} | 2 +- .../{workflows_list.js => workflows_list.ts} | 2 +- .../{workflows_run.js => workflows_run.ts} | 2 +- src/{parser.js => parser.ts} | 0 src/recipes/github/{index.js => index.ts} | 0 .../github/{pr-monitor.js => pr-monitor.ts} | 0 .../github/stages/{pr-view.js => pr-view.ts} | 8 +- src/recipes/{index.js => index.ts} | 0 src/recipes/{registry.js => registry.ts} | 11 +- src/renderers/{json.js => json.ts} | 0 src/{resume.js => resume.ts} | 0 src/{runtime.js => runtime.ts} | 0 src/sdk/{Lobster.js => Lobster.ts} | 6 +- src/sdk/{index.js => index.ts} | 0 src/sdk/primitives/{approve.js => approve.ts} | 4 +- src/sdk/primitives/{diff.js => diff.ts} | 2 +- src/sdk/primitives/{exec.js => exec.ts} | 6 +- src/sdk/primitives/{state.js => state.ts} | 2 +- src/sdk/{runtime.js => runtime.ts} | 0 src/sdk/{token.js => token.ts} | 0 src/state/{store.js => store.ts} | 0 src/{token.js => token.ts} | 2 +- ...hub_pr_monitor.js => github_pr_monitor.ts} | 12 +- src/workflows/{registry.js => registry.ts} | 0 ...wd_invoke.test.js => clawd_invoke.test.ts} | 5 +- ...cy.test.js => clawd_invoke_legacy.test.ts} | 5 +- test/{diff_last.test.js => diff_last.test.ts} | 0 test/{doctor.test.js => doctor.test.ts} | 5 +- ...est.js => github_pr_notify_format.test.ts} | 0 ...mary.test.js => github_pr_summary.test.ts} | 6 +- ....test.js => multi_approval_resume.test.ts} | 0 test/{parser.test.js => parser.test.ts} | 0 test/{resume.test.js => resume.test.ts} | 1 + test/{state.test.js => state.test.ts} | 1 + ....test.js => tool_envelope_version.test.ts} | 0 test/{tool_mode.test.js => tool_mode.test.ts} | 1 + test/{workflows.test.js => workflows.test.ts} | 0 tsconfig.json | 2 + 52 files changed, 189 insertions(+), 43 deletions(-) create mode 100644 pnpm-lock.yaml rename src/{cli.js => cli.ts} (96%) rename src/commands/{registry.js => registry.ts} (100%) rename src/commands/stdlib/{approve.js => approve.ts} (100%) rename src/commands/stdlib/{clawd_invoke.js => clawd_invoke.ts} (97%) rename src/commands/stdlib/{diff_last.js => diff_last.ts} (100%) rename src/commands/stdlib/{exec.js => exec.ts} (97%) rename src/commands/stdlib/{head.js => head.ts} (100%) rename src/commands/stdlib/{json.js => json.ts} (100%) rename src/commands/stdlib/{pick.js => pick.ts} (100%) rename src/commands/stdlib/{state.js => state.ts} (100%) rename src/commands/stdlib/{table.js => table.ts} (100%) rename src/commands/stdlib/{where.js => where.ts} (95%) rename src/commands/workflows/{workflows_list.js => workflows_list.ts} (92%) rename src/commands/workflows/{workflows_run.js => workflows_run.ts} (98%) rename src/{parser.js => parser.ts} (100%) rename src/recipes/github/{index.js => index.ts} (100%) rename src/recipes/github/{pr-monitor.js => pr-monitor.ts} (100%) rename src/recipes/github/stages/{pr-view.js => pr-view.ts} (91%) rename src/recipes/{index.js => index.ts} (100%) rename src/recipes/{registry.js => registry.ts} (65%) rename src/renderers/{json.js => json.ts} (100%) rename src/{resume.js => resume.ts} (100%) rename src/{runtime.js => runtime.ts} (100%) rename src/sdk/{Lobster.js => Lobster.ts} (98%) rename src/sdk/{index.js => index.ts} (100%) rename src/sdk/primitives/{approve.js => approve.ts} (93%) rename src/sdk/primitives/{diff.js => diff.ts} (98%) rename src/sdk/primitives/{exec.js => exec.ts} (96%) rename src/sdk/primitives/{state.js => state.ts} (98%) rename src/sdk/{runtime.js => runtime.ts} (100%) rename src/sdk/{token.js => token.ts} (100%) rename src/state/{store.js => store.ts} (100%) rename src/{token.js => token.ts} (95%) rename src/workflows/{github_pr_monitor.js => github_pr_monitor.ts} (94%) rename src/workflows/{registry.js => registry.ts} (100%) rename test/{clawd_invoke.test.js => clawd_invoke.test.ts} (90%) rename test/{clawd_invoke_legacy.test.js => clawd_invoke_legacy.test.ts} (90%) rename test/{diff_last.test.js => diff_last.test.ts} (100%) rename test/{doctor.test.js => doctor.test.ts} (76%) rename test/{github_pr_notify_format.test.js => github_pr_notify_format.test.ts} (100%) rename test/{github_pr_summary.test.js => github_pr_summary.test.ts} (90%) rename test/{multi_approval_resume.test.js => multi_approval_resume.test.ts} (100%) rename test/{parser.test.js => parser.test.ts} (100%) rename test/{resume.test.js => resume.test.ts} (99%) rename test/{state.test.js => state.test.ts} (99%) rename test/{tool_envelope_version.test.js => tool_envelope_version.test.ts} (100%) rename test/{tool_mode.test.js => tool_mode.test.ts} (99%) rename test/{workflows.test.js => workflows.test.ts} (100%) diff --git a/.oxlintrc.json b/.oxlintrc.json index 4f298c3..e8230ac 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -5,7 +5,7 @@ "es2022": true }, "rules": { - "eslint/no-unused-vars": "error", + "eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_", "varsIgnorePattern": "^_" }], "eslint/no-undef": "error", "typescript/no-explicit-any": "off" } diff --git a/package.json b/package.json index 5bd3558..f869ed7 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit", "lint": "oxlint --tsconfig tsconfig.json src test", "fmt": "oxlint --tsconfig tsconfig.json --fix src test", - "test": "pnpm build && node --test dist/test" + "test": "pnpm build && node --test dist/test/*.test.js" }, "devDependencies": { "@types/node": "^22.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..9276516 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,122 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@types/node': + specifier: ^22.0.0 + version: 22.19.7 + oxlint: + specifier: ^0.15.0 + version: 0.15.15 + typescript: + specifier: ^5.7.0 + version: 5.9.3 + +packages: + + '@oxlint/darwin-arm64@0.15.15': + resolution: {integrity: sha512-7GOyGM6D36lUhsOvavAVpF72SycPVG0Enunx0bzv8g0+9TklzOSFN3FJlZjLst14VPdZWujZMLgkQC7tOp+Rwg==} + cpu: [arm64] + os: [darwin] + + '@oxlint/darwin-x64@0.15.15': + resolution: {integrity: sha512-pbrnYFwMn/fuX0z3IeQ05Nvo/b1zGxjmmWgkrQSDwYHxBxP6NT41hk1pmqkcA+v53xk9wvOa/6vBBI/U30F8Ow==} + cpu: [x64] + os: [darwin] + + '@oxlint/linux-arm64-gnu@0.15.15': + resolution: {integrity: sha512-QWjG3YVsDlIvDTBUPmtPiyqP34ZQpFJqQh2JO94pBih11lFxQ0IGVMEXDhmW3WdiSFPZSJsZGzWynalM9eg+RA==} + cpu: [arm64] + os: [linux] + + '@oxlint/linux-arm64-musl@0.15.15': + resolution: {integrity: sha512-4W0YsmMSbNzzExOWhk+6zNfmJEmKFqSjFIn8CKLtYFvH8kF6KjoW4/0HNsDNYW5Fz+KOut/2JgkvxAiKH+r0zA==} + cpu: [arm64] + os: [linux] + + '@oxlint/linux-x64-gnu@0.15.15': + resolution: {integrity: sha512-agP3e+eQ6tE5tqN6VI4Uukx2yvjwYFjtrDMcB19J7PmGOaFRwuMuT0sNWK/9guvhuS9aCINNZTi3kEhMy9Qgng==} + cpu: [x64] + os: [linux] + + '@oxlint/linux-x64-musl@0.15.15': + resolution: {integrity: sha512-L2qE9NhhUafsJOO4pofLx/0hW5IB0sfJa6bS85q0j+ySaI0f3CxMaAadrZLFSuqHWB3oF18B5yvzaPWsc2ohbQ==} + cpu: [x64] + os: [linux] + + '@oxlint/win32-arm64@0.15.15': + resolution: {integrity: sha512-B7f4VAS/E78n8zy6XZlNeyYOtWTel4BJn/22Ap2yEAlNzO34ot8dGfpLk6MqTUWJrRnARwVBVmc3wRVrsOT5yg==} + cpu: [arm64] + os: [win32] + + '@oxlint/win32-x64@0.15.15': + resolution: {integrity: sha512-ZM9T3/OpaQ3qvrk/VuHO2EQmhNH4cOZdr/b/Ju9VKwBr+ahhqMn3W5srrplWQWxfsb0yd1yBj7iD0jdAps2iLg==} + cpu: [x64] + os: [win32] + + '@types/node@22.19.7': + resolution: {integrity: sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==} + + oxlint@0.15.15: + resolution: {integrity: sha512-oQNc1mAHrrbKiXyKJMGs9VCZfwGfLy7YiQKa4qupi71X/u4xyWqOh36YKXqWOXnmm2y7vfWFpGZlhJPAa9tMqA==} + engines: {node: '>=8.*'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + +snapshots: + + '@oxlint/darwin-arm64@0.15.15': + optional: true + + '@oxlint/darwin-x64@0.15.15': + optional: true + + '@oxlint/linux-arm64-gnu@0.15.15': + optional: true + + '@oxlint/linux-arm64-musl@0.15.15': + optional: true + + '@oxlint/linux-x64-gnu@0.15.15': + optional: true + + '@oxlint/linux-x64-musl@0.15.15': + optional: true + + '@oxlint/win32-arm64@0.15.15': + optional: true + + '@oxlint/win32-x64@0.15.15': + optional: true + + '@types/node@22.19.7': + dependencies: + undici-types: 6.21.0 + + oxlint@0.15.15: + optionalDependencies: + '@oxlint/darwin-arm64': 0.15.15 + '@oxlint/darwin-x64': 0.15.15 + '@oxlint/linux-arm64-gnu': 0.15.15 + '@oxlint/linux-arm64-musl': 0.15.15 + '@oxlint/linux-x64-gnu': 0.15.15 + '@oxlint/linux-x64-musl': 0.15.15 + '@oxlint/win32-arm64': 0.15.15 + '@oxlint/win32-x64': 0.15.15 + + typescript@5.9.3: {} + + undici-types@6.21.0: {} diff --git a/src/cli.js b/src/cli.ts similarity index 96% rename from src/cli.js rename to src/cli.ts index 600f4d3..a7f7aab 100644 --- a/src/cli.js +++ b/src/cli.ts @@ -74,6 +74,7 @@ async function handleRun({ argv, registry }) { const output = await runPipeline({ pipeline, registry, + input: [], stdin: process.stdin, stdout: process.stdout, stderr: process.stderr, @@ -223,7 +224,7 @@ async function readVersion() { const { dirname, join } = await import('node:path'); const here = dirname(fileURLToPath(import.meta.url)); - const pkgPath = join(here, '..', 'package.json'); + const pkgPath = join(here, '..', '..', 'package.json'); const pkg = JSON.parse(await readFile(pkgPath, 'utf8')); return pkg.version ?? '0.0.0'; } @@ -231,25 +232,29 @@ async function readVersion() { async function handleDoctor({ argv, registry }) { const mode = 'tool'; const pipeline = "exec --json --shell 'echo [1]'"; - const output = await (async () => { + const output: any = await (async () => { try { const parsed = parsePipeline(pipeline); return await runPipeline({ pipeline: parsed, registry, + input: [], stdin: process.stdin, stdout: process.stdout, stderr: process.stderr, env: process.env, mode, }); - } catch (err) { + } catch (err: any) { return { error: err }; } })(); if (output?.error) { - writeToolEnvelope({ ok: false, error: { type: 'doctor_error', message: output.error?.message ?? String(output.error) } }); + writeToolEnvelope({ + ok: false, + error: { type: 'doctor_error', message: output.error?.message ?? String(output.error) }, + }); process.exitCode = 1; return; } diff --git a/src/commands/registry.js b/src/commands/registry.ts similarity index 100% rename from src/commands/registry.js rename to src/commands/registry.ts diff --git a/src/commands/stdlib/approve.js b/src/commands/stdlib/approve.ts similarity index 100% rename from src/commands/stdlib/approve.js rename to src/commands/stdlib/approve.ts diff --git a/src/commands/stdlib/clawd_invoke.js b/src/commands/stdlib/clawd_invoke.ts similarity index 97% rename from src/commands/stdlib/clawd_invoke.js rename to src/commands/stdlib/clawd_invoke.ts index 4771d74..969cd95 100644 --- a/src/commands/stdlib/clawd_invoke.js +++ b/src/commands/stdlib/clawd_invoke.ts @@ -14,7 +14,7 @@ export const clawdInvokeCommand = { }, async run({ input, args, ctx }) { // Drain input: for now we don't stream input into clawd calls. - for await (const _ of input) { + for await (const _item of input) { // no-op } @@ -31,7 +31,7 @@ export const clawdInvokeCommand = { if (args['args-json']) { try { toolArgs = JSON.parse(String(args['args-json'])); - } catch (err) { + } catch (_err) { throw new Error('clawd.invoke --args-json must be valid JSON'); } } @@ -63,7 +63,7 @@ export const clawdInvokeCommand = { let parsed; try { parsed = text ? JSON.parse(text) : null; - } catch (err) { + } catch (_err) { throw new Error('clawd.invoke expected JSON response'); } diff --git a/src/commands/stdlib/diff_last.js b/src/commands/stdlib/diff_last.ts similarity index 100% rename from src/commands/stdlib/diff_last.js rename to src/commands/stdlib/diff_last.ts diff --git a/src/commands/stdlib/exec.js b/src/commands/stdlib/exec.ts similarity index 97% rename from src/commands/stdlib/exec.js rename to src/commands/stdlib/exec.ts index 2ea3dd0..7dc4471 100644 --- a/src/commands/stdlib/exec.js +++ b/src/commands/stdlib/exec.ts @@ -43,7 +43,7 @@ export const execCommand = { }; function runProcess(command, argv, { env, cwd }) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const child = spawn(command, argv, { env, cwd, diff --git a/src/commands/stdlib/head.js b/src/commands/stdlib/head.ts similarity index 100% rename from src/commands/stdlib/head.js rename to src/commands/stdlib/head.ts diff --git a/src/commands/stdlib/json.js b/src/commands/stdlib/json.ts similarity index 100% rename from src/commands/stdlib/json.js rename to src/commands/stdlib/json.ts diff --git a/src/commands/stdlib/pick.js b/src/commands/stdlib/pick.ts similarity index 100% rename from src/commands/stdlib/pick.js rename to src/commands/stdlib/pick.ts diff --git a/src/commands/stdlib/state.js b/src/commands/stdlib/state.ts similarity index 100% rename from src/commands/stdlib/state.js rename to src/commands/stdlib/state.ts diff --git a/src/commands/stdlib/table.js b/src/commands/stdlib/table.ts similarity index 100% rename from src/commands/stdlib/table.js rename to src/commands/stdlib/table.ts diff --git a/src/commands/stdlib/where.js b/src/commands/stdlib/where.ts similarity index 95% rename from src/commands/stdlib/where.js rename to src/commands/stdlib/where.ts index 559ca6d..4cb6b34 100644 --- a/src/commands/stdlib/where.js +++ b/src/commands/stdlib/where.ts @@ -1,5 +1,5 @@ function parsePredicate(expr) { - const m = expr.match(/^([a-zA-Z0-9_\.]+)\s*(==|=|!=|<=|>=|<|>)\s*(.+)$/); + const m = expr.match(/^([a-zA-Z0-9_.]+)\s*(==|=|!=|<=|>=|<|>)\s*(.+)$/); if (!m) throw new Error(`Invalid where expression: ${expr}`); const [, path, op, rawValue] = m; diff --git a/src/commands/workflows/workflows_list.js b/src/commands/workflows/workflows_list.ts similarity index 92% rename from src/commands/workflows/workflows_list.js rename to src/commands/workflows/workflows_list.ts index 417c82d..7fea88c 100644 --- a/src/commands/workflows/workflows_list.js +++ b/src/commands/workflows/workflows_list.ts @@ -7,7 +7,7 @@ export const workflowsListCommand = { }, async run({ input }) { // Drain input. - for await (const _ of input) { + for await (const _item of input) { // no-op } diff --git a/src/commands/workflows/workflows_run.js b/src/commands/workflows/workflows_run.ts similarity index 98% rename from src/commands/workflows/workflows_run.js rename to src/commands/workflows/workflows_run.ts index 43839f5..6bf3e63 100644 --- a/src/commands/workflows/workflows_run.js +++ b/src/commands/workflows/workflows_run.ts @@ -17,7 +17,7 @@ export const workflowsRunCommand = { }, async run({ input, args, ctx }) { // Drain input. - for await (const _ of input) { + for await (const _item of input) { // no-op } diff --git a/src/parser.js b/src/parser.ts similarity index 100% rename from src/parser.js rename to src/parser.ts diff --git a/src/recipes/github/index.js b/src/recipes/github/index.ts similarity index 100% rename from src/recipes/github/index.js rename to src/recipes/github/index.ts diff --git a/src/recipes/github/pr-monitor.js b/src/recipes/github/pr-monitor.ts similarity index 100% rename from src/recipes/github/pr-monitor.js rename to src/recipes/github/pr-monitor.ts diff --git a/src/recipes/github/stages/pr-view.js b/src/recipes/github/stages/pr-view.ts similarity index 91% rename from src/recipes/github/stages/pr-view.js rename to src/recipes/github/stages/pr-view.ts index fa80119..96cc39b 100644 --- a/src/recipes/github/stages/pr-view.js +++ b/src/recipes/github/stages/pr-view.ts @@ -19,7 +19,7 @@ import { spawn } from 'node:child_process'; * @returns {Promise<{stdout: string, stderr: string}>} */ function runGh(argv, { env, cwd }) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const child = spawn('gh', argv, { env, cwd, @@ -35,7 +35,7 @@ function runGh(argv, { env, cwd }) { child.stdout.on('data', (d) => { stdout += d; }); child.stderr.on('data', (d) => { stderr += d; }); - child.on('error', (err) => { + child.on('error', (err: any) => { if (err?.code === 'ENOENT') { reject(new Error('gh not found on PATH (install GitHub CLI)')); return; @@ -80,7 +80,7 @@ export function ghPrView(options) { async run({ input, ctx }) { // Drain input - for await (const _ of input) { + for await (const _item of input) { // no-op } @@ -91,7 +91,7 @@ export function ghPrView(options) { '--json', fields.join(','), ]; - const { stdout } = await runGh(argv, { env: ctx.env, cwd: process.cwd() }); + const { stdout } = (await runGh(argv, { env: ctx.env, cwd: process.cwd() })) as any; let parsed; try { diff --git a/src/recipes/index.js b/src/recipes/index.ts similarity index 100% rename from src/recipes/index.js rename to src/recipes/index.ts diff --git a/src/recipes/registry.js b/src/recipes/registry.ts similarity index 65% rename from src/recipes/registry.js rename to src/recipes/registry.ts index 84faac2..a491c26 100644 --- a/src/recipes/registry.js +++ b/src/recipes/registry.ts @@ -4,14 +4,21 @@ import { prMonitor, prMonitorNotify } from "./github/pr-monitor.js"; -const recipes = { +const recipes: Record = { "github.pr.monitor": prMonitor, "github.pr.monitor.notify": prMonitorNotify, }; +export function registerRecipe(fn) { + const meta = fn?.meta ?? {}; + const name = meta.name; + if (!name) throw new Error("Recipe is missing meta.name"); + recipes[name] = fn; +} + export function listRecipes() { return Object.entries(recipes).map(([name, fn]) => { - const meta = fn.meta ?? {}; + const meta: any = (fn as any).meta ?? {}; return { name, description: meta.description ?? "", diff --git a/src/renderers/json.js b/src/renderers/json.ts similarity index 100% rename from src/renderers/json.js rename to src/renderers/json.ts diff --git a/src/resume.js b/src/resume.ts similarity index 100% rename from src/resume.js rename to src/resume.ts diff --git a/src/runtime.js b/src/runtime.ts similarity index 100% rename from src/runtime.js rename to src/runtime.ts diff --git a/src/sdk/Lobster.js b/src/sdk/Lobster.ts similarity index 98% rename from src/sdk/Lobster.js rename to src/sdk/Lobster.ts index b72aad5..b978d7a 100644 --- a/src/sdk/Lobster.js +++ b/src/sdk/Lobster.ts @@ -32,8 +32,8 @@ export class Lobster { /** @type {Array} */ #stages = []; - /** @type {LobsterOptions} */ - #options = {}; + /** @type {any} */ + #options: any = {} as any; /** @type {Object|null} */ #meta = null; @@ -42,7 +42,7 @@ export class Lobster { * Create a new Lobster workflow builder * @param {LobsterOptions} [options] */ - constructor(options = {}) { + constructor(options: any = {}) { this.#options = { env: options.env ?? process.env, stateDir: options.stateDir, diff --git a/src/sdk/index.js b/src/sdk/index.ts similarity index 100% rename from src/sdk/index.js rename to src/sdk/index.ts diff --git a/src/sdk/primitives/approve.js b/src/sdk/primitives/approve.ts similarity index 93% rename from src/sdk/primitives/approve.js rename to src/sdk/primitives/approve.ts index 31c1c51..fcbde74 100644 --- a/src/sdk/primitives/approve.js +++ b/src/sdk/primitives/approve.ts @@ -18,7 +18,7 @@ * @param {boolean} [options.preview=true] - Include items in approval request * @returns {Object} Stage object with run method */ -export function approve(options = {}) { +export function approve(options: any = {}) { const prompt = options.prompt ?? 'Approve?'; const preview = options.preview !== false; @@ -26,7 +26,7 @@ export function approve(options = {}) { type: 'approve', prompt, - async run({ input, ctx }) { + async run({ input, ctx: _ctx }) { // Collect all items const items = []; for await (const item of input) { diff --git a/src/sdk/primitives/diff.js b/src/sdk/primitives/diff.ts similarity index 98% rename from src/sdk/primitives/diff.js rename to src/sdk/primitives/diff.ts index 86bc2ca..ca9d881 100644 --- a/src/sdk/primitives/diff.js +++ b/src/sdk/primitives/diff.ts @@ -72,7 +72,7 @@ function stableStringify(value) { * @param {boolean} [options.changesOnly=false] - If true, suppress output when unchanged * @returns {Object} Stage object with run method */ -export function diffLast(key, options = {}) { +export function diffLast(key, options: any = {}) { if (!key) throw new Error('diffLast requires a key'); const changesOnly = options.changesOnly === true; diff --git a/src/sdk/primitives/exec.js b/src/sdk/primitives/exec.ts similarity index 96% rename from src/sdk/primitives/exec.js rename to src/sdk/primitives/exec.ts index e834dbe..7f59573 100644 --- a/src/sdk/primitives/exec.js +++ b/src/sdk/primitives/exec.ts @@ -19,7 +19,7 @@ import { spawn } from 'node:child_process'; * @returns {Promise<{stdout: string, stderr: string}>} */ function runProcess(command, argv, { env, cwd }) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const child = spawn(command, argv, { env, cwd, @@ -112,7 +112,7 @@ function parseCommand(cmdString) { * @param {string} [options.cwd] - Working directory * @returns {Object} Stage object with run method */ -export function exec(cmdString, options = {}) { +export function exec(cmdString, options: any = {}) { const parseJson = options.json !== false; const useShell = options.shell === true; const cwd = options.cwd ?? process.cwd(); @@ -123,7 +123,7 @@ export function exec(cmdString, options = {}) { async run({ input, ctx }) { // Drain input (exec doesn't use input stream) - for await (const _ of input) { + for await (const _item of input) { // no-op } diff --git a/src/sdk/primitives/state.js b/src/sdk/primitives/state.ts similarity index 98% rename from src/sdk/primitives/state.js rename to src/sdk/primitives/state.ts index c947632..9028a61 100644 --- a/src/sdk/primitives/state.js +++ b/src/sdk/primitives/state.ts @@ -63,7 +63,7 @@ export function stateGet(key) { async run({ input, ctx }) { // Drain input - for await (const _ of input) { + for await (const _item of input) { // no-op } diff --git a/src/sdk/runtime.js b/src/sdk/runtime.ts similarity index 100% rename from src/sdk/runtime.js rename to src/sdk/runtime.ts diff --git a/src/sdk/token.js b/src/sdk/token.ts similarity index 100% rename from src/sdk/token.js rename to src/sdk/token.ts diff --git a/src/state/store.js b/src/state/store.ts similarity index 100% rename from src/state/store.js rename to src/state/store.ts diff --git a/src/token.js b/src/token.ts similarity index 95% rename from src/token.js rename to src/token.ts index 52a23b5..f0bcd3f 100644 --- a/src/token.js +++ b/src/token.ts @@ -9,7 +9,7 @@ export function decodeToken(token) { try { const json = Buffer.from(String(token), 'base64url').toString('utf8'); return JSON.parse(json); - } catch (err) { + } catch (_err) { throw new Error('Invalid token'); } } diff --git a/src/workflows/github_pr_monitor.js b/src/workflows/github_pr_monitor.ts similarity index 94% rename from src/workflows/github_pr_monitor.js rename to src/workflows/github_pr_monitor.ts index cf13fef..3b469ea 100644 --- a/src/workflows/github_pr_monitor.js +++ b/src/workflows/github_pr_monitor.ts @@ -13,7 +13,7 @@ function runProcess(command, argv, { env, cwd }) { child.stdout.on('data', (d) => { stdout += d; }); child.stderr.on('data', (d) => { stderr += d; }); - child.on('error', (err) => { + child.on('error', (err: any) => { if (err?.code === 'ENOENT') { reject(new Error('gh not found on PATH (install GitHub CLI)')); return; @@ -97,7 +97,7 @@ export async function runGithubPrMonitorWorkflow({ args, ctx }) { 'number,title,url,state,isDraft,mergeable,reviewDecision,author,baseRefName,headRefName,updatedAt', ]; - const { stdout } = await runProcess('gh', argv, { env: ctx.env, cwd: process.cwd() }); + const { stdout } = (await runProcess('gh', argv, { env: ctx.env, cwd: process.cwd() })) as any; let current; try { @@ -112,7 +112,7 @@ export async function runGithubPrMonitorWorkflow({ args, ctx }) { return { kind: 'github.pr.monitor', repo, - pr: Number(pr), + prNumber: Number(pr), key, changed: false, suppressed: true, @@ -125,7 +125,7 @@ export async function runGithubPrMonitorWorkflow({ args, ctx }) { return { kind: 'github.pr.monitor', repo, - pr: Number(pr), + prNumber: Number(pr), key, changed, summary, @@ -142,7 +142,7 @@ export async function runGithubPrMonitorWorkflow({ args, ctx }) { return { kind: 'github.pr.monitor', repo, - pr: Number(pr), + prNumber: Number(pr), key, changed, summary, @@ -171,7 +171,7 @@ export async function runGithubPrMonitorNotifyWorkflow({ args, ctx }) { kind: 'github.pr.monitor.notify', changed: Boolean(base.changed), repo: args.repo, - pr: Number(args.pr), + prNumber: Number(args.pr), message: formatPrChangeMessage({ repo: args.repo, pr: Number(args.pr), diff --git a/src/workflows/registry.js b/src/workflows/registry.ts similarity index 100% rename from src/workflows/registry.js rename to src/workflows/registry.ts diff --git a/test/clawd_invoke.test.js b/test/clawd_invoke.test.ts similarity index 90% rename from test/clawd_invoke.test.js rename to test/clawd_invoke.test.ts index d3652c1..9f4ed71 100644 --- a/test/clawd_invoke.test.js +++ b/test/clawd_invoke.test.ts @@ -29,8 +29,9 @@ test('clawd.invoke posts to /tools/invoke and returns JSON', async () => { }); }); - await new Promise((resolve) => server.listen(0, resolve)); - const port = server.address().port; + await new Promise((resolve) => server.listen(0, () => resolve())); + const addr = server.address(); + const port = typeof addr === "string" || addr == null ? 0 : addr.port; try { const registry = createDefaultRegistry(); diff --git a/test/clawd_invoke_legacy.test.js b/test/clawd_invoke_legacy.test.ts similarity index 90% rename from test/clawd_invoke_legacy.test.js rename to test/clawd_invoke_legacy.test.ts index 44d357b..ce9575f 100644 --- a/test/clawd_invoke_legacy.test.js +++ b/test/clawd_invoke_legacy.test.ts @@ -29,8 +29,9 @@ test('clawd.invoke accepts legacy raw JSON response', async () => { }); }); - await new Promise((resolve) => server.listen(0, resolve)); - const port = server.address().port; + await new Promise((resolve) => server.listen(0, () => resolve())); + const addr = server.address(); + const port = typeof addr === "string" || addr == null ? 0 : addr.port; try { const registry = createDefaultRegistry(); diff --git a/test/diff_last.test.js b/test/diff_last.test.ts similarity index 100% rename from test/diff_last.test.js rename to test/diff_last.test.ts diff --git a/test/doctor.test.js b/test/doctor.test.ts similarity index 76% rename from test/doctor.test.js rename to test/doctor.test.ts index ab3d3a1..8838abb 100644 --- a/test/doctor.test.js +++ b/test/doctor.test.ts @@ -5,7 +5,10 @@ import path from 'node:path'; test('doctor returns tool-mode ok with version', () => { const bin = path.join(process.cwd(), 'bin', 'lobster.js'); - const res = spawnSync('node', [bin, 'doctor'], { encoding: 'utf8' }); + const res = spawnSync('node', [bin, 'doctor'], { + encoding: 'utf8', + env: { ...process.env, LOBSTER_STATE_DIR: path.join(process.cwd(), '.tmp-test-state') }, + }); assert.equal(res.status, 0); const out = JSON.parse(res.stdout); assert.equal(out.ok, true); diff --git a/test/github_pr_notify_format.test.js b/test/github_pr_notify_format.test.ts similarity index 100% rename from test/github_pr_notify_format.test.js rename to test/github_pr_notify_format.test.ts diff --git a/test/github_pr_summary.test.js b/test/github_pr_summary.test.ts similarity index 90% rename from test/github_pr_summary.test.js rename to test/github_pr_summary.test.ts index 2541ad2..1a840ed 100644 --- a/test/github_pr_summary.test.js +++ b/test/github_pr_summary.test.ts @@ -2,6 +2,8 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import { buildPrChangeSummary } from '../src/workflows/github_pr_monitor.js'; +const build = buildPrChangeSummary as any; + test('buildPrChangeSummary reports all fields on first snapshot', () => { const after = { number: 1, @@ -16,7 +18,7 @@ test('buildPrChangeSummary reports all fields on first snapshot', () => { headRefName: 'feat', }; - const res = buildPrChangeSummary(null, after); + const res = build(null, after); assert.ok(res.changedFields.length > 0); assert.equal(res.changes.title.to, 'A'); }); @@ -36,7 +38,7 @@ test('buildPrChangeSummary only includes changed fields', () => { }; const after = { ...before, title: 'B', updatedAt: 't2' }; - const res = buildPrChangeSummary(before, after); + const res = build(before, after); assert.deepEqual(res.changedFields.sort(), ['title', 'updatedAt'].sort()); assert.equal(res.changes.title.from, 'A'); assert.equal(res.changes.title.to, 'B'); diff --git a/test/multi_approval_resume.test.js b/test/multi_approval_resume.test.ts similarity index 100% rename from test/multi_approval_resume.test.js rename to test/multi_approval_resume.test.ts diff --git a/test/parser.test.js b/test/parser.test.ts similarity index 100% rename from test/parser.test.js rename to test/parser.test.ts diff --git a/test/resume.test.js b/test/resume.test.ts similarity index 99% rename from test/resume.test.js rename to test/resume.test.ts index 8b56669..31815dd 100644 --- a/test/resume.test.js +++ b/test/resume.test.ts @@ -21,6 +21,7 @@ test('resume token roundtrip and resume pipeline continues', async () => { const first = await runPipeline({ pipeline, registry, + input: [], stdin: process.stdin, stdout: process.stdout, stderr: process.stderr, diff --git a/test/state.test.js b/test/state.test.ts similarity index 99% rename from test/state.test.js rename to test/state.test.ts index 0a57fa0..cb8f062 100644 --- a/test/state.test.js +++ b/test/state.test.ts @@ -47,6 +47,7 @@ test('state.get returns null for missing key', async () => { const output = await runPipeline({ pipeline: [{ name: 'state.get', args: { _: ['missing'] }, raw: 'state.get missing' }], registry, + input: [], stdin: process.stdin, stdout: process.stdout, stderr: process.stderr, diff --git a/test/tool_envelope_version.test.js b/test/tool_envelope_version.test.ts similarity index 100% rename from test/tool_envelope_version.test.js rename to test/tool_envelope_version.test.ts diff --git a/test/tool_mode.test.js b/test/tool_mode.test.ts similarity index 99% rename from test/tool_mode.test.js rename to test/tool_mode.test.ts index 15300dd..fbfc965 100644 --- a/test/tool_mode.test.js +++ b/test/tool_mode.test.ts @@ -19,6 +19,7 @@ test('approve halts pipeline in tool mode', async () => { const output = await runPipeline({ pipeline, registry, + input: [], stdin: process.stdin, stdout: process.stdout, stderr: process.stderr, diff --git a/test/workflows.test.js b/test/workflows.test.ts similarity index 100% rename from test/workflows.test.js rename to test/workflows.test.ts diff --git a/tsconfig.json b/tsconfig.json index 014e89c..a4786bc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,8 @@ "declaration": false, "sourceMap": true, "strict": false, + "useUnknownInCatchVariables": false, + "noImplicitAny": false, "skipLibCheck": true, "noEmitOnError": true },