@gotgenes/pi-autoformat 0.1.0 → 4.0.3

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 (75) hide show
  1. package/.github/workflows/ci.yml +1 -3
  2. package/.github/workflows/release-please.yml +29 -0
  3. package/.markdownlint-cli2.yaml +14 -2
  4. package/.pi/extensions/pi-autoformat/config.json +3 -6
  5. package/.pi/prompts/README.md +59 -0
  6. package/.pi/prompts/plan-issue.md +64 -0
  7. package/.pi/prompts/retro.md +144 -0
  8. package/.pi/prompts/ship-issue.md +77 -0
  9. package/.pi/prompts/tdd-plan.md +67 -0
  10. package/.pi/skills/pi-extension-lifecycle/SKILL.md +256 -0
  11. package/.release-please-manifest.json +1 -1
  12. package/AGENTS.md +39 -0
  13. package/CHANGELOG.md +365 -0
  14. package/README.md +42 -109
  15. package/biome.json +1 -1
  16. package/docs/assets/logo.png +0 -0
  17. package/docs/assets/logo.svg +533 -0
  18. package/docs/configuration.md +358 -38
  19. package/docs/plans/0001-initial-implementation-plan.md +17 -9
  20. package/docs/plans/0002-richer-tui-formatter-summaries.md +220 -0
  21. package/docs/plans/0003-additional-pi-mutation-tools.md +273 -0
  22. package/docs/plans/0004-shell-driven-mutation-coverage.md +296 -0
  23. package/docs/plans/0010-acceptance-test-coverage.md +240 -0
  24. package/docs/plans/0012-remove-unused-formatter-extensions-field.md +152 -0
  25. package/docs/plans/0013-fallback-chain-step-type.md +280 -0
  26. package/docs/plans/0014-batch-by-default-formatter-dispatch.md +195 -0
  27. package/docs/plans/0015-builtin-treefmt-and-treefmt-nix-support.md +290 -0
  28. package/docs/plans/0016-detailed-formatter-output-on-failure.md +245 -0
  29. package/docs/plans/0022-pi-coding-agent-types.md +201 -0
  30. package/docs/plans/0027-format-before-agent-exit-follow-up-turn.md +355 -0
  31. package/docs/plans/0031-turn-end-flush-with-change-detection.md +365 -0
  32. package/docs/retro/0002-richer-tui-formatter-summaries.md +47 -0
  33. package/docs/retro/0013-fallback-chain-step-type.md +67 -0
  34. package/docs/retro/0015-builtin-treefmt-and-treefmt-nix-support.md +56 -0
  35. package/docs/retro/0016-detailed-formatter-output-on-failure.md +60 -0
  36. package/docs/retro/0022-pi-coding-agent-types.md +62 -0
  37. package/docs/testing.md +95 -0
  38. package/package.json +30 -11
  39. package/prek.toml +2 -2
  40. package/schemas/pi-autoformat.schema.json +145 -21
  41. package/src/builtin-formatters.ts +205 -0
  42. package/src/command-probe.ts +66 -0
  43. package/src/config-loader.ts +829 -90
  44. package/src/custom-mutation-tools.ts +125 -0
  45. package/src/extension.ts +469 -82
  46. package/src/format-scope.ts +118 -0
  47. package/src/formatter-config.ts +73 -36
  48. package/src/formatter-executor.ts +230 -34
  49. package/src/formatter-output-report.ts +149 -0
  50. package/src/formatter-registry.ts +139 -30
  51. package/src/index.ts +26 -5
  52. package/src/prompt-autoformatter.ts +148 -23
  53. package/src/shell-mutation-detector.ts +572 -0
  54. package/src/touched-files-queue.ts +72 -11
  55. package/test/acceptance-event-bus.test.ts +138 -0
  56. package/test/acceptance.test.ts +69 -0
  57. package/test/builtin-formatters.test.ts +382 -0
  58. package/test/command-probe.test.ts +79 -0
  59. package/test/config-loader.test.ts +640 -21
  60. package/test/custom-mutation-tools.test.ts +190 -0
  61. package/test/extension.test.ts +1535 -158
  62. package/test/fallback-acceptance.test.ts +98 -0
  63. package/test/fixtures/event-bus-emitter.ts +26 -0
  64. package/test/fixtures/formatter-recorder.mjs +25 -0
  65. package/test/format-scope.test.ts +139 -0
  66. package/test/formatter-config.test.ts +56 -5
  67. package/test/formatter-executor.test.ts +555 -35
  68. package/test/formatter-output-report.test.ts +178 -0
  69. package/test/formatter-registry.test.ts +330 -37
  70. package/test/helpers/rpc.ts +146 -0
  71. package/test/prompt-autoformatter.test.ts +315 -22
  72. package/test/schema.test.ts +149 -0
  73. package/test/shell-mutation-detector.test.ts +221 -0
  74. package/test/touched-files-queue.test.ts +40 -1
  75. package/test/types/theme-stub.test-d.ts +42 -0
@@ -0,0 +1,125 @@
1
+ import type { MutationSourceHandler } from "./touched-files-queue.js";
2
+
3
+ /**
4
+ * Declarative spec for a custom mutation tool.
5
+ *
6
+ * Exactly one of `pathField` or `pathFields` must be provided. Validation
7
+ * of that constraint lives in the config loader so that schema errors
8
+ * surface to the user with proper sourcePath attribution.
9
+ */
10
+ export type CustomMutationToolSpec = {
11
+ toolName: string;
12
+ /** Single dotted path into the tool's `input` payload. */
13
+ pathField?: string;
14
+ /**
15
+ * Multiple dotted paths into the tool's `input` payload. Each value
16
+ * may resolve to a string or a string array; arrays are flattened.
17
+ */
18
+ pathFields?: string[];
19
+ };
20
+
21
+ /**
22
+ * Resolve a dotted path inside a value. Returns `undefined` when any
23
+ * intermediate segment is missing or non-object.
24
+ */
25
+ function resolveDottedPath(value: unknown, dottedPath: string): unknown {
26
+ const segments = dottedPath.split(".");
27
+ let current: unknown = value;
28
+ for (const segment of segments) {
29
+ if (typeof current !== "object" || current === null) {
30
+ return undefined;
31
+ }
32
+ current = (current as Record<string, unknown>)[segment];
33
+ }
34
+ return current;
35
+ }
36
+
37
+ function collectFromValue(value: unknown, out: string[]): void {
38
+ if (typeof value === "string") {
39
+ out.push(value);
40
+ return;
41
+ }
42
+ if (Array.isArray(value)) {
43
+ for (const entry of value) {
44
+ if (typeof entry === "string") {
45
+ out.push(entry);
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Extract candidate paths from a tool's `input` payload using a spec.
53
+ * Pure function — no normalization, no scope filtering. The queue handles
54
+ * those centrally.
55
+ */
56
+ export function extractPathsFromInput(
57
+ input: unknown,
58
+ spec: Pick<CustomMutationToolSpec, "pathField" | "pathFields">,
59
+ ): string[] {
60
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
61
+ return [];
62
+ }
63
+
64
+ const fields: string[] = [];
65
+ if (spec.pathField) {
66
+ fields.push(spec.pathField);
67
+ }
68
+ if (spec.pathFields) {
69
+ fields.push(...spec.pathFields);
70
+ }
71
+
72
+ const results: string[] = [];
73
+ for (const dottedPath of fields) {
74
+ const resolved = resolveDottedPath(input, dottedPath);
75
+ collectFromValue(resolved, results);
76
+ }
77
+ return results;
78
+ }
79
+
80
+ /** Build a single MutationSourceHandler from a spec. */
81
+ export function createCustomToolHandler(
82
+ spec: CustomMutationToolSpec,
83
+ ): MutationSourceHandler {
84
+ return (toolName, payload) => {
85
+ if (toolName !== spec.toolName) {
86
+ return [];
87
+ }
88
+ return extractPathsFromInput(payload, spec);
89
+ };
90
+ }
91
+
92
+ /** Build handlers for a list of specs, preserving order. */
93
+ export function createCustomToolHandlers(
94
+ specs: CustomMutationToolSpec[],
95
+ ): MutationSourceHandler[] {
96
+ return specs.map(createCustomToolHandler);
97
+ }
98
+
99
+ /**
100
+ * Parse a payload received on the EventBus `autoformat:touched` channel.
101
+ * Accepts `{ path: string }` or `{ paths: string[] }`. Other shapes are
102
+ * silently ignored (channel is best-effort; we must not log on every
103
+ * malformed emission from a peer extension).
104
+ */
105
+ export function parseTouchedPayload(value: unknown): string[] {
106
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
107
+ return [];
108
+ }
109
+ const record = value as Record<string, unknown>;
110
+ const results: string[] = [];
111
+
112
+ if (typeof record.path === "string" && record.path.length > 0) {
113
+ results.push(record.path);
114
+ }
115
+
116
+ if (Array.isArray(record.paths)) {
117
+ for (const entry of record.paths) {
118
+ if (typeof entry === "string" && entry.length > 0) {
119
+ results.push(entry);
120
+ }
121
+ }
122
+ }
123
+
124
+ return results;
125
+ }