pi-harness-runtime 1.1.405 → 1.1.407

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/index.ts +67 -0
  2. package/package.json +1 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/connection-runtime/package.json +1 -1
  21. package/packages/context-compiler/package.json +1 -1
  22. package/packages/context-discovery/package.json +1 -1
  23. package/packages/context-manager/package.json +1 -1
  24. package/packages/contracts/package.json +1 -1
  25. package/packages/cookie-sanitizer/package.json +1 -1
  26. package/packages/cost-optimizer/package.json +1 -1
  27. package/packages/dependency-analyzer/package.json +1 -1
  28. package/packages/django-plugin/package.json +1 -1
  29. package/packages/doc-generator/package.json +1 -1
  30. package/packages/durable-review/package.json +1 -1
  31. package/packages/e2e-tester/package.json +1 -1
  32. package/packages/evaluation-engine/package.json +1 -1
  33. package/packages/evaluation-runner/package.json +1 -1
  34. package/packages/event-bus/package.json +1 -1
  35. package/packages/event-sink/package.json +1 -1
  36. package/packages/event-store/package.json +1 -1
  37. package/packages/experience-replay/package.json +1 -1
  38. package/packages/feedback-collector/package.json +1 -1
  39. package/packages/file-copy-helper/package.json +1 -1
  40. package/packages/framework-detector/package.json +1 -1
  41. package/packages/framework-plugin-sdk/package.json +1 -1
  42. package/packages/frappe-plugin/package.json +1 -1
  43. package/packages/generic-web-plugin/package.json +1 -1
  44. package/packages/health-monitor/package.json +1 -1
  45. package/packages/intent-analyzer/package.json +1 -1
  46. package/packages/jev-judge/package.json +1 -1
  47. package/packages/knowledge-graph/package.json +1 -1
  48. package/packages/knowledge-retrieval/package.json +1 -1
  49. package/packages/laravel-plugin/package.json +1 -1
  50. package/packages/learning-engine/package.json +1 -1
  51. package/packages/logging/package.json +1 -1
  52. package/packages/mcp-adapter/package.json +1 -1
  53. package/packages/memory-engine/package.json +1 -1
  54. package/packages/milestone-manager/package.json +1 -1
  55. package/packages/model-registry/package.json +1 -1
  56. package/packages/nextjs-plugin/package.json +1 -1
  57. package/packages/notification/package.json +1 -1
  58. package/packages/observability/package.json +1 -1
  59. package/packages/okf-indexer/package.json +1 -1
  60. package/packages/paperclip-integration/package.json +1 -1
  61. package/packages/performance-optimizer/package.json +1 -1
  62. package/packages/permission-mode/package.json +1 -1
  63. package/packages/privilege-broker/package.json +1 -1
  64. package/packages/project-analyzer/package.json +1 -1
  65. package/packages/project-bootstrap/package.json +1 -1
  66. package/packages/projection-engine/package.json +1 -1
  67. package/packages/prompt-compiler/package.json +1 -1
  68. package/packages/prompt-versioning/package.json +1 -1
  69. package/packages/provider-adapter-sdk/package.json +1 -1
  70. package/packages/provider-router/package.json +1 -1
  71. package/packages/provider-selector/package.json +1 -1
  72. package/packages/providers/package.json +1 -1
  73. package/packages/qdrant-skills/package.json +1 -1
  74. package/packages/quota-manager/package.json +1 -1
  75. package/packages/rate-limiter/package.json +1 -1
  76. package/packages/react-vite-plugin/package.json +1 -1
  77. package/packages/relay/package.json +1 -1
  78. package/packages/release-manager/package.json +1 -1
  79. package/packages/requirement-compiler/package.json +1 -1
  80. package/packages/runtime/package.json +1 -1
  81. package/packages/scheduler/package.json +1 -1
  82. package/packages/scheduler-adapter/package.json +1 -1
  83. package/packages/session/package.json +1 -1
  84. package/packages/session-api/package.json +1 -1
  85. package/packages/session-export/package.json +1 -1
  86. package/packages/shared-context/package.json +1 -1
  87. package/packages/skill-mcp-client/package.json +1 -1
  88. package/packages/skill-registry/package.json +1 -1
  89. package/packages/skills/package.json +1 -1
  90. package/packages/sprint-planner/package.json +1 -1
  91. package/packages/subscription-engine/package.json +1 -1
  92. package/packages/task-compiler/package.json +1 -1
  93. package/packages/telegram/package.json +1 -1
  94. package/packages/tencentdb-memory/package.json +1 -1
  95. package/packages/tencentdb-sync/package.json +1 -1
  96. package/packages/test-data-generator/package.json +1 -1
  97. package/packages/test-generator/package.json +1 -1
  98. package/packages/todo-bd-sync/package.json +1 -1
  99. package/packages/token-estimation/package.json +1 -1
  100. package/packages/token-optimizer/package.json +1 -1
  101. package/packages/tooling-preference/dist/changed-files.d.ts +45 -0
  102. package/packages/tooling-preference/dist/changed-files.d.ts.map +1 -0
  103. package/packages/tooling-preference/dist/changed-files.js +125 -0
  104. package/packages/tooling-preference/dist/changed-files.js.map +1 -0
  105. package/packages/tooling-preference/dist/index.d.ts +2 -0
  106. package/packages/tooling-preference/dist/index.d.ts.map +1 -1
  107. package/packages/tooling-preference/dist/index.js +2 -0
  108. package/packages/tooling-preference/dist/index.js.map +1 -1
  109. package/packages/tooling-preference/dist/quality-check.d.ts +75 -0
  110. package/packages/tooling-preference/dist/quality-check.d.ts.map +1 -0
  111. package/packages/tooling-preference/dist/quality-check.js +216 -0
  112. package/packages/tooling-preference/dist/quality-check.js.map +1 -0
  113. package/packages/tooling-preference/package.json +1 -1
  114. package/packages/tooling-preference/src/changed-files.ts +147 -0
  115. package/packages/tooling-preference/src/index.ts +19 -0
  116. package/packages/tooling-preference/src/quality-check.ts +279 -0
  117. package/packages/tooling-preference/tests/changed-files.test.ts +124 -0
  118. package/packages/tooling-preference/tests/quality-check.test.ts +272 -0
  119. package/packages/trajectory/package.json +1 -1
  120. package/packages/tui/package.json +1 -1
  121. package/packages/types/package.json +1 -1
  122. package/packages/voice-api/package.json +1 -1
  123. package/packages/webmcp-tester/package.json +1 -1
  124. package/packages/wiki/package.json +1 -1
  125. package/packages/workflow-events/package.json +1 -1
  126. package/packages/workspace-scanner/package.json +1 -1
  127. package/packages/worktree/package.json +1 -1
  128. package/packages/write-review/package.json +1 -1
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The post-change quality check of a JavaScript/TypeScript workspace.
3
+ *
4
+ * The check honors the saved preference: "prefer_biome" runs Biome on the
5
+ * changed files, "use_project_tools" runs the project's own lint script.
6
+ * The check never writes: it reports what it found and the agent fixes it.
7
+ */
8
+ import type { ToolingContext } from "./detect.js";
9
+ import type { ToolingPreference } from "./proposal.js";
10
+ /** The tool that checks the changed files. */
11
+ export type QualityCheckTool = "biome" | "project" | "none";
12
+ export interface QualityCheckPlan {
13
+ /** The tool that would run. */
14
+ tool: QualityCheckTool;
15
+ /** The binary to run - never a shell string. */
16
+ command: string | null;
17
+ /** The arguments of the command. */
18
+ args: string[];
19
+ /** The files the check looks at. */
20
+ files: string[];
21
+ /** Why this plan - a human wording. */
22
+ reason: string;
23
+ }
24
+ export interface QualityCheckResult {
25
+ /** Did the check run? */
26
+ ran: boolean;
27
+ /** Did the check pass? Meaningless when the check did not run. */
28
+ passed: boolean;
29
+ /** The tool that ran. */
30
+ tool: QualityCheckTool;
31
+ /** How many files the check looked at. */
32
+ fileCount: number;
33
+ /** The exit code of the check. */
34
+ exitCode: number | null;
35
+ /** Did the check hit the timeout? */
36
+ timedOut: boolean;
37
+ /** The output of the check, truncated. */
38
+ output: string;
39
+ /** Why the result is what it is - a human wording. */
40
+ reason: string;
41
+ }
42
+ /** The scripts of the workspace, if it has a package.json. */
43
+ export declare function readWorkspaceScripts(root: string): Record<string, string>;
44
+ /**
45
+ * Choose the check that runs after a change.
46
+ *
47
+ * The preference decides the tool: Biome when the user asked for it, the
48
+ * project's own script otherwise. A workspace without a saved decision is
49
+ * never checked - the user has not chosen a tool yet.
50
+ */
51
+ export declare function selectQualityCheck(options: {
52
+ context: ToolingContext;
53
+ preference: ToolingPreference | undefined;
54
+ files: string[];
55
+ scripts?: Record<string, string>;
56
+ }): QualityCheckPlan;
57
+ /**
58
+ * Run the check.
59
+ *
60
+ * The command is started without a shell, with a timeout, and it never
61
+ * writes: a check that wants to fix files is not a check.
62
+ */
63
+ export declare function runQualityCheck(plan: QualityCheckPlan, options: {
64
+ cwd: string;
65
+ timeoutMs?: number;
66
+ maxOutputChars?: number;
67
+ }): QualityCheckResult;
68
+ /**
69
+ * The status line of the check.
70
+ *
71
+ * The line stays plain ASCII: it is read by the pi host and by terminals
72
+ * without a font of their own.
73
+ */
74
+ export declare function formatQualityCheckStatus(result: QualityCheckResult): string;
75
+ //# sourceMappingURL=quality-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality-check.d.ts","sourceRoot":"","sources":["../src/quality-check.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,8CAA8C;AAC9C,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAChC,+BAA+B;IAC/B,IAAI,EAAE,gBAAgB,CAAC;IACvB,gDAAgD;IAChD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,oCAAoC;IACpC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,oCAAoC;IACpC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IAClC,yBAAyB;IACzB,GAAG,EAAE,OAAO,CAAC;IACb,kEAAkE;IAClE,MAAM,EAAE,OAAO,CAAC;IAChB,yBAAyB;IACzB,IAAI,EAAE,gBAAgB,CAAC;IACvB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qCAAqC;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;CACf;AAsCD,8DAA8D;AAC9D,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAazE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC3C,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC1C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,GAAG,gBAAgB,CAiEnB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,kBAAkB,CAwEpB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAS3E"}
@@ -0,0 +1,216 @@
1
+ /**
2
+ * The post-change quality check of a JavaScript/TypeScript workspace.
3
+ *
4
+ * The check honors the saved preference: "prefer_biome" runs Biome on the
5
+ * changed files, "use_project_tools" runs the project's own lint script.
6
+ * The check never writes: it reports what it found and the agent fixes it.
7
+ */
8
+ import { spawnSync } from "node:child_process";
9
+ import { existsSync, readFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ /** How the tool is started with the package manager of the workspace. */
12
+ function packageRunner(packageManager, binary) {
13
+ switch (packageManager) {
14
+ case "bun":
15
+ return { command: "bunx", args: [binary] };
16
+ case "pnpm":
17
+ return { command: "pnpm", args: ["exec", binary] };
18
+ case "yarn":
19
+ return { command: "yarn", args: [binary] };
20
+ case "npm":
21
+ return { command: "npx", args: ["--no-install", binary] };
22
+ default:
23
+ return { command: binary, args: [] };
24
+ }
25
+ }
26
+ /** How a package script is started with the package manager of the workspace. */
27
+ function scriptRunner(packageManager, script) {
28
+ switch (packageManager) {
29
+ case "bun":
30
+ return { command: "bun", args: ["run", script] };
31
+ case "pnpm":
32
+ return { command: "pnpm", args: ["run", script] };
33
+ case "yarn":
34
+ return { command: "yarn", args: [script] };
35
+ default:
36
+ return { command: "npm", args: ["run", script] };
37
+ }
38
+ }
39
+ /** The scripts of the workspace, if it has a package.json. */
40
+ export function readWorkspaceScripts(root) {
41
+ try {
42
+ const path = join(root, "package.json");
43
+ if (!existsSync(path))
44
+ return {};
45
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
46
+ const scripts = parsed?.scripts;
47
+ if (!scripts || typeof scripts !== "object")
48
+ return {};
49
+ return scripts;
50
+ }
51
+ catch {
52
+ return {};
53
+ }
54
+ }
55
+ /**
56
+ * Choose the check that runs after a change.
57
+ *
58
+ * The preference decides the tool: Biome when the user asked for it, the
59
+ * project's own script otherwise. A workspace without a saved decision is
60
+ * never checked - the user has not chosen a tool yet.
61
+ */
62
+ export function selectQualityCheck(options) {
63
+ const { context, preference, files } = options;
64
+ const scripts = options.scripts ?? readWorkspaceScripts(context.root);
65
+ const none = (reason) => ({
66
+ tool: "none",
67
+ command: null,
68
+ args: [],
69
+ files: [],
70
+ reason,
71
+ });
72
+ if (files.length === 0) {
73
+ return none("no JavaScript/TypeScript file changed");
74
+ }
75
+ if (!preference) {
76
+ return none("the user has not chosen a tool yet");
77
+ }
78
+ const projectScript = () => {
79
+ for (const name of ["lint", "typecheck"]) {
80
+ if (typeof scripts[name] === "string") {
81
+ return { script: name, spec: scriptRunner(context.packageManager, name) };
82
+ }
83
+ }
84
+ return null;
85
+ };
86
+ if (preference === "prefer_biome") {
87
+ if (!context.usesBiome) {
88
+ // The user asked for Biome, but the workspace does not have it
89
+ // (yet). The project's own tool is the honest fallback.
90
+ const fallback = projectScript();
91
+ if (!fallback) {
92
+ return none("Biome is not installed and the project has no lint script");
93
+ }
94
+ return {
95
+ tool: "project",
96
+ command: fallback.spec.command,
97
+ args: fallback.spec.args,
98
+ files,
99
+ reason: `Biome is not installed yet - the project script "${fallback.script}" runs instead`,
100
+ };
101
+ }
102
+ const runner = packageRunner(context.packageManager, "biome");
103
+ return {
104
+ tool: "biome",
105
+ command: runner.command,
106
+ args: [...runner.args, "check", ...files],
107
+ files,
108
+ reason: "the preference asks for Biome",
109
+ };
110
+ }
111
+ const script = projectScript();
112
+ if (!script) {
113
+ return none("the project has neither a lint nor a typecheck script");
114
+ }
115
+ return {
116
+ tool: "project",
117
+ command: script.spec.command,
118
+ args: script.spec.args,
119
+ files,
120
+ reason: `the project script "${script.script}" runs`,
121
+ };
122
+ }
123
+ /**
124
+ * Run the check.
125
+ *
126
+ * The command is started without a shell, with a timeout, and it never
127
+ * writes: a check that wants to fix files is not a check.
128
+ */
129
+ export function runQualityCheck(plan, options) {
130
+ const maxOutputChars = options.maxOutputChars ?? 2000;
131
+ const base = {
132
+ tool: plan.tool,
133
+ fileCount: plan.files.length,
134
+ };
135
+ if (!plan.command) {
136
+ return {
137
+ ...base,
138
+ ran: false,
139
+ passed: false,
140
+ exitCode: null,
141
+ timedOut: false,
142
+ output: "",
143
+ reason: plan.reason,
144
+ };
145
+ }
146
+ try {
147
+ const result = spawnSync(plan.command, plan.args, {
148
+ cwd: options.cwd,
149
+ encoding: "utf8",
150
+ timeout: options.timeoutMs ?? 60_000,
151
+ maxBuffer: 8 * 1024 * 1024,
152
+ shell: false,
153
+ env: { ...process.env, CI: process.env.CI ?? "1" },
154
+ });
155
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
156
+ const error = result.error;
157
+ const timedOut = Boolean(error && /ETIMEDOUT/i.test(String(error.message)));
158
+ const exitCode = result.status ?? null;
159
+ if (error && !timedOut) {
160
+ // The binary does not exist: the check never started.
161
+ return {
162
+ ...base,
163
+ ran: false,
164
+ passed: false,
165
+ exitCode: null,
166
+ timedOut: false,
167
+ output: output.slice(0, maxOutputChars),
168
+ reason: `the check could not start: ${String(error.message).slice(0, 200)}`,
169
+ };
170
+ }
171
+ let reason = "the check reported problems";
172
+ if (timedOut) {
173
+ reason = "the check hit the timeout";
174
+ }
175
+ else if (exitCode === 0) {
176
+ reason = "the check passed";
177
+ }
178
+ return {
179
+ ...base,
180
+ ran: true,
181
+ passed: !timedOut && exitCode === 0,
182
+ exitCode,
183
+ timedOut,
184
+ output: output.slice(0, maxOutputChars),
185
+ reason,
186
+ };
187
+ }
188
+ catch (error) {
189
+ return {
190
+ ...base,
191
+ ran: false,
192
+ passed: false,
193
+ exitCode: null,
194
+ timedOut: false,
195
+ output: "",
196
+ reason: `the check could not start: ${String(error).slice(0, 200)}`,
197
+ };
198
+ }
199
+ }
200
+ /**
201
+ * The status line of the check.
202
+ *
203
+ * The line stays plain ASCII: it is read by the pi host and by terminals
204
+ * without a font of their own.
205
+ */
206
+ export function formatQualityCheckStatus(result) {
207
+ if (!result.ran) {
208
+ return `[TOOL] no check: ${result.reason}`;
209
+ }
210
+ const tool = result.tool === "biome" ? "biome" : "project lint";
211
+ if (result.timedOut) {
212
+ return `[TOOL] ${tool}: ${result.fileCount} file(s), timeout`;
213
+ }
214
+ return `[TOOL] ${tool}: ${result.fileCount} file(s), ${result.passed ? "ok" : "failed"}`;
215
+ }
216
+ //# sourceMappingURL=quality-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality-check.js","sourceRoot":"","sources":["../src/quality-check.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tooling-preference",
3
- "version": "1.1.405",
3
+ "version": "1.1.407",
4
4
  "description": "pi-harness-runtime — JS/TS tooling preference (Biome vs ESLint/Prettier) proposal",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,147 @@
1
+ /**
2
+ * The JavaScript/TypeScript files a session changed.
3
+ *
4
+ * The harness has no list of "the files Pi changed": the worktree is the
5
+ * only honest source. The detection reads the git status of the workspace
6
+ * (modified, staged and untracked files) and keeps the JavaScript and
7
+ * TypeScript ones. A workspace that is not a git repository yields nothing
8
+ * - the harness never guesses which files belong to the session.
9
+ */
10
+
11
+ import { spawnSync } from "node:child_process";
12
+
13
+ /** The extensions the quality check looks at. */
14
+ export const JS_TS_EXTENSIONS = [
15
+ ".ts",
16
+ ".tsx",
17
+ ".mts",
18
+ ".cts",
19
+ ".js",
20
+ ".jsx",
21
+ ".mjs",
22
+ ".cjs",
23
+ ] as const;
24
+
25
+ /** Directories that never hold source the check should read. */
26
+ const IGNORED_PATH_PARTS = new Set([
27
+ "node_modules",
28
+ "dist",
29
+ "build",
30
+ "out",
31
+ "coverage",
32
+ ".next",
33
+ ".turbo",
34
+ ]);
35
+
36
+ export interface ChangedFilesResult {
37
+ /** The changed JS/TS files, relative to the workspace root. */
38
+ files: string[];
39
+ /** Was the worktree read at all? */
40
+ detected: boolean;
41
+ /** Why the result is what it is - a human wording. */
42
+ reason: string;
43
+ }
44
+
45
+ /**
46
+ * Is this path a JavaScript or TypeScript source file?
47
+ */
48
+ export function isJsTsFile(path: string): boolean {
49
+ const lower = path.toLowerCase();
50
+ return JS_TS_EXTENSIONS.some((extension) => lower.endsWith(extension));
51
+ }
52
+
53
+ /**
54
+ * Is this path inside a directory the check must not read?
55
+ */
56
+ export function isIgnoredPath(path: string): boolean {
57
+ return path
58
+ .split("/")
59
+ .filter((part) => part.length > 0)
60
+ .some((part) => IGNORED_PATH_PARTS.has(part));
61
+ }
62
+
63
+ /**
64
+ * Parse the output of `git status --porcelain -z`.
65
+ *
66
+ * Every record is "XY <path>"; a rename or a copy carries the original
67
+ * path in the next record, which is not a change of its own.
68
+ */
69
+ export function parseGitStatus(output: string): string[] {
70
+ const paths: string[] = [];
71
+ const records = output.split("\0").filter((record) => record.length > 0);
72
+
73
+ for (let index = 0; index < records.length; index++) {
74
+ const record = records[index];
75
+ if (!record || record.length < 4) continue;
76
+ const status = record.slice(0, 2);
77
+ const path = record.slice(3).trim();
78
+ if (!path) continue;
79
+ paths.push(path);
80
+ // A rename or a copy is followed by the original path.
81
+ if (status.includes("R") || status.includes("C")) index++;
82
+ }
83
+
84
+ return paths;
85
+ }
86
+
87
+ /**
88
+ * The JavaScript/TypeScript files that changed in the workspace.
89
+ *
90
+ * The detection never fails the session: a workspace without git (or
91
+ * without a repository) simply has no changed files.
92
+ */
93
+ export function detectChangedJsTsFiles(
94
+ workspaceRoot: string,
95
+ options: { timeoutMs?: number; gitBin?: string } = {},
96
+ ): ChangedFilesResult {
97
+ const timeoutMs = options.timeoutMs ?? 5000;
98
+ const gitBin = options.gitBin ?? "git";
99
+
100
+ let output = "";
101
+ try {
102
+ const result = spawnSync(
103
+ gitBin,
104
+ ["status", "--porcelain=v1", "-z", "--untracked-files=all"],
105
+ {
106
+ cwd: workspaceRoot,
107
+ encoding: "utf8",
108
+ timeout: timeoutMs,
109
+ maxBuffer: 8 * 1024 * 1024,
110
+ },
111
+ );
112
+ if (result.error || result.status !== 0) {
113
+ return {
114
+ files: [],
115
+ detected: false,
116
+ reason: result.error
117
+ ? "git could not be run"
118
+ : "the workspace is not a git repository",
119
+ };
120
+ }
121
+ output = result.stdout ?? "";
122
+ } catch {
123
+ return { files: [], detected: false, reason: "git could not be run" };
124
+ }
125
+
126
+ const seen = new Set<string>();
127
+ const files: string[] = [];
128
+ for (const path of parseGitStatus(output)) {
129
+ const normalized = path.replace(/\\/g, "/");
130
+ if (seen.has(normalized)) continue;
131
+ if (!isJsTsFile(normalized) || isIgnoredPath(normalized)) continue;
132
+ seen.add(normalized);
133
+ files.push(normalized);
134
+ }
135
+ files.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
136
+
137
+ let reason = "no changed file";
138
+ if (files.length > 0) {
139
+ reason = `${files.length} changed file(s)`;
140
+ }
141
+
142
+ return {
143
+ files,
144
+ detected: true,
145
+ reason,
146
+ };
147
+ }
@@ -42,3 +42,22 @@ export {
42
42
  type ToolingRequest,
43
43
  type ToolingResolution,
44
44
  } from "./registry.js";
45
+
46
+ export {
47
+ detectChangedJsTsFiles,
48
+ isIgnoredPath,
49
+ isJsTsFile,
50
+ parseGitStatus,
51
+ JS_TS_EXTENSIONS,
52
+ type ChangedFilesResult,
53
+ } from "./changed-files.js";
54
+
55
+ export {
56
+ formatQualityCheckStatus,
57
+ readWorkspaceScripts,
58
+ runQualityCheck,
59
+ selectQualityCheck,
60
+ type QualityCheckPlan,
61
+ type QualityCheckResult,
62
+ type QualityCheckTool,
63
+ } from "./quality-check.js";