auto-mode 0.1.0 → 0.2.0
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 +91 -107
- package/dist/classify-locally-rTUwxBZi.js +6203 -0
- package/dist/cli.js +22 -31
- package/dist/index.d.ts +9 -135
- package/dist/index.js +2 -2
- package/package.json +51 -26
- package/dist/classify-locally-BHl0xiqa.js +0 -786
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as loadPolicy, c as
|
|
2
|
+
import { a as loadPolicy, c as buildHookConfig, g as resolveConfigPath, l as renderVerdict, m as loadConfig, n as classifyWithModel, o as SETTINGS_PATHS, s as SETUP_NOTES, t as classifyLocally, u as parsePayload } from "./classify-locally-rTUwxBZi.js";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
|
+
import { text } from "node:stream/consumers";
|
|
4
5
|
//#region src/cli.ts
|
|
5
6
|
const USAGE = `auto-mode — a permission classifier that runs as a hook
|
|
6
7
|
|
|
@@ -15,45 +16,36 @@ Options:
|
|
|
15
16
|
--explain With run: also write the reasoning to stderr
|
|
16
17
|
--local-only With run: skip the model tier
|
|
17
18
|
`;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
21
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
19
|
+
function readStdin() {
|
|
20
|
+
return text(process.stdin);
|
|
22
21
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Decides one tool call. Writing nothing is a real answer: it means auto-mode
|
|
25
|
-
* has no opinion, and the harness then does whatever it would have done alone.
|
|
26
|
-
* That is the right output for an unknown harness, for an event that is not a
|
|
27
|
-
* tool gate, and for anything the local tier declines to judge until the model
|
|
28
|
-
* tier exists.
|
|
29
|
-
*/
|
|
30
22
|
async function run(explain, localOnly) {
|
|
31
23
|
const raw = await readStdin();
|
|
32
24
|
let body;
|
|
33
25
|
try {
|
|
34
26
|
body = JSON.parse(raw);
|
|
35
27
|
} catch {
|
|
36
|
-
return
|
|
28
|
+
return printNote(explain, "stdin is not JSON, so no verdict");
|
|
37
29
|
}
|
|
38
30
|
const payload = parsePayload(body);
|
|
39
|
-
if (payload === null) return
|
|
31
|
+
if (payload === null) return printNote(explain, "not a tool gate this hook judges, so no verdict");
|
|
40
32
|
const local = classifyLocally(payload);
|
|
41
33
|
if (local.kind === "allow") {
|
|
42
34
|
process.stdout.write(renderVerdict(payload.event, { kind: "allow" }));
|
|
43
|
-
return
|
|
35
|
+
return printNote(explain, `allowed by ${local.exception} (${payload.harness}, local)`);
|
|
44
36
|
}
|
|
45
|
-
if (localOnly) return
|
|
37
|
+
if (localOnly) return printNote(explain, `${payload.toolName} needs the model tier, which --local-only skipped`);
|
|
46
38
|
const config = await loadConfig();
|
|
47
39
|
const outcome = await classifyWithModel(payload, config);
|
|
48
40
|
if (outcome.verdict !== null) process.stdout.write(renderVerdict(payload.event, outcome.verdict));
|
|
49
|
-
return
|
|
41
|
+
return printNote(explain, outcome.note);
|
|
50
42
|
}
|
|
51
|
-
function
|
|
52
|
-
if (explain) process.stderr.write(`auto-mode: ${
|
|
43
|
+
function printNote(explain, message) {
|
|
44
|
+
if (explain) process.stderr.write(`auto-mode: ${message}\n`);
|
|
53
45
|
return 0;
|
|
54
46
|
}
|
|
55
47
|
async function main(argv) {
|
|
56
|
-
const
|
|
48
|
+
const args = parseArgs({
|
|
57
49
|
args: [...argv],
|
|
58
50
|
allowPositionals: true,
|
|
59
51
|
options: {
|
|
@@ -67,29 +59,28 @@ async function main(argv) {
|
|
|
67
59
|
}
|
|
68
60
|
}
|
|
69
61
|
});
|
|
70
|
-
const command = positionals
|
|
71
|
-
if (values.help === true || command === void 0) {
|
|
62
|
+
const [command] = args.positionals;
|
|
63
|
+
if (args.values.help === true || command === void 0) {
|
|
72
64
|
process.stdout.write(USAGE);
|
|
73
65
|
return 0;
|
|
74
66
|
}
|
|
75
|
-
if (command === "run") return run(values.explain === true, values["local-only"] === true);
|
|
67
|
+
if (command === "run") return run(args.values.explain === true, args.values["local-only"] === true);
|
|
76
68
|
if (command === "init") {
|
|
77
|
-
const harness = positionals
|
|
69
|
+
const [, harness] = args.positionals;
|
|
78
70
|
if (harness !== "claude" && harness !== "codex" && harness !== "muse") {
|
|
79
71
|
process.stderr.write("auto-mode init: name a harness — claude, codex or muse\n");
|
|
80
72
|
return 2;
|
|
81
73
|
}
|
|
82
|
-
|
|
83
|
-
process.stdout.write(`#
|
|
84
|
-
process.stdout.write(
|
|
85
|
-
process.stdout.write(
|
|
86
|
-
for (const line of SETUP_NOTES[target]) process.stdout.write(`# ${line}\n`);
|
|
74
|
+
process.stdout.write(`# Add this to ${SETTINGS_PATHS[harness]}\n`);
|
|
75
|
+
process.stdout.write(`# Configuration lives at ${resolveConfigPath()}\n`);
|
|
76
|
+
process.stdout.write(`${buildHookConfig(harness, `${process.execPath} ${process.argv[1] ?? "auto-mode"} run`)}\n`);
|
|
77
|
+
for (const line of SETUP_NOTES[harness]) process.stdout.write(`# ${line}\n`);
|
|
87
78
|
return 0;
|
|
88
79
|
}
|
|
89
80
|
if (command === "print-prompt") {
|
|
90
81
|
const prompt = await loadPolicy({
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
classifierPath: args.values.classifier,
|
|
83
|
+
rulesPath: args.values.rules
|
|
93
84
|
});
|
|
94
85
|
process.stdout.write(`${prompt}\n`);
|
|
95
86
|
return 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,63 +1,29 @@
|
|
|
1
1
|
//#region src/config/config.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* How to reach the judging model. Every supported provider speaks the Anthropic
|
|
4
|
-
* Messages API, so one shape covers Claude, Muse, GLM and Kimi — only the base
|
|
5
|
-
* URL, the model id and the key change.
|
|
6
|
-
*/
|
|
7
2
|
interface ProviderConfig {
|
|
8
3
|
readonly baseURL: string;
|
|
9
4
|
readonly model: string;
|
|
10
|
-
|
|
11
|
-
readonly
|
|
12
|
-
/** Command that prints the key. Tried when the variable is unset or empty. */
|
|
13
|
-
readonly apiKeyCommand?: string;
|
|
14
|
-
/**
|
|
15
|
-
* Whether this model reasons before answering. It drives three things at once
|
|
16
|
-
* — how the prompt asks for the answer, how many tokens to allow, and how long
|
|
17
|
-
* to wait — because a reasoning model needs more of all three.
|
|
18
|
-
*/
|
|
5
|
+
readonly apiKeyEnv?: string | undefined;
|
|
6
|
+
readonly apiKeyCommand?: string | undefined;
|
|
19
7
|
readonly reasoning: boolean;
|
|
20
8
|
readonly maxTokens: number;
|
|
21
9
|
readonly timeoutMs: number;
|
|
22
10
|
}
|
|
23
11
|
interface Config {
|
|
24
12
|
readonly provider: ProviderConfig;
|
|
25
|
-
readonly classifierPath?: string;
|
|
26
|
-
readonly rulesPath?: string;
|
|
27
|
-
/** How many transcript entries to show the model, newest last. */
|
|
13
|
+
readonly classifierPath?: string | undefined;
|
|
14
|
+
readonly rulesPath?: string | undefined;
|
|
28
15
|
readonly transcriptEntries: number;
|
|
29
|
-
/**
|
|
30
|
-
* What to do when the model cannot be reached or does not answer. `defer`
|
|
31
|
-
* writes nothing, so the harness asks the operator as it would without this
|
|
32
|
-
* hook. `deny` fails closed.
|
|
33
|
-
*/
|
|
34
16
|
readonly onFailure: 'defer' | 'deny';
|
|
35
17
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Muse Spark is the default because it reasons, it was correct on every case we
|
|
38
|
-
* measured, and a contributor-tier call costs about a fortieth of the
|
|
39
|
-
* alternatives. Its token budget is not a preference: below roughly 2000 it
|
|
40
|
-
* returns nothing at all.
|
|
41
|
-
*/
|
|
42
18
|
export declare const DEFAULT_CONFIG: Config;
|
|
43
|
-
/** Provider presets, so a config need only name one. */
|
|
44
19
|
export declare const PRESETS: Readonly<Record<string, ProviderConfig>>;
|
|
45
|
-
export declare function
|
|
46
|
-
/**
|
|
47
|
-
* Reads the config, falling back to the shipped defaults. A missing file is
|
|
48
|
-
* normal. A malformed one is not, and it throws rather than silently running a
|
|
49
|
-
* policy the user did not write.
|
|
50
|
-
*/
|
|
20
|
+
export declare function resolveConfigPath(): string;
|
|
51
21
|
export declare function loadConfig(path?: string): Promise<Config>;
|
|
52
|
-
/** Reads the key from the environment, then from the helper command. */
|
|
53
22
|
export declare function resolveApiKey(provider: ProviderConfig): Promise<string | null>;
|
|
54
23
|
//#endregion
|
|
55
24
|
//#region src/harness/types.d.ts
|
|
56
|
-
/** A harness is the program that runs the agent and calls this hook. */
|
|
57
25
|
type Harness = 'claude' | 'codex' | 'muse';
|
|
58
|
-
/** The hook events auto-mode acts on. Others are read and ignored. */
|
|
59
26
|
type HookEvent = 'PreToolUse' | 'PermissionRequest';
|
|
60
|
-
/** One tool call, normalised across harnesses. */
|
|
61
27
|
interface HookPayload {
|
|
62
28
|
readonly harness: Harness;
|
|
63
29
|
readonly event: HookEvent;
|
|
@@ -65,15 +31,9 @@ interface HookPayload {
|
|
|
65
31
|
readonly cwd: string;
|
|
66
32
|
readonly toolName: string;
|
|
67
33
|
readonly toolInput: Readonly<Record<string, unknown>>;
|
|
68
|
-
|
|
69
|
-
readonly transcriptPath?: string;
|
|
70
|
-
/** The payload as it arrived, for anything this shape drops. */
|
|
34
|
+
readonly transcriptPath?: string | undefined;
|
|
71
35
|
readonly raw: Readonly<Record<string, unknown>>;
|
|
72
36
|
}
|
|
73
|
-
/**
|
|
74
|
-
* What auto-mode decides. `ask` hands the call back to the operator, which is
|
|
75
|
-
* what the harness would have done without this hook.
|
|
76
|
-
*/
|
|
77
37
|
type Verdict = {
|
|
78
38
|
readonly kind: 'allow';
|
|
79
39
|
} | {
|
|
@@ -85,64 +45,16 @@ type Verdict = {
|
|
|
85
45
|
};
|
|
86
46
|
//#endregion
|
|
87
47
|
//#region src/harness/detect-harness.d.ts
|
|
88
|
-
/**
|
|
89
|
-
* Names the harness that sent a hook payload.
|
|
90
|
-
*
|
|
91
|
-
* The three shapes overlap, so order matters. Muse carries `model_provider`,
|
|
92
|
-
* which neither of the others sends, so it is tested first — Muse also sends
|
|
93
|
-
* `turn_id`, and testing that first would call every Muse payload Codex. Claude
|
|
94
|
-
* is the only one that sends `prompt_id`. Codex is then what is left that
|
|
95
|
-
* carries `turn_id`.
|
|
96
|
-
*
|
|
97
|
-
* Environment variables cannot help here: Muse runs hook commands with a
|
|
98
|
-
* scrubbed environment, so a Muse hook sees none of them.
|
|
99
|
-
*/
|
|
100
48
|
export declare function detectHarness(payload: Readonly<Record<string, unknown>>): Harness | null;
|
|
101
49
|
//#endregion
|
|
102
50
|
//#region src/harness/parse-payload.d.ts
|
|
103
|
-
/**
|
|
104
|
-
* Normalises one harness payload, or returns null when auto-mode has nothing to
|
|
105
|
-
* say about it — an unknown harness, an event that is not a tool gate, or a
|
|
106
|
-
* body that is not an object. A null is not a refusal: the caller stays out of
|
|
107
|
-
* the way and the harness decides for itself.
|
|
108
|
-
*/
|
|
109
51
|
export declare function parsePayload(body: unknown): HookPayload | null;
|
|
110
52
|
//#endregion
|
|
111
53
|
//#region src/harness/render-verdict.d.ts
|
|
112
|
-
/**
|
|
113
|
-
* Turns a verdict into the JSON the harness reads on stdout.
|
|
114
|
-
*
|
|
115
|
-
* All three harnesses take the same shape, because Codex and Muse both model
|
|
116
|
-
* their hook contract on Claude's. `allow` and `ask` differ: `allow` runs the
|
|
117
|
-
* tool with no prompt, while `ask` falls through to whatever the harness would
|
|
118
|
-
* have done on its own, which is usually to ask the operator.
|
|
119
|
-
*/
|
|
120
54
|
export declare function renderVerdict(event: HookEvent, verdict: Verdict): string;
|
|
121
55
|
//#endregion
|
|
122
56
|
//#region src/install/hook-config.d.ts
|
|
123
|
-
|
|
124
|
-
* Prints the hook entry a harness needs, for the user to paste into their own
|
|
125
|
-
* settings. auto-mode never writes those files itself: they are the user's
|
|
126
|
-
* configuration, and an agent that can edit its own restraints has none.
|
|
127
|
-
*
|
|
128
|
-
* All three take a different entry shape, and each difference is load-bearing:
|
|
129
|
-
*
|
|
130
|
-
* - Claude matches tool names against a regular expression, so matching every
|
|
131
|
-
* tool is `.*`. Two near-misses both leave the hook installed but never
|
|
132
|
-
* firing: `*` is not a valid regular expression, and `""` matches only a tool
|
|
133
|
-
* whose name is the empty string.
|
|
134
|
-
* - Muse takes `""` to mean every tool, so the same value means opposite things
|
|
135
|
-
* in the two harnesses.
|
|
136
|
-
* - Codex registers a bare handler list with no matcher.
|
|
137
|
-
* - Muse 1.3 rejects a handler carrying `timeout_ms`, reporting an unknown
|
|
138
|
-
* field and skipping the whole handler silently, so the hook never runs. A
|
|
139
|
-
* user-tier Muse entry takes `type` and `command` and nothing else.
|
|
140
|
-
*
|
|
141
|
-
* The timeout is 90 seconds, not the usual 30: a reasoning model takes 13-24
|
|
142
|
-
* seconds on a hard case, and a harness timeout shorter than the model call
|
|
143
|
-
* turns every hard case into a silent failure.
|
|
144
|
-
*/
|
|
145
|
-
export declare function hookConfig(harness: Harness, command: string): string;
|
|
57
|
+
export declare function buildHookConfig(harness: Harness, command: string): string;
|
|
146
58
|
export declare const SETTINGS_PATHS: Readonly<Record<Harness, string>>;
|
|
147
59
|
//#endregion
|
|
148
60
|
//#region src/model/classify-with-model.d.ts
|
|
@@ -150,44 +62,19 @@ interface ModelOutcome {
|
|
|
150
62
|
readonly verdict: Verdict | null;
|
|
151
63
|
readonly note: string;
|
|
152
64
|
}
|
|
153
|
-
/**
|
|
154
|
-
* Asks the model. A null verdict means auto-mode has no opinion, and the caller
|
|
155
|
-
* writes nothing so the harness decides for itself.
|
|
156
|
-
*/
|
|
157
65
|
export declare function classifyWithModel(payload: HookPayload, config: Config): Promise<ModelOutcome>;
|
|
158
66
|
//#endregion
|
|
159
67
|
//#region src/model/parse-verdict.d.ts
|
|
160
|
-
/**
|
|
161
|
-
* Reads the classifier's answer.
|
|
162
|
-
*
|
|
163
|
-
* Every ambiguity resolves to allow, on the policy's own reasoning: a block
|
|
164
|
-
* nobody can name is a false positive, and a false positive costs more than the
|
|
165
|
-
* action it stopped. A model that says `yes` but names no rule has not made a
|
|
166
|
-
* case, so there is nothing to show the user and nothing to appeal.
|
|
167
|
-
*/
|
|
168
68
|
export declare function parseModelVerdict(text: string): Verdict;
|
|
169
69
|
//#endregion
|
|
170
70
|
//#region src/policy/load-policy.d.ts
|
|
171
71
|
interface PolicyPaths {
|
|
172
|
-
|
|
173
|
-
readonly
|
|
174
|
-
/** The rule list: what to judge against. Defaults to the shipped `policy/rules.md`. */
|
|
175
|
-
readonly rulesPath?: string;
|
|
72
|
+
readonly classifierPath?: string | undefined;
|
|
73
|
+
readonly rulesPath?: string | undefined;
|
|
176
74
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Reads the two policy files and splices them into the one system prompt the
|
|
179
|
-
* model receives. Nothing else is added, so `auto-mode print-prompt` shows
|
|
180
|
-
* exactly what the classifier reads.
|
|
181
|
-
*/
|
|
182
75
|
export declare function loadPolicy(paths?: PolicyPaths): Promise<string>;
|
|
183
76
|
//#endregion
|
|
184
77
|
//#region src/rules/classify-locally.d.ts
|
|
185
|
-
/**
|
|
186
|
-
* What the local tier decides. It allows or it declines to decide. It never
|
|
187
|
-
* denies: a wrong local allow costs one unwatched action, while a wrong local
|
|
188
|
-
* deny stops work the user asked for, and the rules that deny are prose that
|
|
189
|
-
* needs a reader.
|
|
190
|
-
*/
|
|
191
78
|
type LocalVerdict = {
|
|
192
79
|
readonly kind: 'allow';
|
|
193
80
|
readonly exception: string;
|
|
@@ -197,23 +84,10 @@ type LocalVerdict = {
|
|
|
197
84
|
export declare function classifyLocally(payload: HookPayload): LocalVerdict;
|
|
198
85
|
//#endregion
|
|
199
86
|
//#region src/transcript/read-transcript.d.ts
|
|
200
|
-
/** One line of the conversation, flattened to text the classifier can read. */
|
|
201
87
|
interface TranscriptEntry {
|
|
202
88
|
readonly role: string;
|
|
203
89
|
readonly text: string;
|
|
204
90
|
}
|
|
205
|
-
/**
|
|
206
|
-
* Reads the tail of a harness transcript.
|
|
207
|
-
*
|
|
208
|
-
* Only the tail, and always the same tail for a given session length, so the
|
|
209
|
-
* prompt prefix grows by appending rather than sliding. A sliding window would
|
|
210
|
-
* change the cached prefix on every call and pay full price each time.
|
|
211
|
-
*
|
|
212
|
-
* Every harness writes JSONL but none writes the same schema, so this reads
|
|
213
|
-
* defensively and drops what it cannot understand. A transcript it cannot parse
|
|
214
|
-
* yields no entries, and the classifier then judges the action with no history —
|
|
215
|
-
* which is weaker, not wrong.
|
|
216
|
-
*/
|
|
217
91
|
export declare function readTranscript(path: string | undefined, limit: number): Promise<readonly TranscriptEntry[]>;
|
|
218
92
|
//#endregion
|
|
219
93
|
export type { Config, Harness, HookEvent, HookPayload, LocalVerdict, ModelOutcome, PolicyPaths, ProviderConfig, TranscriptEntry, Verdict };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as loadPolicy, c as
|
|
2
|
-
export { DEFAULT_CONFIG, PRESETS, SETTINGS_PATHS, classifyLocally, classifyWithModel,
|
|
1
|
+
import { a as loadPolicy, c as buildHookConfig, d as detectHarness, f as DEFAULT_CONFIG, g as resolveConfigPath, h as resolveApiKey, i as readTranscript, l as renderVerdict, m as loadConfig, n as classifyWithModel, o as SETTINGS_PATHS, p as PRESETS, r as parseModelVerdict, t as classifyLocally, u as parsePayload } from "./classify-locally-rTUwxBZi.js";
|
|
2
|
+
export { DEFAULT_CONFIG, PRESETS, SETTINGS_PATHS, buildHookConfig, classifyLocally, classifyWithModel, detectHarness, loadConfig, loadPolicy, parseModelVerdict, parsePayload, readTranscript, renderVerdict, resolveApiKey, resolveConfigPath };
|
package/package.json
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auto-mode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A permission classifier that runs as a hook, so agents that are not Claude can run unattended",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
7
|
-
"hook",
|
|
8
|
-
"permissions",
|
|
9
7
|
"claude-code",
|
|
10
8
|
"codex",
|
|
11
|
-
"
|
|
9
|
+
"hook",
|
|
10
|
+
"muse",
|
|
11
|
+
"permissions"
|
|
12
12
|
],
|
|
13
|
+
"homepage": "https://github.com/zgeoff/auto-mode#readme",
|
|
14
|
+
"bugs": "https://github.com/zgeoff/auto-mode/issues",
|
|
13
15
|
"license": "MIT",
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
"
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/zgeoff/auto-mode.git"
|
|
17
19
|
},
|
|
18
20
|
"bin": {
|
|
19
21
|
"auto-mode": "./dist/cli.js"
|
|
20
22
|
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"policy",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
21
29
|
"exports": {
|
|
22
30
|
".": {
|
|
23
31
|
"types": "./dist/index.d.ts",
|
|
@@ -26,30 +34,47 @@
|
|
|
26
34
|
"./policy/classifier.md": "./policy/classifier.md",
|
|
27
35
|
"./policy/rules.md": "./policy/rules.md"
|
|
28
36
|
},
|
|
29
|
-
"files": [
|
|
30
|
-
"dist",
|
|
31
|
-
"policy",
|
|
32
|
-
"LICENSE"
|
|
33
|
-
],
|
|
34
37
|
"scripts": {
|
|
38
|
+
"audit": "bun audit",
|
|
35
39
|
"build": "tsdown",
|
|
36
|
-
"
|
|
37
|
-
"
|
|
40
|
+
"build:agents": "bash scripts/build-agents-md.sh agents/shared.md agents/project.md AGENTS.md",
|
|
41
|
+
"check": "bun run format:check && bun run lint && bun run typecheck && bun run deadcode && bun test && bun run build && bun run publint",
|
|
42
|
+
"deadcode": "knip",
|
|
43
|
+
"first-publish": "scripts/first-publish.sh",
|
|
44
|
+
"format": "oxfmt . && format-codemod --quiet .",
|
|
45
|
+
"format:check": "oxfmt --check . && format-codemod --check --quiet .",
|
|
46
|
+
"lint": "oxlint --type-aware --type-check --report-unused-disable-directives-severity error",
|
|
47
|
+
"lint:fix": "oxlint --type-aware --type-check --report-unused-disable-directives-severity error --fix",
|
|
48
|
+
"prepare": "command -v git > /dev/null 2>&1 && lefthook install --force || true",
|
|
38
49
|
"publint": "publint",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
50
|
+
"test": "bun test",
|
|
51
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
41
52
|
},
|
|
42
53
|
"devDependencies": {
|
|
43
|
-
"@
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
54
|
+
"@commitlint/cli": "21.2.2",
|
|
55
|
+
"@faker-js/faker": "10.6.0",
|
|
56
|
+
"@stylistic/eslint-plugin": "5.10.0",
|
|
57
|
+
"@tsconfig/strictest": "2.0.8",
|
|
58
|
+
"@types/bun": "1.3.14",
|
|
59
|
+
"@types/node": "26.5.1",
|
|
60
|
+
"@zgeoff/bun-test-extended": "0.1.0",
|
|
61
|
+
"@zgeoff/format-codemod": "0.1.0",
|
|
62
|
+
"@zgeoff/oxlint-config": "0.1.0",
|
|
63
|
+
"knip": "6.35.1",
|
|
64
|
+
"lefthook": "2.1.12",
|
|
65
|
+
"msw": "2.15.0",
|
|
66
|
+
"oxfmt": "0.67.0",
|
|
67
|
+
"oxlint": "1.82.0",
|
|
68
|
+
"oxlint-tsgolint": "7.0.2001",
|
|
69
|
+
"publint": "0.3.24",
|
|
70
|
+
"tiny-invariant": "1.3.3",
|
|
71
|
+
"ts-pattern": "5.9.0",
|
|
72
|
+
"tsdown": "0.23.0",
|
|
73
|
+
"typescript": "7.0.2",
|
|
74
|
+
"zod": "4.6.2"
|
|
48
75
|
},
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"url": "git+https://github.com/zgeoff/auto-mode.git"
|
|
76
|
+
"engines": {
|
|
77
|
+
"node": ">=24"
|
|
52
78
|
},
|
|
53
|
-
"
|
|
54
|
-
"bugs": "https://github.com/zgeoff/auto-mode/issues"
|
|
79
|
+
"packageManager": "bun@1.3.10"
|
|
55
80
|
}
|