@haystackeditor/cli 0.15.25 → 0.15.27
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 +39 -3
- package/dist/assets/hooks/package-lock.json +598 -0
- package/dist/assets/skills/map-cloud-verifier-universe/SKILL.md +2051 -0
- package/dist/assets/skills/map-cloud-verifier-universe/agents/openai.yaml +4 -0
- package/dist/assets/skills/map-cloud-verifier-universe/references/output-contract.md +3411 -0
- package/dist/assets/telemetry/browser-runtime.js +861 -0
- package/dist/assets/telemetry/runtime.cjs +1509 -0
- package/dist/commands/cloud-verifier-behaviors.d.ts +27 -0
- package/dist/commands/cloud-verifier-behaviors.js +218 -0
- package/dist/commands/cloud-verifier-data-store-census.d.ts +47 -0
- package/dist/commands/cloud-verifier-data-store-census.js +539 -0
- package/dist/commands/cloud-verifier-data-store-drift.d.ts +42 -0
- package/dist/commands/cloud-verifier-data-store-drift.js +158 -0
- package/dist/commands/cloud-verifier-identity-census.d.ts +88 -0
- package/dist/commands/cloud-verifier-identity-census.js +4057 -0
- package/dist/commands/cloud-verifier-materialization.d.ts +16 -0
- package/dist/commands/cloud-verifier-materialization.js +704 -0
- package/dist/commands/cloud-verifier-pascal-selector-census.d.ts +29 -0
- package/dist/commands/cloud-verifier-pascal-selector-census.js +1382 -0
- package/dist/commands/cloud-verifier-python-manifest-selector-census.d.ts +27 -0
- package/dist/commands/cloud-verifier-python-manifest-selector-census.js +2015 -0
- package/dist/commands/cloud-verifier-specialized-operational-census.d.ts +51 -0
- package/dist/commands/cloud-verifier-specialized-operational-census.js +11432 -0
- package/dist/commands/cloud-verifier-universe.d.ts +31 -0
- package/dist/commands/cloud-verifier-universe.js +10178 -0
- package/dist/commands/design-verify.d.ts +23 -0
- package/dist/commands/design-verify.js +142 -0
- package/dist/commands/hooks.js +2 -2
- package/dist/commands/prepare-universe-review.d.ts +115 -0
- package/dist/commands/prepare-universe-review.js +1092 -0
- package/dist/commands/production-source-deny-policy.d.ts +15 -0
- package/dist/commands/production-source-deny-policy.js +100 -0
- package/dist/commands/scaffold-provisional-universe.d.ts +468 -0
- package/dist/commands/scaffold-provisional-universe.js +808 -0
- package/dist/commands/schema-cmd.js +32 -0
- package/dist/commands/skills.d.ts +2 -2
- package/dist/commands/skills.js +415 -47
- package/dist/commands/telemetry.d.ts +53 -0
- package/dist/commands/telemetry.js +832 -0
- package/dist/commands/verify-hosted-reproducibility.d.ts +6 -2
- package/dist/commands/verify-hosted-reproducibility.js +65 -20
- package/dist/index.js +142 -13
- package/dist/triage/runner.js +7 -6
- package/dist/utils/design-verifier-api.d.ts +129 -0
- package/dist/utils/design-verifier-api.js +175 -0
- package/dist/utils/design-verifier-result.d.ts +52 -0
- package/dist/utils/design-verifier-result.js +299 -0
- package/dist/utils/haystack-api.d.ts +2 -0
- package/dist/utils/haystack-api.js +10 -3
- package/dist/utils/hooks.js +2 -1
- package/package.json +7 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design-verify command — Kick off a blind design-by-execution verification
|
|
3
|
+
* run for a PR.
|
|
4
|
+
*
|
|
5
|
+
* haystack design-verify <pr>
|
|
6
|
+
*
|
|
7
|
+
* Unlike `haystack review` (finding-oriented analysis of the diff), design
|
|
8
|
+
* verification executes the change: it infers intent from the diff alone,
|
|
9
|
+
* designs pre-registered behavioral test cases, runs them against both sides
|
|
10
|
+
* of the PR in isolated sandboxes, and produces a mechanical verdict of
|
|
11
|
+
* unexpected behavior changes.
|
|
12
|
+
*
|
|
13
|
+
* The command waits by default and returns an agent-facing behavioral result.
|
|
14
|
+
* --no-wait preserves the asynchronous trigger-only path.
|
|
15
|
+
*/
|
|
16
|
+
export interface DesignVerifyOptions {
|
|
17
|
+
json?: boolean;
|
|
18
|
+
wait?: boolean;
|
|
19
|
+
run?: string;
|
|
20
|
+
pollInterval?: string;
|
|
21
|
+
timeout?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function designVerifyCommand(identifier: string, options: DesignVerifyOptions): Promise<void>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design-verify command — Kick off a blind design-by-execution verification
|
|
3
|
+
* run for a PR.
|
|
4
|
+
*
|
|
5
|
+
* haystack design-verify <pr>
|
|
6
|
+
*
|
|
7
|
+
* Unlike `haystack review` (finding-oriented analysis of the diff), design
|
|
8
|
+
* verification executes the change: it infers intent from the diff alone,
|
|
9
|
+
* designs pre-registered behavioral test cases, runs them against both sides
|
|
10
|
+
* of the PR in isolated sandboxes, and produces a mechanical verdict of
|
|
11
|
+
* unexpected behavior changes.
|
|
12
|
+
*
|
|
13
|
+
* The command waits by default and returns an agent-facing behavioral result.
|
|
14
|
+
* --no-wait preserves the asynchronous trigger-only path.
|
|
15
|
+
*/
|
|
16
|
+
import chalk from 'chalk';
|
|
17
|
+
import { loadToken } from '../utils/auth.js';
|
|
18
|
+
import { parsePrRef } from '../utils/pr-ref.js';
|
|
19
|
+
import { triggerDesignVerifier, waitForDesignVerifier } from '../utils/design-verifier-api.js';
|
|
20
|
+
import { buildDesignVerifierAgentReport, designVerifierStageLabel, formatDesignVerifierAgentReport, } from '../utils/design-verifier-result.js';
|
|
21
|
+
function emit(json, payload, human) {
|
|
22
|
+
if (json) {
|
|
23
|
+
console.log(JSON.stringify(payload));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
human();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function designVerifyCommand(identifier, options) {
|
|
30
|
+
let pr;
|
|
31
|
+
try {
|
|
32
|
+
pr = parsePrRef(identifier, 'design-verify');
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
emit(options.json, { ok: false, error: err.message }, () => {
|
|
36
|
+
console.error(chalk.red(err.message));
|
|
37
|
+
});
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const prLabel = `${pr.owner}/${pr.repo}#${pr.prNumber}`;
|
|
42
|
+
let token = null;
|
|
43
|
+
try {
|
|
44
|
+
token = await loadToken({ owner: pr.owner, repo: pr.repo });
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
emit(options.json, { ok: false, pr: prLabel, error: `Could not load Haystack credentials: ${err.message}` }, () => {
|
|
48
|
+
console.error(chalk.red(`Could not load Haystack credentials: ${err.message}`));
|
|
49
|
+
});
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!token) {
|
|
54
|
+
emit(options.json, { ok: false, pr: prLabel, error: 'Authentication required. Run `haystack login` first.' }, () => {
|
|
55
|
+
console.error(chalk.red('Design verification requires authentication. Run `haystack login` first.'));
|
|
56
|
+
});
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const pollIntervalSeconds = Number(options.pollInterval || '3');
|
|
61
|
+
const timeoutSeconds = Number(options.timeout || '7200');
|
|
62
|
+
if (!(Number.isFinite(pollIntervalSeconds) && pollIntervalSeconds >= 1 && pollIntervalSeconds <= 60)) {
|
|
63
|
+
emit(options.json, { ok: false, error: '--poll-interval must be between 1 and 60 seconds' }, () => {
|
|
64
|
+
console.error(chalk.red('--poll-interval must be between 1 and 60 seconds'));
|
|
65
|
+
});
|
|
66
|
+
process.exitCode = 2;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (!(Number.isFinite(timeoutSeconds) && timeoutSeconds >= 1 && timeoutSeconds <= 7200)) {
|
|
70
|
+
emit(options.json, { ok: false, error: '--timeout must be between 1 and 7200 seconds' }, () => {
|
|
71
|
+
console.error(chalk.red('--timeout must be between 1 and 7200 seconds'));
|
|
72
|
+
});
|
|
73
|
+
process.exitCode = 2;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!options.json) {
|
|
77
|
+
console.log(chalk.dim(options.run
|
|
78
|
+
? `\nResuming design verification ${options.run}...`
|
|
79
|
+
: `\nStarting design verification for ${prLabel}...`));
|
|
80
|
+
}
|
|
81
|
+
const result = options.run
|
|
82
|
+
? { status: 'duplicate', runId: options.run }
|
|
83
|
+
: await triggerDesignVerifier(pr.owner, pr.repo, pr.prNumber, token);
|
|
84
|
+
switch (result.status) {
|
|
85
|
+
case 'started':
|
|
86
|
+
case 'queued':
|
|
87
|
+
case 'duplicate':
|
|
88
|
+
if (options.wait === false) {
|
|
89
|
+
emit(options.json, { ok: true, pr: prLabel, runId: result.runId, outcome: result.status }, () => {
|
|
90
|
+
const suffix = result.status === 'queued'
|
|
91
|
+
? ' (at capacity — queued; it will start as a slot frees up)'
|
|
92
|
+
: '';
|
|
93
|
+
console.log(chalk.green(`✓ Design verification ${result.status}${suffix}`));
|
|
94
|
+
console.log(chalk.dim(` Run: ${result.runId}`));
|
|
95
|
+
console.log(chalk.dim(` Resume: haystack design-verify ${prLabel} --run ${result.runId}`));
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
emit(options.json, { ok: false, pr: prLabel, error: result.message, kind: result.status }, () => {
|
|
102
|
+
console.error(chalk.red(`✗ ${result.message}`));
|
|
103
|
+
});
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
let lastProgress = '';
|
|
108
|
+
const waited = await waitForDesignVerifier(pr.owner, pr.repo, pr.prNumber, result.runId, token, {
|
|
109
|
+
intervalMs: pollIntervalSeconds * 1000,
|
|
110
|
+
timeoutMs: timeoutSeconds * 1000,
|
|
111
|
+
onProgress: (run) => {
|
|
112
|
+
if (options.json)
|
|
113
|
+
return;
|
|
114
|
+
const label = designVerifierStageLabel(run.stage);
|
|
115
|
+
if (label === lastProgress)
|
|
116
|
+
return;
|
|
117
|
+
lastProgress = label;
|
|
118
|
+
console.error(chalk.dim(` ${label}...`));
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
if (waited.status === 'timeout') {
|
|
122
|
+
const message = `Timed out waiting; the run continues. Resume with: haystack design-verify ${prLabel} --run ${result.runId}`;
|
|
123
|
+
emit(options.json, { ok: false, status: 'pending', runId: result.runId, error: message }, () => {
|
|
124
|
+
console.error(chalk.yellow(message));
|
|
125
|
+
});
|
|
126
|
+
process.exitCode = 2;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (waited.status === 'error') {
|
|
130
|
+
emit(options.json, { ok: false, status: 'failed', runId: result.runId, error: waited.message }, () => {
|
|
131
|
+
console.error(chalk.red(`Verification status failed: ${waited.message}`));
|
|
132
|
+
});
|
|
133
|
+
process.exitCode = 2;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const report = buildDesignVerifierAgentReport(waited.run);
|
|
137
|
+
if (options.json)
|
|
138
|
+
console.log(JSON.stringify(report));
|
|
139
|
+
else
|
|
140
|
+
console.log(`\n${formatDesignVerifierAgentReport(report)}`);
|
|
141
|
+
process.exitCode = report.exitCode;
|
|
142
|
+
}
|
package/dist/commands/hooks.js
CHANGED
|
@@ -68,7 +68,7 @@ export async function hooksInstall(options) {
|
|
|
68
68
|
console.log(chalk.green('✓ Hook dependencies installed'));
|
|
69
69
|
}
|
|
70
70
|
catch {
|
|
71
|
-
console.log(chalk.yellow('⚠ Failed to install hook dependencies. Run `npm
|
|
71
|
+
console.log(chalk.yellow('⚠ Failed to install hook dependencies. Run `npm ci` in hooks/ manually.'));
|
|
72
72
|
}
|
|
73
73
|
// Step 6: Configure git hooks path
|
|
74
74
|
try {
|
|
@@ -181,7 +181,7 @@ export async function hooksStatus() {
|
|
|
181
181
|
const nodeModules = path.join(hooksDir, 'node_modules');
|
|
182
182
|
console.log(existsSync(nodeModules)
|
|
183
183
|
? chalk.green('✓ Hook dependencies installed')
|
|
184
|
-
: chalk.yellow('⚠ Hook dependencies not installed (run npm
|
|
184
|
+
: chalk.yellow('⚠ Hook dependencies not installed (run npm ci in hooks/)'));
|
|
185
185
|
// Check git core.hooksPath
|
|
186
186
|
try {
|
|
187
187
|
const hooksPath = execSync('git config core.hooksPath', {
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export interface ReviewExclusion {
|
|
2
|
+
path: string;
|
|
3
|
+
rule_id: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ReviewSymlinkIssue {
|
|
6
|
+
path: string;
|
|
7
|
+
target: string;
|
|
8
|
+
reason: 'absolute-target' | 'target-escapes-repository' | 'target-is-excluded' | 'target-is-not-retained-file' | 'target-cycle' | 'ambiguous-target' | 'unreadable-link';
|
|
9
|
+
}
|
|
10
|
+
export interface InlineTestCandidate {
|
|
11
|
+
path: string;
|
|
12
|
+
}
|
|
13
|
+
export interface SnapshotLimitBlocker {
|
|
14
|
+
kind: 'entry-count' | 'total-output-bytes';
|
|
15
|
+
path: string;
|
|
16
|
+
limit: number;
|
|
17
|
+
attempted: number;
|
|
18
|
+
reason: string;
|
|
19
|
+
}
|
|
20
|
+
export interface PrepareUniverseReviewResult {
|
|
21
|
+
status: 'ready-for-confinement' | 'blocked';
|
|
22
|
+
path: string;
|
|
23
|
+
process_confinement_required: true;
|
|
24
|
+
counts: {
|
|
25
|
+
tracked: number;
|
|
26
|
+
retained: number;
|
|
27
|
+
copied: number;
|
|
28
|
+
excluded: number;
|
|
29
|
+
submodules_omitted: number;
|
|
30
|
+
approved_inactive_submodules_omitted: number;
|
|
31
|
+
symlinks_materialized: number;
|
|
32
|
+
symlink_issues: number;
|
|
33
|
+
symlinks_omitted_target_not_retained: number;
|
|
34
|
+
omitted_unscannable: number;
|
|
35
|
+
missing_or_changed_type: number;
|
|
36
|
+
inline_test_candidates: number;
|
|
37
|
+
inline_scan_skipped: number;
|
|
38
|
+
binary_inline_scan_skipped: number;
|
|
39
|
+
oversized_text_inline_scan_blockers: number;
|
|
40
|
+
snapshot_limit_blockers: number;
|
|
41
|
+
copied_bytes: number;
|
|
42
|
+
};
|
|
43
|
+
exclusions: ReviewExclusion[];
|
|
44
|
+
submodules_omitted: string[];
|
|
45
|
+
approved_inactive_submodule_omissions: {
|
|
46
|
+
rule: {
|
|
47
|
+
id: string;
|
|
48
|
+
description: string;
|
|
49
|
+
};
|
|
50
|
+
paths: string[];
|
|
51
|
+
};
|
|
52
|
+
rules: Array<{
|
|
53
|
+
id: string;
|
|
54
|
+
description: string;
|
|
55
|
+
}>;
|
|
56
|
+
activated_conditional_rule_ids: string[];
|
|
57
|
+
inline_marker_rules: Array<{
|
|
58
|
+
id: string;
|
|
59
|
+
description: string;
|
|
60
|
+
}>;
|
|
61
|
+
symlink_issues: ReviewSymlinkIssue[];
|
|
62
|
+
omitted_symlinks: ReviewSymlinkIssue[];
|
|
63
|
+
omitted_unscannable: {
|
|
64
|
+
path: string;
|
|
65
|
+
reason: string;
|
|
66
|
+
}[];
|
|
67
|
+
missing_or_changed_type: Array<{
|
|
68
|
+
path: string;
|
|
69
|
+
reason: string;
|
|
70
|
+
}>;
|
|
71
|
+
inline_test_candidates: InlineTestCandidate[];
|
|
72
|
+
inline_scan_skipped: Array<{
|
|
73
|
+
path: string;
|
|
74
|
+
reason: string;
|
|
75
|
+
}>;
|
|
76
|
+
snapshot_limit_blockers: SnapshotLimitBlocker[];
|
|
77
|
+
receipt: {
|
|
78
|
+
detail: 'compact' | 'full';
|
|
79
|
+
sample_limit: number;
|
|
80
|
+
complete_blocking_details: true;
|
|
81
|
+
collections: {
|
|
82
|
+
exclusions: ReceiptCollectionSummary;
|
|
83
|
+
approved_inactive_submodule_omissions: ReceiptCollectionSummary;
|
|
84
|
+
binary_inline_scan_skips: ReceiptCollectionSummary;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
manifest: {
|
|
88
|
+
path: string;
|
|
89
|
+
sha256: string;
|
|
90
|
+
entries: number;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export interface PrepareUniverseReviewOptions {
|
|
94
|
+
excludeInactiveSubmodules?: string[];
|
|
95
|
+
/** Omit files whose test content cannot be ruled out instead of blocking. */
|
|
96
|
+
omitUnscannableFiles?: boolean;
|
|
97
|
+
fullReceipt?: boolean;
|
|
98
|
+
limits?: {
|
|
99
|
+
maxSnapshotEntries?: number;
|
|
100
|
+
maxSnapshotOutputBytes?: number;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
interface ReceiptCollectionSummary {
|
|
104
|
+
total: number;
|
|
105
|
+
returned: number;
|
|
106
|
+
truncated: number;
|
|
107
|
+
}
|
|
108
|
+
export declare function prepareUniverseReview(root: string, options?: PrepareUniverseReviewOptions): PrepareUniverseReviewResult;
|
|
109
|
+
export declare function prepareUniverseReviewCommand(options: {
|
|
110
|
+
json?: boolean;
|
|
111
|
+
excludeInactiveSubmodule?: string[];
|
|
112
|
+
omitUnscannableFiles?: boolean;
|
|
113
|
+
fullReceipt?: boolean;
|
|
114
|
+
}): Promise<void>;
|
|
115
|
+
export {};
|