@spikedpunch/align-cli 0.1.0
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/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/align-dir.d.ts +55 -0
- package/dist/align-dir.d.ts.map +1 -0
- package/dist/align-dir.js +184 -0
- package/dist/align-dir.js.map +1 -0
- package/dist/commands/agent.d.ts +14 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +142 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/baseline.d.ts +4 -0
- package/dist/commands/baseline.d.ts.map +1 -0
- package/dist/commands/baseline.js +57 -0
- package/dist/commands/baseline.js.map +1 -0
- package/dist/commands/build-report.d.ts +4 -0
- package/dist/commands/build-report.d.ts.map +1 -0
- package/dist/commands/build-report.js +122 -0
- package/dist/commands/build-report.js.map +1 -0
- package/dist/commands/build.d.ts +73 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +289 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/check.d.ts +31 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +203 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/doctor.d.ts +20 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +173 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/explain.d.ts +3 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +54 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/export-ir.d.ts +21 -0
- package/dist/commands/export-ir.d.ts.map +1 -0
- package/dist/commands/export-ir.js +33 -0
- package/dist/commands/export-ir.js.map +1 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +93 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/skill.d.ts +20 -0
- package/dist/commands/skill.d.ts.map +1 -0
- package/dist/commands/skill.js +23 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/telemetry.d.ts +52 -0
- package/dist/commands/telemetry.d.ts.map +1 -0
- package/dist/commands/telemetry.js +238 -0
- package/dist/commands/telemetry.js.map +1 -0
- package/dist/composition-root.d.ts +14 -0
- package/dist/composition-root.d.ts.map +1 -0
- package/dist/composition-root.js +18 -0
- package/dist/composition-root.js.map +1 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +39 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/init/claude-md.d.ts +6 -0
- package/dist/init/claude-md.d.ts.map +1 -0
- package/dist/init/claude-md.js +42 -0
- package/dist/init/claude-md.js.map +1 -0
- package/dist/init/config-comment.d.ts +7 -0
- package/dist/init/config-comment.d.ts.map +1 -0
- package/dist/init/config-comment.js +44 -0
- package/dist/init/config-comment.js.map +1 -0
- package/dist/init/detect-components.d.ts +16 -0
- package/dist/init/detect-components.d.ts.map +1 -0
- package/dist/init/detect-components.js +121 -0
- package/dist/init/detect-components.js.map +1 -0
- package/dist/init/gitignore.d.ts +8 -0
- package/dist/init/gitignore.d.ts.map +1 -0
- package/dist/init/gitignore.js +27 -0
- package/dist/init/gitignore.js.map +1 -0
- package/dist/init/render-config.d.ts +18 -0
- package/dist/init/render-config.d.ts.map +1 -0
- package/dist/init/render-config.js +57 -0
- package/dist/init/render-config.js.map +1 -0
- package/dist/init/suggest-layers.d.ts +16 -0
- package/dist/init/suggest-layers.d.ts.map +1 -0
- package/dist/init/suggest-layers.js +65 -0
- package/dist/init/suggest-layers.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +182 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/program.d.ts +11 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +199 -0
- package/dist/program.js.map +1 -0
- package/dist/skill/bullet-grammar.d.ts +2 -0
- package/dist/skill/bullet-grammar.d.ts.map +1 -0
- package/dist/skill/bullet-grammar.js +12 -0
- package/dist/skill/bullet-grammar.js.map +1 -0
- package/dist/skill/cli-inventory.d.ts +16 -0
- package/dist/skill/cli-inventory.d.ts.map +1 -0
- package/dist/skill/cli-inventory.js +17 -0
- package/dist/skill/cli-inventory.js.map +1 -0
- package/dist/skill/condensed.d.ts +2 -0
- package/dist/skill/condensed.d.ts.map +1 -0
- package/dist/skill/condensed.js +24 -0
- package/dist/skill/condensed.js.map +1 -0
- package/dist/skill/dsl-verbs.d.ts +2 -0
- package/dist/skill/dsl-verbs.d.ts.map +1 -0
- package/dist/skill/dsl-verbs.js +10 -0
- package/dist/skill/dsl-verbs.js.map +1 -0
- package/dist/skill/fix-loop-protocol.d.ts +18 -0
- package/dist/skill/fix-loop-protocol.d.ts.map +1 -0
- package/dist/skill/fix-loop-protocol.js +38 -0
- package/dist/skill/fix-loop-protocol.js.map +1 -0
- package/dist/skill/gates.d.ts +2 -0
- package/dist/skill/gates.d.ts.map +1 -0
- package/dist/skill/gates.js +20 -0
- package/dist/skill/gates.js.map +1 -0
- package/dist/skill/install.d.ts +25 -0
- package/dist/skill/install.d.ts.map +1 -0
- package/dist/skill/install.js +49 -0
- package/dist/skill/install.js.map +1 -0
- package/dist/skill/render.d.ts +15 -0
- package/dist/skill/render.d.ts.map +1 -0
- package/dist/skill/render.js +35 -0
- package/dist/skill/render.js.map +1 -0
- package/dist/skill/rule-kinds.d.ts +12 -0
- package/dist/skill/rule-kinds.d.ts.map +1 -0
- package/dist/skill/rule-kinds.js +47 -0
- package/dist/skill/rule-kinds.js.map +1 -0
- package/dist/skill/static-sections.d.ts +5 -0
- package/dist/skill/static-sections.d.ts.map +1 -0
- package/dist/skill/static-sections.js +74 -0
- package/dist/skill/static-sections.js.map +1 -0
- package/dist/skill/version-stamp.d.ts +22 -0
- package/dist/skill/version-stamp.d.ts.map +1 -0
- package/dist/skill/version-stamp.js +29 -0
- package/dist/skill/version-stamp.js.map +1 -0
- package/dist/telemetry/check-event.d.ts +7 -0
- package/dist/telemetry/check-event.d.ts.map +1 -0
- package/dist/telemetry/check-event.js +29 -0
- package/dist/telemetry/check-event.js.map +1 -0
- package/dist/telemetry/index.d.ts +8 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +8 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/process-context.d.ts +9 -0
- package/dist/telemetry/process-context.d.ts.map +1 -0
- package/dist/telemetry/process-context.js +10 -0
- package/dist/telemetry/process-context.js.map +1 -0
- package/dist/telemetry/recorder.d.ts +43 -0
- package/dist/telemetry/recorder.d.ts.map +1 -0
- package/dist/telemetry/recorder.js +61 -0
- package/dist/telemetry/recorder.js.map +1 -0
- package/dist/telemetry/resolve.d.ts +19 -0
- package/dist/telemetry/resolve.d.ts.map +1 -0
- package/dist/telemetry/resolve.js +18 -0
- package/dist/telemetry/resolve.js.map +1 -0
- package/dist/telemetry/ruleset-hash.d.ts +10 -0
- package/dist/telemetry/ruleset-hash.d.ts.map +1 -0
- package/dist/telemetry/ruleset-hash.js +12 -0
- package/dist/telemetry/ruleset-hash.js.map +1 -0
- package/dist/telemetry/session.d.ts +6 -0
- package/dist/telemetry/session.d.ts.map +1 -0
- package/dist/telemetry/session.js +9 -0
- package/dist/telemetry/session.js.map +1 -0
- package/dist/telemetry/violations.d.ts +10 -0
- package/dist/telemetry/violations.d.ts.map +1 -0
- package/dist/telemetry/violations.js +36 -0
- package/dist/telemetry/violations.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 align contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @spikedpunch/align-cli
|
|
2
|
+
|
|
3
|
+
The `align` command-line tool and MCP server — an **architecture-conformance verification oracle**
|
|
4
|
+
for humans and LLM coding agents.
|
|
5
|
+
|
|
6
|
+
`align` compiles a fluent TypeScript DSL into portable JSON IR, then evaluates it against a fresh
|
|
7
|
+
scan of your real dependency graph on every call — catching import cycles, layering violations, and
|
|
8
|
+
forbidden dependency directions that a single-file linter structurally cannot see. Violations
|
|
9
|
+
surface as compact, structured payloads so an agent can run check → fix → re-check in a tight loop
|
|
10
|
+
until the repo is green.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm i -g @spikedpunch/align-cli # or: pnpm add -D @spikedpunch/align-cli
|
|
16
|
+
align --version
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quickstart
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
align init # detect components, write a starter align.config.ts, seed the baseline
|
|
23
|
+
align check # fresh full scan; exit 0 iff green
|
|
24
|
+
align mcp # start the stdio MCP server for a connected coding agent
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> **Security note**: `align check` executes your `align.config.ts` (and any `custom.host`
|
|
28
|
+
> predicates it registers). Do not run it against a repository whose config you have not reviewed —
|
|
29
|
+
> use `align check --untrusted` for that (see the root README).
|
|
30
|
+
|
|
31
|
+
This package is the composition root; it wires
|
|
32
|
+
[`@spikedpunch/align-core`](https://www.npmjs.com/package/@spikedpunch/align-core),
|
|
33
|
+
[`@spikedpunch/align-plugin-typescript`](https://www.npmjs.com/package/@spikedpunch/align-plugin-typescript),
|
|
34
|
+
and [`@spikedpunch/align-agent`](https://www.npmjs.com/package/@spikedpunch/align-agent).
|
|
35
|
+
|
|
36
|
+
Full docs — the DSL reference, the baseline model, greenfield mode, `align build`, untrusted mode,
|
|
37
|
+
telemetry, and the BYOK fix agent (`align agent run`) — are in the
|
|
38
|
+
[root README](https://github.com/SpikedPunchVictim/align#readme).
|
|
39
|
+
|
|
40
|
+
## License
|
|
41
|
+
|
|
42
|
+
MIT
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type BaselineEntry, type ExportedRuleset, type GeneratedRulesFile, type RulesLock, type TelemetryState } from '@spikedpunch/align-core';
|
|
2
|
+
export declare const ALIGN_DIR = ".align";
|
|
3
|
+
export declare function alignDirPath(rootDir: string): string;
|
|
4
|
+
export declare function generatedRulesPath(rootDir: string): string;
|
|
5
|
+
export declare function rulesLockPath(rootDir: string): string;
|
|
6
|
+
export declare function lastBuildReportPath(rootDir: string): string;
|
|
7
|
+
export declare function ensureAlignDir(rootDir: string): void;
|
|
8
|
+
export declare function readBaseline(rootDir: string): BaselineEntry[];
|
|
9
|
+
export declare function writeBaseline(rootDir: string, entries: readonly BaselineEntry[]): void;
|
|
10
|
+
/** Raw on-disk bytes of `.align/generated-rules.json`, or `undefined` if absent — used both to
|
|
11
|
+
* `.parse()` the file (below) and to content-hash it verbatim for divergence detection (ADR 011:
|
|
12
|
+
* a hand-edit to this file must be detectable even if the edit still happens to be valid JSON). */
|
|
13
|
+
export declare function readGeneratedRulesRaw(rootDir: string): string | undefined;
|
|
14
|
+
/** Parses and zod-validates `.align/generated-rules.json` (ADR 002 parse-don't-validate) — throws
|
|
15
|
+
* a descriptive error on a corrupted/hand-mangled file rather than silently ignoring it, since
|
|
16
|
+
* silently dropping generated rules would be a false-green (a doc-built rule stops being
|
|
17
|
+
* enforced with no signal). */
|
|
18
|
+
export declare function readGeneratedRules(rootDir: string): GeneratedRulesFile | undefined;
|
|
19
|
+
/** Returns the exact raw bytes written, so callers can content-hash the same string that will be
|
|
20
|
+
* read back later (`readGeneratedRulesRaw`) — the divergence-detection hash must be computed over
|
|
21
|
+
* identical serialization on both sides of the round-trip. */
|
|
22
|
+
export declare function writeGeneratedRules(rootDir: string, file: GeneratedRulesFile): string;
|
|
23
|
+
export declare function readRulesLock(rootDir: string): RulesLock | undefined;
|
|
24
|
+
export declare function writeRulesLock(rootDir: string, lock: RulesLock): void;
|
|
25
|
+
export declare function writeLastBuildReport(rootDir: string, markdown: string): void;
|
|
26
|
+
/** ADR 014's untrusted-mode artifact. Defaults to `.align/ruleset-ir.json`; `align export-ir
|
|
27
|
+
* --out`/`align check --ir` both take an explicit override path (absolute or repo-root-relative)
|
|
28
|
+
* for repos that want to commit it somewhere else or keep more than one exported snapshot. */
|
|
29
|
+
export declare function rulesetIrPath(rootDir: string, override?: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Reads and zod-validates the exported ruleset (`.align/ruleset-ir.json` by default). Returns
|
|
32
|
+
* `undefined` only when the file doesn't exist — `align check --untrusted`'s caller turns that
|
|
33
|
+
* into a hard refuse-don't-fallback message (ADR 014), never a silent switch to executing
|
|
34
|
+
* align.config.ts. A file that exists but fails to parse as JSON or fails schema validation
|
|
35
|
+
* throws — a corrupted or hand-mangled artifact must never be treated as "absent" (that would
|
|
36
|
+
* silently drop rules, the same false-green class `readGeneratedRules` already guards against).
|
|
37
|
+
*/
|
|
38
|
+
export declare function readRulesetIr(rootDir: string, override?: string): ExportedRuleset | undefined;
|
|
39
|
+
export declare function writeRulesetIr(rootDir: string, data: ExportedRuleset, override?: string): string;
|
|
40
|
+
export declare function telemetryJsonlPath(rootDir: string, override?: string): string;
|
|
41
|
+
/** Appends one already-serialized JSON line (`serializeTelemetryEvent`, `@spikedpunch/align-core`) to
|
|
42
|
+
* `.align/telemetry.jsonl` — the only fs-append call in this codebase's telemetry surface. Callers
|
|
43
|
+
* are expected to have already checked telemetry is enabled; this function does no gating itself
|
|
44
|
+
* so it stays a single, easily-audited "yes, this really does write to disk" primitive. */
|
|
45
|
+
export declare function appendTelemetryLine(rootDir: string, line: string, override?: string): void;
|
|
46
|
+
/** Reads `.align/telemetry-state.json` (the previous check's violation-fingerprint set, used for
|
|
47
|
+
* appear/resolve diffing). A missing OR corrupt file is treated identically — the empty state —
|
|
48
|
+
* deliberately different from `readGeneratedRules`'/`readRulesetIr`'s "corrupted is never treated
|
|
49
|
+
* as absent" discipline above: this file is a soft, regenerable cache of the last-seen violation
|
|
50
|
+
* set (IMPLEMENTATION_PLAN.md's telemetry spec: "cheap and robust to a missing/corrupt state
|
|
51
|
+
* file"), not a portable ruleset artifact whose silent loss would under-enforce a rule. Worst case
|
|
52
|
+
* on corruption: one check's worth of appear/resolve events look like everything just appeared. */
|
|
53
|
+
export declare function readTelemetryState(rootDir: string): TelemetryState;
|
|
54
|
+
export declare function writeTelemetryState(rootDir: string, state: TelemetryState): void;
|
|
55
|
+
//# sourceMappingURL=align-dir.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"align-dir.d.ts","sourceRoot":"","sources":["../src/align-dir.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAEjC,eAAO,MAAM,SAAS,WAAW,CAAC;AAclC,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAMD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAS7D;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAItF;AAED;;mGAEmG;AACnG,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED;;;+BAG+B;AAC/B,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAUlF;AAED;;8DAE8D;AAC9D,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAKrF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAKpE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAGrE;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG5E;AAED;;8FAE8F;AAC9F,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAU7F;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAKhG;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAG7E;AAMD;;;2FAG2F;AAC3F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAI1F;AAED;;;;;;mGAMmG;AACnG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAYlE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAGhF"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Imperative shell around `@spikedpunch/align-core`'s pure `BaselineStore` (CODING_BEST_PRACTICES.md §15/16:
|
|
3
|
+
* functional core, imperative shell) — all filesystem I/O for `.align/` lives here, not in core.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import { exportedRulesetSchema, generatedRulesFileSchema, rulesLockSchema, } from '@spikedpunch/align-core';
|
|
8
|
+
export const ALIGN_DIR = '.align';
|
|
9
|
+
const BASELINE_FILENAME = 'baseline.json';
|
|
10
|
+
const GENERATED_RULES_FILENAME = 'generated-rules.json';
|
|
11
|
+
const RULES_LOCK_FILENAME = 'rules.lock.json';
|
|
12
|
+
const LAST_BUILD_REPORT_FILENAME = 'last-build-report.md';
|
|
13
|
+
// Default location for `align export-ir`'s output / `align check --untrusted`'s input (ADR 014).
|
|
14
|
+
// Overridable per-invocation via `align export-ir --out <path>` / `align check --ir <path>`.
|
|
15
|
+
const RULESET_IR_FILENAME = 'ruleset-ir.json';
|
|
16
|
+
// Telemetry (IMPLEMENTATION_PLAN.md's telemetry Design Reserve entry, ADR 015): append-only, opt-in,
|
|
17
|
+
// local-file-only. Both are gitignored by default (align's own .gitignore, and `align init` adds
|
|
18
|
+
// the same two entries to the target repo's) — neither is a portable artifact meant to be committed.
|
|
19
|
+
const TELEMETRY_JSONL_FILENAME = 'telemetry.jsonl';
|
|
20
|
+
const TELEMETRY_STATE_FILENAME = 'telemetry-state.json';
|
|
21
|
+
export function alignDirPath(rootDir) {
|
|
22
|
+
return path.join(rootDir, ALIGN_DIR);
|
|
23
|
+
}
|
|
24
|
+
function baselinePath(rootDir) {
|
|
25
|
+
return path.join(alignDirPath(rootDir), BASELINE_FILENAME);
|
|
26
|
+
}
|
|
27
|
+
export function generatedRulesPath(rootDir) {
|
|
28
|
+
return path.join(alignDirPath(rootDir), GENERATED_RULES_FILENAME);
|
|
29
|
+
}
|
|
30
|
+
export function rulesLockPath(rootDir) {
|
|
31
|
+
return path.join(alignDirPath(rootDir), RULES_LOCK_FILENAME);
|
|
32
|
+
}
|
|
33
|
+
export function lastBuildReportPath(rootDir) {
|
|
34
|
+
return path.join(alignDirPath(rootDir), LAST_BUILD_REPORT_FILENAME);
|
|
35
|
+
}
|
|
36
|
+
export function ensureAlignDir(rootDir) {
|
|
37
|
+
fs.mkdirSync(alignDirPath(rootDir), { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
export function readBaseline(rootDir) {
|
|
40
|
+
const file = baselinePath(rootDir);
|
|
41
|
+
if (!fs.existsSync(file))
|
|
42
|
+
return [];
|
|
43
|
+
try {
|
|
44
|
+
const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
45
|
+
return Array.isArray(raw) ? raw : [];
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function writeBaseline(rootDir, entries) {
|
|
52
|
+
ensureAlignDir(rootDir);
|
|
53
|
+
const sorted = [...entries].sort((a, b) => a.fingerprint.localeCompare(b.fingerprint));
|
|
54
|
+
fs.writeFileSync(baselinePath(rootDir), `${JSON.stringify(sorted, null, 2)}\n`, 'utf8');
|
|
55
|
+
}
|
|
56
|
+
/** Raw on-disk bytes of `.align/generated-rules.json`, or `undefined` if absent — used both to
|
|
57
|
+
* `.parse()` the file (below) and to content-hash it verbatim for divergence detection (ADR 011:
|
|
58
|
+
* a hand-edit to this file must be detectable even if the edit still happens to be valid JSON). */
|
|
59
|
+
export function readGeneratedRulesRaw(rootDir) {
|
|
60
|
+
const file = generatedRulesPath(rootDir);
|
|
61
|
+
if (!fs.existsSync(file))
|
|
62
|
+
return undefined;
|
|
63
|
+
return fs.readFileSync(file, 'utf8');
|
|
64
|
+
}
|
|
65
|
+
/** Parses and zod-validates `.align/generated-rules.json` (ADR 002 parse-don't-validate) — throws
|
|
66
|
+
* a descriptive error on a corrupted/hand-mangled file rather than silently ignoring it, since
|
|
67
|
+
* silently dropping generated rules would be a false-green (a doc-built rule stops being
|
|
68
|
+
* enforced with no signal). */
|
|
69
|
+
export function readGeneratedRules(rootDir) {
|
|
70
|
+
const raw = readGeneratedRulesRaw(rootDir);
|
|
71
|
+
if (raw === undefined)
|
|
72
|
+
return undefined;
|
|
73
|
+
let parsed;
|
|
74
|
+
try {
|
|
75
|
+
parsed = JSON.parse(raw);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
throw new Error(`${generatedRulesPath(rootDir)} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
79
|
+
}
|
|
80
|
+
return generatedRulesFileSchema.parse(parsed);
|
|
81
|
+
}
|
|
82
|
+
/** Returns the exact raw bytes written, so callers can content-hash the same string that will be
|
|
83
|
+
* read back later (`readGeneratedRulesRaw`) — the divergence-detection hash must be computed over
|
|
84
|
+
* identical serialization on both sides of the round-trip. */
|
|
85
|
+
export function writeGeneratedRules(rootDir, file) {
|
|
86
|
+
ensureAlignDir(rootDir);
|
|
87
|
+
const raw = `${JSON.stringify(file, null, 2)}\n`;
|
|
88
|
+
fs.writeFileSync(generatedRulesPath(rootDir), raw, 'utf8');
|
|
89
|
+
return raw;
|
|
90
|
+
}
|
|
91
|
+
export function readRulesLock(rootDir) {
|
|
92
|
+
const file = rulesLockPath(rootDir);
|
|
93
|
+
if (!fs.existsSync(file))
|
|
94
|
+
return undefined;
|
|
95
|
+
const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
96
|
+
return rulesLockSchema.parse(raw);
|
|
97
|
+
}
|
|
98
|
+
export function writeRulesLock(rootDir, lock) {
|
|
99
|
+
ensureAlignDir(rootDir);
|
|
100
|
+
fs.writeFileSync(rulesLockPath(rootDir), `${JSON.stringify(lock, null, 2)}\n`, 'utf8');
|
|
101
|
+
}
|
|
102
|
+
export function writeLastBuildReport(rootDir, markdown) {
|
|
103
|
+
ensureAlignDir(rootDir);
|
|
104
|
+
fs.writeFileSync(lastBuildReportPath(rootDir), markdown, 'utf8');
|
|
105
|
+
}
|
|
106
|
+
/** ADR 014's untrusted-mode artifact. Defaults to `.align/ruleset-ir.json`; `align export-ir
|
|
107
|
+
* --out`/`align check --ir` both take an explicit override path (absolute or repo-root-relative)
|
|
108
|
+
* for repos that want to commit it somewhere else or keep more than one exported snapshot. */
|
|
109
|
+
export function rulesetIrPath(rootDir, override) {
|
|
110
|
+
if (override !== undefined)
|
|
111
|
+
return path.isAbsolute(override) ? override : path.join(rootDir, override);
|
|
112
|
+
return path.join(alignDirPath(rootDir), RULESET_IR_FILENAME);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Reads and zod-validates the exported ruleset (`.align/ruleset-ir.json` by default). Returns
|
|
116
|
+
* `undefined` only when the file doesn't exist — `align check --untrusted`'s caller turns that
|
|
117
|
+
* into a hard refuse-don't-fallback message (ADR 014), never a silent switch to executing
|
|
118
|
+
* align.config.ts. A file that exists but fails to parse as JSON or fails schema validation
|
|
119
|
+
* throws — a corrupted or hand-mangled artifact must never be treated as "absent" (that would
|
|
120
|
+
* silently drop rules, the same false-green class `readGeneratedRules` already guards against).
|
|
121
|
+
*/
|
|
122
|
+
export function readRulesetIr(rootDir, override) {
|
|
123
|
+
const file = rulesetIrPath(rootDir, override);
|
|
124
|
+
if (!fs.existsSync(file))
|
|
125
|
+
return undefined;
|
|
126
|
+
let parsed;
|
|
127
|
+
try {
|
|
128
|
+
parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
throw new Error(`${file} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
132
|
+
}
|
|
133
|
+
return exportedRulesetSchema.parse(parsed);
|
|
134
|
+
}
|
|
135
|
+
export function writeRulesetIr(rootDir, data, override) {
|
|
136
|
+
const file = rulesetIrPath(rootDir, override);
|
|
137
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
138
|
+
fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
|
|
139
|
+
return file;
|
|
140
|
+
}
|
|
141
|
+
export function telemetryJsonlPath(rootDir, override) {
|
|
142
|
+
if (override !== undefined)
|
|
143
|
+
return path.isAbsolute(override) ? override : path.join(rootDir, override);
|
|
144
|
+
return path.join(alignDirPath(rootDir), TELEMETRY_JSONL_FILENAME);
|
|
145
|
+
}
|
|
146
|
+
function telemetryStatePath(rootDir) {
|
|
147
|
+
return path.join(alignDirPath(rootDir), TELEMETRY_STATE_FILENAME);
|
|
148
|
+
}
|
|
149
|
+
/** Appends one already-serialized JSON line (`serializeTelemetryEvent`, `@spikedpunch/align-core`) to
|
|
150
|
+
* `.align/telemetry.jsonl` — the only fs-append call in this codebase's telemetry surface. Callers
|
|
151
|
+
* are expected to have already checked telemetry is enabled; this function does no gating itself
|
|
152
|
+
* so it stays a single, easily-audited "yes, this really does write to disk" primitive. */
|
|
153
|
+
export function appendTelemetryLine(rootDir, line, override) {
|
|
154
|
+
const file = telemetryJsonlPath(rootDir, override);
|
|
155
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
156
|
+
fs.appendFileSync(file, `${line}\n`, 'utf8');
|
|
157
|
+
}
|
|
158
|
+
/** Reads `.align/telemetry-state.json` (the previous check's violation-fingerprint set, used for
|
|
159
|
+
* appear/resolve diffing). A missing OR corrupt file is treated identically — the empty state —
|
|
160
|
+
* deliberately different from `readGeneratedRules`'/`readRulesetIr`'s "corrupted is never treated
|
|
161
|
+
* as absent" discipline above: this file is a soft, regenerable cache of the last-seen violation
|
|
162
|
+
* set (IMPLEMENTATION_PLAN.md's telemetry spec: "cheap and robust to a missing/corrupt state
|
|
163
|
+
* file"), not a portable ruleset artifact whose silent loss would under-enforce a rule. Worst case
|
|
164
|
+
* on corruption: one check's worth of appear/resolve events look like everything just appeared. */
|
|
165
|
+
export function readTelemetryState(rootDir) {
|
|
166
|
+
const file = telemetryStatePath(rootDir);
|
|
167
|
+
if (!fs.existsSync(file))
|
|
168
|
+
return { violations: [] };
|
|
169
|
+
try {
|
|
170
|
+
const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
171
|
+
if (raw !== null && typeof raw === 'object' && Array.isArray(raw.violations)) {
|
|
172
|
+
return raw;
|
|
173
|
+
}
|
|
174
|
+
return { violations: [] };
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return { violations: [] };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export function writeTelemetryState(rootDir, state) {
|
|
181
|
+
ensureAlignDir(rootDir);
|
|
182
|
+
fs.writeFileSync(telemetryStatePath(rootDir), `${JSON.stringify(state, null, 2)}\n`, 'utf8');
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=align-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"align-dir.js","sourceRoot":"","sources":["../src/align-dir.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { toRepoRelativePath } from '@spikedpunch/align-core';
|
|
2
|
+
export interface AgentRunCliOptions {
|
|
3
|
+
readonly maxAttempts: number;
|
|
4
|
+
readonly pr: boolean;
|
|
5
|
+
readonly autoMerge: boolean;
|
|
6
|
+
readonly allowUntested: boolean;
|
|
7
|
+
readonly allowSymbolRemovals: boolean;
|
|
8
|
+
readonly model?: string;
|
|
9
|
+
readonly dryRun: boolean;
|
|
10
|
+
readonly telemetryPreConfig?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function runAgentCommand(rootDir: string, options: AgentRunCliOptions): Promise<number>;
|
|
13
|
+
export { toRepoRelativePath };
|
|
14
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,kBAAkB,EAAqD,MAAM,yBAAyB,CAAC;AAkBhH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACvC;AA4HD,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBnG;AAID,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `align agent run` — the CLI composition root for the Stage 4 BYOK fix loop. Mirrors
|
|
3
|
+
* `composition-root.ts`'s discipline: `@spikedpunch/align-agent` never imports `@spikedpunch/align-plugin-typescript` or
|
|
4
|
+
* touches the filesystem/git directly (per its stated dependency budget, `@spikedpunch/align-core` +
|
|
5
|
+
* `@anthropic-ai/sdk`); this file is the only place that wires the real `TypeScriptPlugin` scanner,
|
|
6
|
+
* `node:fs`, and real git into `AgentEffects`.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { toRepoRelativePath } from '@spikedpunch/align-core';
|
|
11
|
+
import { TypeScriptPlugin } from '@spikedpunch/align-plugin-typescript';
|
|
12
|
+
import { AnthropicFixProvider, MemoizingFixProvider, createNodeGitEffects, defaultWorkBranchName, formatIfAvailable, runAgentLoop, } from '@spikedpunch/align-agent';
|
|
13
|
+
import { loadConfig } from '../config.js';
|
|
14
|
+
import { createOrchestrator } from '../composition-root.js';
|
|
15
|
+
import { readBaseline } from '../align-dir.js';
|
|
16
|
+
import { computeRulesetIrHash, createTelemetryRecorder } from '../telemetry/index.js';
|
|
17
|
+
function buildEffects(rootDir, ruleset, excludes, hostRules, options, fixProvider) {
|
|
18
|
+
const plugin = new TypeScriptPlugin();
|
|
19
|
+
return {
|
|
20
|
+
fixProvider,
|
|
21
|
+
runCheck: async () => {
|
|
22
|
+
const { orchestrator } = createOrchestrator(ruleset, readBaseline(rootDir), hostRules);
|
|
23
|
+
return orchestrator.check({ rootDir, excludes });
|
|
24
|
+
},
|
|
25
|
+
scanGraph: () => plugin.scanner.scan({ rootDir, components: ruleset.components, excludes }),
|
|
26
|
+
readFile: async (p) => fs.readFileSync(path.join(rootDir, p), 'utf8'),
|
|
27
|
+
writeFile: async (p, content) => {
|
|
28
|
+
fs.mkdirSync(path.dirname(path.join(rootDir, p)), { recursive: true });
|
|
29
|
+
fs.writeFileSync(path.join(rootDir, p), content, 'utf8');
|
|
30
|
+
},
|
|
31
|
+
formatIfAvailable: (paths) => formatIfAvailable(rootDir, paths),
|
|
32
|
+
git: createNodeGitEffects(rootDir),
|
|
33
|
+
now: () => Date.now(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* `agent` telemetry (IMPLEMENTATION_PLAN.md's telemetry spec): `attempts` is the count of real
|
|
38
|
+
* `FixProvider` calls this run made (`MemoizingFixProvider.providerCallCount` — a REPAIR retry
|
|
39
|
+
* that re-invokes the model IS an attempt; a memoized cache hit for identical state is not, since
|
|
40
|
+
* no actual model call happened); `iterations` is the number of file GROUPs DISCOVER+GROUP
|
|
41
|
+
* produced (the macro-level loop count). `converged` covers both "fixed everything" and "there was
|
|
42
|
+
* nothing to fix" — a `dry-run`/`refused`/`partial-escalated` verdict did not converge.
|
|
43
|
+
*/
|
|
44
|
+
function recordAgentTelemetry(rootDir, result, memoizingProvider, anthropicProvider, ruleset, telemetryPreConfig, configTelemetry) {
|
|
45
|
+
const recorder = createTelemetryRecorder(rootDir, 'agent run', telemetryPreConfig, configTelemetry);
|
|
46
|
+
if (!recorder.enabled)
|
|
47
|
+
return;
|
|
48
|
+
const escalatedGroups = result.groups.filter((g) => g.status === 'escalated');
|
|
49
|
+
const converged = result.verdict === 'done' || result.verdict === 'nothing-to-fix';
|
|
50
|
+
const usage = anthropicProvider.getUsageTotals();
|
|
51
|
+
recorder.record({
|
|
52
|
+
kind: 'agent',
|
|
53
|
+
attempts: memoizingProvider.providerCallCount,
|
|
54
|
+
converged,
|
|
55
|
+
iterations: result.groups.length,
|
|
56
|
+
escalated: escalatedGroups.length > 0,
|
|
57
|
+
...(escalatedGroups.length > 0 ? { escalationReason: escalatedGroups.map((g) => g.reason).join('; ').slice(0, 200) } : {}),
|
|
58
|
+
...(usage !== undefined ? { usage } : {}),
|
|
59
|
+
}, { rulesetIrHash: computeRulesetIrHash(ruleset) });
|
|
60
|
+
}
|
|
61
|
+
function printResult(result) {
|
|
62
|
+
console.log(`align agent run — verdict: ${result.verdict}`);
|
|
63
|
+
if (result.refusalReason !== undefined)
|
|
64
|
+
console.log(` refused: ${result.refusalReason}`);
|
|
65
|
+
for (const g of result.groups) {
|
|
66
|
+
if (g.status === 'done') {
|
|
67
|
+
console.log(` DONE ${g.file} (${g.commitSha.slice(0, 7)}) ${g.rationale}`);
|
|
68
|
+
}
|
|
69
|
+
else if (g.status === 'escalated') {
|
|
70
|
+
console.log(` ESCALATED ${g.file} ${g.reason}`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.log(` DRY-RUN ${g.file}`);
|
|
74
|
+
for (const f of g.proposal.files) {
|
|
75
|
+
console.log(` file: ${f.path}`);
|
|
76
|
+
for (const e of f.edits) {
|
|
77
|
+
console.log(` search: ${JSON.stringify(e.search)}`);
|
|
78
|
+
console.log(` replace: ${JSON.stringify(e.replace)}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
console.log(` rationale: ${g.proposal.rationale}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const tm = result.terminalMerge;
|
|
85
|
+
if (tm !== undefined) {
|
|
86
|
+
switch (tm.status) {
|
|
87
|
+
case 'pr-created':
|
|
88
|
+
console.log(` PR opened: ${tm.url}`);
|
|
89
|
+
break;
|
|
90
|
+
case 'no-remote-or-no-gh':
|
|
91
|
+
console.log(` No remote/gh available — branch left in place: ${tm.branch}\n${tm.summary}`);
|
|
92
|
+
break;
|
|
93
|
+
case 'auto-merged':
|
|
94
|
+
console.log(' Auto-merged into the base branch; work branch deleted.');
|
|
95
|
+
break;
|
|
96
|
+
case 'rebase-conflict':
|
|
97
|
+
console.log(' TERMINAL MERGE escalated: rebase conflict — never auto-resolved. Resolve manually on the work branch.');
|
|
98
|
+
break;
|
|
99
|
+
case 'final-check-red':
|
|
100
|
+
console.log(' TERMINAL MERGE escalated: the FULL check is red on the rebased tip — investigate before merging.');
|
|
101
|
+
break;
|
|
102
|
+
case 'no-commits':
|
|
103
|
+
console.log(' No group reached DONE — nothing to merge.');
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function exitCodeFor(result) {
|
|
109
|
+
if (result.verdict === 'refused' || result.verdict === 'partial-escalated')
|
|
110
|
+
return 1;
|
|
111
|
+
if (result.verdict === 'done') {
|
|
112
|
+
const tm = result.terminalMerge;
|
|
113
|
+
if (tm?.status === 'rebase-conflict' || tm?.status === 'final-check-red')
|
|
114
|
+
return 1;
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
return 0; // nothing-to-fix / dry-run
|
|
118
|
+
}
|
|
119
|
+
export async function runAgentCommand(rootDir, options) {
|
|
120
|
+
const { ruleset, excludes, hostRules, telemetry } = await loadConfig(rootDir);
|
|
121
|
+
const anthropicProvider = options.model !== undefined ? new AnthropicFixProvider({ model: options.model }) : new AnthropicFixProvider();
|
|
122
|
+
const memoizingProvider = new MemoizingFixProvider(anthropicProvider);
|
|
123
|
+
const effects = buildEffects(rootDir, ruleset, excludes, hostRules, options, memoizingProvider);
|
|
124
|
+
const baseBranch = await effects.git.currentBranch();
|
|
125
|
+
const runOptions = {
|
|
126
|
+
maxAttempts: options.maxAttempts,
|
|
127
|
+
mode: options.autoMerge ? 'auto-merge' : 'pr',
|
|
128
|
+
allowUntested: options.allowUntested,
|
|
129
|
+
allowSymbolRemovals: options.allowSymbolRemovals,
|
|
130
|
+
dryRun: options.dryRun,
|
|
131
|
+
workBranchName: defaultWorkBranchName(),
|
|
132
|
+
baseBranch,
|
|
133
|
+
};
|
|
134
|
+
const result = await runAgentLoop(effects, ruleset, runOptions);
|
|
135
|
+
printResult(result);
|
|
136
|
+
recordAgentTelemetry(rootDir, result, memoizingProvider, anthropicProvider, ruleset, options.telemetryPreConfig, telemetry);
|
|
137
|
+
return exitCodeFor(result);
|
|
138
|
+
}
|
|
139
|
+
// Re-exported so tests/CLI wiring can construct a RepoRelativePath without importing @spikedpunch/align-core
|
|
140
|
+
// directly in every call site.
|
|
141
|
+
export { toRepoRelativePath };
|
|
142
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function baselineAccept(rootDir: string, ruleId?: string, telemetryPreConfig?: boolean): Promise<number>;
|
|
2
|
+
export declare function baselinePrune(rootDir: string, telemetryPreConfig?: boolean): Promise<number>;
|
|
3
|
+
export declare function baselineShow(rootDir: string, ruleId?: string): Promise<number>;
|
|
4
|
+
//# sourceMappingURL=baseline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../../src/commands/baseline.ts"],"names":[],"mappings":"AAeA,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBpH;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBlG;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYpF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { InMemoryBaselineStore, toRuleId } from '@spikedpunch/align-core';
|
|
2
|
+
import { loadConfig } from '../config.js';
|
|
3
|
+
import { createOrchestrator } from '../composition-root.js';
|
|
4
|
+
import { readBaseline, writeBaseline } from '../align-dir.js';
|
|
5
|
+
import { computeRulesetIrHash, createTelemetryRecorder } from '../telemetry/index.js';
|
|
6
|
+
async function currentViolations(rootDir) {
|
|
7
|
+
const { ruleset, excludes, hostRules, telemetry } = await loadConfig(rootDir);
|
|
8
|
+
// An empty baseline store surfaces every violation as "red" regardless of what's actually
|
|
9
|
+
// baselined on disk — exactly the full current violation set `prune`/`accept` need.
|
|
10
|
+
const { orchestrator } = createOrchestrator(ruleset, [], hostRules);
|
|
11
|
+
const run = await orchestrator.check({ rootDir, excludes });
|
|
12
|
+
return { violations: run.gates.flatMap((g) => g.violations), ruleset, telemetry };
|
|
13
|
+
}
|
|
14
|
+
export async function baselineAccept(rootDir, ruleId, telemetryPreConfig) {
|
|
15
|
+
const { violations, ruleset, telemetry } = await currentViolations(rootDir);
|
|
16
|
+
const targeted = ruleId === undefined ? violations : violations.filter((v) => v.ruleId === toRuleId(ruleId));
|
|
17
|
+
const store = new InMemoryBaselineStore(readBaseline(rootDir));
|
|
18
|
+
store.accept(targeted, 'manual');
|
|
19
|
+
writeBaseline(rootDir, store.snapshot());
|
|
20
|
+
console.log(`Accepted ${targeted.length} violation(s)${ruleId === undefined ? '' : ` for rule '${ruleId}'`} into the baseline.`);
|
|
21
|
+
const recorder = createTelemetryRecorder(rootDir, 'baseline accept', telemetryPreConfig, telemetry);
|
|
22
|
+
recorder.record({
|
|
23
|
+
kind: 'baseline',
|
|
24
|
+
action: 'accept',
|
|
25
|
+
counts: { accepted: targeted.length },
|
|
26
|
+
...(ruleId !== undefined ? { ruleScope: ruleId } : {}),
|
|
27
|
+
}, { rulesetIrHash: computeRulesetIrHash(ruleset) });
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
export async function baselinePrune(rootDir, telemetryPreConfig) {
|
|
31
|
+
const { ruleset, excludes, hostRules, telemetry } = await loadConfig(rootDir);
|
|
32
|
+
const store = new InMemoryBaselineStore(readBaseline(rootDir));
|
|
33
|
+
const { orchestrator } = createOrchestrator(ruleset, [], hostRules);
|
|
34
|
+
const run = await orchestrator.check({ rootDir, excludes });
|
|
35
|
+
const allViolations = run.gates.flatMap((g) => g.violations);
|
|
36
|
+
const result = store.prune({ nodes: [], edges: [], externalNodes: [], externalEdges: [], uncertain: [], scannedAt: Date.now() }, allViolations);
|
|
37
|
+
writeBaseline(rootDir, store.snapshot());
|
|
38
|
+
console.log(`Pruned ${result.removed.length} fixed violation(s) from the baseline; ` +
|
|
39
|
+
`${result.moved.length} ${result.moved.length === 1 ? 'entry' : 'entries'} transferred (file moves).`);
|
|
40
|
+
const recorder = createTelemetryRecorder(rootDir, 'baseline prune', telemetryPreConfig, telemetry);
|
|
41
|
+
recorder.record({ kind: 'baseline', action: 'prune', counts: { removed: result.removed.length, moved: result.moved.length } }, { rulesetIrHash: computeRulesetIrHash(ruleset) });
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
export async function baselineShow(rootDir, ruleId) {
|
|
45
|
+
const store = new InMemoryBaselineStore(readBaseline(rootDir));
|
|
46
|
+
const entries = store.show(ruleId === undefined ? undefined : { ruleId: toRuleId(ruleId) });
|
|
47
|
+
if (entries.length === 0) {
|
|
48
|
+
console.log('Baseline is empty.');
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
console.log(` ${entry.file} [${entry.ruleId}] accepted ${new Date(entry.acceptedAt).toISOString()} (${entry.acceptedBy})`);
|
|
53
|
+
}
|
|
54
|
+
console.log(`\n${entries.length} baselined violation(s).`);
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/commands/baseline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-report.d.ts","sourceRoot":"","sources":["../../src/commands/build-report.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA6C9D;AAsBD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAyC5D"}
|