@monte3l/groundwork 0.1.0-next.1 → 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.
- package/README.md +16 -8
- package/bin/m3l-groundwork.mjs +36 -2
- package/dist/assets.d.ts +10 -0
- package/dist/assets.js +14 -0
- package/dist/baseline-stage.d.ts +173 -0
- package/dist/baseline-stage.js +215 -0
- package/dist/caps.d.ts +4 -1
- package/dist/caps.js +17 -3
- package/dist/conflicts.d.ts +23 -2
- package/dist/conflicts.js +89 -11
- package/dist/customize-paths.d.ts +92 -0
- package/dist/customize-paths.js +115 -0
- package/dist/emit.d.ts +50 -1
- package/dist/emit.js +121 -13
- package/dist/fatal.d.ts +70 -0
- package/dist/fatal.js +132 -0
- package/dist/format-error.d.ts +113 -0
- package/dist/format-error.js +553 -0
- package/dist/fs-guard.d.ts +142 -0
- package/dist/fs-guard.js +222 -0
- package/dist/git.js +10 -1
- package/dist/harness/conformance.js +2 -0
- package/dist/harness/frontmatter.js +2 -13
- package/dist/harness/grade.js +37 -8
- package/dist/harness/rules.d.ts +20 -3
- package/dist/harness/rules.js +108 -6
- package/dist/harness/types.d.ts +13 -0
- package/dist/harness/types.js +3 -0
- package/dist/inventory.d.ts +77 -3
- package/dist/inventory.js +103 -12
- package/dist/jsonc.d.ts +49 -2
- package/dist/jsonc.js +140 -7
- package/dist/main.d.ts +62 -3
- package/dist/main.js +538 -67
- package/dist/merge-json.d.ts +47 -4
- package/dist/merge-json.js +120 -8
- package/dist/mode.js +12 -2
- package/dist/pack-stage.d.ts +210 -0
- package/dist/pack-stage.js +287 -0
- package/dist/packs.d.ts +19 -13
- package/dist/packs.js +231 -28
- package/dist/palette.d.ts +23 -0
- package/dist/palette.js +22 -0
- package/dist/plugin.d.ts +122 -6
- package/dist/plugin.js +687 -47
- package/dist/report.d.ts +21 -2
- package/dist/report.js +93 -5
- package/dist/staging.d.ts +176 -0
- package/dist/staging.js +375 -0
- package/dist/survey/fs-walk.d.ts +34 -2
- package/dist/survey/fs-walk.js +70 -5
- package/dist/survey/internal/blocked-path.d.ts +27 -0
- package/dist/survey/internal/blocked-path.js +129 -0
- package/dist/survey/internal/package-json.d.ts +13 -0
- package/dist/survey/internal/package-json.js +37 -0
- package/dist/survey/internal/read-guard.d.ts +178 -0
- package/dist/survey/internal/read-guard.js +276 -0
- package/dist/survey/survey-docs.d.ts +17 -2
- package/dist/survey/survey-docs.js +61 -21
- package/dist/survey/survey-harness.d.ts +20 -2
- package/dist/survey/survey-harness.js +87 -46
- package/dist/survey/survey-shape.d.ts +17 -2
- package/dist/survey/survey-shape.js +60 -46
- package/dist/survey/survey-toolchain.d.ts +16 -1
- package/dist/survey/survey-toolchain.js +69 -66
- package/dist/survey/survey.js +6 -4
- package/dist/survey/types.d.ts +79 -0
- package/dist/survey/types.js +2 -6
- package/dist/term.d.ts +80 -0
- package/dist/term.js +145 -0
- package/dist/tokens.js +2 -0
- package/dist/toolchain/conformance.js +2 -0
- package/dist/toolchain/grade.js +26 -8
- package/dist/toolchain/rules.d.ts +13 -4
- package/dist/toolchain/rules.js +16 -0
- package/dist/toolchain/tsconfig-chain.d.ts +2 -0
- package/dist/toolchain/tsconfig-chain.js +32 -8
- package/dist/toolchain/types.d.ts +16 -4
- package/dist/toolchain/types.js +3 -0
- package/package.json +4 -3
- package/plugin/skills/customize/SKILL.md +292 -18
- package/plugin/src/domain-map.ts +39 -14
- package/plugin/src/index.ts +5 -1
- package/plugin/src/kind-facet-map.ts +25 -8
- package/plugin/src/pack-map.ts +147 -25
- package/plugin/src/plugin-map.ts +236 -0
- package/templates/core/.claude/agents/Explore.md +0 -1
- package/templates/core/.claude/agents/code-implementer.md +4 -4
- package/templates/core/.claude/agents/code-reviewer.md +4 -4
- package/templates/core/.claude/agents/silent-failure-hunter.md +2 -2
- package/templates/core/.claude/agents/test-author.md +7 -5
- package/templates/core/.claude/hooks/guard-branch-isolation.mjs +56 -10
- package/templates/core/.claude/hooks/guard-double-background.mjs +13 -8
- package/templates/core/.claude/hooks/guard-git-push-signed.mjs +12 -9
- package/templates/core/.claude/hooks/guard-hub-src-writes.mjs +1890 -38
- package/templates/core/.claude/hooks/guard-js-extension.mjs +2 -1
- package/templates/core/.claude/hooks/guard-no-commonjs.mjs +7 -5
- package/templates/core/.claude/hooks/guard-secret-writes.mjs +7 -5
- package/templates/core/.claude/hooks/inject-decision-gate.mjs +12 -9
- package/templates/core/.claude/hooks/post-edit-verify.mjs +276 -98
- package/templates/core/.claude/rules/agent-dispatch.md +7 -0
- package/templates/core/.claude/rules/tests.md +2 -2
- package/templates/core/.claude/settings.json +5 -0
- package/templates/core/.claude/skills/finishing-work/SKILL.md +58 -10
- package/templates/core/.claude/skills/harness-guidance/SKILL.md +16 -7
- package/templates/core/.claude/skills/starting-work/SKILL.md +5 -0
- package/templates/core/.claude/skills/triaging-ci/SKILL.md +9 -8
- package/templates/core/.claude/skills/typescript-guidance/SKILL.md +137 -20
- package/templates/core/.claude/skills/typescript-guidance/references/area-catalog.md +135 -0
- package/templates/core/.claude/skills/typescript-guidance/references/tooling-sources.md +113 -0
- package/templates/core/.claude/skills/writing-commits/SKILL.md +2 -2
- package/templates/core/.github/dependabot.yml +18 -0
- package/templates/core/.github/workflows/ci.yml +15 -15
- package/templates/core/.github/workflows/dependency-review.yml +2 -2
- package/templates/core/.github/workflows/security-audit.yml +10 -4
- package/templates/core/.prettierignore +4 -0
- package/templates/core/CLAUDE.md +53 -3
- package/templates/core/README.md +30 -10
- package/templates/core/_gitignore +17 -0
- package/templates/core/bin/check-exports.mjs +11 -5
- package/templates/core/bin/lib/agent-roster.mjs +1 -1
- package/templates/core/bin/lib/frontmatter.mjs +4 -2
- package/templates/core/bin/lib/harness-rules.mjs +169 -20
- package/templates/core/bin/lib/protected-paths.mjs +93 -5
- package/templates/core/bin/lib/toolchain-rules.mjs +187 -26
- package/templates/core/bin/lib/verify-steps.mjs +29 -11
- package/templates/core/bin/verify.mjs +12 -0
- package/templates/core/eslint.config.js +5 -0
- package/templates/core/package.json +7 -7
- package/templates/core/vitest.config.ts +8 -1
- package/templates/packs/README.md +35 -24
- package/templates/packs/github/files/.claude/skills/reviewing-dependabot-prs/SKILL.md +133 -0
- package/templates/packs/github/files/.claude/skills/triaging-scan-alerts/SKILL.md +88 -0
- package/templates/packs/github/files/.claude/skills/watching-pr-checks/SKILL.md +94 -0
- package/templates/packs/github/files/.github/workflows/claude-pr-review.yml +267 -0
- package/templates/packs/github/files/.github/workflows/claude.yml +106 -0
- package/templates/packs/github/pack.json +19 -0
- package/templates/packs/harness-extras/files/.claude/hooks/guard-readonly-bash.mjs +1122 -65
- package/templates/packs/harness-extras/files/.claude/hooks/reinject-compact-handoff.mjs +147 -13
- package/templates/packs/{statusline → harness-extras}/files/.claude/hooks/statusline.mjs +6 -2
- package/templates/packs/harness-extras/files/.claude/hooks/write-compact-handoff.mjs +227 -44
- package/templates/packs/harness-extras/pack.json +18 -13
- package/templates/packs/publishing/files/.changeset/README.md +25 -0
- package/templates/packs/publishing/files/.changeset/config.json +7 -0
- package/templates/packs/publishing/files/.github/release-tools/package.json +9 -0
- package/templates/packs/publishing/files/.github/workflows/release.yml +293 -0
- package/templates/packs/publishing/files/REUSE.toml +28 -0
- package/templates/packs/publishing/files/bin/check-dts-deps.mjs +234 -0
- package/templates/packs/publishing/files/bin/check-license-headers.mjs +217 -0
- package/templates/packs/publishing/files/bin/check-publish-version.mjs +149 -0
- package/templates/packs/publishing/files/bin/lib/npm-publish-args.mjs +86 -0
- package/templates/packs/publishing/files/bin/pnpm-publish-shim.mjs +86 -0
- package/templates/packs/publishing/pack.json +35 -0
- package/templates/packs/{harness-extras → quality}/files/bin/check-file-budget.mjs +6 -4
- package/templates/packs/quality/pack.json +29 -0
- package/templates/packs/supply-chain/files/.github/workflows/gitleaks.yml +52 -0
- package/templates/packs/supply-chain/files/.github/workflows/scorecard.yml +48 -0
- package/templates/packs/supply-chain/files/.gitleaks.toml +2 -0
- package/templates/packs/supply-chain/pack.json +19 -0
- package/templates/packs/worktrees/files/.claude/hooks/ensure-worktree-deps.mjs +283 -0
- package/templates/packs/worktrees/files/.claude/hooks/guard-worktree-only.mjs +245 -0
- package/templates/packs/worktrees/files/.claude/hooks/repair-core-bare.mjs +335 -0
- package/templates/packs/worktrees/files/.claude/skills/working-in-worktrees/SKILL.md +139 -0
- package/templates/packs/worktrees/files/.worktreeinclude +11 -0
- package/templates/packs/worktrees/pack.json +64 -0
- package/templates/packs/statusline/pack.json +0 -31
- /package/templates/packs/{statusline → harness-extras}/files/.claude/hooks/statusline-layout.mjs +0 -0
- /package/templates/packs/{statusline → harness-extras}/files/.claude/hooks/subagent-statusline.mjs +0 -0
- /package/templates/packs/{harness-extras → quality}/files/.claude/agents/type-design-analyzer.md +0 -0
- /package/templates/packs/{harness-extras → quality}/files/bin/file-budget-baseline.json +0 -0
package/dist/harness/types.d.ts
CHANGED
|
@@ -5,24 +5,37 @@
|
|
|
5
5
|
* distilled from Anthropic's published guidance and only ever warn -- a
|
|
6
6
|
* subjective rule must never block a push.
|
|
7
7
|
*/
|
|
8
|
+
/** How severe a finding is: `structural` fails the gate; `rubric` only ever warns. */
|
|
8
9
|
export type RuleLevel = "structural" | "rubric";
|
|
10
|
+
/** Which area of the harness (settings, hooks, skills, agents, rules, claude-md) a finding is about. */
|
|
9
11
|
export type HarnessCategory = "settings" | "hooks" | "skills" | "agents" | "rules" | "claude-md";
|
|
12
|
+
/** Every {@link HarnessCategory}, as an ordered list -- so rubric tallies are iterated in a fixed order. */
|
|
10
13
|
export declare const HARNESS_CATEGORIES: readonly HarnessCategory[];
|
|
14
|
+
/** One rule's result against one subject. */
|
|
11
15
|
export interface HarnessFinding {
|
|
16
|
+
/** The id of the rule this finding came from. */
|
|
12
17
|
ruleId: string;
|
|
18
|
+
/** Whether this finding fails the gate (`structural`) or only warns (`rubric`). */
|
|
13
19
|
level: RuleLevel;
|
|
20
|
+
/** Which harness area this finding is about. */
|
|
14
21
|
category: HarnessCategory;
|
|
15
22
|
/** The file, skill, agent, or registration the finding is about. */
|
|
16
23
|
subject: string;
|
|
24
|
+
/** Human-readable description of what the rule found. */
|
|
17
25
|
message: string;
|
|
18
26
|
}
|
|
19
27
|
/** How many subjects a set of rules examined, and how many of those failed. */
|
|
20
28
|
export interface CheckTally {
|
|
29
|
+
/** How many subjects the rules examined. */
|
|
21
30
|
checked: number;
|
|
31
|
+
/** How many of the examined subjects failed. */
|
|
22
32
|
failed: number;
|
|
23
33
|
}
|
|
34
|
+
/** The full result of grading one project's harness. */
|
|
24
35
|
export interface HarnessGrade {
|
|
36
|
+
/** Every finding from every rule, in rule order. */
|
|
25
37
|
findings: HarnessFinding[];
|
|
38
|
+
/** The tally of structural checks: how many were checked and how many failed. */
|
|
26
39
|
structural: CheckTally;
|
|
27
40
|
/** Rubric tallies per category -- the absolute-quality measurement. */
|
|
28
41
|
rubric: Record<HarnessCategory, CheckTally>;
|
package/dist/harness/types.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright the m3l-groundwork contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
/** Every {@link HarnessCategory}, as an ordered list -- so rubric tallies are iterated in a fixed order. */
|
|
1
4
|
export const HARNESS_CATEGORIES = [
|
|
2
5
|
"settings",
|
|
3
6
|
"hooks",
|
package/dist/inventory.d.ts
CHANGED
|
@@ -1,13 +1,36 @@
|
|
|
1
|
+
import type { StagedBaselineFile } from "./baseline-stage.js";
|
|
1
2
|
import type { CapCounts } from "./caps.js";
|
|
2
3
|
import type { FileConflict } from "./conflicts.js";
|
|
3
4
|
import type { HarnessConformance } from "./harness/conformance.js";
|
|
4
5
|
import type { HarnessGrade } from "./harness/types.js";
|
|
5
6
|
import type { ModeDetection } from "./mode.js";
|
|
7
|
+
import type { StagedPack } from "./pack-stage.js";
|
|
6
8
|
import type { PackWiring } from "./packs.js";
|
|
7
9
|
import type { ProjectSurvey } from "./survey/survey.js";
|
|
8
10
|
import type { ToolchainConformance } from "./toolchain/conformance.js";
|
|
9
11
|
import type { ToolchainGrade } from "./toolchain/types.js";
|
|
10
|
-
export declare const INVENTORY_SCHEMA_VERSION =
|
|
12
|
+
export declare const INVENTORY_SCHEMA_VERSION = 5;
|
|
13
|
+
/**
|
|
14
|
+
* Where adopt mode staged the baseline files the project lacks entirely
|
|
15
|
+
* (`baseline-stage.ts`), and how each staged copy is named.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const stagedBaseline: StagedBaseline = {
|
|
20
|
+
* dir: ".groundwork/baseline",
|
|
21
|
+
* suffix: ".staged",
|
|
22
|
+
* files: [{ path: "eslint.config.js", staged: "eslint.config.js.staged", sha256: "…" }],
|
|
23
|
+
* };
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export interface StagedBaseline {
|
|
27
|
+
/** The staging directory, relative to the project root (e.g. `.groundwork/baseline`). */
|
|
28
|
+
dir: string;
|
|
29
|
+
/** The suffix every staged file name carries (`.staged`), so no toolchain glob ever matches one. */
|
|
30
|
+
suffix: string;
|
|
31
|
+
/** The staged files: install path, staged name relative to `dir`, and sha256 of the staged bytes. */
|
|
32
|
+
files: StagedBaselineFile[];
|
|
33
|
+
}
|
|
11
34
|
export interface PackSurvey {
|
|
12
35
|
name: string;
|
|
13
36
|
modes: string[];
|
|
@@ -25,10 +48,15 @@ export interface Inventory {
|
|
|
25
48
|
cliVersion: string;
|
|
26
49
|
generatedAt: string;
|
|
27
50
|
modeSignal: string;
|
|
51
|
+
/** The template tree's absolute path, in the platform's native form (not normalized). */
|
|
28
52
|
templateRoot: string;
|
|
53
|
+
/** The adopted project's absolute path, in the platform's native form (not normalized). */
|
|
29
54
|
targetDir: string;
|
|
55
|
+
/** The project survey; its paths are native, not normalized. */
|
|
30
56
|
survey: ProjectSurvey;
|
|
57
|
+
/** Baseline-vs-project file collisions; every `relPath` uses `/` on every platform (normalized by `buildInventory`). */
|
|
31
58
|
conflicts: FileConflict[];
|
|
59
|
+
/** Per-pack surveys; every `fileConflicts[].relPath` uses `/` on every platform, like `conflicts`. */
|
|
32
60
|
packs: PackSurvey[];
|
|
33
61
|
/** Wiring integrity and rubric quality of the project's existing harness. Absent when schemaVersion is below 3. */
|
|
34
62
|
harnessGrade: HarnessGrade;
|
|
@@ -38,6 +66,10 @@ export interface Inventory {
|
|
|
38
66
|
toolchainGrade: ToolchainGrade;
|
|
39
67
|
/** How far the project's toolchain files have drifted from the baseline's -- information, never a defect. Absent when schemaVersion is below 4. */
|
|
40
68
|
toolchainConformance: ToolchainConformance;
|
|
69
|
+
/** The "absent" baseline files, copied verbatim as inert `<path>.staged` copies for `/customize` to install from. Absent when schemaVersion is below 5; `dir` is project-relative, and `dir` and every `files[].path`/`files[].staged` use `/` on every platform. */
|
|
70
|
+
stagedBaseline: StagedBaseline;
|
|
71
|
+
/** Every pack, staged as inert `.staged` copies (manifest included) under `.groundwork/packs/<name>/` for `/customize` to install from after confirmation; one entry per pack, `dir` project-relative, and `dir` and every `path`/`staged` use `/` on every platform. Absent from inventories written before pack staging became inert (see the module header). */
|
|
72
|
+
stagedPacks: StagedPack[];
|
|
41
73
|
}
|
|
42
74
|
/**
|
|
43
75
|
* Resolves this CLI package's own declared version. `packageJsonPath`
|
|
@@ -55,9 +87,51 @@ export interface BuildInventoryParams {
|
|
|
55
87
|
packs: PackSurvey[];
|
|
56
88
|
harnessGrade: HarnessGrade;
|
|
57
89
|
toolchainGrade: ToolchainGrade;
|
|
90
|
+
stagedBaseline: StagedBaseline;
|
|
91
|
+
stagedPacks: StagedPack[];
|
|
58
92
|
}
|
|
59
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* Builds the inventory object. Does not write anything -- see `writeInventory`.
|
|
95
|
+
*
|
|
96
|
+
* Exactly these paths use `/` on every platform: each `conflicts[].relPath`
|
|
97
|
+
* and each `packs[].fileConflicts[].relPath`, normalized here once with
|
|
98
|
+
* {@link toPosixPath}, plus `stagedBaseline.dir` (built by the caller as a
|
|
99
|
+
* `/`-joined literal) and each `stagedBaseline.files[].path`/`.staged`
|
|
100
|
+
* (already normalized by `stageBaselineAdditions` with the same
|
|
101
|
+
* {@link toPosixPath}, so the conflict and staged paths agree), and
|
|
102
|
+
* `stagedPacks` (passed through verbatim: `stagePacks` builds each `dir` as
|
|
103
|
+
* a `/`-joined literal and normalizes every `path`/`staged` the same way).
|
|
104
|
+
* The harness/toolchain conformance
|
|
105
|
+
* summaries are computed from the normalized conflict paths. Every other
|
|
106
|
+
* path -- `templateRoot`, `targetDir`, and every path inside `survey` -- is
|
|
107
|
+
* passed through in the platform's native form. The caller's `conflicts` and
|
|
108
|
+
* `packs` are never mutated; new objects are returned.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* const inventory = buildInventory({ detection, templateRoot, targetDir, survey,
|
|
113
|
+
* conflicts, packs, harnessGrade, toolchainGrade, stagedBaseline, stagedPacks });
|
|
114
|
+
* inventory.conflicts[0]?.relPath; // "src/index.ts", even on Windows
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
60
117
|
export declare function buildInventory(params: BuildInventoryParams): Inventory;
|
|
61
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Writes `inventory.json` into `groundworkDir`, creating it if needed.
|
|
120
|
+
*
|
|
121
|
+
* The write is atomic: the JSON goes to `inventory.json.tmp` first and is
|
|
122
|
+
* renamed over `inventory.json` only once complete, so a reader never sees a
|
|
123
|
+
* half-written file. On failure -- creating `groundworkDir` included -- the
|
|
124
|
+
* temp file is removed (best effort: a
|
|
125
|
+
* failed removal only warns, naming the temp file), any existing
|
|
126
|
+
* `inventory.json` is left untouched, and an `Error` is thrown, with the
|
|
127
|
+
* original failure as its `cause`, saying `.groundwork/` is incomplete and
|
|
128
|
+
* the CLI should be re-run.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* const path = writeInventory(inventory, ".groundwork");
|
|
133
|
+
* // path: ".groundwork/inventory.json"
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
62
136
|
export declare function writeInventory(inventory: Inventory, groundworkDir: string): string;
|
|
63
137
|
//# sourceMappingURL=inventory.d.ts.map
|
package/dist/inventory.js
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright the m3l-groundwork contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
1
3
|
/**
|
|
2
4
|
* Serializes a survey + conflict plan + pack survey into
|
|
3
5
|
* `.groundwork/inventory.json` -- the machine-readable handoff
|
|
4
|
-
* `/customize`'s Step 0
|
|
6
|
+
* `/customize`'s Step 0 (the adopt-mode reconcile step in `/customize`)
|
|
7
|
+
* reads instead of re-deriving the survey itself.
|
|
5
8
|
* Schema-versioned so a future CLI release can tell an old inventory apart
|
|
6
9
|
* from a current one; `/customize` must tolerate an older inventory
|
|
7
10
|
* rather than crash on one -- `schemaVersion: 1` predates packs (no `packs`
|
|
8
11
|
* field), `schemaVersion` below 3 predates the harness grade (no
|
|
9
12
|
* `harnessGrade`/`harnessConformance`), and `schemaVersion` below 4 predates
|
|
10
|
-
* the toolchain grade (no `toolchainGrade`/`toolchainConformance`)
|
|
13
|
+
* the toolchain grade (no `toolchainGrade`/`toolchainConformance`), and
|
|
14
|
+
* `schemaVersion` below 5 predates staged baseline additions (no
|
|
15
|
+
* `stagedBaseline`) -- `/customize` then falls back to reading absent files
|
|
16
|
+
* from `templateRoot`. From schema 5 on, each staged file is an inert copy
|
|
17
|
+
* named `<path>` + `stagedBaseline.suffix` (`.staged`) and carries the sha256
|
|
18
|
+
* of its staged bytes, so `/customize` can verify a copy before installing it.
|
|
19
|
+
* Schema 5 also carries `stagedPacks`, added additively without a version
|
|
20
|
+
* bump: every pack staged the same inert way under `.groundwork/packs/<name>/`
|
|
21
|
+
* (`pack-stage.ts`), its manifest included. No published release has
|
|
22
|
+
* written schema 5 yet; an inventory without `stagedPacks` came only from an
|
|
23
|
+
* unreleased development build, which staged packs unsuffixed at the same
|
|
24
|
+
* location.
|
|
11
25
|
*/
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
26
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
13
27
|
import { dirname, join } from "node:path";
|
|
14
28
|
import { fileURLToPath } from "node:url";
|
|
29
|
+
import { toPosixPath } from "./assets.js";
|
|
15
30
|
import { summarizeHarnessConformance } from "./harness/conformance.js";
|
|
16
31
|
import { summarizeToolchainConformance } from "./toolchain/conformance.js";
|
|
17
|
-
export const INVENTORY_SCHEMA_VERSION =
|
|
32
|
+
export const INVENTORY_SCHEMA_VERSION = 5;
|
|
18
33
|
/** Resolves this CLI package's own `package.json`, relative to this module's runtime location. */
|
|
19
34
|
function defaultPackageJsonPath() {
|
|
20
35
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -38,8 +53,44 @@ export function resolveCliVersion(packageJsonPath = defaultPackageJsonPath()) {
|
|
|
38
53
|
return "unknown";
|
|
39
54
|
}
|
|
40
55
|
}
|
|
41
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Returns new `FileConflict` objects whose `relPath` uses `/`, leaving the
|
|
58
|
+
* caller's array and objects untouched (main.ts keeps the native form for
|
|
59
|
+
* real file operations).
|
|
60
|
+
*/
|
|
61
|
+
function toPosixConflicts(conflicts) {
|
|
62
|
+
return conflicts.map((c) => ({ ...c, relPath: toPosixPath(c.relPath) }));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Builds the inventory object. Does not write anything -- see `writeInventory`.
|
|
66
|
+
*
|
|
67
|
+
* Exactly these paths use `/` on every platform: each `conflicts[].relPath`
|
|
68
|
+
* and each `packs[].fileConflicts[].relPath`, normalized here once with
|
|
69
|
+
* {@link toPosixPath}, plus `stagedBaseline.dir` (built by the caller as a
|
|
70
|
+
* `/`-joined literal) and each `stagedBaseline.files[].path`/`.staged`
|
|
71
|
+
* (already normalized by `stageBaselineAdditions` with the same
|
|
72
|
+
* {@link toPosixPath}, so the conflict and staged paths agree), and
|
|
73
|
+
* `stagedPacks` (passed through verbatim: `stagePacks` builds each `dir` as
|
|
74
|
+
* a `/`-joined literal and normalizes every `path`/`staged` the same way).
|
|
75
|
+
* The harness/toolchain conformance
|
|
76
|
+
* summaries are computed from the normalized conflict paths. Every other
|
|
77
|
+
* path -- `templateRoot`, `targetDir`, and every path inside `survey` -- is
|
|
78
|
+
* passed through in the platform's native form. The caller's `conflicts` and
|
|
79
|
+
* `packs` are never mutated; new objects are returned.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const inventory = buildInventory({ detection, templateRoot, targetDir, survey,
|
|
84
|
+
* conflicts, packs, harnessGrade, toolchainGrade, stagedBaseline, stagedPacks });
|
|
85
|
+
* inventory.conflicts[0]?.relPath; // "src/index.ts", even on Windows
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
42
88
|
export function buildInventory(params) {
|
|
89
|
+
const conflicts = toPosixConflicts(params.conflicts);
|
|
90
|
+
const packs = params.packs.map((pack) => ({
|
|
91
|
+
...pack,
|
|
92
|
+
fileConflicts: toPosixConflicts(pack.fileConflicts),
|
|
93
|
+
}));
|
|
43
94
|
return {
|
|
44
95
|
schemaVersion: INVENTORY_SCHEMA_VERSION,
|
|
45
96
|
cliVersion: resolveCliVersion(),
|
|
@@ -48,19 +99,59 @@ export function buildInventory(params) {
|
|
|
48
99
|
templateRoot: params.templateRoot,
|
|
49
100
|
targetDir: params.targetDir,
|
|
50
101
|
survey: params.survey,
|
|
51
|
-
conflicts
|
|
52
|
-
packs
|
|
102
|
+
conflicts,
|
|
103
|
+
packs,
|
|
53
104
|
harnessGrade: params.harnessGrade,
|
|
54
|
-
harnessConformance: summarizeHarnessConformance(
|
|
105
|
+
harnessConformance: summarizeHarnessConformance(conflicts),
|
|
55
106
|
toolchainGrade: params.toolchainGrade,
|
|
56
|
-
toolchainConformance: summarizeToolchainConformance(
|
|
107
|
+
toolchainConformance: summarizeToolchainConformance(conflicts),
|
|
108
|
+
stagedBaseline: params.stagedBaseline,
|
|
109
|
+
stagedPacks: params.stagedPacks,
|
|
57
110
|
};
|
|
58
111
|
}
|
|
59
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Writes `inventory.json` into `groundworkDir`, creating it if needed.
|
|
114
|
+
*
|
|
115
|
+
* The write is atomic: the JSON goes to `inventory.json.tmp` first and is
|
|
116
|
+
* renamed over `inventory.json` only once complete, so a reader never sees a
|
|
117
|
+
* half-written file. On failure -- creating `groundworkDir` included -- the
|
|
118
|
+
* temp file is removed (best effort: a
|
|
119
|
+
* failed removal only warns, naming the temp file), any existing
|
|
120
|
+
* `inventory.json` is left untouched, and an `Error` is thrown, with the
|
|
121
|
+
* original failure as its `cause`, saying `.groundwork/` is incomplete and
|
|
122
|
+
* the CLI should be re-run.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* const path = writeInventory(inventory, ".groundwork");
|
|
127
|
+
* // path: ".groundwork/inventory.json"
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
60
130
|
export function writeInventory(inventory, groundworkDir) {
|
|
61
|
-
mkdirSync(groundworkDir, { recursive: true });
|
|
62
131
|
const path = join(groundworkDir, "inventory.json");
|
|
63
|
-
|
|
132
|
+
const tmpPath = `${path}.tmp`;
|
|
133
|
+
try {
|
|
134
|
+
mkdirSync(groundworkDir, { recursive: true });
|
|
135
|
+
// Remove whatever already sits at the temp path (a crashed run's
|
|
136
|
+
// leftover, or a symlink planted there), then create it exclusively:
|
|
137
|
+
// "wx" fails rather than following a symlink raced in between.
|
|
138
|
+
rmSync(tmpPath, { force: true });
|
|
139
|
+
writeFileSync(tmpPath, `${JSON.stringify(inventory, null, 2)}\n`, {
|
|
140
|
+
flag: "wx",
|
|
141
|
+
});
|
|
142
|
+
renameSync(tmpPath, path);
|
|
143
|
+
}
|
|
144
|
+
catch (cause) {
|
|
145
|
+
try {
|
|
146
|
+
rmSync(tmpPath, { force: true });
|
|
147
|
+
}
|
|
148
|
+
catch (cleanupError) {
|
|
149
|
+
// Best effort: the write failure below is the error worth reporting,
|
|
150
|
+
// so a failed cleanup only warns, naming the leftover file.
|
|
151
|
+
console.warn(`warning: could not remove the temporary file ${tmpPath} -- delete it by hand (${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)})`);
|
|
152
|
+
}
|
|
153
|
+
throw new Error(`writing ${path} failed, so .groundwork/ is incomplete -- fix the cause and re-run the CLI`, { cause });
|
|
154
|
+
}
|
|
64
155
|
return path;
|
|
65
156
|
}
|
|
66
157
|
//# sourceMappingURL=inventory.js.map
|
package/dist/jsonc.d.ts
CHANGED
|
@@ -1,14 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The outcome of reading/parsing JSONC. A failure says which step failed:
|
|
3
|
+
* `"read"` (the file is missing, unreadable, unresolvable or not a regular
|
|
4
|
+
* file) or `"parse"` (its text is not JSONC) -- so a caller never reports an
|
|
5
|
+
* unreadable file as unparseable.
|
|
6
|
+
*/
|
|
1
7
|
export type JsoncReadResult = {
|
|
2
8
|
ok: true;
|
|
3
9
|
value: unknown;
|
|
4
10
|
} | {
|
|
5
11
|
ok: false;
|
|
12
|
+
stage: "read" | "parse";
|
|
6
13
|
error: string;
|
|
7
14
|
};
|
|
8
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Strips `//` and block comments and trailing commas from JSONC source. A
|
|
17
|
+
* trailing comma is removed in the same pass that tracks string literals, so
|
|
18
|
+
* a `,}` or `,]` inside a string (value or key) is never touched.
|
|
19
|
+
*/
|
|
9
20
|
export declare function stripJsoncNoise(content: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Strips `//` and block comments from JavaScript/TypeScript source, leaving
|
|
23
|
+
* single-quoted, double-quoted and template-literal strings alone. Unlike {@link stripJsoncNoise}
|
|
24
|
+
* it keeps trailing commas -- they are valid JS syntax, and removing them is
|
|
25
|
+
* a JSON-only cleanup.
|
|
26
|
+
*
|
|
27
|
+
* A character scanner, not a parser: a regex literal containing a quote
|
|
28
|
+
* (`/["']/`) or `//` can be misread as opening a string or a comment, and a
|
|
29
|
+
* `${...}` expression inside a template literal containing its own backtick
|
|
30
|
+
* or quote can close the outer template early. Neither shape appears in this
|
|
31
|
+
* project's actual `eslint.config.js`/`vitest.config.ts`/`verify-steps.mjs`
|
|
32
|
+
* files.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { stripJsComments } from "./jsonc.js";
|
|
37
|
+
* stripJsComments("const g = ['**' + '/*.js']; // note");
|
|
38
|
+
* // => "const g = ['**' + '/*.js']; "
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function stripJsComments(content: string): string;
|
|
10
42
|
/** Parses JSONC source text, returning a structured result rather than throwing. */
|
|
11
43
|
export declare function parseJsonc(content: string): JsoncReadResult;
|
|
12
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Reads and parses a JSONC file. A missing (`ENOENT`/`ENOTDIR`), unreadable
|
|
46
|
+
* (`EACCES`/`EPERM`), unresolvable (a symlink loop's `ELOOP`), not a regular
|
|
47
|
+
* file (a directory's `EISDIR`) or unparseable file is reported, not thrown
|
|
48
|
+
* -- each is a property of the file. The existence check is a real `stat`, never `existsSync`, so a file
|
|
49
|
+
* under a directory this process cannot search is reported with its errno
|
|
50
|
+
* rather than as absent. Any other failure, on the check or the read
|
|
51
|
+
* (`EIO`, `EMFILE`, ...), throws an `Error` naming the path, with the
|
|
52
|
+
* original failure as `cause`.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const read = readJsoncFile("/path/to/project/tsconfig.json");
|
|
57
|
+
* if (!read.ok) console.log(read.error);
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
13
60
|
export declare function readJsoncFile(path: string): JsoncReadResult;
|
|
14
61
|
//# sourceMappingURL=jsonc.d.ts.map
|
package/dist/jsonc.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright the m3l-groundwork contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
1
3
|
/**
|
|
2
4
|
* A tolerant reader for JSON-with-comments (JSONC) -- `tsconfig.json` and
|
|
3
5
|
* friends use `//`/`/* *\/` comments and trailing commas that `JSON.parse`
|
|
@@ -6,13 +8,23 @@
|
|
|
6
8
|
* to `JSON.parse`. It is not a full JSON5 parser -- no unquoted keys, no
|
|
7
9
|
* single-quoted strings -- tsconfig-shaped input is the only intended use.
|
|
8
10
|
*/
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
+
import { readFileSync, statSync } from "node:fs";
|
|
12
|
+
import { errnoCode, isAbsentError, readFailure, unresolvableCode, } from "./survey/internal/read-guard.js";
|
|
13
|
+
/** Matches exactly the whitespace class `\s` covers, so a trailing comma is recognized across the same gaps as before. */
|
|
14
|
+
const WHITESPACE = /\s/;
|
|
15
|
+
/**
|
|
16
|
+
* Strips `//` and block comments and trailing commas from JSONC source. A
|
|
17
|
+
* trailing comma is removed in the same pass that tracks string literals, so
|
|
18
|
+
* a `,}` or `,]` inside a string (value or key) is never touched.
|
|
19
|
+
*/
|
|
11
20
|
export function stripJsoncNoise(content) {
|
|
12
21
|
let result = "";
|
|
13
22
|
let inString = false;
|
|
14
23
|
let inLineComment = false;
|
|
15
24
|
let inBlockComment = false;
|
|
25
|
+
// Index in `result` of the last comma emitted outside a string with only
|
|
26
|
+
// whitespace (or stripped comments) after it; -1 when there is none.
|
|
27
|
+
let pendingComma = -1;
|
|
16
28
|
for (let i = 0; i < content.length; i++) {
|
|
17
29
|
const ch = content[i];
|
|
18
30
|
const next = content[i + 1];
|
|
@@ -44,6 +56,89 @@ export function stripJsoncNoise(content) {
|
|
|
44
56
|
}
|
|
45
57
|
if (ch === '"') {
|
|
46
58
|
inString = true;
|
|
59
|
+
pendingComma = -1;
|
|
60
|
+
result += ch;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (ch === "/" && next === "/") {
|
|
64
|
+
inLineComment = true;
|
|
65
|
+
i++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (ch === "/" && next === "*") {
|
|
69
|
+
inBlockComment = true;
|
|
70
|
+
i++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if ((ch === "}" || ch === "]") && pendingComma !== -1) {
|
|
74
|
+
result = result.slice(0, pendingComma) + result.slice(pendingComma + 1);
|
|
75
|
+
}
|
|
76
|
+
if (ch === ",") {
|
|
77
|
+
pendingComma = result.length;
|
|
78
|
+
}
|
|
79
|
+
else if (ch !== undefined && !WHITESPACE.test(ch)) {
|
|
80
|
+
pendingComma = -1;
|
|
81
|
+
}
|
|
82
|
+
result += ch;
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Strips `//` and block comments from JavaScript/TypeScript source, leaving
|
|
88
|
+
* single-quoted, double-quoted and template-literal strings alone. Unlike {@link stripJsoncNoise}
|
|
89
|
+
* it keeps trailing commas -- they are valid JS syntax, and removing them is
|
|
90
|
+
* a JSON-only cleanup.
|
|
91
|
+
*
|
|
92
|
+
* A character scanner, not a parser: a regex literal containing a quote
|
|
93
|
+
* (`/["']/`) or `//` can be misread as opening a string or a comment, and a
|
|
94
|
+
* `${...}` expression inside a template literal containing its own backtick
|
|
95
|
+
* or quote can close the outer template early. Neither shape appears in this
|
|
96
|
+
* project's actual `eslint.config.js`/`vitest.config.ts`/`verify-steps.mjs`
|
|
97
|
+
* files.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* import { stripJsComments } from "./jsonc.js";
|
|
102
|
+
* stripJsComments("const g = ['**' + '/*.js']; // note");
|
|
103
|
+
* // => "const g = ['**' + '/*.js']; "
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export function stripJsComments(content) {
|
|
107
|
+
let result = "";
|
|
108
|
+
let quote;
|
|
109
|
+
let inLineComment = false;
|
|
110
|
+
let inBlockComment = false;
|
|
111
|
+
for (let i = 0; i < content.length; i++) {
|
|
112
|
+
const ch = content[i];
|
|
113
|
+
const next = content[i + 1];
|
|
114
|
+
if (inLineComment) {
|
|
115
|
+
if (ch === "\n") {
|
|
116
|
+
inLineComment = false;
|
|
117
|
+
result += ch;
|
|
118
|
+
}
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (inBlockComment) {
|
|
122
|
+
if (ch === "*" && next === "/") {
|
|
123
|
+
inBlockComment = false;
|
|
124
|
+
i++;
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (quote !== undefined) {
|
|
129
|
+
result += ch;
|
|
130
|
+
if (ch === "\\") {
|
|
131
|
+
result += next ?? "";
|
|
132
|
+
i++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (ch === quote) {
|
|
136
|
+
quote = undefined;
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
141
|
+
quote = ch;
|
|
47
142
|
result += ch;
|
|
48
143
|
continue;
|
|
49
144
|
}
|
|
@@ -59,7 +154,7 @@ export function stripJsoncNoise(content) {
|
|
|
59
154
|
}
|
|
60
155
|
result += ch;
|
|
61
156
|
}
|
|
62
|
-
return result
|
|
157
|
+
return result;
|
|
63
158
|
}
|
|
64
159
|
/** Parses JSONC source text, returning a structured result rather than throwing. */
|
|
65
160
|
export function parseJsonc(content) {
|
|
@@ -69,15 +164,53 @@ export function parseJsonc(content) {
|
|
|
69
164
|
catch (error) {
|
|
70
165
|
return {
|
|
71
166
|
ok: false,
|
|
167
|
+
stage: "parse",
|
|
72
168
|
error: error instanceof Error ? error.message : String(error),
|
|
73
169
|
};
|
|
74
170
|
}
|
|
75
171
|
}
|
|
76
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* Reads and parses a JSONC file. A missing (`ENOENT`/`ENOTDIR`), unreadable
|
|
174
|
+
* (`EACCES`/`EPERM`), unresolvable (a symlink loop's `ELOOP`), not a regular
|
|
175
|
+
* file (a directory's `EISDIR`) or unparseable file is reported, not thrown
|
|
176
|
+
* -- each is a property of the file. The existence check is a real `stat`, never `existsSync`, so a file
|
|
177
|
+
* under a directory this process cannot search is reported with its errno
|
|
178
|
+
* rather than as absent. Any other failure, on the check or the read
|
|
179
|
+
* (`EIO`, `EMFILE`, ...), throws an `Error` naming the path, with the
|
|
180
|
+
* original failure as `cause`.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* const read = readJsoncFile("/path/to/project/tsconfig.json");
|
|
185
|
+
* if (!read.ok) console.log(read.error);
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
77
188
|
export function readJsoncFile(path) {
|
|
78
|
-
|
|
79
|
-
|
|
189
|
+
let content;
|
|
190
|
+
try {
|
|
191
|
+
statSync(path);
|
|
192
|
+
content = readFileSync(path, "utf8");
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (isAbsentError(error)) {
|
|
196
|
+
return { ok: false, stage: "read", error: `${path} does not exist` };
|
|
197
|
+
}
|
|
198
|
+
if (errnoCode(error) === "EISDIR") {
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
stage: "read",
|
|
202
|
+
error: `${path} is not a regular file`,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const code = unresolvableCode(error);
|
|
206
|
+
if (code === undefined)
|
|
207
|
+
throw readFailure(path, error);
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
stage: "read",
|
|
211
|
+
error: `${path} is unreadable (${code})`,
|
|
212
|
+
};
|
|
80
213
|
}
|
|
81
|
-
return parseJsonc(
|
|
214
|
+
return parseJsonc(content);
|
|
82
215
|
}
|
|
83
216
|
//# sourceMappingURL=jsonc.js.map
|
package/dist/main.d.ts
CHANGED
|
@@ -30,10 +30,69 @@ export declare class CliUsageError extends Error {
|
|
|
30
30
|
export declare function parseArgs(argv: string[]): CliOptions;
|
|
31
31
|
/** Resolves `templates/core` for a source checkout or a published tarball -- see `assets.ts`. */
|
|
32
32
|
export declare function templatesCoreDir(): string;
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* The result of {@link formatCapsSummary}: the rendered summary text plus an
|
|
35
|
+
* explicit over-cap flag, so a caller never has to string-match the text to
|
|
36
|
+
* decide how to present it.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const summary: CapsSummary = { text: "= 5 agents, ...", overCap: false };
|
|
41
|
+
* console.log(summary.overCap ? `warning: ${summary.text}` : summary.text);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export interface CapsSummary {
|
|
45
|
+
/** The summary line(s), newline-joined, exactly as printed. */
|
|
46
|
+
readonly text: string;
|
|
47
|
+
/** `true` when the baseline plus installed packs exceeds any cap in `CAP_LIMITS`. */
|
|
48
|
+
readonly overCap: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The post-`--pack`-install caps summary line(s) printed to fresh-mode's
|
|
52
|
+
* console output, plus whether any cap is exceeded.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const summary = formatCapsSummary(baselineCounts, [
|
|
57
|
+
* { name: "harness-extras", budget: packBudget },
|
|
58
|
+
* ]);
|
|
59
|
+
* console.log(summary.text);
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
34
62
|
export declare function formatCapsSummary(baseline: CapCounts, installed: {
|
|
35
63
|
name: string;
|
|
36
64
|
budget: CapCounts;
|
|
37
|
-
}[]):
|
|
38
|
-
|
|
65
|
+
}[]): CapsSummary;
|
|
66
|
+
/**
|
|
67
|
+
* Adopt mode's write-scope invariant (docs/assurance-case.md's trust
|
|
68
|
+
* boundary around the adopted project): every path it writes resolves under
|
|
69
|
+
* `<targetDir>/.groundwork/` or the guarded `/customize` install at
|
|
70
|
+
* `<targetDir>/.claude/skills/customize/` -- never an existing project file.
|
|
71
|
+
* `paths` may be absolute or relative to `targetDir`. Containment is
|
|
72
|
+
* {@link isPathContained}'s, so a sibling that merely shares a root's name
|
|
73
|
+
* as a prefix (`.groundwork-evil/`) is rejected. Both roots are derived from
|
|
74
|
+
* `customize-paths.ts`'s segment constants, so they cannot drift from where
|
|
75
|
+
* the install actually writes. The check is lexical -- it never touches the
|
|
76
|
+
* filesystem, so it does not detect a symlinked directory component; that
|
|
77
|
+
* refusal lives in the writers themselves (`fs-guard.ts`).
|
|
78
|
+
*
|
|
79
|
+
* @throws `AssertionError` (from `node:assert/strict`) naming the first path
|
|
80
|
+
* that escapes both allowed roots.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* import { assertAdoptWriteScope } from "./main.js";
|
|
85
|
+
*
|
|
86
|
+
* assertAdoptWriteScope("/work/app", [".groundwork/inventory.json"]); // ok
|
|
87
|
+
* assertAdoptWriteScope("/work/app", ["/work/app/package.json"]); // throws
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export declare function assertAdoptWriteScope(targetDir: string, paths: readonly string[]): void;
|
|
91
|
+
/**
|
|
92
|
+
* Runs the CLI. `platform` is injectable so fresh mode's Windows refusal
|
|
93
|
+
* (a runtime error, exit 1, raised before anything is written) is
|
|
94
|
+
* unit-testable on any OS; it defaults to `process.platform`. Adopt mode and
|
|
95
|
+
* `--help`/`--version`/`--list-packs` run on every platform.
|
|
96
|
+
*/
|
|
97
|
+
export declare function main(argv: string[], platform?: NodeJS.Platform): void;
|
|
39
98
|
//# sourceMappingURL=main.d.ts.map
|