claude-yes 1.31.2 → 1.32.2
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/README.md +225 -21
- package/dist/agent-yes.js +2 -0
- package/dist/amp-yes.js +2 -0
- package/dist/auggie-yes.js +2 -0
- package/dist/claude-yes.js +2 -20432
- package/dist/cli.js +18342 -10955
- package/dist/codex-yes.js +2 -20432
- package/dist/copilot-yes.js +2 -20432
- package/dist/cursor-yes.js +2 -20432
- package/dist/gemini-yes.js +2 -20432
- package/dist/grok-yes.js +2 -20432
- package/dist/index.js +16258 -13586
- package/dist/qwen-yes.js +2 -20432
- package/package.json +93 -81
- package/ts/ReadyManager.spec.ts +10 -10
- package/ts/ReadyManager.ts +1 -1
- package/ts/SUPPORTED_CLIS.ts +4 -0
- package/ts/catcher.spec.ts +69 -70
- package/ts/cli-idle.spec.ts +8 -8
- package/ts/cli.ts +18 -26
- package/ts/defineConfig.ts +4 -4
- package/ts/idleWaiter.spec.ts +9 -9
- package/ts/index.ts +474 -233
- package/ts/logger.ts +22 -0
- package/ts/parseCliArgs.spec.ts +146 -147
- package/ts/parseCliArgs.ts +127 -59
- package/ts/postbuild.ts +29 -15
- package/ts/pty-fix.ts +155 -0
- package/ts/pty.ts +19 -0
- package/ts/removeControlCharacters.spec.ts +37 -38
- package/ts/removeControlCharacters.ts +2 -1
- package/ts/runningLock.spec.ts +119 -125
- package/ts/runningLock.ts +44 -55
- package/ts/session-integration.spec.ts +34 -42
- package/ts/utils.spec.ts +35 -35
- package/ts/utils.ts +7 -7
- package/dist/cli.js.map +0 -365
- package/dist/index.js.map +0 -323
- package/ts/codex-resume.spec.ts +0 -239
- package/ts/codexSessionManager.spec.ts +0 -51
- package/ts/codexSessionManager.test.ts +0 -259
- package/ts/codexSessionManager.ts +0 -312
- package/ts/yesLog.spec.ts +0 -74
- package/ts/yesLog.ts +0 -27
package/package.json
CHANGED
|
@@ -1,134 +1,146 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-yes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.32.2",
|
|
4
4
|
"description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"anthropic",
|
|
8
|
+
"assistant",
|
|
9
|
+
"auto-response",
|
|
10
|
+
"automation",
|
|
6
11
|
"claude",
|
|
7
|
-
"
|
|
8
|
-
"gemini",
|
|
12
|
+
"cli",
|
|
9
13
|
"codex",
|
|
10
14
|
"copilot",
|
|
11
15
|
"cursor",
|
|
16
|
+
"gemini",
|
|
12
17
|
"grok",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"cli",
|
|
16
|
-
"wrapper",
|
|
17
|
-
"assistant",
|
|
18
|
-
"anthropic",
|
|
19
|
-
"auto-response"
|
|
18
|
+
"qwen",
|
|
19
|
+
"wrapper"
|
|
20
20
|
],
|
|
21
|
-
"homepage": "https://github.com/snomiao/
|
|
21
|
+
"homepage": "https://github.com/snomiao/agent-yes#readme",
|
|
22
22
|
"bugs": {
|
|
23
|
-
"url": "https://github.com/snomiao/
|
|
24
|
-
},
|
|
25
|
-
"repository": {
|
|
26
|
-
"type": "git",
|
|
27
|
-
"url": "git+https://github.com/snomiao/claude-yes.git"
|
|
23
|
+
"url": "https://github.com/snomiao/agent-yes/issues"
|
|
28
24
|
},
|
|
29
25
|
"license": "MIT",
|
|
30
26
|
"author": "snomiao <snomiao@gmail.com>",
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"types": "./ts/index.ts"
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/snomiao/agent-yes.git"
|
|
35
30
|
},
|
|
36
|
-
"module": "ts/index.ts",
|
|
37
|
-
"types": "./ts/index.ts",
|
|
38
31
|
"bin": {
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
32
|
+
"agent-yes": "./dist/agent-yes.js",
|
|
33
|
+
"amp-yes": "./dist/amp-yes.js",
|
|
34
|
+
"auggie-yes": "./dist/auggie-yes.js",
|
|
35
|
+
"claude-yes": "./dist/claude-yes.js",
|
|
36
|
+
"codex-yes": "./dist/codex-yes.js",
|
|
37
|
+
"copilot-yes": "./dist/copilot-yes.js",
|
|
38
|
+
"cursor-yes": "./dist/cursor-yes.js",
|
|
39
|
+
"gemini-yes": "./dist/gemini-yes.js",
|
|
40
|
+
"grok-yes": "./dist/grok-yes.js",
|
|
41
|
+
"qwen-yes": "./dist/qwen-yes.js"
|
|
47
42
|
},
|
|
48
43
|
"directories": {
|
|
49
44
|
"doc": "docs"
|
|
50
45
|
},
|
|
51
46
|
"files": [
|
|
52
|
-
"
|
|
53
|
-
"dist",
|
|
54
|
-
"scripts"
|
|
47
|
+
"dist/**/*.js",
|
|
48
|
+
"!dist/**/*.map",
|
|
49
|
+
"scripts",
|
|
50
|
+
"ts/*.ts"
|
|
55
51
|
],
|
|
52
|
+
"type": "module",
|
|
53
|
+
"module": "ts/index.ts",
|
|
54
|
+
"types": "./ts/index.ts",
|
|
55
|
+
"exports": {
|
|
56
|
+
"types": "./ts/index.ts",
|
|
57
|
+
"import": "./dist/index.js"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public",
|
|
61
|
+
"registry": "https://registry.npmjs.org/"
|
|
62
|
+
},
|
|
56
63
|
"scripts": {
|
|
57
|
-
"build": "bun
|
|
64
|
+
"build": "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
|
|
58
65
|
"postbuild": "bun ./ts/postbuild.ts",
|
|
59
|
-
"build:cli": "bun build ts/cli.ts --outdir=dist --target=node --sourcemap --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
|
|
60
|
-
"build:index": "bun build ts/index.ts --outdir=dist --target=node --sourcemap --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
|
|
61
66
|
"demo": "bun run build && bun link && claude-yes -- demo",
|
|
62
67
|
"dev": "bun ts/index.ts",
|
|
63
|
-
"fmt": "
|
|
64
|
-
"
|
|
65
|
-
"prepare": "
|
|
68
|
+
"fmt": "oxlint --fix --fix-suggestions && oxfmt",
|
|
69
|
+
"_prepack": "bun run build",
|
|
70
|
+
"prepare": "husky",
|
|
71
|
+
"release": "standard-version && npm publish",
|
|
72
|
+
"release:beta": "standard-version && npm publish --tag beta",
|
|
66
73
|
"test": "bun test --coverage"
|
|
67
74
|
},
|
|
68
|
-
"lint-staged": {
|
|
69
|
-
"*.{ts,js,json,md}": [
|
|
70
|
-
"bunx @biomejs/biome check --fix"
|
|
71
|
-
]
|
|
72
|
-
},
|
|
73
|
-
"release": {
|
|
74
|
-
"branches": [
|
|
75
|
-
"main"
|
|
76
|
-
],
|
|
77
|
-
"plugins": [
|
|
78
|
-
"@semantic-release/commit-analyzer",
|
|
79
|
-
"@semantic-release/release-notes-generator",
|
|
80
|
-
"@semantic-release/changelog",
|
|
81
|
-
"@semantic-release/npm",
|
|
82
|
-
[
|
|
83
|
-
"@semantic-release/exec",
|
|
84
|
-
{
|
|
85
|
-
"publishCmd": "npm pkg set name=claude-yes"
|
|
86
|
-
}
|
|
87
|
-
],
|
|
88
|
-
"@semantic-release/npm",
|
|
89
|
-
"@semantic-release/git",
|
|
90
|
-
"@semantic-release/github"
|
|
91
|
-
]
|
|
92
|
-
},
|
|
93
75
|
"dependencies": {
|
|
94
|
-
"bun": "^
|
|
95
|
-
"bun-pty": "^0.
|
|
76
|
+
"@snomiao/bun-pty": "^0.3.4",
|
|
77
|
+
"bun-pty": "^0.4.8",
|
|
96
78
|
"from-node-stream": "^0.1.2"
|
|
97
79
|
},
|
|
98
80
|
"devDependencies": {
|
|
99
|
-
"@
|
|
81
|
+
"@anthropic-ai/sdk": "^0.71.2",
|
|
100
82
|
"@semantic-release/changelog": "^6.0.3",
|
|
101
83
|
"@semantic-release/exec": "^7.1.0",
|
|
102
84
|
"@semantic-release/git": "^10.0.1",
|
|
103
85
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
104
|
-
"@types/bun": "^1.
|
|
86
|
+
"@types/bun": "^1.3.6",
|
|
105
87
|
"@types/jest": "^30.0.0",
|
|
106
88
|
"@types/ms": "^2.1.0",
|
|
107
|
-
"@types/node": "^
|
|
108
|
-
"@types/yargs": "^17.0.
|
|
89
|
+
"@types/node": "^25.0.10",
|
|
90
|
+
"@types/yargs": "^17.0.35",
|
|
109
91
|
"cpu-wait": "^0.0.10",
|
|
110
|
-
"execa": "^9.6.
|
|
92
|
+
"execa": "^9.6.1",
|
|
111
93
|
"husky": "^9.1.7",
|
|
112
|
-
"
|
|
94
|
+
"ink": "^6.6.0",
|
|
95
|
+
"lint-staged": "^16.2.7",
|
|
113
96
|
"ms": "^2.1.3",
|
|
114
|
-
"
|
|
97
|
+
"node-pty": "^1.1.0",
|
|
98
|
+
"openai": "^6.16.0",
|
|
99
|
+
"oxfmt": "^0.26.0",
|
|
100
|
+
"oxlint": "^1.41.0",
|
|
101
|
+
"p-map": "^7.0.4",
|
|
115
102
|
"phpdie": "^1.7.0",
|
|
116
|
-
"rambda": "^
|
|
117
|
-
"semantic-release": "^
|
|
118
|
-
"sflow": "^1.
|
|
103
|
+
"rambda": "^11.0.1",
|
|
104
|
+
"semantic-release": "^25.0.2",
|
|
105
|
+
"sflow": "^1.27.0",
|
|
106
|
+
"standard-version": "^9.5.0",
|
|
119
107
|
"strip-ansi-control-characters": "^2.0.0",
|
|
120
|
-
"terminal-render": "^1.2.
|
|
121
|
-
"tsa-composer": "^3.0.
|
|
122
|
-
"vitest": "^
|
|
108
|
+
"terminal-render": "^1.2.2",
|
|
109
|
+
"tsa-composer": "^3.0.3",
|
|
110
|
+
"vitest": "^4.0.17",
|
|
111
|
+
"winston": "^3.19.0",
|
|
123
112
|
"yargs": "^18.0.0"
|
|
124
113
|
},
|
|
125
114
|
"peerDependencies": {
|
|
126
|
-
"node-pty": "
|
|
127
|
-
"typescript": "^5.
|
|
115
|
+
"node-pty": "latest",
|
|
116
|
+
"typescript": "^5.9.3"
|
|
128
117
|
},
|
|
129
118
|
"peerDependenciesMeta": {
|
|
130
119
|
"node-pty": {
|
|
131
120
|
"optional": true
|
|
132
121
|
}
|
|
122
|
+
},
|
|
123
|
+
"engines": {
|
|
124
|
+
"node": ">=22.0.0"
|
|
125
|
+
},
|
|
126
|
+
"_release": {
|
|
127
|
+
"branches": [
|
|
128
|
+
"main"
|
|
129
|
+
],
|
|
130
|
+
"plugins": [
|
|
131
|
+
"@semantic-release/commit-analyzer",
|
|
132
|
+
"@semantic-release/release-notes-generator",
|
|
133
|
+
"@semantic-release/changelog",
|
|
134
|
+
"@semantic-release/npm",
|
|
135
|
+
[
|
|
136
|
+
"@semantic-release/exec",
|
|
137
|
+
{
|
|
138
|
+
"publishCmd": "npm pkg set name=claude-yes"
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"@semantic-release/npm",
|
|
142
|
+
"@semantic-release/git",
|
|
143
|
+
"@semantic-release/github"
|
|
144
|
+
]
|
|
133
145
|
}
|
|
134
146
|
}
|
package/ts/ReadyManager.spec.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { ReadyManager } from
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { ReadyManager } from "./ReadyManager";
|
|
3
3
|
|
|
4
|
-
describe(
|
|
5
|
-
it(
|
|
4
|
+
describe("ReadyManager", () => {
|
|
5
|
+
it("should start in not ready state", () => {
|
|
6
6
|
const manager = new ReadyManager();
|
|
7
7
|
expect(manager.wait()).toBeInstanceOf(Promise);
|
|
8
8
|
});
|
|
9
9
|
|
|
10
|
-
it(
|
|
10
|
+
it("should resolve wait when ready is called", async () => {
|
|
11
11
|
const manager = new ReadyManager();
|
|
12
12
|
const waitPromise = manager.wait();
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ describe('ReadyManager', () => {
|
|
|
16
16
|
await expect(waitPromise).resolves.toBeUndefined();
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
it(
|
|
19
|
+
it("should resolve immediately if already ready", async () => {
|
|
20
20
|
const manager = new ReadyManager();
|
|
21
21
|
manager.ready();
|
|
22
22
|
|
|
@@ -24,7 +24,7 @@ describe('ReadyManager', () => {
|
|
|
24
24
|
expect(result).toBeUndefined();
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
it(
|
|
27
|
+
it("should handle multiple waiters", async () => {
|
|
28
28
|
const manager = new ReadyManager();
|
|
29
29
|
const wait1 = manager.wait();
|
|
30
30
|
const wait2 = manager.wait();
|
|
@@ -39,7 +39,7 @@ describe('ReadyManager', () => {
|
|
|
39
39
|
]);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
it(
|
|
42
|
+
it("should reset to not ready when unready is called", async () => {
|
|
43
43
|
const manager = new ReadyManager();
|
|
44
44
|
manager.ready();
|
|
45
45
|
manager.unready();
|
|
@@ -47,13 +47,13 @@ describe('ReadyManager', () => {
|
|
|
47
47
|
expect(manager.wait()).toBeInstanceOf(Promise);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
it(
|
|
50
|
+
it("should handle ready with no waiting queue", () => {
|
|
51
51
|
const manager = new ReadyManager();
|
|
52
52
|
manager.ready(); // Should not throw even if no one is waiting
|
|
53
53
|
expect(manager.wait()).toBeUndefined(); // Should be ready now
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
it(
|
|
56
|
+
it("should handle multiple ready/unready cycles", async () => {
|
|
57
57
|
const manager = new ReadyManager();
|
|
58
58
|
|
|
59
59
|
// First cycle
|
package/ts/ReadyManager.ts
CHANGED
package/ts/catcher.spec.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { catcher } from
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { catcher } from "./catcher";
|
|
3
3
|
|
|
4
|
-
describe(
|
|
5
|
-
describe(
|
|
6
|
-
it(
|
|
7
|
-
const catchFn = () =>
|
|
4
|
+
describe("catcher", () => {
|
|
5
|
+
describe("curried overload", () => {
|
|
6
|
+
it("should return a function when called with only catchFn", () => {
|
|
7
|
+
const catchFn = () => "error";
|
|
8
8
|
const result = catcher(catchFn);
|
|
9
|
-
expect(typeof result).toBe(
|
|
9
|
+
expect(typeof result).toBe("function");
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
it(
|
|
12
|
+
it("should catch errors and call catchFn with error, function, and args", () => {
|
|
13
13
|
let catchedError: unknown;
|
|
14
14
|
let catchedFn: unknown;
|
|
15
15
|
let catchedArgs: unknown[];
|
|
@@ -17,49 +17,49 @@ describe('catcher', () => {
|
|
|
17
17
|
catchedError = error;
|
|
18
18
|
catchedFn = fn;
|
|
19
19
|
catchedArgs = args;
|
|
20
|
-
return
|
|
20
|
+
return "caught";
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
let calledArgs: unknown[] = [];
|
|
24
24
|
const errorFn = (...args: unknown[]) => {
|
|
25
25
|
calledArgs = args;
|
|
26
|
-
throw new Error(
|
|
26
|
+
throw new Error("test error");
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const wrappedFn = catcher(catchFn)(errorFn);
|
|
30
|
-
const result = wrappedFn(
|
|
30
|
+
const result = wrappedFn("arg1", "arg2");
|
|
31
31
|
|
|
32
|
-
expect(result).toBe(
|
|
32
|
+
expect(result).toBe("caught");
|
|
33
33
|
expect(catchedError).toBeInstanceOf(Error);
|
|
34
34
|
expect(catchedFn).toBe(errorFn);
|
|
35
|
-
expect(catchedArgs).toEqual([
|
|
36
|
-
expect(calledArgs).toEqual([
|
|
35
|
+
expect(catchedArgs).toEqual(["arg1", "arg2"]);
|
|
36
|
+
expect(calledArgs).toEqual(["arg1", "arg2"]);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
it(
|
|
39
|
+
it("should return normal result when no error occurs", () => {
|
|
40
40
|
let catchCalled = false;
|
|
41
41
|
const catchFn = () => {
|
|
42
42
|
catchCalled = true;
|
|
43
|
-
return
|
|
43
|
+
return "error";
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
let calledArgs: unknown[] = [];
|
|
47
47
|
const normalFn = (...args: unknown[]) => {
|
|
48
48
|
calledArgs = args;
|
|
49
|
-
return
|
|
49
|
+
return "success";
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
const wrappedFn = catcher(catchFn)(normalFn);
|
|
53
|
-
const result = wrappedFn(
|
|
53
|
+
const result = wrappedFn("arg1", "arg2");
|
|
54
54
|
|
|
55
|
-
expect(result).toBe(
|
|
55
|
+
expect(result).toBe("success");
|
|
56
56
|
expect(catchCalled).toBe(false);
|
|
57
|
-
expect(calledArgs).toEqual([
|
|
57
|
+
expect(calledArgs).toEqual(["arg1", "arg2"]);
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
describe(
|
|
62
|
-
it(
|
|
61
|
+
describe("direct overload", () => {
|
|
62
|
+
it("should catch errors and call catchFn with error, function, and args directly", () => {
|
|
63
63
|
let catchedError: unknown;
|
|
64
64
|
let catchedFn: unknown;
|
|
65
65
|
let catchedArgs: unknown[];
|
|
@@ -67,73 +67,73 @@ describe('catcher', () => {
|
|
|
67
67
|
catchedError = error;
|
|
68
68
|
catchedFn = fn;
|
|
69
69
|
catchedArgs = args;
|
|
70
|
-
return
|
|
70
|
+
return "caught";
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
let calledArgs: unknown[] = [];
|
|
74
74
|
const errorFn = (...args: unknown[]) => {
|
|
75
75
|
calledArgs = args;
|
|
76
|
-
throw new Error(
|
|
76
|
+
throw new Error("test error");
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
const wrappedFn = catcher(catchFn, errorFn);
|
|
80
|
-
const result = wrappedFn(
|
|
80
|
+
const result = wrappedFn("arg1", "arg2");
|
|
81
81
|
|
|
82
|
-
expect(result).toBe(
|
|
82
|
+
expect(result).toBe("caught");
|
|
83
83
|
expect(catchedError).toBeInstanceOf(Error);
|
|
84
84
|
expect(catchedFn).toBe(errorFn);
|
|
85
|
-
expect(catchedArgs).toEqual([
|
|
86
|
-
expect(calledArgs).toEqual([
|
|
85
|
+
expect(catchedArgs).toEqual(["arg1", "arg2"]);
|
|
86
|
+
expect(calledArgs).toEqual(["arg1", "arg2"]);
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
-
it(
|
|
89
|
+
it("should return normal result when no error occurs directly", () => {
|
|
90
90
|
let catchCalled = false;
|
|
91
91
|
const catchFn = () => {
|
|
92
92
|
catchCalled = true;
|
|
93
|
-
return
|
|
93
|
+
return "error";
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
let calledArgs: unknown[] = [];
|
|
97
97
|
const normalFn = (...args: unknown[]) => {
|
|
98
98
|
calledArgs = args;
|
|
99
|
-
return
|
|
99
|
+
return "success";
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
const wrappedFn = catcher(catchFn, normalFn);
|
|
103
|
-
const result = wrappedFn(
|
|
103
|
+
const result = wrappedFn("arg1", "arg2");
|
|
104
104
|
|
|
105
|
-
expect(result).toBe(
|
|
105
|
+
expect(result).toBe("success");
|
|
106
106
|
expect(catchCalled).toBe(false);
|
|
107
|
-
expect(calledArgs).toEqual([
|
|
107
|
+
expect(calledArgs).toEqual(["arg1", "arg2"]);
|
|
108
108
|
});
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
-
describe(
|
|
112
|
-
it(
|
|
111
|
+
describe("error handling", () => {
|
|
112
|
+
it("should handle different error types and pass function context", () => {
|
|
113
113
|
const results: unknown[] = [];
|
|
114
114
|
const functions: unknown[] = [];
|
|
115
|
-
const catchFn = (error: unknown, fn: unknown, ...
|
|
115
|
+
const catchFn = (error: unknown, fn: unknown, ..._args: unknown[]) => {
|
|
116
116
|
results.push(error);
|
|
117
117
|
functions.push(fn);
|
|
118
|
-
return
|
|
118
|
+
return "handled";
|
|
119
119
|
};
|
|
120
120
|
|
|
121
121
|
// String error
|
|
122
122
|
const stringErrorFn = () => {
|
|
123
|
-
throw
|
|
123
|
+
throw "string error";
|
|
124
124
|
};
|
|
125
125
|
const wrappedStringFn = catcher(catchFn, stringErrorFn);
|
|
126
|
-
expect(wrappedStringFn()).toBe(
|
|
127
|
-
expect(results[0]).toBe(
|
|
126
|
+
expect(wrappedStringFn()).toBe("handled");
|
|
127
|
+
expect(results[0]).toBe("string error");
|
|
128
128
|
expect(functions[0]).toBe(stringErrorFn);
|
|
129
129
|
|
|
130
130
|
// Object error
|
|
131
|
-
const objectError = { message:
|
|
131
|
+
const objectError = { message: "object error" };
|
|
132
132
|
const objectErrorFn = () => {
|
|
133
133
|
throw objectError;
|
|
134
134
|
};
|
|
135
135
|
const wrappedObjectFn = catcher(catchFn, objectErrorFn);
|
|
136
|
-
expect(wrappedObjectFn()).toBe(
|
|
136
|
+
expect(wrappedObjectFn()).toBe("handled");
|
|
137
137
|
expect(results[1]).toBe(objectError);
|
|
138
138
|
expect(functions[1]).toBe(objectErrorFn);
|
|
139
139
|
|
|
@@ -142,12 +142,12 @@ describe('catcher', () => {
|
|
|
142
142
|
throw null;
|
|
143
143
|
};
|
|
144
144
|
const wrappedNullFn = catcher(catchFn, nullErrorFn);
|
|
145
|
-
expect(wrappedNullFn()).toBe(
|
|
145
|
+
expect(wrappedNullFn()).toBe("handled");
|
|
146
146
|
expect(results[2]).toBe(null);
|
|
147
147
|
expect(functions[2]).toBe(nullErrorFn);
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
it(
|
|
150
|
+
it("should preserve function parameters and pass them to catchFn", () => {
|
|
151
151
|
let caughtError: unknown;
|
|
152
152
|
let caughtFn: unknown;
|
|
153
153
|
let caughtArgs: unknown[];
|
|
@@ -155,31 +155,31 @@ describe('catcher', () => {
|
|
|
155
155
|
caughtError = error;
|
|
156
156
|
caughtFn = fn;
|
|
157
157
|
caughtArgs = args;
|
|
158
|
-
return
|
|
158
|
+
return "caught";
|
|
159
159
|
};
|
|
160
160
|
|
|
161
161
|
let testArgs: [number, string, boolean] | undefined;
|
|
162
162
|
const testFn = (a: number, b: string, c: boolean) => {
|
|
163
163
|
testArgs = [a, b, c];
|
|
164
|
-
if (a > 5) throw new Error(
|
|
164
|
+
if (a > 5) throw new Error("too big");
|
|
165
165
|
return `${a}-${b}-${c}`;
|
|
166
166
|
};
|
|
167
167
|
|
|
168
168
|
const wrappedFn = catcher(catchFn, testFn);
|
|
169
169
|
|
|
170
170
|
// Normal execution
|
|
171
|
-
expect(wrappedFn(3,
|
|
172
|
-
expect(testArgs).toEqual([3,
|
|
171
|
+
expect(wrappedFn(3, "test", true)).toBe("3-test-true");
|
|
172
|
+
expect(testArgs).toEqual([3, "test", true]);
|
|
173
173
|
|
|
174
174
|
// Error execution
|
|
175
|
-
expect(wrappedFn(10,
|
|
176
|
-
expect(testArgs).toEqual([10,
|
|
175
|
+
expect(wrappedFn(10, "error", false)).toBe("caught");
|
|
176
|
+
expect(testArgs).toEqual([10, "error", false]);
|
|
177
177
|
expect(caughtError).toBeInstanceOf(Error);
|
|
178
178
|
expect(caughtFn).toBe(testFn);
|
|
179
|
-
expect(caughtArgs).toEqual([10,
|
|
179
|
+
expect(caughtArgs).toEqual([10, "error", false]);
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
-
it(
|
|
182
|
+
it("should handle functions with no parameters", () => {
|
|
183
183
|
let caughtError: unknown;
|
|
184
184
|
let caughtFn: unknown;
|
|
185
185
|
let caughtArgs: unknown[];
|
|
@@ -187,26 +187,26 @@ describe('catcher', () => {
|
|
|
187
187
|
caughtError = error;
|
|
188
188
|
caughtFn = fn;
|
|
189
189
|
caughtArgs = args;
|
|
190
|
-
return
|
|
190
|
+
return "no params caught";
|
|
191
191
|
};
|
|
192
192
|
|
|
193
193
|
let called = false;
|
|
194
194
|
const noParamsFn = () => {
|
|
195
195
|
called = true;
|
|
196
|
-
throw new Error(
|
|
196
|
+
throw new Error("no params error");
|
|
197
197
|
};
|
|
198
198
|
|
|
199
199
|
const wrappedFn = catcher(catchFn, noParamsFn);
|
|
200
200
|
const result = wrappedFn();
|
|
201
201
|
|
|
202
|
-
expect(result).toBe(
|
|
202
|
+
expect(result).toBe("no params caught");
|
|
203
203
|
expect(called).toBe(true);
|
|
204
204
|
expect(caughtError).toBeInstanceOf(Error);
|
|
205
205
|
expect(caughtFn).toBe(noParamsFn);
|
|
206
206
|
expect(caughtArgs).toEqual([]);
|
|
207
207
|
});
|
|
208
208
|
|
|
209
|
-
it(
|
|
209
|
+
it("should handle functions returning different types", () => {
|
|
210
210
|
const catchFn = () => null;
|
|
211
211
|
|
|
212
212
|
// Function returning number
|
|
@@ -214,7 +214,7 @@ describe('catcher', () => {
|
|
|
214
214
|
expect(numberFn()).toBe(42);
|
|
215
215
|
|
|
216
216
|
// Function returning object
|
|
217
|
-
const obj = { key:
|
|
217
|
+
const obj = { key: "value" };
|
|
218
218
|
const objectFn = catcher(catchFn, () => obj);
|
|
219
219
|
expect(objectFn()).toBe(obj);
|
|
220
220
|
|
|
@@ -224,37 +224,36 @@ describe('catcher', () => {
|
|
|
224
224
|
});
|
|
225
225
|
});
|
|
226
226
|
|
|
227
|
-
describe(
|
|
228
|
-
it(
|
|
229
|
-
const catchFn = (
|
|
230
|
-
'error';
|
|
227
|
+
describe("type safety", () => {
|
|
228
|
+
it("should maintain function signature", () => {
|
|
229
|
+
const catchFn = (_error: unknown, _fn: unknown, ..._args: unknown[]) => "error";
|
|
231
230
|
const originalFn = (a: number, b: string): string => `${a}-${b}`;
|
|
232
231
|
|
|
233
232
|
const wrappedFn = catcher(catchFn, originalFn);
|
|
234
233
|
|
|
235
234
|
// This should be type-safe
|
|
236
|
-
const result: string = wrappedFn(1,
|
|
237
|
-
expect(result).toBe(
|
|
235
|
+
const result: string = wrappedFn(1, "test");
|
|
236
|
+
expect(result).toBe("1-test");
|
|
238
237
|
});
|
|
239
238
|
|
|
240
|
-
it(
|
|
239
|
+
it("should pass function reference and arguments to catchFn", () => {
|
|
241
240
|
let capturedFn: unknown;
|
|
242
241
|
let capturedArgs: unknown[];
|
|
243
242
|
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
244
243
|
capturedFn = fn;
|
|
245
244
|
capturedArgs = args;
|
|
246
|
-
return
|
|
245
|
+
return "handled";
|
|
247
246
|
};
|
|
248
247
|
|
|
249
|
-
const testFn = (
|
|
250
|
-
throw new Error(
|
|
248
|
+
const testFn = (_x: number, _y: string) => {
|
|
249
|
+
throw new Error("test");
|
|
251
250
|
};
|
|
252
251
|
|
|
253
252
|
const wrappedFn = catcher(catchFn, testFn);
|
|
254
|
-
wrappedFn(42,
|
|
253
|
+
wrappedFn(42, "hello");
|
|
255
254
|
|
|
256
255
|
expect(capturedFn).toBe(testFn);
|
|
257
|
-
expect(capturedArgs).toEqual([42,
|
|
256
|
+
expect(capturedArgs).toEqual([42, "hello"]);
|
|
258
257
|
});
|
|
259
258
|
});
|
|
260
259
|
});
|
package/ts/cli-idle.spec.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { exec } from
|
|
2
|
-
import { fromStdio } from
|
|
3
|
-
import sflow from
|
|
4
|
-
import { describe, it } from
|
|
5
|
-
import { sleepms } from
|
|
1
|
+
import { exec } from "child_process";
|
|
2
|
+
import { fromStdio } from "from-node-stream";
|
|
3
|
+
import sflow from "sflow";
|
|
4
|
+
import { describe, it } from "vitest";
|
|
5
|
+
import { sleepms } from "./utils";
|
|
6
6
|
|
|
7
|
-
describe(
|
|
7
|
+
describe("CLI idle tests", () => {
|
|
8
8
|
// 2025-08-11 ok
|
|
9
|
-
it.skip(
|
|
9
|
+
it.skip("CLI --exit-on-idle flag with custom timeout", async () => {
|
|
10
10
|
const p = exec(
|
|
11
11
|
`bunx tsx ./ts/cli.ts claude --verbose --logFile=./cli-idle.log --exit-on-idle=3s "say hello and wait"`,
|
|
12
12
|
);
|
|
13
13
|
const tr = new TransformStream<string, string>();
|
|
14
14
|
const output = await sflow(tr.readable).by(fromStdio(p)).log().text();
|
|
15
15
|
console.log(output);
|
|
16
|
-
expect(output).toContain(
|
|
16
|
+
expect(output).toContain("hello");
|
|
17
17
|
await sleepms(1000); // wait for process exit
|
|
18
18
|
expect(p.exitCode).toBe(0);
|
|
19
19
|
}, 20e3);
|