@monte3l/groundwork 1.0.0-rc.2 → 1.0.0-rc.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 (170) hide show
  1. package/README.md +13 -5
  2. package/bin/m3l-groundwork.mjs +36 -2
  3. package/dist/assets.d.ts +10 -0
  4. package/dist/assets.js +14 -0
  5. package/dist/baseline-stage.d.ts +173 -0
  6. package/dist/baseline-stage.js +215 -0
  7. package/dist/caps.d.ts +4 -1
  8. package/dist/caps.js +17 -3
  9. package/dist/conflicts.d.ts +23 -2
  10. package/dist/conflicts.js +89 -11
  11. package/dist/customize-paths.d.ts +92 -0
  12. package/dist/customize-paths.js +115 -0
  13. package/dist/emit.d.ts +50 -1
  14. package/dist/emit.js +121 -13
  15. package/dist/fatal.d.ts +70 -0
  16. package/dist/fatal.js +132 -0
  17. package/dist/format-error.d.ts +113 -0
  18. package/dist/format-error.js +553 -0
  19. package/dist/fs-guard.d.ts +142 -0
  20. package/dist/fs-guard.js +222 -0
  21. package/dist/git.js +10 -1
  22. package/dist/harness/conformance.js +2 -0
  23. package/dist/harness/frontmatter.js +2 -13
  24. package/dist/harness/grade.js +37 -8
  25. package/dist/harness/rules.d.ts +20 -3
  26. package/dist/harness/rules.js +108 -6
  27. package/dist/harness/types.d.ts +13 -0
  28. package/dist/harness/types.js +3 -0
  29. package/dist/inventory.d.ts +77 -3
  30. package/dist/inventory.js +103 -12
  31. package/dist/jsonc.d.ts +49 -2
  32. package/dist/jsonc.js +140 -7
  33. package/dist/main.d.ts +62 -3
  34. package/dist/main.js +538 -67
  35. package/dist/merge-json.d.ts +47 -4
  36. package/dist/merge-json.js +120 -8
  37. package/dist/mode.js +12 -2
  38. package/dist/pack-stage.d.ts +210 -0
  39. package/dist/pack-stage.js +287 -0
  40. package/dist/packs.d.ts +19 -13
  41. package/dist/packs.js +231 -28
  42. package/dist/palette.d.ts +23 -0
  43. package/dist/palette.js +22 -0
  44. package/dist/plugin.d.ts +122 -6
  45. package/dist/plugin.js +687 -47
  46. package/dist/report.d.ts +21 -2
  47. package/dist/report.js +93 -5
  48. package/dist/staging.d.ts +176 -0
  49. package/dist/staging.js +375 -0
  50. package/dist/survey/fs-walk.d.ts +34 -2
  51. package/dist/survey/fs-walk.js +70 -5
  52. package/dist/survey/internal/blocked-path.d.ts +27 -0
  53. package/dist/survey/internal/blocked-path.js +129 -0
  54. package/dist/survey/internal/package-json.d.ts +13 -0
  55. package/dist/survey/internal/package-json.js +37 -0
  56. package/dist/survey/internal/read-guard.d.ts +178 -0
  57. package/dist/survey/internal/read-guard.js +276 -0
  58. package/dist/survey/survey-docs.d.ts +17 -2
  59. package/dist/survey/survey-docs.js +61 -21
  60. package/dist/survey/survey-harness.d.ts +20 -2
  61. package/dist/survey/survey-harness.js +87 -46
  62. package/dist/survey/survey-shape.d.ts +17 -2
  63. package/dist/survey/survey-shape.js +60 -46
  64. package/dist/survey/survey-toolchain.d.ts +16 -1
  65. package/dist/survey/survey-toolchain.js +69 -66
  66. package/dist/survey/survey.js +6 -4
  67. package/dist/survey/types.d.ts +79 -0
  68. package/dist/survey/types.js +2 -6
  69. package/dist/term.d.ts +80 -0
  70. package/dist/term.js +145 -0
  71. package/dist/tokens.js +2 -0
  72. package/dist/toolchain/conformance.js +2 -0
  73. package/dist/toolchain/grade.js +26 -8
  74. package/dist/toolchain/rules.d.ts +13 -4
  75. package/dist/toolchain/rules.js +16 -0
  76. package/dist/toolchain/tsconfig-chain.d.ts +2 -0
  77. package/dist/toolchain/tsconfig-chain.js +32 -8
  78. package/dist/toolchain/types.d.ts +16 -4
  79. package/dist/toolchain/types.js +3 -0
  80. package/package.json +4 -3
  81. package/plugin/skills/customize/SKILL.md +292 -18
  82. package/plugin/src/domain-map.ts +39 -14
  83. package/plugin/src/index.ts +5 -1
  84. package/plugin/src/kind-facet-map.ts +25 -8
  85. package/plugin/src/pack-map.ts +147 -25
  86. package/plugin/src/plugin-map.ts +236 -0
  87. package/templates/core/.claude/agents/Explore.md +0 -1
  88. package/templates/core/.claude/agents/code-implementer.md +4 -4
  89. package/templates/core/.claude/agents/code-reviewer.md +4 -4
  90. package/templates/core/.claude/agents/silent-failure-hunter.md +2 -2
  91. package/templates/core/.claude/agents/test-author.md +7 -5
  92. package/templates/core/.claude/hooks/guard-branch-isolation.mjs +56 -10
  93. package/templates/core/.claude/hooks/guard-double-background.mjs +13 -8
  94. package/templates/core/.claude/hooks/guard-git-push-signed.mjs +12 -9
  95. package/templates/core/.claude/hooks/guard-hub-src-writes.mjs +1890 -38
  96. package/templates/core/.claude/hooks/guard-js-extension.mjs +2 -1
  97. package/templates/core/.claude/hooks/guard-no-commonjs.mjs +7 -5
  98. package/templates/core/.claude/hooks/guard-secret-writes.mjs +7 -5
  99. package/templates/core/.claude/hooks/inject-decision-gate.mjs +12 -9
  100. package/templates/core/.claude/hooks/post-edit-verify.mjs +276 -98
  101. package/templates/core/.claude/rules/agent-dispatch.md +7 -0
  102. package/templates/core/.claude/rules/tests.md +2 -2
  103. package/templates/core/.claude/settings.json +5 -0
  104. package/templates/core/.claude/skills/finishing-work/SKILL.md +58 -10
  105. package/templates/core/.claude/skills/harness-guidance/SKILL.md +16 -7
  106. package/templates/core/.claude/skills/starting-work/SKILL.md +5 -0
  107. package/templates/core/.claude/skills/triaging-ci/SKILL.md +9 -8
  108. package/templates/core/.claude/skills/typescript-guidance/SKILL.md +137 -20
  109. package/templates/core/.claude/skills/typescript-guidance/references/area-catalog.md +135 -0
  110. package/templates/core/.claude/skills/typescript-guidance/references/tooling-sources.md +113 -0
  111. package/templates/core/.claude/skills/writing-commits/SKILL.md +2 -2
  112. package/templates/core/.github/dependabot.yml +18 -0
  113. package/templates/core/.github/workflows/ci.yml +15 -15
  114. package/templates/core/.github/workflows/dependency-review.yml +2 -2
  115. package/templates/core/.github/workflows/security-audit.yml +10 -4
  116. package/templates/core/.prettierignore +4 -0
  117. package/templates/core/CLAUDE.md +53 -3
  118. package/templates/core/README.md +30 -10
  119. package/templates/core/_gitignore +17 -0
  120. package/templates/core/bin/check-exports.mjs +11 -5
  121. package/templates/core/bin/lib/agent-roster.mjs +1 -1
  122. package/templates/core/bin/lib/frontmatter.mjs +4 -2
  123. package/templates/core/bin/lib/harness-rules.mjs +169 -20
  124. package/templates/core/bin/lib/protected-paths.mjs +93 -5
  125. package/templates/core/bin/lib/toolchain-rules.mjs +187 -26
  126. package/templates/core/bin/lib/verify-steps.mjs +29 -11
  127. package/templates/core/bin/verify.mjs +12 -0
  128. package/templates/core/eslint.config.js +5 -0
  129. package/templates/core/package.json +7 -7
  130. package/templates/core/vitest.config.ts +8 -1
  131. package/templates/packs/README.md +35 -24
  132. package/templates/packs/github/files/.claude/skills/reviewing-dependabot-prs/SKILL.md +133 -0
  133. package/templates/packs/github/files/.claude/skills/triaging-scan-alerts/SKILL.md +88 -0
  134. package/templates/packs/github/files/.claude/skills/watching-pr-checks/SKILL.md +94 -0
  135. package/templates/packs/github/files/.github/workflows/claude-pr-review.yml +267 -0
  136. package/templates/packs/github/files/.github/workflows/claude.yml +106 -0
  137. package/templates/packs/github/pack.json +19 -0
  138. package/templates/packs/harness-extras/files/.claude/hooks/guard-readonly-bash.mjs +1122 -65
  139. package/templates/packs/harness-extras/files/.claude/hooks/reinject-compact-handoff.mjs +147 -13
  140. package/templates/packs/{statusline → harness-extras}/files/.claude/hooks/statusline.mjs +6 -2
  141. package/templates/packs/harness-extras/files/.claude/hooks/write-compact-handoff.mjs +227 -44
  142. package/templates/packs/harness-extras/pack.json +18 -13
  143. package/templates/packs/publishing/files/.changeset/README.md +25 -0
  144. package/templates/packs/publishing/files/.changeset/config.json +7 -0
  145. package/templates/packs/publishing/files/.github/release-tools/package.json +9 -0
  146. package/templates/packs/publishing/files/.github/workflows/release.yml +293 -0
  147. package/templates/packs/publishing/files/REUSE.toml +28 -0
  148. package/templates/packs/publishing/files/bin/check-dts-deps.mjs +234 -0
  149. package/templates/packs/publishing/files/bin/check-license-headers.mjs +217 -0
  150. package/templates/packs/publishing/files/bin/check-publish-version.mjs +149 -0
  151. package/templates/packs/publishing/files/bin/lib/npm-publish-args.mjs +86 -0
  152. package/templates/packs/publishing/files/bin/pnpm-publish-shim.mjs +86 -0
  153. package/templates/packs/publishing/pack.json +35 -0
  154. package/templates/packs/{harness-extras → quality}/files/bin/check-file-budget.mjs +6 -4
  155. package/templates/packs/quality/pack.json +29 -0
  156. package/templates/packs/supply-chain/files/.github/workflows/gitleaks.yml +52 -0
  157. package/templates/packs/supply-chain/files/.github/workflows/scorecard.yml +48 -0
  158. package/templates/packs/supply-chain/files/.gitleaks.toml +2 -0
  159. package/templates/packs/supply-chain/pack.json +19 -0
  160. package/templates/packs/worktrees/files/.claude/hooks/ensure-worktree-deps.mjs +283 -0
  161. package/templates/packs/worktrees/files/.claude/hooks/guard-worktree-only.mjs +245 -0
  162. package/templates/packs/worktrees/files/.claude/hooks/repair-core-bare.mjs +335 -0
  163. package/templates/packs/worktrees/files/.claude/skills/working-in-worktrees/SKILL.md +139 -0
  164. package/templates/packs/worktrees/files/.worktreeinclude +11 -0
  165. package/templates/packs/worktrees/pack.json +64 -0
  166. package/templates/packs/statusline/pack.json +0 -31
  167. /package/templates/packs/{statusline → harness-extras}/files/.claude/hooks/statusline-layout.mjs +0 -0
  168. /package/templates/packs/{statusline → harness-extras}/files/.claude/hooks/subagent-statusline.mjs +0 -0
  169. /package/templates/packs/{harness-extras → quality}/files/.claude/agents/type-design-analyzer.md +0 -0
  170. /package/templates/packs/{harness-extras → quality}/files/bin/file-budget-baseline.json +0 -0
package/dist/fatal.js ADDED
@@ -0,0 +1,132 @@
1
+ // SPDX-FileCopyrightText: Copyright the m3l-groundwork contributors
2
+ // SPDX-License-Identifier: MIT
3
+ /**
4
+ * The CLI's top-level catch handler, `bin/m3l-groundwork.mjs`'s last line of
5
+ * defence: whatever was thrown, and whatever fails while reporting it, the
6
+ * process ends with an exit code set and never with an uncaught throw.
7
+ */
8
+ import { escapeControls, formatFatalError } from "./format-error.js";
9
+ /** Printed when neither the formatted report nor the raw stack could be printed on the raw channel. */
10
+ const LAST_RESORT = "[unprintable error]";
11
+ /**
12
+ * `error.stack` when readable, otherwise `String(error)`, control-escaped by
13
+ * {@link escapeControls} -- the fallback text when printing the formatted
14
+ * chain failed.
15
+ */
16
+ function rawText(error) {
17
+ // Read `stack` once, and only off an object; a throwing getter or Proxy
18
+ // trap propagates to the caller's own fallback.
19
+ const stack = typeof error === "object" && error !== null && "stack" in error
20
+ ? error.stack
21
+ : undefined;
22
+ return escapeControls(String(stack ?? error));
23
+ }
24
+ /**
25
+ * Reports a fatal `error`: sets the exit code first -- 2 when
26
+ * `isUsageError(error)` says it is a usage error, 1 otherwise, including
27
+ * when `isUsageError` itself throws -- then prints the report through
28
+ * `io.print`. For a usage error the report is exactly
29
+ * `formatErrorChain(error)`. For any other failure it is
30
+ * `formatFatalError(error, { withName: true, withStack: debug })` (both in
31
+ * `./format-error.ts`): the same chain, its top line prefixed `<name>: `
32
+ * when `error` is an `Error` with a readable, non-blank `name` other than
33
+ * `Error` and a non-blank message (`TypeError: boom`), and, when `debug` is
34
+ * set, the stack frames appended as `formatFatalError` describes
35
+ * (control-escaped, header dropped, capped at its `MAX_STACK_LINES`). Only
36
+ * the top error's own stack is printed: a `cause`'s stack is deliberately
37
+ * omitted, since the chain already names every cause. If that print
38
+ * throws, the fallbacks never touch `io.print` again (it is the channel
39
+ * that just failed -- in the CLI, the one that paints colour), so `print`
40
+ * is called exactly once: `io.printRaw` prints the same report, so the
41
+ * cause chain (and any name prefix and stack) survives a failing `print`;
42
+ * if that throws, `io.printRaw` prints `String(error.stack ?? error)`,
43
+ * control-escaped the same way the chain is (`escapeControls` in
44
+ * `./format-error.ts`); if that throws, `io.printRaw` prints
45
+ * `[unprintable error]`; if even that throws, gives up silently, the exit
46
+ * code already set. Never throws. A `setExitCode` that throws is swallowed
47
+ * and not retried -- the report is still attempted -- but it is not
48
+ * otherwise handled: assigning Node's `process.exitCode` cannot throw, so
49
+ * the CLI never hits that case.
50
+ *
51
+ * @param error - The value the CLI's `main()` threw.
52
+ * @param io - Where the exit code and the report go: `print` for the
53
+ * formatted chain, `printRaw` -- a plain write with nothing in it that can
54
+ * fail the way `print` did -- for all three fallbacks.
55
+ * @param isUsageError - Whether `error` is a bad invocation rather than a
56
+ * runtime failure.
57
+ * @param debug - Append the top error's own stack (never a cause's) to a
58
+ * runtime failure's report (never to a usage error's); defaults to `false`.
59
+ * The CLI sets it when `M3L_DEBUG` is non-empty.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * import { CliUsageError, main } from "./main.js";
64
+ * import { handleFatal } from "./fatal.js";
65
+ *
66
+ * try {
67
+ * main(process.argv.slice(2));
68
+ * } catch (error) {
69
+ * handleFatal(
70
+ * error,
71
+ * {
72
+ * setExitCode: (code) => {
73
+ * process.exitCode = code;
74
+ * },
75
+ * print: (text) => {
76
+ * console.error(text);
77
+ * },
78
+ * printRaw: (text) => {
79
+ * console.error(text);
80
+ * },
81
+ * },
82
+ * (e) => e instanceof CliUsageError,
83
+ * (process.env["M3L_DEBUG"] ?? "") !== "",
84
+ * );
85
+ * }
86
+ * ```
87
+ */
88
+ export function handleFatal(error, io, isUsageError, debug = false) {
89
+ let code = 1;
90
+ try {
91
+ code = isUsageError(error) ? 2 : 1;
92
+ }
93
+ catch {
94
+ // A classifier that throws cannot vouch for a usage error: a runtime failure.
95
+ }
96
+ try {
97
+ io.setExitCode(code);
98
+ }
99
+ catch {
100
+ // Nothing else can record the code; still try to print the report below.
101
+ }
102
+ const runtime = code === 1;
103
+ const options = { withName: runtime, withStack: runtime && debug };
104
+ try {
105
+ io.print(formatFatalError(error, options));
106
+ return;
107
+ }
108
+ catch {
109
+ // Fall through to the same report, on the raw channel.
110
+ }
111
+ try {
112
+ io.printRaw(formatFatalError(error, options));
113
+ return;
114
+ }
115
+ catch {
116
+ // Fall through to the raw stack.
117
+ }
118
+ try {
119
+ io.printRaw(rawText(error));
120
+ return;
121
+ }
122
+ catch {
123
+ // Fall through to the fixed placeholder.
124
+ }
125
+ try {
126
+ io.printRaw(LAST_RESORT);
127
+ }
128
+ catch {
129
+ // stderr itself is unusable: the exit code set above is all that is left.
130
+ }
131
+ }
132
+ //# sourceMappingURL=fatal.js.map
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Makes `text` safe to write to a terminal: every line break it contains
3
+ * (`\r\n`, `\n`, a lone `\r`, `\v`, `\f`, U+0085, U+2028, U+2029) becomes a
4
+ * plain `\n`, and every other C0 control except TAB (U+0000-U+001F except
5
+ * U+0009), DEL (U+007F) and every C1 control except NEL (U+0080-U+009F
6
+ * except U+0085) is replaced by the literal text `\xNN`, lowercase two-digit
7
+ * hex -- so an ESC renders as `\x1b` and cannot start an escape sequence.
8
+ * Every bidi embedding/override and isolate control (U+202A-U+202E,
9
+ * U+2066-U+2069) is replaced by the literal text `\uNNNN`, lowercase
10
+ * four-digit hex -- so an RLO renders as `\u202e` and cannot reorder what
11
+ * the terminal displays. TAB and every other character pass through
12
+ * unchanged.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { escapeControls } from "./format-error.js";
17
+ *
18
+ * escapeControls("bad\u001b[2J\rline two"); // "bad\\x1b[2J\nline two"
19
+ * escapeControls("bad\u202eexe.txt"); // "bad\\u202eexe.txt"
20
+ * ```
21
+ */
22
+ export declare function escapeControls(text: string): string;
23
+ /**
24
+ * Formats `error` as a multi-line string: its own message on the first line
25
+ * (any further line of it indented two spaces), then one
26
+ * `caused by: <message>` line per chained cause, indented two more spaces
27
+ * per depth; any further line of a cause's message is indented two spaces
28
+ * past its own `caused by:` line. Every such continuation line is marked
29
+ * `| ` after its indent (an empty one prints a bare `|`), so no continuation
30
+ * line can pass for a real `caused by:` line; the top message's first line
31
+ * is printed unmarked (but control-escaped, below). Messages split on
32
+ * `\r\n`, `\n`, a lone `\r`, `\v`, `\f`, U+0085, U+2028 or U+2029; trailing
33
+ * empty lines are dropped. Every line of every message -- the top message's
34
+ * first line, any cause at any depth, a non-`Error`'s `String(value)` -- is
35
+ * then control-escaped exactly as {@link escapeControls} describes (every C0
36
+ * control but TAB, DEL, every C1 control but NEL become `\xNN`; every bidi
37
+ * control in U+202A-U+202E and U+2066-U+2069 becomes `\uNNNN`), so no
38
+ * message can emit a terminal escape sequence. A top message that is empty
39
+ * or whitespace-only renders as the `Error`'s `name` instead (when that is
40
+ * a readable, non-blank string), otherwise as `(empty message)`, so the
41
+ * first line is never blank. An `AggregateError`'s `errors` are each listed
42
+ * as a `caused by:` line at the next depth (after its own `cause`, if any);
43
+ * an `errors` property that is not an array is ignored. At most 32 `errors`
44
+ * members are printed per parent; the rest collapse into one
45
+ * `... and N more` line at the children's indent. A `cause` never counts
46
+ * against that cap, so it is always printed.
47
+ *
48
+ * A message is an `Error`'s `message`, otherwise `String(value)`; it renders
49
+ * as `[unprintable value]` when stringifying throws, when it is a `Symbol`,
50
+ * when the `message`, `cause` or `errors` accessor itself throws, or when the
51
+ * value cannot even be classified (a Proxy whose `getPrototypeOf` trap
52
+ * throws, a revoked Proxy) -- such a value has no children.
53
+ *
54
+ * A cause is not printed again (its own causes still are, at the same
55
+ * indent) when its message is not empty or whitespace-only and either equals
56
+ * the nearest printed ancestor's message after `trim()` or is at least 8
57
+ * characters long and contained in it.
58
+ *
59
+ * An object (or function) that is its own ancestor -- a cause cycle -- is
60
+ * cut silently at the repeat; one already printed on another branch renders
61
+ * as `caused by: (see above)`. Primitives are never deduplicated. Every
62
+ * branch follows at most 32 links -- suppressed links count too -- and a
63
+ * parent whose children are cut there gets exactly one `...` line, at the
64
+ * cut point, with any sibling branches still printed after it. The walk is
65
+ * iterative and bounded, so the function always terminates and never
66
+ * throws, however long the chain.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import { formatErrorChain } from "./format-error.js";
71
+ *
72
+ * const error = new Error("staging failed", { cause: new Error("ENOSPC") });
73
+ * formatErrorChain(error);
74
+ * // "staging failed\n caused by: ENOSPC"
75
+ * ```
76
+ */
77
+ export declare function formatErrorChain(error: unknown): string;
78
+ /**
79
+ * Formats `error` for the CLI's fatal-error report: exactly
80
+ * {@link formatErrorChain}'s text, with two optional additions.
81
+ *
82
+ * - `withName`: when `error` is an `Error` whose `name` is a readable,
83
+ * non-blank string other than the generic `Error`, and its message is not
84
+ * blank, the top line is prefixed `<name>: ` (`TypeError: boom`), the
85
+ * name control-escaped like a message and any line break in it rendered
86
+ * as a literal `\n`. A blank message already renders as the name alone,
87
+ * so it gets no prefix; `caused by:` lines never do.
88
+ * - `withStack`: when `error` is an `Error` whose `stack` (read once) is a
89
+ * string, its lines are appended after the chain -- minus V8's
90
+ * `Name: message` header, which repeats the top line. The header is
91
+ * dropped by position: as many leading lines as `Name: message` spans
92
+ * when the stack starts with exactly those lines (so a message line
93
+ * shaped like an `at ` frame never reappears), otherwise every line
94
+ * before the first `at ` frame. Each line is control-escaped like a
95
+ * message, at most {@link MAX_STACK_LINES} of them, the rest collapsing
96
+ * into one `... and N more` line. A missing, non-string or throwing
97
+ * `stack` appends nothing.
98
+ *
99
+ * Never throws, like {@link formatErrorChain}.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * import { formatFatalError } from "./format-error.js";
104
+ *
105
+ * formatFatalError(new TypeError("bad input"), { withName: true, withStack: false });
106
+ * // "TypeError: bad input"
107
+ * ```
108
+ */
109
+ export declare function formatFatalError(error: unknown, options: {
110
+ readonly withName: boolean;
111
+ readonly withStack: boolean;
112
+ }): string;
113
+ //# sourceMappingURL=format-error.d.ts.map