@oh-my-pi/pi-coding-agent 17.2.13 → 17.2.15
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/CHANGELOG.md +30 -0
- package/dist/{CHANGELOG-d8xh7keh.md → CHANGELOG-fk29zh06.md} +30 -0
- package/dist/cli.js +5114 -5417
- package/dist/types/cleanse/agent.d.ts +3 -0
- package/dist/types/cleanse/checkers.d.ts +21 -1
- package/dist/types/cleanse/index.d.ts +4 -0
- package/dist/types/cleanse/parsers.d.ts +3 -1
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/cleanse-picker.d.ts +17 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/cli/progress-reporter.d.ts +19 -0
- package/dist/types/commands/cleanse.d.ts +11 -0
- package/dist/types/commands/compress.d.ts +37 -0
- package/dist/types/commands/launch-help.d.ts +3 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/compress/index.d.ts +27 -0
- package/dist/types/compress/protocol.d.ts +49 -0
- package/dist/types/compress/session.d.ts +15 -0
- package/dist/types/compress/types.d.ts +54 -0
- package/dist/types/config/settings-schema.d.ts +10 -0
- package/dist/types/internal-urls/local-protocol.d.ts +8 -0
- package/dist/types/mcp/transports/header-policy.d.ts +8 -0
- package/dist/types/mcp/transports/http.d.ts +2 -0
- package/dist/types/mcp/types.d.ts +19 -0
- package/dist/types/modes/components/late-diagnostics-message.d.ts +2 -0
- package/dist/types/modes/components/todo-reminder.d.ts +2 -0
- package/dist/types/modes/components/tool-activity.d.ts +17 -0
- package/dist/types/modes/components/transcript-container.d.ts +2 -0
- package/dist/types/modes/components/ttsr-notification.d.ts +2 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +2 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +3 -1
- package/dist/types/session/agent-session-types.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/session/session-tools.d.ts +13 -0
- package/dist/types/tools/builtin-names.d.ts +1 -2
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/think.d.ts +41 -0
- package/dist/types/utils/zip.d.ts +19 -9
- package/package.json +13 -13
- package/src/cleanse/agent.ts +67 -2
- package/src/cleanse/checkers.ts +252 -21
- package/src/cleanse/index.ts +90 -36
- package/src/cleanse/parsers.ts +243 -20
- package/src/cleanse/prompts/discovery.md +74 -0
- package/src/cli/args.ts +3 -0
- package/src/cli/cleanse-picker.ts +86 -0
- package/src/cli/command-help.ts +4 -0
- package/src/cli/flag-tables.ts +1 -0
- package/src/cli/gallery-fixtures/agentic.ts +16 -0
- package/src/{cleanse/progress.ts → cli/progress-reporter.ts} +12 -7
- package/src/cli-commands.ts +5 -0
- package/src/commands/cleanse.ts +19 -4
- package/src/commands/compress.ts +45 -0
- package/src/commands/launch-help.ts +3 -0
- package/src/commit/agentic/prompts/analyze-file.md +6 -6
- package/src/commit/agentic/prompts/session-user.md +4 -4
- package/src/compress/index.ts +318 -0
- package/src/compress/prompts/request.md +11 -0
- package/src/compress/prompts/review.md +17 -0
- package/src/compress/prompts/system.md +81 -0
- package/src/compress/protocol.ts +210 -0
- package/src/compress/session.ts +72 -0
- package/src/compress/types.ts +59 -0
- package/src/config/settings-schema.ts +11 -0
- package/src/discovery/builtin-rules/go-add-cleanup.md +8 -8
- package/src/discovery/builtin-rules/go-exp-promoted.md +8 -8
- package/src/discovery/builtin-rules/go-ioutil.md +12 -12
- package/src/discovery/builtin-rules/go-new-expr.md +8 -8
- package/src/discovery/builtin-rules/go-range-int.md +5 -5
- package/src/discovery/builtin-rules/rs-box-leak.md +7 -7
- package/src/discovery/builtin-rules/rs-future-prelude.md +4 -4
- package/src/discovery/builtin-rules/rs-parking-lot.md +6 -6
- package/src/discovery/builtin-rules/ts-no-any.md +5 -5
- package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +8 -8
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +13 -14
- package/src/discovery/builtin-rules/ts-no-local-is-record.md +7 -7
- package/src/discovery/builtin-rules/ts-no-test-timers.md +3 -9
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +7 -7
- package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +2 -2
- package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +3 -9
- package/src/discovery/builtin-rules/ts-set-map.md +2 -4
- package/src/internal-urls/local-protocol.ts +42 -0
- package/src/live/prompts/live-instructions.md +10 -10
- package/src/main.ts +4 -0
- package/src/mcp/client.ts +7 -3
- package/src/mcp/transports/header-policy.ts +28 -0
- package/src/mcp/transports/http.ts +200 -35
- package/src/mcp/types.ts +21 -0
- package/src/modes/components/chat-transcript-builder.ts +17 -5
- package/src/modes/components/late-diagnostics-message.ts +12 -1
- package/src/modes/components/todo-reminder.ts +12 -0
- package/src/modes/components/tool-activity.ts +45 -0
- package/src/modes/components/transcript-container.ts +16 -0
- package/src/modes/components/ttsr-notification.ts +12 -0
- package/src/modes/controllers/event-controller.ts +3 -6
- package/src/modes/controllers/input-controller.ts +6 -6
- package/src/modes/controllers/selector-controller.ts +8 -6
- package/src/modes/interactive-mode.ts +5 -40
- package/src/modes/print-mode.ts +13 -54
- package/src/modes/types.ts +1 -1
- package/src/modes/utils/transcript-render-helpers.ts +3 -2
- package/src/modes/utils/ui-helpers.ts +17 -7
- package/src/prompts/advisor/active-repo-watchdog.md +3 -4
- package/src/prompts/advisor/advise-tool.md +3 -3
- package/src/prompts/advisor/context-files.md +1 -1
- package/src/prompts/advisor/system.md +45 -65
- package/src/prompts/agents/designer.md +44 -44
- package/src/prompts/agents/init.md +18 -18
- package/src/prompts/agents/librarian.md +31 -31
- package/src/prompts/agents/reviewer.md +34 -39
- package/src/prompts/agents/security-reviewer.md +3 -5
- package/src/prompts/agents/task.md +11 -12
- package/src/prompts/bench.md +2 -5
- package/src/prompts/ci-green-request.md +13 -15
- package/src/prompts/dry-balance-bench.md +5 -5
- package/src/prompts/goals/goal-budget-limit.md +4 -5
- package/src/prompts/goals/goal-continuation.md +11 -11
- package/src/prompts/goals/goal-mode-active.md +7 -7
- package/src/prompts/goals/goal-todo-context.md +2 -2
- package/src/prompts/goals/guided-goal-interview.md +18 -24
- package/src/prompts/memories/read-path.md +9 -9
- package/src/prompts/memories/stage_one_system.md +9 -11
- package/src/prompts/review-custom-request.md +8 -11
- package/src/prompts/review-headless-request.md +3 -10
- package/src/prompts/security/scan-coordinator.md +6 -5
- package/src/prompts/security/validate-request.md +4 -7
- package/src/prompts/skills/user-invocation.md +2 -2
- package/src/prompts/steering/parent-irc.md +1 -1
- package/src/prompts/steering/user-interjection.md +1 -3
- package/src/prompts/system/active-repo-context.md +4 -2
- package/src/prompts/system/agent-creation-architect.md +22 -37
- package/src/prompts/system/agent-creation-user.md +3 -3
- package/src/prompts/system/auto-continue.md +1 -1
- package/src/prompts/system/auto-thinking-difficulty-local.md +5 -7
- package/src/prompts/system/auto-thinking-difficulty.md +8 -10
- package/src/prompts/system/autolearn-guidance-learn.md +2 -1
- package/src/prompts/system/autolearn-guidance.md +5 -4
- package/src/prompts/system/autolearn-nudge-autocontinue.md +3 -3
- package/src/prompts/system/background-tan-dispatch.md +3 -3
- package/src/prompts/system/btw-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +8 -6
- package/src/prompts/system/eager-task.md +3 -3
- package/src/prompts/system/empty-stop-retry.md +1 -1
- package/src/prompts/system/gemini-tool-call-reminder.md +5 -5
- package/src/prompts/system/interrupted-thinking.md +3 -3
- package/src/prompts/system/irc-autoreply.md +1 -1
- package/src/prompts/system/irc-incoming.md +3 -3
- package/src/prompts/system/manual-continue.md +3 -3
- package/src/prompts/system/mcp-xdev-guidance.md +2 -2
- package/src/prompts/system/memory-consolidation-system.md +2 -2
- package/src/prompts/system/mid-run-todo-nudge.md +1 -1
- package/src/prompts/system/orchestrate-notice.md +25 -25
- package/src/prompts/system/personalities/default.md +9 -9
- package/src/prompts/system/personalities/friendly.md +11 -11
- package/src/prompts/system/personalities/pragmatic.md +8 -8
- package/src/prompts/system/plan-mode-active.md +61 -64
- package/src/prompts/system/plan-mode-approved.md +10 -11
- package/src/prompts/system/plan-mode-compact-instructions.md +11 -11
- package/src/prompts/system/plan-mode-reference.md +5 -5
- package/src/prompts/system/plan-yolo-handoff.md +2 -2
- package/src/prompts/system/prewalk-checklist.md +5 -5
- package/src/prompts/system/prewalk-continue.md +1 -1
- package/src/prompts/system/prewalk-plan.md +8 -9
- package/src/prompts/system/project-prompt.md +11 -12
- package/src/prompts/system/recap-user.md +1 -1
- package/src/prompts/system/resolve-device-reminder.md +1 -1
- package/src/prompts/system/rewind-report.md +3 -3
- package/src/prompts/system/side-channel-no-tools.md +3 -1
- package/src/prompts/system/snapcompact-context-stub.md +1 -1
- package/src/prompts/system/snapcompact-system-frames-note.md +1 -1
- package/src/prompts/system/snapcompact-system-stub.md +1 -1
- package/src/prompts/system/snapcompact-toolresult-note.md +1 -1
- package/src/prompts/system/speech-rewrite.md +11 -13
- package/src/prompts/system/subagent-async-pending.md +5 -5
- package/src/prompts/system/subagent-system-prompt.md +5 -15
- package/src/prompts/system/subagent-user-prompt.md +1 -1
- package/src/prompts/system/subagent-yield-reminder.md +12 -12
- package/src/prompts/system/system-prompt.md +110 -136
- package/src/prompts/system/tan-context-switch.md +8 -14
- package/src/prompts/system/task-label.md +3 -3
- package/src/prompts/system/thinking-loop-redirect.md +6 -6
- package/src/prompts/system/title-marker-instruction.md +2 -1
- package/src/prompts/system/title-system.md +3 -5
- package/src/prompts/system/ttsr-interrupt.md +3 -3
- package/src/prompts/system/ttsr-tool-reminder.md +1 -1
- package/src/prompts/system/ultrathink-notice.md +1 -1
- package/src/prompts/system/unexpected-stop-classifier.md +3 -3
- package/src/prompts/system/vibe-mode-active.md +15 -15
- package/src/prompts/system/web-search.md +16 -16
- package/src/prompts/system/workflow-notice.md +35 -33
- package/src/prompts/system/xdev-mount-notice.md +4 -4
- package/src/prompts/tools/apply-patch.md +22 -23
- package/src/prompts/tools/approve.md +5 -0
- package/src/prompts/tools/ask.md +10 -10
- package/src/prompts/tools/checkpoint.md +7 -7
- package/src/prompts/tools/computer.md +16 -16
- package/src/prompts/tools/github.md +12 -12
- package/src/prompts/tools/goal.md +8 -9
- package/src/prompts/tools/grep.md +5 -5
- package/src/prompts/tools/image-attachment-describe-system.md +6 -6
- package/src/prompts/tools/image-attachment-describe.md +3 -8
- package/src/prompts/tools/image-gen.md +4 -4
- package/src/prompts/tools/inspect-image-system.md +10 -10
- package/src/prompts/tools/inspect-image.md +10 -13
- package/src/prompts/tools/learn.md +4 -4
- package/src/prompts/tools/manage-skill.md +9 -6
- package/src/prompts/tools/memory-edit.md +8 -8
- package/src/prompts/tools/recall.md +4 -4
- package/src/prompts/tools/reflect.md +2 -2
- package/src/prompts/tools/replace.md +13 -11
- package/src/prompts/tools/retain.md +3 -4
- package/src/prompts/tools/rewind.md +7 -8
- package/src/prompts/tools/rewrite.md +12 -0
- package/src/prompts/tools/security-publish.md +5 -1
- package/src/prompts/tools/security-scan.md +10 -1
- package/src/prompts/tools/task-async-contract.md +7 -1
- package/src/prompts/tools/todo.md +27 -25
- package/src/prompts/tools/vibe-kill.md +2 -2
- package/src/prompts/tools/vibe-list.md +2 -2
- package/src/prompts/tools/vibe-send.md +6 -7
- package/src/prompts/tools/vibe-spawn.md +8 -6
- package/src/prompts/tools/web-search.md +5 -5
- package/src/sdk.ts +10 -1
- package/src/session/agent-session-types.ts +2 -0
- package/src/session/agent-session.ts +28 -0
- package/src/session/session-handoff.ts +21 -0
- package/src/session/session-tools.ts +47 -0
- package/src/tools/builtin-names.ts +1 -2
- package/src/tools/index.ts +10 -0
- package/src/tools/read-format.ts +8 -3
- package/src/tools/read.ts +18 -7
- package/src/tools/renderers.ts +2 -0
- package/src/tools/think.ts +84 -0
- package/src/tools/write.ts +1 -1
- package/src/utils/file-mentions.ts +8 -2
- package/src/utils/zip.ts +858 -94
- package/dist/types/cleanse/progress.d.ts +0 -14
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { Model } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import { type Component } from "@oh-my-pi/pi-tui";
|
|
4
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types.js";
|
|
5
|
+
import { type Theme } from "../modes/theme/theme.js";
|
|
6
|
+
/** Whether a model transport can suppress native reasoning while private scratchpad thoughts are active. */
|
|
7
|
+
export declare function supportsExternalThinking(model: Model | null | undefined): boolean;
|
|
8
|
+
declare const thinkSchema: import("@oh-my-pi/omptype").FluentType<{
|
|
9
|
+
thoughts: string;
|
|
10
|
+
}, {
|
|
11
|
+
thoughts: string;
|
|
12
|
+
}>;
|
|
13
|
+
type ThinkParams = typeof thinkSchema.infer;
|
|
14
|
+
export type ThinkRenderArgs = {
|
|
15
|
+
thoughts?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const thinkToolRenderer: {
|
|
18
|
+
inline: boolean;
|
|
19
|
+
renderCall(args: ThinkRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component;
|
|
20
|
+
renderResult(): Component;
|
|
21
|
+
};
|
|
22
|
+
interface ThinkToolDetails {
|
|
23
|
+
recorded: true;
|
|
24
|
+
}
|
|
25
|
+
/** Records private scratchpad thoughts while native model reasoning is disabled. */
|
|
26
|
+
export declare class ThinkTool implements AgentTool<typeof thinkSchema, ThinkToolDetails> {
|
|
27
|
+
readonly name = "think";
|
|
28
|
+
readonly approval: "read";
|
|
29
|
+
readonly label = "Think";
|
|
30
|
+
readonly summary = "Record private scratchpad thoughts";
|
|
31
|
+
readonly description = "private scratchpad; not shown to user";
|
|
32
|
+
readonly parameters: import("@oh-my-pi/omptype").FluentType<{
|
|
33
|
+
thoughts: string;
|
|
34
|
+
}, {
|
|
35
|
+
thoughts: string;
|
|
36
|
+
}>;
|
|
37
|
+
readonly strict = true;
|
|
38
|
+
readonly intent: "omit";
|
|
39
|
+
execute(_toolCallId: string, _params: ThinkParams): Promise<AgentToolResult<ThinkToolDetails>>;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
@@ -10,13 +10,16 @@ export declare function unzipText(entries: Unzipped, entryPath: string): string
|
|
|
10
10
|
export declare function unzip(bytes: Uint8Array): Unzipped;
|
|
11
11
|
export type ArchiveFormat = "zip" | "tar" | "tar.gz";
|
|
12
12
|
/**
|
|
13
|
-
* Where to read an archive from:
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Where to read an archive from: an extension-inferred filesystem path, a
|
|
14
|
+
* format-tagged filesystem path, or in-memory bytes with an explicit format.
|
|
15
|
+
* ZIP paths are read lazily via ranged central-directory access.
|
|
16
16
|
*/
|
|
17
17
|
export type ArchiveSource = string | {
|
|
18
18
|
bytes: Uint8Array;
|
|
19
19
|
format: ArchiveFormat;
|
|
20
|
+
} | {
|
|
21
|
+
path: string;
|
|
22
|
+
format: ArchiveFormat;
|
|
20
23
|
};
|
|
21
24
|
/** Content for a member when packing or extracting an archive. */
|
|
22
25
|
export type ArchiveMemberContent = string | Uint8Array | Blob;
|
|
@@ -43,7 +46,13 @@ interface ByteSource {
|
|
|
43
46
|
}
|
|
44
47
|
interface TarStorage {
|
|
45
48
|
type: "tar";
|
|
46
|
-
|
|
49
|
+
buffer: Uint8Array;
|
|
50
|
+
dataOffset: number;
|
|
51
|
+
sparse: boolean;
|
|
52
|
+
}
|
|
53
|
+
interface TarLinkStorage {
|
|
54
|
+
type: "tar-link";
|
|
55
|
+
targetPath: string;
|
|
47
56
|
}
|
|
48
57
|
interface ZipStorage {
|
|
49
58
|
type: "zip";
|
|
@@ -53,7 +62,7 @@ interface ZipStorage {
|
|
|
53
62
|
flags: number;
|
|
54
63
|
localHeaderOffset: number;
|
|
55
64
|
}
|
|
56
|
-
type EntryStorage = TarStorage | ZipStorage;
|
|
65
|
+
type EntryStorage = TarStorage | TarLinkStorage | ZipStorage;
|
|
57
66
|
interface ArchiveIndexEntry extends ArchiveNode {
|
|
58
67
|
storage?: EntryStorage;
|
|
59
68
|
}
|
|
@@ -71,7 +80,8 @@ export declare function parseArchivePathCandidates(filePath: string): ArchivePat
|
|
|
71
80
|
/**
|
|
72
81
|
* An indexed, read-only view over a single archive. ZIP archives are indexed
|
|
73
82
|
* from the central directory and members are inflated on demand; tar archives
|
|
74
|
-
* are
|
|
83
|
+
* are parsed from one in-memory buffer, members are sliced on demand, and
|
|
84
|
+
* directory symlink aliases are traversed lazily.
|
|
75
85
|
*/
|
|
76
86
|
export declare class ArchiveReader {
|
|
77
87
|
#private;
|
|
@@ -93,9 +103,9 @@ export declare function listArchiveRoot(bytes: Uint8Array, format: ArchiveFormat
|
|
|
93
103
|
}): Promise<string>;
|
|
94
104
|
/**
|
|
95
105
|
* Fully materialize every file member into a `path → content` map: ZIP members
|
|
96
|
-
* are inflated in memory, tar members are
|
|
97
|
-
* when you need every entry (rewrite, extract); for browsing
|
|
98
|
-
* reads prefer `openArchive`, which is lazy for ZIP.
|
|
106
|
+
* are inflated in memory, tar members are sliced from the decoded archive
|
|
107
|
+
* buffer. Use this when you need every entry (rewrite, extract); for browsing
|
|
108
|
+
* or single-member reads prefer `openArchive`, which is lazy for ZIP.
|
|
99
109
|
*/
|
|
100
110
|
export declare function readArchiveEntries(source: ArchiveSource): Promise<Map<string, ArchiveMemberContent>>;
|
|
101
111
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "17.2.
|
|
4
|
+
"version": "17.2.15",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -50,18 +50,18 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@babel/parser": "^7.29.7",
|
|
53
|
-
"@oh-my-pi/hashline": "17.2.
|
|
54
|
-
"@oh-my-pi/omp-stats": "17.2.
|
|
55
|
-
"@oh-my-pi/omptype": "17.2.
|
|
56
|
-
"@oh-my-pi/pi-agent-core": "17.2.
|
|
57
|
-
"@oh-my-pi/pi-ai": "17.2.
|
|
58
|
-
"@oh-my-pi/pi-catalog": "17.2.
|
|
59
|
-
"@oh-my-pi/pi-mnemopi": "17.2.
|
|
60
|
-
"@oh-my-pi/pi-natives": "17.2.
|
|
61
|
-
"@oh-my-pi/pi-tui": "17.2.
|
|
62
|
-
"@oh-my-pi/pi-utils": "17.2.
|
|
63
|
-
"@oh-my-pi/pi-wire": "17.2.
|
|
64
|
-
"@oh-my-pi/snapcompact": "17.2.
|
|
53
|
+
"@oh-my-pi/hashline": "17.2.15",
|
|
54
|
+
"@oh-my-pi/omp-stats": "17.2.15",
|
|
55
|
+
"@oh-my-pi/omptype": "17.2.15",
|
|
56
|
+
"@oh-my-pi/pi-agent-core": "17.2.15",
|
|
57
|
+
"@oh-my-pi/pi-ai": "17.2.15",
|
|
58
|
+
"@oh-my-pi/pi-catalog": "17.2.15",
|
|
59
|
+
"@oh-my-pi/pi-mnemopi": "17.2.15",
|
|
60
|
+
"@oh-my-pi/pi-natives": "17.2.15",
|
|
61
|
+
"@oh-my-pi/pi-tui": "17.2.15",
|
|
62
|
+
"@oh-my-pi/pi-utils": "17.2.15",
|
|
63
|
+
"@oh-my-pi/pi-wire": "17.2.15",
|
|
64
|
+
"@oh-my-pi/snapcompact": "17.2.15",
|
|
65
65
|
"@opentelemetry/api": "^1.9.1",
|
|
66
66
|
"@opentelemetry/api-logs": "^0.220.0",
|
|
67
67
|
"@opentelemetry/context-async-hooks": "^2.9.0",
|
package/src/cleanse/agent.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getProjectDir, prompt } from "@oh-my-pi/pi-utils";
|
|
1
|
+
import { getProjectDir, isRecord, prompt } from "@oh-my-pi/pi-utils";
|
|
2
2
|
import { ModelRegistry } from "../config/model-registry";
|
|
3
3
|
import { formatModelString, resolveCliModel } from "../config/model-resolver";
|
|
4
4
|
import { Settings } from "../config/settings";
|
|
@@ -9,7 +9,10 @@ import { mapWithConcurrencyLimitAllSettled } from "../task/parallel";
|
|
|
9
9
|
import { runStructuredSubagent } from "../task/structured-subagent";
|
|
10
10
|
import type { ToolSession } from "../tools";
|
|
11
11
|
import { EventBus } from "../utils/event-bus";
|
|
12
|
+
import type { CustomCleanseCheckerSpec } from "./checkers";
|
|
13
|
+
import { CLEANSE_PARSER_KINDS } from "./parsers";
|
|
12
14
|
import assignmentPrompt from "./prompts/assignment.md" with { type: "text" };
|
|
15
|
+
import discoveryPrompt from "./prompts/discovery.md" with { type: "text" };
|
|
13
16
|
import type {
|
|
14
17
|
CleanseAgentOutcome,
|
|
15
18
|
CleanseAssignment,
|
|
@@ -21,6 +24,28 @@ import type {
|
|
|
21
24
|
|
|
22
25
|
const MAX_DIAGNOSTIC_MESSAGE = 4_000;
|
|
23
26
|
|
|
27
|
+
/** Structured output contract for the prompted checker-discovery agent. */
|
|
28
|
+
const DISCOVERY_SCHEMA = {
|
|
29
|
+
type: "object",
|
|
30
|
+
required: ["checkers"],
|
|
31
|
+
properties: {
|
|
32
|
+
checkers: {
|
|
33
|
+
type: "array",
|
|
34
|
+
items: {
|
|
35
|
+
type: "object",
|
|
36
|
+
required: ["label", "command"],
|
|
37
|
+
properties: {
|
|
38
|
+
label: { type: "string" },
|
|
39
|
+
language: { type: "string" },
|
|
40
|
+
cwd: { type: "string" },
|
|
41
|
+
command: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
42
|
+
parser: { type: "string", enum: [...CLEANSE_PARSER_KINDS] },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
24
49
|
/** Hooks used by the standalone command to render subagent lifecycle progress. */
|
|
25
50
|
export interface CleanseAgentHooks {
|
|
26
51
|
onStart?(name: string, assignment: CleanseAssignment): void;
|
|
@@ -31,6 +56,8 @@ export interface CleanseAgentHooks {
|
|
|
31
56
|
export interface CleanseAgentRuntime {
|
|
32
57
|
readonly model: string;
|
|
33
58
|
readonly sessionFile: string;
|
|
59
|
+
/** Run one discovery subagent that translates a user request into runnable checker specs. */
|
|
60
|
+
discoverCheckers(request: string, signal?: AbortSignal): Promise<CustomCleanseCheckerSpec[]>;
|
|
34
61
|
dispatch(
|
|
35
62
|
assignments: CleanseAssignment[],
|
|
36
63
|
wave: number,
|
|
@@ -80,7 +107,7 @@ export async function createCleanseAgentRuntime(options: {
|
|
|
80
107
|
getArtifactsDir: () => sessionManager.getArtifactsDir(),
|
|
81
108
|
getArtifactManager: () => sessionManager.getArtifactManager(),
|
|
82
109
|
getAgentId: () => MAIN_AGENT_ID,
|
|
83
|
-
getSessionSpawns: () => "sonic",
|
|
110
|
+
getSessionSpawns: () => "sonic,task",
|
|
84
111
|
getModelString: () => modelSelector,
|
|
85
112
|
getActiveModelString: () => modelSelector,
|
|
86
113
|
getActiveModel: () => resolved.model,
|
|
@@ -94,6 +121,23 @@ export async function createCleanseAgentRuntime(options: {
|
|
|
94
121
|
return {
|
|
95
122
|
model: modelDisplay,
|
|
96
123
|
sessionFile,
|
|
124
|
+
async discoverCheckers(request: string, signal?: AbortSignal): Promise<CustomCleanseCheckerSpec[]> {
|
|
125
|
+
sessionManager.appendCustomEntry("cleanse_discovery", { request });
|
|
126
|
+
const result = await runStructuredSubagent({
|
|
127
|
+
session: toolSession,
|
|
128
|
+
invocationKind: "task",
|
|
129
|
+
assignment: prompt.render(discoveryPrompt, { request }),
|
|
130
|
+
agent: "task",
|
|
131
|
+
model: modelSelector,
|
|
132
|
+
outputSchema: DISCOVERY_SCHEMA,
|
|
133
|
+
identity: { label: "CleanseDiscovery" },
|
|
134
|
+
enableLsp: true,
|
|
135
|
+
enableIrc: false,
|
|
136
|
+
signal,
|
|
137
|
+
});
|
|
138
|
+
if (result.result.error) throw new Error(`Checker discovery failed: ${result.result.error}`);
|
|
139
|
+
return parseDiscoverySpecs(result.result.structuredOutput?.data);
|
|
140
|
+
},
|
|
97
141
|
async dispatch(
|
|
98
142
|
assignments: CleanseAssignment[],
|
|
99
143
|
wave: number,
|
|
@@ -166,6 +210,27 @@ export async function createCleanseAgentRuntime(options: {
|
|
|
166
210
|
};
|
|
167
211
|
}
|
|
168
212
|
|
|
213
|
+
/** Defensively validate discovery-agent output into runnable checker specs. */
|
|
214
|
+
function parseDiscoverySpecs(data: unknown): CustomCleanseCheckerSpec[] {
|
|
215
|
+
if (!isRecord(data) || !Array.isArray(data.checkers)) return [];
|
|
216
|
+
const specs: CustomCleanseCheckerSpec[] = [];
|
|
217
|
+
for (const value of data.checkers) {
|
|
218
|
+
if (!isRecord(value)) continue;
|
|
219
|
+
const command = Array.isArray(value.command)
|
|
220
|
+
? value.command.filter((part): part is string => typeof part === "string" && part.length > 0)
|
|
221
|
+
: [];
|
|
222
|
+
if (typeof value.label !== "string" || !value.label.trim() || command.length === 0) continue;
|
|
223
|
+
specs.push({
|
|
224
|
+
label: value.label.trim(),
|
|
225
|
+
language: typeof value.language === "string" ? value.language : undefined,
|
|
226
|
+
cwd: typeof value.cwd === "string" ? value.cwd : undefined,
|
|
227
|
+
command,
|
|
228
|
+
parser: typeof value.parser === "string" ? value.parser : undefined,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return specs;
|
|
232
|
+
}
|
|
233
|
+
|
|
169
234
|
function renderAssignment(
|
|
170
235
|
assignment: CleanseAssignment,
|
|
171
236
|
allAssignments: readonly CleanseAssignment[],
|
package/src/cleanse/checkers.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs/promises";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { $which, isRecord, ptree, sanitizeText } from "@oh-my-pi/pi-utils";
|
|
4
4
|
import * as git from "../utils/git";
|
|
5
|
-
import { type CleanseParserKind, parseCleanseDiagnostics } from "./parsers";
|
|
5
|
+
import { CLEANSE_PARSER_KINDS, type CleanseParserKind, parseCleanseDiagnostics } from "./parsers";
|
|
6
6
|
import type { CleanseCheckResult, CleanseDiagnostic, CleanseDiagnosticReport, SkippedCleanseCheck } from "./types";
|
|
7
7
|
|
|
8
8
|
const IGNORED_DIRECTORIES: Record<string, true> = {
|
|
@@ -43,6 +43,8 @@ interface PlanRequest {
|
|
|
43
43
|
parser: CleanseParserKind;
|
|
44
44
|
executable?: string;
|
|
45
45
|
mutates?: boolean;
|
|
46
|
+
/** Skip silently instead of recording a skip when the binary is missing (alternative toolings). */
|
|
47
|
+
optional?: boolean;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
interface DiscoveryState {
|
|
@@ -58,10 +60,21 @@ export interface CleanseDiagnosticSuiteOptions {
|
|
|
58
60
|
includeTests?: boolean;
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
/** Identity and display metadata for one runnable checker. */
|
|
64
|
+
export interface CleanseCheckerDescriptor {
|
|
65
|
+
id: string;
|
|
66
|
+
label: string;
|
|
67
|
+
language: string;
|
|
68
|
+
command: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
61
71
|
/** Re-runnable checker set discovered from one project snapshot. */
|
|
62
72
|
export interface CleanseDiagnosticSuite {
|
|
63
|
-
|
|
73
|
+
/** Every discovered checker; unaffected by {@link CleanseDiagnosticSuite.select}. */
|
|
74
|
+
readonly checkers: readonly CleanseCheckerDescriptor[];
|
|
64
75
|
readonly skipped: readonly SkippedCleanseCheck[];
|
|
76
|
+
/** Narrow subsequent {@link CleanseDiagnosticSuite.run} calls to the named checker ids. */
|
|
77
|
+
select(ids: readonly string[]): void;
|
|
65
78
|
run(signal?: AbortSignal): Promise<CleanseDiagnosticReport>;
|
|
66
79
|
}
|
|
67
80
|
|
|
@@ -81,7 +94,7 @@ export async function discoverCleanseDiagnosticSuite(
|
|
|
81
94
|
};
|
|
82
95
|
await discoverRust(state);
|
|
83
96
|
discoverGo(state);
|
|
84
|
-
discoverPython(state);
|
|
97
|
+
await discoverPython(state);
|
|
85
98
|
await discoverJavaScript(state);
|
|
86
99
|
discoverRuby(state);
|
|
87
100
|
discoverPhp(state);
|
|
@@ -96,29 +109,109 @@ export async function discoverCleanseDiagnosticSuite(
|
|
|
96
109
|
discoverDotnet(state);
|
|
97
110
|
discoverZig(state);
|
|
98
111
|
discoverJvm(state);
|
|
112
|
+
discoverGitHubActions(state);
|
|
99
113
|
|
|
100
|
-
|
|
114
|
+
return createSuite(resolvedCwd, state.plans, state.skipped, new Set(state.files));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createSuite(
|
|
118
|
+
projectCwd: string,
|
|
119
|
+
plans: readonly CheckerPlan[],
|
|
120
|
+
skipped: SkippedCleanseCheck[],
|
|
121
|
+
allowedFiles: ReadonlySet<string>,
|
|
122
|
+
): CleanseDiagnosticSuite {
|
|
123
|
+
let active = [...plans];
|
|
101
124
|
return {
|
|
102
|
-
|
|
103
|
-
skipped
|
|
125
|
+
checkers: plans.map(plan => ({ id: plan.id, label: plan.label, language: plan.language, command: plan.command })),
|
|
126
|
+
skipped,
|
|
127
|
+
select(ids: readonly string[]): void {
|
|
128
|
+
const wanted = new Set(ids);
|
|
129
|
+
active = plans.filter(plan => wanted.has(plan.id));
|
|
130
|
+
},
|
|
104
131
|
async run(signal?: AbortSignal): Promise<CleanseDiagnosticReport> {
|
|
105
132
|
const mutatingChecks: CleanseCheckResult[] = [];
|
|
106
|
-
for (const plan of
|
|
107
|
-
if (plan.mutates) mutatingChecks.push(await runChecker(plan,
|
|
133
|
+
for (const plan of active) {
|
|
134
|
+
if (plan.mutates) mutatingChecks.push(await runChecker(plan, projectCwd, allowedFiles, signal));
|
|
108
135
|
}
|
|
109
136
|
const parallelChecks = await Promise.all(
|
|
110
|
-
|
|
137
|
+
active.filter(plan => !plan.mutates).map(plan => runChecker(plan, projectCwd, allowedFiles, signal)),
|
|
111
138
|
);
|
|
112
139
|
const checks = [...mutatingChecks, ...parallelChecks];
|
|
113
140
|
return {
|
|
114
141
|
checks,
|
|
115
142
|
diagnostics: deduplicateProjectDiagnostics(checks.flatMap(check => check.diagnostics)),
|
|
116
|
-
skipped: [...
|
|
143
|
+
skipped: [...skipped],
|
|
117
144
|
};
|
|
118
145
|
},
|
|
119
146
|
};
|
|
120
147
|
}
|
|
121
148
|
|
|
149
|
+
/** One checker proposed by the prompted discovery agent. */
|
|
150
|
+
export interface CustomCleanseCheckerSpec {
|
|
151
|
+
label: string;
|
|
152
|
+
language?: string;
|
|
153
|
+
cwd?: string;
|
|
154
|
+
command: string[];
|
|
155
|
+
parser?: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Build a runnable suite from discovery-agent checker specs, dropping unrunnable entries into `skipped`. */
|
|
159
|
+
export async function buildCustomCleanseSuite(
|
|
160
|
+
projectCwd: string,
|
|
161
|
+
specs: readonly CustomCleanseCheckerSpec[],
|
|
162
|
+
): Promise<CleanseDiagnosticSuite> {
|
|
163
|
+
const absoluteCwd = path.resolve(projectCwd);
|
|
164
|
+
const resolvedCwd = await fs.realpath(absoluteCwd).catch(() => absoluteCwd);
|
|
165
|
+
const files = await listProjectFiles(resolvedCwd);
|
|
166
|
+
const plans: CheckerPlan[] = [];
|
|
167
|
+
const skipped: SkippedCleanseCheck[] = [];
|
|
168
|
+
for (const [index, spec] of specs.entries()) {
|
|
169
|
+
const [binary, ...args] = spec.command;
|
|
170
|
+
const label = spec.label.trim() || `custom checker ${index + 1}`;
|
|
171
|
+
const language = spec.language?.trim() || "Custom";
|
|
172
|
+
if (!binary) {
|
|
173
|
+
skipped.push({ label, language, reason: "empty command" });
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const root = normalizeCustomRoot(resolvedCwd, spec.cwd);
|
|
177
|
+
if (root === undefined) {
|
|
178
|
+
skipped.push({ label, language, reason: `working directory escapes the project: ${spec.cwd}` });
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
let executable: string | undefined;
|
|
182
|
+
if (/[\\/]/.test(binary)) {
|
|
183
|
+
const candidate = path.resolve(resolvedCwd, root, binary);
|
|
184
|
+
executable = (await Bun.file(candidate).exists()) ? candidate : undefined;
|
|
185
|
+
} else {
|
|
186
|
+
executable = resolveBinary(resolvedCwd, root, [binary]);
|
|
187
|
+
}
|
|
188
|
+
if (!executable) {
|
|
189
|
+
skipped.push({ label, language, reason: `executable not found: ${binary}` });
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
plans.push({
|
|
193
|
+
id: `custom-${index + 1}`,
|
|
194
|
+
label,
|
|
195
|
+
language,
|
|
196
|
+
cwd: path.resolve(resolvedCwd, root),
|
|
197
|
+
executable,
|
|
198
|
+
args,
|
|
199
|
+
parser: CLEANSE_PARSER_KINDS.find(kind => kind === spec.parser) ?? "generic",
|
|
200
|
+
command: formatCommand([path.basename(executable), ...args]),
|
|
201
|
+
mutates: false,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return createSuite(resolvedCwd, plans, skipped, new Set(files));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function normalizeCustomRoot(projectCwd: string, cwd: string | undefined): string | undefined {
|
|
208
|
+
const trimmed = cwd?.trim();
|
|
209
|
+
if (!trimmed || trimmed === ".") return ".";
|
|
210
|
+
const relative = path.relative(projectCwd, path.resolve(projectCwd, trimmed));
|
|
211
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) return undefined;
|
|
212
|
+
return relative.split(path.sep).join("/") || ".";
|
|
213
|
+
}
|
|
214
|
+
|
|
122
215
|
async function listProjectFiles(cwd: string): Promise<string[]> {
|
|
123
216
|
try {
|
|
124
217
|
const [tracked, untracked] = await Promise.all([git.ls.files(cwd), git.ls.untracked(cwd)]);
|
|
@@ -187,8 +280,8 @@ function rootsForPattern(files: readonly string[], pattern: RegExp): string[] {
|
|
|
187
280
|
return markerRoots(files, file => pattern.test(file));
|
|
188
281
|
}
|
|
189
282
|
|
|
190
|
-
function resolveBinary(
|
|
191
|
-
const cwd = path.resolve(
|
|
283
|
+
function resolveBinary(projectCwd: string, root: string, names: readonly string[]): string | undefined {
|
|
284
|
+
const cwd = path.resolve(projectCwd, root);
|
|
192
285
|
const searchDirectories: string[] = [];
|
|
193
286
|
let current = cwd;
|
|
194
287
|
while (true) {
|
|
@@ -198,10 +291,9 @@ function resolveBinary(state: DiscoveryState, root: string, names: readonly stri
|
|
|
198
291
|
path.join(current, "venv", process.platform === "win32" ? "Scripts" : "bin"),
|
|
199
292
|
path.join(current, "vendor", "bin"),
|
|
200
293
|
);
|
|
201
|
-
if (current ===
|
|
294
|
+
if (current === projectCwd) break;
|
|
202
295
|
const parent = path.dirname(current);
|
|
203
|
-
if (parent === current || (!parent.startsWith(`${
|
|
204
|
-
break;
|
|
296
|
+
if (parent === current || (!parent.startsWith(`${projectCwd}${path.sep}`) && parent !== projectCwd)) break;
|
|
205
297
|
current = parent;
|
|
206
298
|
}
|
|
207
299
|
const searchPath = [...new Set(searchDirectories), Bun.env.PATH ?? ""].filter(Boolean).join(path.delimiter);
|
|
@@ -214,9 +306,10 @@ function resolveBinary(state: DiscoveryState, root: string, names: readonly stri
|
|
|
214
306
|
|
|
215
307
|
function addPlan(state: DiscoveryState, request: PlanRequest): void {
|
|
216
308
|
const cwd = path.resolve(state.projectCwd, request.root);
|
|
217
|
-
const executable = request.executable ?? resolveBinary(state, request.root, request.binaries);
|
|
309
|
+
const executable = request.executable ?? resolveBinary(state.projectCwd, request.root, request.binaries);
|
|
218
310
|
const rootLabel = request.root === "." ? "." : request.root;
|
|
219
311
|
if (!executable) {
|
|
312
|
+
if (request.optional) return;
|
|
220
313
|
state.skipped.push({
|
|
221
314
|
label: `${request.label} (${rootLabel})`,
|
|
222
315
|
language: request.language,
|
|
@@ -244,7 +337,7 @@ function formatCommand(argv: readonly string[]): string {
|
|
|
244
337
|
|
|
245
338
|
async function discoverRust(state: DiscoveryState): Promise<void> {
|
|
246
339
|
for (const root of rootsForBasenames(state.files, { "Cargo.toml": true })) {
|
|
247
|
-
const cargo = resolveBinary(state, root, ["cargo"]);
|
|
340
|
+
const cargo = resolveBinary(state.projectCwd, root, ["cargo"]);
|
|
248
341
|
if (!cargo) {
|
|
249
342
|
addPlan(state, {
|
|
250
343
|
id: "clippy",
|
|
@@ -363,6 +456,7 @@ function discoverGo(state: DiscoveryState): void {
|
|
|
363
456
|
const workRoots = rootsForBasenames(state.files, { "go.work": true });
|
|
364
457
|
const roots = workRoots.length > 0 ? workRoots : rootsForBasenames(state.files, { "go.mod": true });
|
|
365
458
|
for (const root of roots) {
|
|
459
|
+
const prefix = root === "." ? "" : `${root}/`;
|
|
366
460
|
addPlan(state, {
|
|
367
461
|
id: "go-vet",
|
|
368
462
|
label: "go vet",
|
|
@@ -372,6 +466,16 @@ function discoverGo(state: DiscoveryState): void {
|
|
|
372
466
|
args: ["vet", "-json", "./..."],
|
|
373
467
|
parser: "go",
|
|
374
468
|
});
|
|
469
|
+
addPlan(state, {
|
|
470
|
+
id: "staticcheck",
|
|
471
|
+
label: "staticcheck",
|
|
472
|
+
language: "Go",
|
|
473
|
+
root,
|
|
474
|
+
binaries: ["staticcheck"],
|
|
475
|
+
args: ["-f", "json", "./..."],
|
|
476
|
+
parser: "staticcheck",
|
|
477
|
+
optional: !state.files.includes(`${prefix}staticcheck.conf`),
|
|
478
|
+
});
|
|
375
479
|
if (state.includeTests) {
|
|
376
480
|
addPlan(state, {
|
|
377
481
|
id: "go-test",
|
|
@@ -384,9 +488,20 @@ function discoverGo(state: DiscoveryState): void {
|
|
|
384
488
|
});
|
|
385
489
|
}
|
|
386
490
|
}
|
|
491
|
+
for (const root of rootsForPattern(state.files, /(?:^|\/)\.golangci\.(?:ya?ml|toml|json)$/)) {
|
|
492
|
+
addPlan(state, {
|
|
493
|
+
id: "golangci",
|
|
494
|
+
label: "golangci-lint",
|
|
495
|
+
language: "Go",
|
|
496
|
+
root,
|
|
497
|
+
binaries: ["golangci-lint"],
|
|
498
|
+
args: ["run"],
|
|
499
|
+
parser: "golangci",
|
|
500
|
+
});
|
|
501
|
+
}
|
|
387
502
|
}
|
|
388
503
|
|
|
389
|
-
function discoverPython(state: DiscoveryState): void {
|
|
504
|
+
async function discoverPython(state: DiscoveryState): Promise<void> {
|
|
390
505
|
if (!containsExtension(state.files, [".py", ".pyi"])) return;
|
|
391
506
|
const roots = rootsForBasenames(state.files, {
|
|
392
507
|
".ruff.toml": true,
|
|
@@ -423,11 +538,70 @@ function discoverPython(state: DiscoveryState): void {
|
|
|
423
538
|
label: "pyright",
|
|
424
539
|
language: "Python",
|
|
425
540
|
root,
|
|
426
|
-
binaries: ["pyright"],
|
|
541
|
+
binaries: ["pyright", "basedpyright"],
|
|
427
542
|
args: ["--outputjson"],
|
|
428
543
|
parser: "pyright",
|
|
429
544
|
});
|
|
430
545
|
}
|
|
546
|
+
const pyprojectRoots = rootsForBasenames(state.files, { "pyproject.toml": true });
|
|
547
|
+
const pyprojectContent = new Map<string, string>();
|
|
548
|
+
for (const root of pyprojectRoots) {
|
|
549
|
+
const file = root === "." ? "pyproject.toml" : `${root}/pyproject.toml`;
|
|
550
|
+
pyprojectContent.set(
|
|
551
|
+
root,
|
|
552
|
+
await Bun.file(path.resolve(state.projectCwd, file))
|
|
553
|
+
.text()
|
|
554
|
+
.catch(() => ""),
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
const withSection = (section: string): string[] =>
|
|
558
|
+
pyprojectRoots.filter(root => pyprojectContent.get(root)?.includes(section) === true);
|
|
559
|
+
const toolRoots = (basenames: Record<string, true>, section: string): string[] =>
|
|
560
|
+
topmostRoots([...rootsForBasenames(state.files, basenames), ...withSection(section)]);
|
|
561
|
+
for (const root of toolRoots({ "mypy.ini": true, ".mypy.ini": true }, "[tool.mypy")) {
|
|
562
|
+
addPlan(state, {
|
|
563
|
+
id: "mypy",
|
|
564
|
+
label: "mypy",
|
|
565
|
+
language: "Python",
|
|
566
|
+
root,
|
|
567
|
+
binaries: ["mypy"],
|
|
568
|
+
args: ["--no-error-summary", "--no-pretty", "."],
|
|
569
|
+
parser: "mypy",
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
for (const root of toolRoots({ ".pylintrc": true, pylintrc: true }, "[tool.pylint")) {
|
|
573
|
+
addPlan(state, {
|
|
574
|
+
id: "pylint",
|
|
575
|
+
label: "pylint",
|
|
576
|
+
language: "Python",
|
|
577
|
+
root,
|
|
578
|
+
binaries: ["pylint"],
|
|
579
|
+
args: ["--output-format=json", "--recursive=y", "."],
|
|
580
|
+
parser: "pylint",
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
for (const root of rootsForBasenames(state.files, { ".flake8": true })) {
|
|
584
|
+
addPlan(state, {
|
|
585
|
+
id: "flake8",
|
|
586
|
+
label: "flake8",
|
|
587
|
+
language: "Python",
|
|
588
|
+
root,
|
|
589
|
+
binaries: ["flake8"],
|
|
590
|
+
args: ["."],
|
|
591
|
+
parser: "flake8",
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
for (const root of toolRoots({ "ty.toml": true }, "[tool.ty")) {
|
|
595
|
+
addPlan(state, {
|
|
596
|
+
id: "ty",
|
|
597
|
+
label: "ty check",
|
|
598
|
+
language: "Python",
|
|
599
|
+
root,
|
|
600
|
+
binaries: ["ty"],
|
|
601
|
+
args: ["check", "--output-format", "concise"],
|
|
602
|
+
parser: "ty",
|
|
603
|
+
});
|
|
604
|
+
}
|
|
431
605
|
}
|
|
432
606
|
|
|
433
607
|
async function discoverJavaScript(state: DiscoveryState): Promise<void> {
|
|
@@ -457,16 +631,53 @@ async function discoverJavaScript(state: DiscoveryState): Promise<void> {
|
|
|
457
631
|
});
|
|
458
632
|
}
|
|
459
633
|
for (const root of rootsForBasenames(state.files, { "tsconfig.json": true })) {
|
|
634
|
+
const prefix = root === "." ? "" : `${root}/`;
|
|
635
|
+
const hasVue = state.files.some(file => file.startsWith(prefix) && file.endsWith(".vue"));
|
|
460
636
|
addPlan(state, {
|
|
461
637
|
id: "typescript",
|
|
462
|
-
label: "TypeScript",
|
|
638
|
+
label: hasVue ? "Vue TypeScript" : "TypeScript",
|
|
463
639
|
language: "TypeScript",
|
|
464
640
|
root,
|
|
465
|
-
binaries: ["tsgo", "tsc"],
|
|
641
|
+
binaries: hasVue ? ["vue-tsc", "tsgo", "tsc"] : ["tsgo", "tsc"],
|
|
466
642
|
args: ["--noEmit", "--pretty", "false"],
|
|
467
643
|
parser: "generic",
|
|
468
644
|
});
|
|
469
645
|
}
|
|
646
|
+
for (const root of rootsForBasenames(state.files, { ".oxlintrc.json": true })) {
|
|
647
|
+
addPlan(state, {
|
|
648
|
+
id: "oxlint",
|
|
649
|
+
label: "oxlint",
|
|
650
|
+
language: "JavaScript/TypeScript",
|
|
651
|
+
root,
|
|
652
|
+
binaries: ["oxlint"],
|
|
653
|
+
args: ["--format=unix"],
|
|
654
|
+
parser: "oxlint",
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
for (const root of rootsForBasenames(state.files, { "deno.json": true, "deno.jsonc": true })) {
|
|
658
|
+
addPlan(state, {
|
|
659
|
+
id: "deno-lint",
|
|
660
|
+
label: "deno lint",
|
|
661
|
+
language: "JavaScript/TypeScript",
|
|
662
|
+
root,
|
|
663
|
+
binaries: ["deno"],
|
|
664
|
+
args: ["lint", "--json"],
|
|
665
|
+
parser: "deno-lint",
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
const stylelintConfig =
|
|
669
|
+
/(?:^|\/)(?:\.stylelintrc(?:\.(?:json|ya?ml|js|cjs|mjs))?|stylelint\.config\.(?:js|cjs|mjs|ts))$/;
|
|
670
|
+
for (const root of rootsForPattern(state.files, stylelintConfig)) {
|
|
671
|
+
addPlan(state, {
|
|
672
|
+
id: "stylelint",
|
|
673
|
+
label: "stylelint",
|
|
674
|
+
language: "CSS",
|
|
675
|
+
root,
|
|
676
|
+
binaries: ["stylelint"],
|
|
677
|
+
args: ["**/*.{css,scss,sass,less}", "--formatter", "json", "--allow-empty-input"],
|
|
678
|
+
parser: "stylelint",
|
|
679
|
+
});
|
|
680
|
+
}
|
|
470
681
|
if (state.includeTests) await discoverPackageTests(state);
|
|
471
682
|
}
|
|
472
683
|
|
|
@@ -905,6 +1116,26 @@ function discoverJvm(state: DiscoveryState): void {
|
|
|
905
1116
|
}
|
|
906
1117
|
}
|
|
907
1118
|
|
|
1119
|
+
function discoverGitHubActions(state: DiscoveryState): void {
|
|
1120
|
+
const roots = new Set<string>();
|
|
1121
|
+
for (const file of state.files) {
|
|
1122
|
+
const match = /^(?:(.*)\/)?\.github\/workflows\/[^/]+\.ya?ml$/.exec(file);
|
|
1123
|
+
if (match) roots.add(match[1] ?? ".");
|
|
1124
|
+
}
|
|
1125
|
+
for (const root of topmostRoots([...roots])) {
|
|
1126
|
+
addPlan(state, {
|
|
1127
|
+
id: "actionlint",
|
|
1128
|
+
label: "actionlint",
|
|
1129
|
+
language: "GitHub Actions",
|
|
1130
|
+
root,
|
|
1131
|
+
binaries: ["actionlint"],
|
|
1132
|
+
args: ["-format", "{{json .}}"],
|
|
1133
|
+
parser: "actionlint",
|
|
1134
|
+
optional: true,
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
908
1139
|
async function runChecker(
|
|
909
1140
|
plan: CheckerPlan,
|
|
910
1141
|
projectCwd: string,
|