@0xcraft/powershot 1.1.0 → 1.1.2
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 +43 -18
- package/dist/cli/reports.js +3 -0
- package/dist/cli/review-command.js +8 -2
- package/dist/cli/session-command.js +2 -0
- package/dist/config.js +5 -0
- package/dist/ground.js +297 -95
- package/dist/judges/tools.js +7 -7
- package/dist/lang/packs.js +97 -14
- package/dist/lang/parse-worker.js +15 -0
- package/dist/lang/python-deps.js +21 -8
- package/dist/manifest.js +49 -0
- package/dist/plan.js +7 -0
- package/dist/report/markdown.js +18 -2
- package/dist/report/terminal.js +12 -1
- package/dist/report/viewer.js +11 -1
- package/dist/review.js +43 -20
- package/dist/selftest.js +402 -8
- package/dist/session.js +2 -0
- package/dist/verifiers/foreign-phantom-dep.js +8 -2
- package/dist/verifiers/phantom-config.js +7 -6
- package/docs/architecture.md +52 -12
- package/docs/ci.md +21 -4
- package/examples/github-actions/cli.yml +1 -1
- package/examples/gitlab/.gitlab-ci.yml +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,13 +20,14 @@ PowerShot reviews the failure modes that plausible-looking generated code tends
|
|
|
20
20
|
hide: invented APIs, undeclared dependencies, dropped guards, swallowed errors,
|
|
21
21
|
tests that prove nothing, bent expectations, stale callers, and duplicated helpers.
|
|
22
22
|
|
|
23
|
-
It asks
|
|
24
|
-
|
|
23
|
+
It asks self-contained parsers, manifests, and pre/post ASTs first, then uses compiler
|
|
24
|
+
types and reference graphs when the environment can supply them. Optional model judges
|
|
25
|
+
only handle questions that still require judgement.
|
|
25
26
|
|
|
26
27
|
<table>
|
|
27
28
|
<tr>
|
|
28
29
|
<td width="33%"><strong>Deterministic first</strong><br>Local checks need no model, key, tokens, or network calls.</td>
|
|
29
|
-
<td width="33%"><strong>Honest
|
|
30
|
+
<td width="33%"><strong>Honest coverage</strong><br>Portable, full, partial, and failed runs stay distinguishable.</td>
|
|
30
31
|
<td width="33%"><strong>CI-native output</strong><br>One run emits terminal, Markdown, JSON, SARIF, manifest, and Code Quality reports.</td>
|
|
31
32
|
</tr>
|
|
32
33
|
</table>
|
|
@@ -89,7 +90,7 @@ flowchart LR
|
|
|
89
90
|
|
|
90
91
|
1. **Snapshot** resolves the exact tree the review is about.
|
|
91
92
|
2. **Ground** builds the available type, syntax, dependency, and reference oracles.
|
|
92
|
-
3. **Plan** assigns checks and
|
|
93
|
+
3. **Plan** assigns baseline checks and enriched semantic capabilities to each file individually.
|
|
93
94
|
4. **Verify** runs deterministic checks and records what actually executed.
|
|
94
95
|
5. **Judge** optionally reviews bounded bundles of related files.
|
|
95
96
|
6. **Manifest** decides whether the result is complete, partial, or failed.
|
|
@@ -108,8 +109,11 @@ Every finding says where it came from:
|
|
|
108
109
|
| `verified` | `firm` | A deterministic heuristic fired; inspect the evidence |
|
|
109
110
|
| `judged` | `firm` or `tentative` | A model supplied the judgement and provenance |
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
Portable coverage is the default: self-contained oracles run without bootstrapping the
|
|
113
|
+
reviewed repository, while unavailable compiler/reference depth stays visible in the
|
|
114
|
+
manifest and reports. Set `"coverage": "strict"`, or explicitly select a check with
|
|
115
|
+
`--checks`, when a missing semantic oracle must make the run partial. An unavailable
|
|
116
|
+
oracle is never counted as a pass in either profile.
|
|
113
117
|
|
|
114
118
|
## Deterministic checks
|
|
115
119
|
|
|
@@ -174,33 +178,38 @@ commands.
|
|
|
174
178
|
|
|
175
179
|
```mermaid
|
|
176
180
|
flowchart LR
|
|
177
|
-
START["Selected files and checks"] --> ACCOUNT{"
|
|
178
|
-
ACCOUNT -- "yes" -->
|
|
181
|
+
START["Selected files and checks"] --> ACCOUNT{"Required work accounted for?"}
|
|
182
|
+
ACCOUNT -- "yes" --> DEPTH{"Enriched semantic depth available?"}
|
|
183
|
+
DEPTH -- "yes" --> FULL["full coverage"]
|
|
184
|
+
DEPTH -- "no · portable policy" --> PORTABLE["portable coverage · gaps named"]
|
|
185
|
+
FULL --> FINDINGS{"Findings?"}
|
|
186
|
+
PORTABLE --> FINDINGS
|
|
179
187
|
FINDINGS -- "no" --> CLEAN["exit 0 · complete and clean"]
|
|
180
188
|
FINDINGS -- "yes" --> FOUND["exit 1 · complete with findings"]
|
|
181
|
-
ACCOUNT -- "
|
|
189
|
+
ACCOUNT -- "required oracle or budget gap" --> PARTIAL["exit 3 · partial"]
|
|
182
190
|
ACCOUNT -- "required stage failed" --> FAILED["exit 3 · failed"]
|
|
183
191
|
|
|
184
192
|
classDef neutral fill:#172033,stroke:#57a6ff,color:#f0f6fc,stroke-width:2px
|
|
185
193
|
classDef good fill:#17251f,stroke:#4ac58b,color:#f0f6fc,stroke-width:2px
|
|
186
194
|
classDef warn fill:#2a2117,stroke:#f2b84b,color:#f0f6fc,stroke-width:2px
|
|
187
195
|
classDef bad fill:#2a191b,stroke:#ff675c,color:#f0f6fc,stroke-width:2px
|
|
188
|
-
class START,ACCOUNT,FINDINGS neutral
|
|
196
|
+
class START,ACCOUNT,DEPTH,FINDINGS neutral
|
|
189
197
|
class CLEAN good
|
|
190
|
-
class FOUND,PARTIAL warn
|
|
198
|
+
class FULL,PORTABLE,FOUND,PARTIAL warn
|
|
191
199
|
class FAILED bad
|
|
192
200
|
```
|
|
193
201
|
|
|
194
202
|
| Exit | Contract |
|
|
195
203
|
|---:|---|
|
|
196
|
-
| `0` | Review completed and found nothing at the selected severity |
|
|
197
|
-
| `1` | Review completed and reported findings |
|
|
204
|
+
| `0` | Review completed in full or portable coverage and found nothing at the selected severity |
|
|
205
|
+
| `1` | Review completed in full or portable coverage and reported findings |
|
|
198
206
|
| `2` | Command or Git input was invalid |
|
|
199
207
|
| `3` | Review is incomplete; findings may be missing |
|
|
200
208
|
|
|
201
|
-
“No findings
|
|
202
|
-
outcomes. Use `--format manifest` to inspect file
|
|
203
|
-
|
|
209
|
+
“No findings in portable coverage”, “No findings”, and “PowerShot could not look” are
|
|
210
|
+
intentionally different outcomes. Use `--format manifest` to inspect `coverage`, file
|
|
211
|
+
dispositions, executed and unavailable checks, failures, judge units, and
|
|
212
|
+
`notLookedAt`.
|
|
204
213
|
|
|
205
214
|
## CI integration
|
|
206
215
|
|
|
@@ -220,6 +229,18 @@ The composite action is the shortest setup for GitHub:
|
|
|
220
229
|
fail-on-findings: 'true'
|
|
221
230
|
```
|
|
222
231
|
|
|
232
|
+
The default portable profile needs no install from the checked-out repository. That is
|
|
233
|
+
the safe default for private monorepos and fork pull requests: do not expose a package
|
|
234
|
+
registry credential merely to enrich a review of untrusted code. If a trusted job
|
|
235
|
+
already has dependencies, PowerShot uses their TypeScript declarations automatically.
|
|
236
|
+
Set `"coverage": "strict"` when missing compiler/reference oracles must block instead.
|
|
237
|
+
|
|
238
|
+
PowerShot discovers `tsconfig.json` and `tsconfig.*.json` along the ancestor chain of
|
|
239
|
+
each changed file. One review can use several independent package projects, skip
|
|
240
|
+
empty solution configs in favour of their leaf configs, and type-check test files
|
|
241
|
+
that a production config excludes. Discovery is change-scoped: unrelated packages
|
|
242
|
+
and configless source trees are not crawled just to build the TypeScript ground.
|
|
243
|
+
|
|
223
244
|
`@v1` follows compatible `1.x` releases. Pin the action to a full commit SHA in a
|
|
224
245
|
protected required workflow when immutable dependencies are required.
|
|
225
246
|
|
|
@@ -252,6 +273,7 @@ Anthropic, OpenAI, and Gemini providers are supported.
|
|
|
252
273
|
"judges": {
|
|
253
274
|
"enable": ["plausible-logic", "test-adequacy", "intent"]
|
|
254
275
|
},
|
|
276
|
+
"coverage": "portable",
|
|
255
277
|
"minSeverity": "low",
|
|
256
278
|
"ignore": ["**/generated/**"],
|
|
257
279
|
"promptCache": true
|
|
@@ -286,8 +308,11 @@ psh review --verify-only --absorb /tmp/powershot-findings.json
|
|
|
286
308
|
| C | Syntax-backed checks that do not require exception semantics |
|
|
287
309
|
| Solidity | Declared syntax-backed checks |
|
|
288
310
|
|
|
289
|
-
|
|
290
|
-
|
|
311
|
+
Every declared language is parsed in disposable, language-isolated workers. Sources
|
|
312
|
+
are sent in bounded batches, so a mixed-language monorepo does not accumulate every
|
|
313
|
+
compiled WASM grammar in one process. If a declared parser cannot run, the review
|
|
314
|
+
fails loudly; it is never silently waived. All eleven packs plus TypeScript and
|
|
315
|
+
JavaScript are exercised together by the integration suite.
|
|
291
316
|
|
|
292
317
|
## Project guide
|
|
293
318
|
|
package/dist/cli/reports.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { unavailableCoverage } from '#app/manifest.js';
|
|
2
3
|
import { dim } from '#app/report/ansi.js';
|
|
3
4
|
import { codeQuality } from '#app/report/codequality.js';
|
|
4
5
|
import { compact } from '#app/report/compact.js';
|
|
@@ -44,6 +45,8 @@ export function renderReport(format, result, manifest, target) {
|
|
|
44
45
|
...result.stats,
|
|
45
46
|
state: manifest.state,
|
|
46
47
|
notLookedAt: manifest.notLookedAt,
|
|
48
|
+
coverage: manifest.coverage,
|
|
49
|
+
unavailableCoverage: unavailableCoverage(manifest),
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
export function publishReports(options) {
|
|
@@ -5,7 +5,7 @@ import { loadConfig, policyChanged } from '#app/config.js';
|
|
|
5
5
|
import { absorbDelegated, delegateBrief } from '#app/delegate.js';
|
|
6
6
|
import { baseRefOf, checkRange, headSha, repoRoot, shaOf } from '#app/git.js';
|
|
7
7
|
import { JUDGES } from '#app/judges/prompts.js';
|
|
8
|
-
import { RunManifest, coverageProblems, hashOf, writeManifest } from '#app/manifest.js';
|
|
8
|
+
import { RunManifest, coverageProblems, hashOf, unavailableCoverage, writeManifest } from '#app/manifest.js';
|
|
9
9
|
import { Trace } from '#app/otel.js';
|
|
10
10
|
import { PACKAGE_VERSION } from '#app/package-meta.js';
|
|
11
11
|
import { dim, yellow } from '#app/report/ansi.js';
|
|
@@ -225,6 +225,7 @@ export async function runReviewCommand(command, values, positionals) {
|
|
|
225
225
|
},
|
|
226
226
|
files: result.plan?.items() ?? [],
|
|
227
227
|
skippedChecks: result.skippedChecks ?? [],
|
|
228
|
+
unavailableChecks: result.unavailableChecks ?? [],
|
|
228
229
|
findings: {
|
|
229
230
|
total: result.findings.length,
|
|
230
231
|
verified: result.stats.verified,
|
|
@@ -246,7 +247,12 @@ export async function runReviewCommand(command, values, positionals) {
|
|
|
246
247
|
record.notLookedAt.push(failure);
|
|
247
248
|
process.stderr.write(yellow(' ◇ manifest') + dim(' ' + gaps.join('; ')) + '\n');
|
|
248
249
|
}
|
|
249
|
-
session?.saveReport(result.findings, {
|
|
250
|
+
session?.saveReport(result.findings, {
|
|
251
|
+
state: record.state,
|
|
252
|
+
notLookedAt: record.notLookedAt,
|
|
253
|
+
coverage: record.coverage,
|
|
254
|
+
unavailableCoverage: unavailableCoverage(record),
|
|
255
|
+
});
|
|
250
256
|
writeManifest(root, record);
|
|
251
257
|
publishReports({
|
|
252
258
|
format: values.format,
|
|
@@ -42,6 +42,8 @@ export function runSessionCommand(positionals) {
|
|
|
42
42
|
started: session.started,
|
|
43
43
|
state: session.report.state ?? 'unknown',
|
|
44
44
|
notLookedAt: session.report.notLookedAt ?? ['session predates verdict recording'],
|
|
45
|
+
coverage: session.report.coverage,
|
|
46
|
+
unavailableCoverage: session.report.unavailableCoverage,
|
|
45
47
|
}));
|
|
46
48
|
process.stdout.write(output + '\n');
|
|
47
49
|
return 0;
|
package/dist/config.js
CHANGED
|
@@ -9,6 +9,7 @@ const DEFAULTS = {
|
|
|
9
9
|
// security may duplicate SAST; convention needs repository idioms in the diff
|
|
10
10
|
judges: ['plausible-logic', 'test-adequacy', 'intent'],
|
|
11
11
|
minSeverity: 'low',
|
|
12
|
+
coverage: 'portable',
|
|
12
13
|
// findings in vendored trees are not decisions made by this repository
|
|
13
14
|
ignore: [
|
|
14
15
|
'**/node_modules/**', '**/dist/**', '**/build/**', '**/*.generated.*', '**/*.min.js',
|
|
@@ -33,6 +34,7 @@ export function policyChanged(root, baseRef) {
|
|
|
33
34
|
}
|
|
34
35
|
const KEYS = new Set([...Object.keys(DEFAULTS), 'checks']);
|
|
35
36
|
const PROVIDERS = new Set(['anthropic', 'openai', 'gemini']);
|
|
37
|
+
const COVERAGE = new Set(['portable', 'strict']);
|
|
36
38
|
/** A misspelled name in the config is the quietest way to get a clean review. */
|
|
37
39
|
export function validateConfig(raw, known) {
|
|
38
40
|
const problems = [];
|
|
@@ -50,6 +52,9 @@ export function validateConfig(raw, known) {
|
|
|
50
52
|
if (raw.minSeverity !== undefined && !SEVERITIES.includes(raw.minSeverity)) {
|
|
51
53
|
problems.push('minSeverity "' + String(raw.minSeverity) + '" is not one of: ' + SEVERITIES.join(', '));
|
|
52
54
|
}
|
|
55
|
+
if (raw.coverage !== undefined && !COVERAGE.has(String(raw.coverage))) {
|
|
56
|
+
problems.push('coverage "' + String(raw.coverage) + '" is not one of: ' + [...COVERAGE].join(', '));
|
|
57
|
+
}
|
|
53
58
|
for (const [field, names] of [['verifiers', known.verifiers], ['judges', known.judges]]) {
|
|
54
59
|
const value = raw[field];
|
|
55
60
|
if (value === undefined)
|