@0xcraft/powershot 1.0.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 +202 -0
- package/README.md +306 -0
- package/dist/agents.js +82 -0
- package/dist/bench.js +179 -0
- package/dist/budget.js +59 -0
- package/dist/bundle.js +173 -0
- package/dist/cache.js +155 -0
- package/dist/cli/agent-command.js +27 -0
- package/dist/cli/app.js +31 -0
- package/dist/cli/args.js +76 -0
- package/dist/cli/bench-command.js +89 -0
- package/dist/cli/dismiss-command.js +42 -0
- package/dist/cli/environment.js +32 -0
- package/dist/cli/reports.js +64 -0
- package/dist/cli/review-command.js +268 -0
- package/dist/cli/session-command.js +62 -0
- package/dist/cli.js +7 -0
- package/dist/config.js +130 -0
- package/dist/delegate.js +84 -0
- package/dist/dismissed.js +130 -0
- package/dist/fspolicy.js +62 -0
- package/dist/git.js +238 -0
- package/dist/ground.js +286 -0
- package/dist/judges/judge.js +85 -0
- package/dist/judges/llm.js +234 -0
- package/dist/judges/prompts.js +86 -0
- package/dist/judges/tools.js +125 -0
- package/dist/lang/packs.js +557 -0
- package/dist/lang/pyright.js +108 -0
- package/dist/lang/python-deps.js +174 -0
- package/dist/lang/ruby-deps.js +77 -0
- package/dist/langtest.js +248 -0
- package/dist/manifest.js +209 -0
- package/dist/otel.js +75 -0
- package/dist/package-meta.js +13 -0
- package/dist/package-smoke.js +110 -0
- package/dist/plan.js +134 -0
- package/dist/position.js +94 -0
- package/dist/report/ansi.js +18 -0
- package/dist/report/codequality.js +19 -0
- package/dist/report/compact.js +15 -0
- package/dist/report/highlight.js +54 -0
- package/dist/report/markdown.js +113 -0
- package/dist/report/sarif.js +66 -0
- package/dist/report/terminal.js +170 -0
- package/dist/report/viewer.js +148 -0
- package/dist/review.js +355 -0
- package/dist/scan.js +67 -0
- package/dist/selftest.js +1928 -0
- package/dist/session.js +140 -0
- package/dist/snapshot.js +101 -0
- package/dist/text.js +50 -0
- package/dist/types.js +2 -0
- package/dist/verifiers/assertion-drift.js +137 -0
- package/dist/verifiers/contract-drift.js +140 -0
- package/dist/verifiers/copy-paste-drift.js +106 -0
- package/dist/verifiers/dead-on-arrival.js +92 -0
- package/dist/verifiers/dropped-guard.js +144 -0
- package/dist/verifiers/foreign-contract-drift.js +114 -0
- package/dist/verifiers/foreign-copy-paste-drift.js +83 -0
- package/dist/verifiers/foreign-dropped-guard.js +78 -0
- package/dist/verifiers/foreign-phantom-api.js +36 -0
- package/dist/verifiers/foreign-phantom-config.js +40 -0
- package/dist/verifiers/foreign-phantom-dep.js +82 -0
- package/dist/verifiers/foreign-reinvented.js +65 -0
- package/dist/verifiers/foreign-scope-creep.js +42 -0
- package/dist/verifiers/foreign-swallowed-error.js +36 -0
- package/dist/verifiers/foreign-tests.js +143 -0
- package/dist/verifiers/foreign-tokens.js +94 -0
- package/dist/verifiers/foreign.js +16 -0
- package/dist/verifiers/index.js +38 -0
- package/dist/verifiers/lying-comment.js +90 -0
- package/dist/verifiers/phantom-api.js +88 -0
- package/dist/verifiers/phantom-config.js +93 -0
- package/dist/verifiers/phantom-dep.js +110 -0
- package/dist/verifiers/reinvented.js +74 -0
- package/dist/verifiers/scope-creep.js +77 -0
- package/dist/verifiers/swallowed-error.js +110 -0
- package/dist/verifiers/vacuous-test.js +138 -0
- package/docs/architecture.md +191 -0
- package/docs/assets/cli-preview.svg +68 -0
- package/docs/assets/powershot-logo.png +0 -0
- package/docs/ci.md +151 -0
- package/examples/github-actions/action.yml +23 -0
- package/examples/github-actions/cli.yml +43 -0
- package/examples/gitlab/.gitlab-ci.yml +21 -0
- package/package.json +65 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
PowerShot is a single-package review engine with a small CLI adapter. The package is
|
|
4
|
+
split by runtime responsibility, not by hypothetical deployment boundaries: no module
|
|
5
|
+
below is independently versioned or consumed.
|
|
6
|
+
|
|
7
|
+
## System shape
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
flowchart LR
|
|
11
|
+
subgraph adapters["Delivery adapters"]
|
|
12
|
+
CLI["CLI commands"]
|
|
13
|
+
CI["CI workflows"]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
subgraph engine["Review engine"]
|
|
17
|
+
SNAP["Target snapshot"]
|
|
18
|
+
GROUND["Ground<br/>parsers · types · symbols"]
|
|
19
|
+
PLAN["Selection plan<br/>per-file capabilities"]
|
|
20
|
+
VERIFY["Deterministic verifiers"]
|
|
21
|
+
BUNDLE["Review bundles"]
|
|
22
|
+
JUDGE["Optional judges"]
|
|
23
|
+
MANIFEST["Run manifest"]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
subgraph outputs["Output adapters"]
|
|
27
|
+
TERM["Terminal"]
|
|
28
|
+
MD["Markdown"]
|
|
29
|
+
SARIF["SARIF"]
|
|
30
|
+
JSON["JSON · manifest"]
|
|
31
|
+
CQ["GitLab Code Quality"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
CLI --> SNAP
|
|
35
|
+
CI --> CLI
|
|
36
|
+
SNAP --> GROUND --> PLAN
|
|
37
|
+
PLAN --> VERIFY --> MANIFEST
|
|
38
|
+
PLAN -. when enabled .-> BUNDLE --> JUDGE --> MANIFEST
|
|
39
|
+
MANIFEST --> TERM
|
|
40
|
+
MANIFEST --> MD
|
|
41
|
+
MANIFEST --> SARIF
|
|
42
|
+
MANIFEST --> JSON
|
|
43
|
+
MANIFEST --> CQ
|
|
44
|
+
|
|
45
|
+
classDef adapter fill:#172033,stroke:#57a6ff,color:#f0f6fc,stroke-width:2px
|
|
46
|
+
classDef core fill:#241b20,stroke:#ff675c,color:#f0f6fc,stroke-width:2px
|
|
47
|
+
classDef output fill:#17251f,stroke:#4ac58b,color:#f0f6fc,stroke-width:2px
|
|
48
|
+
class CLI,CI adapter
|
|
49
|
+
class SNAP,GROUND,PLAN,VERIFY,BUNDLE,JUDGE,MANIFEST core
|
|
50
|
+
class TERM,MD,SARIF,JSON,CQ output
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The dependency direction is inward. Delivery and report adapters depend on the review
|
|
54
|
+
engine. The engine does not depend on a workflow provider or terminal layout.
|
|
55
|
+
|
|
56
|
+
## Module boundaries
|
|
57
|
+
|
|
58
|
+
| Module | Owns | Does not own |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `src/cli/` | Argument parsing, command dispatch, report publication, exit mapping | Review algorithms |
|
|
61
|
+
| `src/review.ts` | One review run and its stage orchestration | CLI parsing or presentation |
|
|
62
|
+
| `src/ground.ts` | TypeScript project, parse trees, manifests, symbol index | Check selection |
|
|
63
|
+
| `src/plan.ts` | File selection and per-file capability accounting | Finding generation |
|
|
64
|
+
| `src/manifest.ts` | Completion state and the authoritative run record | Rendering |
|
|
65
|
+
| `src/verifiers/` | Deterministic check implementations | Model calls |
|
|
66
|
+
| `src/judges/` | Prompt data, bounded model loop, tool adapter | Git target selection |
|
|
67
|
+
| `src/lang/` | Language-pack data and optional language oracles | Cross-run policy |
|
|
68
|
+
| `src/report/` | Pure output adapters | Re-running or reinterpreting a review |
|
|
69
|
+
| `src/bench.ts` | Historical and labelled evaluation | Production command dispatch |
|
|
70
|
+
| `src/session.ts`, `src/cache.ts` | Reuse of completed judge work | Completion decisions |
|
|
71
|
+
|
|
72
|
+
`src/cli.ts` contains only the executable boundary. It delegates to `src/cli/app.ts`,
|
|
73
|
+
which routes a command to a deeper module. This keeps a new output format or maintenance
|
|
74
|
+
command from increasing the dependency fan-out of the executable.
|
|
75
|
+
|
|
76
|
+
### Import discipline
|
|
77
|
+
|
|
78
|
+
Use `./` while the dependency is reachable without leaving the current directory. Use
|
|
79
|
+
`#app/*.js` instead of `../` traversal. The alias is a native Node.js package import
|
|
80
|
+
mapped to `dist`, so TypeScript, the built CLI, and the installed npm package share one
|
|
81
|
+
resolution rule. Keep `node:*` for the standard library and package names for external
|
|
82
|
+
dependencies. The self-test checks every TypeScript source recursively and rejects
|
|
83
|
+
static, side-effect, dynamic, and CommonJS parent imports.
|
|
84
|
+
|
|
85
|
+
## Runtime sequence
|
|
86
|
+
|
|
87
|
+
```mermaid
|
|
88
|
+
sequenceDiagram
|
|
89
|
+
autonumber
|
|
90
|
+
actor User as Developer / CI
|
|
91
|
+
participant CLI as CLI adapter
|
|
92
|
+
participant Git as Target snapshot
|
|
93
|
+
participant Ground as Ground
|
|
94
|
+
participant Plan as Selection plan
|
|
95
|
+
participant Verify as Verifiers
|
|
96
|
+
participant Judge as Judges
|
|
97
|
+
participant Manifest as Run manifest
|
|
98
|
+
participant Report as Report adapters
|
|
99
|
+
|
|
100
|
+
User->>CLI: psh review
|
|
101
|
+
CLI->>Git: resolve range and target tree
|
|
102
|
+
Git->>Ground: changed files + base content
|
|
103
|
+
Ground->>Plan: parsers, types, references, manifests
|
|
104
|
+
Plan->>Verify: eligible file/check pairs
|
|
105
|
+
Verify-->>Manifest: findings + coverage
|
|
106
|
+
opt model judges enabled
|
|
107
|
+
Plan->>Judge: bounded related-file bundles
|
|
108
|
+
Judge-->>Manifest: judged findings + usage
|
|
109
|
+
end
|
|
110
|
+
Manifest->>Manifest: compute complete / partial / failed
|
|
111
|
+
Manifest->>Report: one result, many formats
|
|
112
|
+
Report-->>User: terminal, Markdown, SARIF, JSON
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Load-bearing invariants
|
|
116
|
+
|
|
117
|
+
### One target tree
|
|
118
|
+
|
|
119
|
+
A branch or commit review reads source from the target revision, not from whatever is
|
|
120
|
+
currently present in the working directory. Grounding, verification, bundling, and
|
|
121
|
+
positioning all receive the same tree.
|
|
122
|
+
|
|
123
|
+
### Capabilities belong to files
|
|
124
|
+
|
|
125
|
+
A run can contain a typed TypeScript file beside a Python file or a TypeScript file
|
|
126
|
+
excluded from `tsconfig`. Capabilities therefore live on each selected file. A checker
|
|
127
|
+
available somewhere in the run is not evidence that it inspected every file.
|
|
128
|
+
|
|
129
|
+
### The manifest owns completion
|
|
130
|
+
|
|
131
|
+
Findings alone cannot distinguish a clean review from an interrupted or unsupported
|
|
132
|
+
one. `RunManifest` accounts for selected files, executed checks, judge units, failures,
|
|
133
|
+
limits, and skips. Renderers and the CLI consume that state instead of deriving their
|
|
134
|
+
own verdict.
|
|
135
|
+
|
|
136
|
+
```mermaid
|
|
137
|
+
stateDiagram-v2
|
|
138
|
+
[*] --> Selected
|
|
139
|
+
Selected --> Complete: every file and unit accounted for
|
|
140
|
+
Selected --> Partial: capability, budget, or cancellation gap
|
|
141
|
+
Selected --> Failed: required stage or file failed
|
|
142
|
+
Complete --> Exit0: no findings
|
|
143
|
+
Complete --> Exit1: findings
|
|
144
|
+
Partial --> Exit3
|
|
145
|
+
Failed --> Exit3
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### One run, many reports
|
|
149
|
+
|
|
150
|
+
`--report` fans one review result into multiple adapters. CI should never execute the
|
|
151
|
+
review once for SARIF and again for Markdown: optional judges can answer differently,
|
|
152
|
+
and duplicated runs waste both time and tokens.
|
|
153
|
+
|
|
154
|
+
## Extension paths
|
|
155
|
+
|
|
156
|
+
### Add a deterministic verifier
|
|
157
|
+
|
|
158
|
+
1. Implement the `Verifier` contract in `src/verifiers/`.
|
|
159
|
+
2. Declare its domain and required capabilities.
|
|
160
|
+
3. Export one unique id from `src/verifiers/index.ts`.
|
|
161
|
+
4. Add positive and negative cases to the self-check suite.
|
|
162
|
+
5. Document the check in the README only after the oracle is exercised by tests.
|
|
163
|
+
|
|
164
|
+
### Add a language pack
|
|
165
|
+
|
|
166
|
+
Add grammar data and conventions in `src/lang/packs.ts`, then add a dedicated fixture
|
|
167
|
+
to `src/langtest.ts`. Language packs run in separate processes so all grammars remain
|
|
168
|
+
covered without sharing one unbounded WASM heap.
|
|
169
|
+
|
|
170
|
+
### Add a report format
|
|
171
|
+
|
|
172
|
+
Implement a pure function in `src/report/`, register it in `src/cli/reports.ts`, and
|
|
173
|
+
test its consumer contract. A renderer receives findings and the manifest; it does not
|
|
174
|
+
call the review engine.
|
|
175
|
+
|
|
176
|
+
### Add a CLI command
|
|
177
|
+
|
|
178
|
+
Place command behavior under `src/cli/` and add one dispatch line to `app.ts`. If the
|
|
179
|
+
command needs review results, use the existing review command interface rather than
|
|
180
|
+
importing individual verifiers into the executable.
|
|
181
|
+
|
|
182
|
+
## Verification strategy
|
|
183
|
+
|
|
184
|
+
- `npm run build` checks module contracts with strict TypeScript.
|
|
185
|
+
- `npm test` runs core self-checks and every enabled language pack.
|
|
186
|
+
- `npm run smoke` packs, installs, and exercises the public binary in a clean project.
|
|
187
|
+
- `node dist/cli.js review --verify-only` reviews the working diff through PowerShot's
|
|
188
|
+
own deterministic pipeline.
|
|
189
|
+
|
|
190
|
+
See [CI integration](ci.md) for the delivery contract and
|
|
191
|
+
[CONTRIBUTING.md](../CONTRIBUTING.md) for the change workflow.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="680" viewBox="0 0 1200 680" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">PowerShot CLI reporting a swallowed error</title>
|
|
3
|
+
<desc id="desc">A deterministic PowerShot scan reports one proven high-severity finding in an empty catch block.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<linearGradient id="canvas" x1="0" y1="0" x2="1" y2="1">
|
|
6
|
+
<stop offset="0" stop-color="#080b12"/>
|
|
7
|
+
<stop offset="1" stop-color="#111827"/>
|
|
8
|
+
</linearGradient>
|
|
9
|
+
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
|
10
|
+
<feDropShadow dx="0" dy="18" stdDeviation="24" flood-color="#000000" flood-opacity="0.42"/>
|
|
11
|
+
</filter>
|
|
12
|
+
<style>
|
|
13
|
+
.mono { font-family: "SFMono-Regular", "Cascadia Code", "Liberation Mono", Consolas, monospace; }
|
|
14
|
+
.muted { fill: #778196; }
|
|
15
|
+
.body { fill: #d9e0ea; }
|
|
16
|
+
.bright { fill: #f6f8fb; }
|
|
17
|
+
.red { fill: #ff5a62; }
|
|
18
|
+
.gold { fill: #ffc857; }
|
|
19
|
+
.steel { fill: #7dd3fc; }
|
|
20
|
+
.green { fill: #61d095; }
|
|
21
|
+
</style>
|
|
22
|
+
</defs>
|
|
23
|
+
|
|
24
|
+
<rect width="1200" height="680" rx="28" fill="url(#canvas)"/>
|
|
25
|
+
<rect x="44" y="38" width="1112" height="604" rx="18" fill="#0c111b" stroke="#242d3c" filter="url(#shadow)"/>
|
|
26
|
+
<path d="M44 94H1156" stroke="#242d3c"/>
|
|
27
|
+
<circle cx="78" cy="66" r="7" fill="#ff5f57"/>
|
|
28
|
+
<circle cx="102" cy="66" r="7" fill="#febc2e"/>
|
|
29
|
+
<circle cx="126" cy="66" r="7" fill="#28c840"/>
|
|
30
|
+
<text x="600" y="72" text-anchor="middle" class="mono muted" font-size="16">powershot — deterministic review</text>
|
|
31
|
+
|
|
32
|
+
<g class="mono" font-size="18">
|
|
33
|
+
<text x="78" y="132" class="green">$</text>
|
|
34
|
+
<text x="102" y="132" class="bright">psh scan src/example.ts --verify-only</text>
|
|
35
|
+
|
|
36
|
+
<text x="78" y="178" class="muted">◇ ground</text>
|
|
37
|
+
<text x="200" y="178" class="body">1 file · 1 symbol</text>
|
|
38
|
+
<text x="78" y="208" class="muted">◇ verify</text>
|
|
39
|
+
<text x="200" y="208" class="body">1 check × 1 file · 0 tokens</text>
|
|
40
|
+
|
|
41
|
+
<text x="78" y="256" class="bright" font-weight="700">PowerShot</text>
|
|
42
|
+
<text x="196" y="256" class="muted">· scan src/example.ts</text>
|
|
43
|
+
<path d="M78 278H1122" stroke="#2b3444"/>
|
|
44
|
+
|
|
45
|
+
<text x="78" y="318" class="steel">src/example.ts</text>
|
|
46
|
+
<text x="78" y="360" class="gold" font-weight="700">▣ HIGH</text>
|
|
47
|
+
<text x="174" y="360" class="body">swallowed-error ·</text>
|
|
48
|
+
<text x="388" y="360" class="red">proven</text>
|
|
49
|
+
|
|
50
|
+
<text x="106" y="397" class="bright">Empty catch block — the failure is discarded and the caller is</text>
|
|
51
|
+
<text x="106" y="425" class="bright">told it succeeded</text>
|
|
52
|
+
|
|
53
|
+
<text x="106" y="470" class="muted">3 │</text>
|
|
54
|
+
<text x="164" y="470" class="body">return JSON.parse(input)</text>
|
|
55
|
+
<text x="78" y="500" class="red">❯</text>
|
|
56
|
+
<text x="106" y="500" class="muted">4 │</text>
|
|
57
|
+
<text x="164" y="500" class="bright">} catch {</text>
|
|
58
|
+
<text x="252" y="527" class="red">^</text>
|
|
59
|
+
<text x="106" y="550" class="muted">5 │</text>
|
|
60
|
+
<text x="164" y="550" class="body">}</text>
|
|
61
|
+
<text x="164" y="583" class="muted">╰ AST: catch body contains no statements</text>
|
|
62
|
+
|
|
63
|
+
<path d="M78 605H1122" stroke="#2b3444"/>
|
|
64
|
+
<text x="78" y="630" class="bright">1 finding</text>
|
|
65
|
+
<text x="204" y="630" class="gold">1 high</text>
|
|
66
|
+
<text x="290" y="630" class="muted">· 1 verified · 0 judged</text>
|
|
67
|
+
</g>
|
|
68
|
+
</svg>
|
|
Binary file
|
package/docs/ci.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# CI integration
|
|
2
|
+
|
|
3
|
+
PowerShot has two CI surfaces:
|
|
4
|
+
|
|
5
|
+
- the composite GitHub Action for the shortest GitHub setup;
|
|
6
|
+
- the `psh` CLI for GitHub, GitLab, and any runner that can execute Node.js 24.
|
|
7
|
+
|
|
8
|
+
Use the composite action for GitHub-native reporting or install the versioned npm
|
|
9
|
+
package when the CLI needs to fit into another CI system.
|
|
10
|
+
|
|
11
|
+
## Choose a policy first
|
|
12
|
+
|
|
13
|
+
There are two independent decisions:
|
|
14
|
+
|
|
15
|
+
1. Must the review complete? Usually yes. Exit `2` or `3` should always fail a gate.
|
|
16
|
+
2. Do findings block the change? Set this per repository. Exit `1` is a complete
|
|
17
|
+
verdict with findings, not an engine failure.
|
|
18
|
+
|
|
19
|
+
| Exit | Meaning | Recommended CI handling |
|
|
20
|
+
|---:|---|---|
|
|
21
|
+
| `0` | Complete, no findings | Pass |
|
|
22
|
+
| `1` | Complete, findings reported | Pass or fail according to repository policy |
|
|
23
|
+
| `2` | Invalid command or Git input | Fail |
|
|
24
|
+
| `3` | Partial or failed review | Fail |
|
|
25
|
+
| `130` | Interrupted | Fail or retry |
|
|
26
|
+
|
|
27
|
+
## GitHub Action
|
|
28
|
+
|
|
29
|
+
The action runs one review, adds a job summary, uploads SARIF, and can maintain one
|
|
30
|
+
pull-request comment.
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
name: PowerShot
|
|
34
|
+
|
|
35
|
+
on:
|
|
36
|
+
pull_request:
|
|
37
|
+
|
|
38
|
+
permissions:
|
|
39
|
+
contents: read
|
|
40
|
+
pull-requests: write
|
|
41
|
+
security-events: write
|
|
42
|
+
|
|
43
|
+
jobs:
|
|
44
|
+
review:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v7
|
|
48
|
+
with:
|
|
49
|
+
fetch-depth: 0
|
|
50
|
+
|
|
51
|
+
- uses: xcrft/powershot@v1
|
|
52
|
+
with:
|
|
53
|
+
verify-only: 'true'
|
|
54
|
+
comment: 'true'
|
|
55
|
+
fail-on-findings: 'true'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The major tag follows compatible `1.x` releases. Pin a full commit SHA in a protected
|
|
59
|
+
required workflow when immutable dependencies are required. The copy-paste version
|
|
60
|
+
lives at [`examples/github-actions/action.yml`](../examples/github-actions/action.yml).
|
|
61
|
+
|
|
62
|
+
### Action inputs
|
|
63
|
+
|
|
64
|
+
| Input | Default | Purpose |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `verify-only` | `true` | Run deterministic checks without a model |
|
|
67
|
+
| `anthropic-api-key` | empty | Enable configured model judges |
|
|
68
|
+
| `min-severity` | `low` | Filter findings below a severity |
|
|
69
|
+
| `checks` | empty | Select comma-separated check ids |
|
|
70
|
+
| `comment` | `true` | Maintain a pull-request comment |
|
|
71
|
+
| `fail-on-findings` | `false` | Turn a complete finding verdict into a failed job |
|
|
72
|
+
| `approve` | `false` | Approve only a complete, clean review |
|
|
73
|
+
|
|
74
|
+
The outputs are `findings` and `complete`. `complete` is the important one when a
|
|
75
|
+
later job decides whether to publish or deploy.
|
|
76
|
+
|
|
77
|
+
## Direct CLI on GitHub Actions
|
|
78
|
+
|
|
79
|
+
Use the CLI when the workflow needs custom artifact handling or when PowerShot is one
|
|
80
|
+
step inside a larger quality job. The complete example is
|
|
81
|
+
[`examples/github-actions/cli.yml`](../examples/github-actions/cli.yml).
|
|
82
|
+
|
|
83
|
+
The core pattern is:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install --global --ignore-scripts @0xcraft/powershot@1.0.0
|
|
87
|
+
|
|
88
|
+
STATUS=0
|
|
89
|
+
psh review --verify-only \
|
|
90
|
+
--from "$BASE" --to HEAD \
|
|
91
|
+
--report sarif=powershot.sarif \
|
|
92
|
+
--report markdown=powershot.md \
|
|
93
|
+
--format compact || STATUS=$?
|
|
94
|
+
|
|
95
|
+
case "$STATUS" in
|
|
96
|
+
0) exit 0 ;;
|
|
97
|
+
1) exit 1 ;; # change to 0 when findings are advisory
|
|
98
|
+
*) exit "$STATUS" ;;
|
|
99
|
+
esac
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Do not pipe the review directly into another command without preserving its exit code.
|
|
103
|
+
Do not run once per report: repeated `--report` flags create all artifacts from the
|
|
104
|
+
same verdict.
|
|
105
|
+
|
|
106
|
+
## GitLab Code Quality
|
|
107
|
+
|
|
108
|
+
The CLI's `codequality` format is accepted by GitLab's Code Quality report. Use a full
|
|
109
|
+
clone or fetch both merge-request SHAs, then preserve the CLI status separately from
|
|
110
|
+
the redirected report:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
STATUS=0
|
|
114
|
+
node "$POWERSHOT/dist/cli.js" review --verify-only \
|
|
115
|
+
--from "$CI_MERGE_REQUEST_DIFF_BASE_SHA" \
|
|
116
|
+
--to "$CI_COMMIT_SHA" \
|
|
117
|
+
--format codequality > gl-code-quality-report.json || STATUS=$?
|
|
118
|
+
|
|
119
|
+
test "$STATUS" -le 1 || exit "$STATUS"
|
|
120
|
+
test "$STATUS" -eq 0
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
See [`examples/gitlab/.gitlab-ci.yml`](../examples/gitlab/.gitlab-ci.yml) for the job
|
|
124
|
+
and artifact declaration.
|
|
125
|
+
|
|
126
|
+
## CI checklist
|
|
127
|
+
|
|
128
|
+
- Use Node.js 24 or newer.
|
|
129
|
+
- Fetch enough Git history for both endpoints of the review range.
|
|
130
|
+
- Use immutable base and head SHAs where the CI provider exposes them.
|
|
131
|
+
- Run `--verify-only` as the fast required check; add model judges only where their
|
|
132
|
+
cost and latency are intentional.
|
|
133
|
+
- Treat exit `2`, `3`, and `130` as infrastructure or completeness failures.
|
|
134
|
+
- Generate every report from one invocation.
|
|
135
|
+
- Publish the Markdown report for humans and SARIF or Code Quality for annotations.
|
|
136
|
+
- Pin the PowerShot source version in protected workflows.
|
|
137
|
+
- Keep `powershot.config.json` in review so policy changes are visible.
|
|
138
|
+
|
|
139
|
+
## Local parity
|
|
140
|
+
|
|
141
|
+
Run the same deterministic gate before pushing:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
node dist/cli.js review --verify-only --from main --to HEAD
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
For an uncommitted workspace, omit the range:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
node dist/cli.js review --verify-only
|
|
151
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: PowerShot
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
pull-requests: write
|
|
9
|
+
security-events: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
review:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
|
|
19
|
+
- uses: xcrft/powershot@v1
|
|
20
|
+
with:
|
|
21
|
+
verify-only: 'true'
|
|
22
|
+
comment: 'true'
|
|
23
|
+
fail-on-findings: 'true'
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: PowerShot CLI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
review:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v7
|
|
14
|
+
with:
|
|
15
|
+
fetch-depth: 0
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-node@v7
|
|
18
|
+
with:
|
|
19
|
+
node-version: '24'
|
|
20
|
+
|
|
21
|
+
- name: Install PowerShot
|
|
22
|
+
run: npm install --global --ignore-scripts @0xcraft/powershot@1.0.0
|
|
23
|
+
|
|
24
|
+
- name: Review pull request
|
|
25
|
+
env:
|
|
26
|
+
BASE: origin/${{ github.base_ref }}
|
|
27
|
+
run: |
|
|
28
|
+
STATUS=0
|
|
29
|
+
psh review --verify-only \
|
|
30
|
+
--from "$BASE" --to HEAD \
|
|
31
|
+
--report markdown=powershot.md \
|
|
32
|
+
--report sarif=powershot.sarif \
|
|
33
|
+
--format compact || STATUS=$?
|
|
34
|
+
if [ -f powershot.md ]; then
|
|
35
|
+
cat powershot.md >> "$GITHUB_STEP_SUMMARY"
|
|
36
|
+
else
|
|
37
|
+
echo "PowerShot produced no Markdown report. Exit: $STATUS." >> "$GITHUB_STEP_SUMMARY"
|
|
38
|
+
fi
|
|
39
|
+
case "$STATUS" in
|
|
40
|
+
0) exit 0 ;;
|
|
41
|
+
1) exit 1 ;;
|
|
42
|
+
*) exit "$STATUS" ;;
|
|
43
|
+
esac
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
powershot:
|
|
2
|
+
image: node:24
|
|
3
|
+
rules:
|
|
4
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
5
|
+
variables:
|
|
6
|
+
GIT_DEPTH: "0"
|
|
7
|
+
before_script:
|
|
8
|
+
- npm install --global --ignore-scripts @0xcraft/powershot@1.0.0
|
|
9
|
+
script:
|
|
10
|
+
- |
|
|
11
|
+
STATUS=0
|
|
12
|
+
psh review --verify-only \
|
|
13
|
+
--from "$CI_MERGE_REQUEST_DIFF_BASE_SHA" \
|
|
14
|
+
--to "$CI_COMMIT_SHA" \
|
|
15
|
+
--format codequality > gl-code-quality-report.json || STATUS=$?
|
|
16
|
+
test "$STATUS" -le 1 || exit "$STATUS"
|
|
17
|
+
test "$STATUS" -eq 0
|
|
18
|
+
artifacts:
|
|
19
|
+
when: always
|
|
20
|
+
reports:
|
|
21
|
+
codequality: gl-code-quality-report.json
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@0xcraft/powershot",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Oracle-first code review for machine-written code, with deterministic verification and CI-ready reports.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "aglumova <alina.glumova@gmail.com>",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"ai",
|
|
9
|
+
"ci",
|
|
10
|
+
"cli",
|
|
11
|
+
"code-review",
|
|
12
|
+
"developer-tools",
|
|
13
|
+
"github-actions",
|
|
14
|
+
"static-analysis",
|
|
15
|
+
"typescript"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/xcrft/powershot.git"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/xcrft/powershot#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/xcrft/powershot/issues"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"imports": {
|
|
27
|
+
"#app/*.js": "./dist/*.js"
|
|
28
|
+
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"powershot": "dist/cli.js",
|
|
31
|
+
"psh": "dist/cli.js"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=24"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist/**/*.js",
|
|
41
|
+
"docs/**/*.md",
|
|
42
|
+
"docs/assets/*",
|
|
43
|
+
"examples/**/*",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
49
|
+
"build": "tsc",
|
|
50
|
+
"dev": "tsc --watch",
|
|
51
|
+
"test": "npm run build && node dist/selftest.js && node dist/langtest.js",
|
|
52
|
+
"psh": "npm run build && node dist/cli.js",
|
|
53
|
+
"prepack": "npm run clean && npm run build && npm test",
|
|
54
|
+
"smoke": "node dist/package-smoke.js"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"ts-morph": "^23.0.0",
|
|
58
|
+
"web-tree-sitter": "0.22.6",
|
|
59
|
+
"tree-sitter-wasms": "0.1.13"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^20.14.0",
|
|
63
|
+
"typescript": "^5.5.0"
|
|
64
|
+
}
|
|
65
|
+
}
|