@haystackeditor/cli 0.15.7 → 0.15.9
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.
|
@@ -29,6 +29,8 @@ After submitting, use `haystack triage <pr-number>` to check analysis results (r
|
|
|
29
29
|
- Do not add silent fallbacks.
|
|
30
30
|
- Do not add backwards compatibility, unless you confirmed with the user first that it's desired.
|
|
31
31
|
- Do not add TODOs and incomplete code unless they have been explicitly flagged to the user.
|
|
32
|
+
- When the user asks for a general or systematic fix, do not satisfy it with a check that only catches the known example. Implement the requested class of protection, or explicitly say what remains unfinished.
|
|
33
|
+
- Finish both sides of cross-boundary implementations. When adding a new keyed capability, mode, event, route, config value, feature flag, enum variant, or serialized field, verify and update every required consumer, producer, registry, allow-list, schema, persistence path, and delivery surface. If the existing system requires "also register it in X", do that wiring or explicitly report that it is intentionally out of scope.
|
|
32
34
|
|
|
33
35
|
## No Truncation Without Permission
|
|
34
36
|
|
package/dist/commands/setup.js
CHANGED
|
@@ -1032,9 +1032,10 @@ async function stepConfirm(byRepo, token) {
|
|
|
1032
1032
|
// waits for the user. Step 7 ensures .entire/settings.json rode along in
|
|
1033
1033
|
// the same commit.
|
|
1034
1034
|
bootstrapPRs.set(repoFullName, writeResult.bootstrapPR);
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1035
|
+
// Don't print the GitHub PR URL: the user reviews and merges from their
|
|
1036
|
+
// Haystack feed, not the GitHub PR page. The feed link is the single CTA
|
|
1037
|
+
// in the Next steps block below.
|
|
1038
|
+
console.log(chalk.green(` ✓ ${repoFullName}`) + chalk.dim(' (configured)'));
|
|
1038
1039
|
}
|
|
1039
1040
|
catch (err) {
|
|
1040
1041
|
ui.clearProgress();
|
|
@@ -1124,23 +1125,37 @@ async function runSetupFlow(options) {
|
|
|
1124
1125
|
else {
|
|
1125
1126
|
await stepInstallEntire(selectedRepos, token, bootstrapPRs);
|
|
1126
1127
|
}
|
|
1127
|
-
// Close the loop
|
|
1128
|
-
//
|
|
1129
|
-
//
|
|
1128
|
+
// Close the loop with ONE prominent CTA: the Haystack feed. The Configure
|
|
1129
|
+
// Haystack PR (and every PR after it) is reviewed and merged from the feed,
|
|
1130
|
+
// NOT the GitHub PR page, so the feed is the only link we surface. In JSON
|
|
1131
|
+
// mode these console writes go to stderr, so a driving agent instead gets the
|
|
1132
|
+
// same content as `summary` in the `result` event to relay verbatim.
|
|
1130
1133
|
const feedUrl = `${HAYSTACK_API}/inbox`;
|
|
1131
|
-
|
|
1132
|
-
|
|
1134
|
+
const prs = [...bootstrapPRs.entries()].map(([repo, pr]) => ({ repo, url: pr.prUrl }));
|
|
1135
|
+
// Plain-text summary an agent can print as-is (no ANSI codes).
|
|
1136
|
+
const summary = [
|
|
1137
|
+
'Open your Haystack feed to review and merge your Configure Haystack PR:',
|
|
1138
|
+
` ${feedUrl}`,
|
|
1139
|
+
].join('\n');
|
|
1140
|
+
// Styled version for the human at a TTY.
|
|
1141
|
+
console.log(chalk.green.bold('\n Next steps\n'));
|
|
1142
|
+
console.log(' Open your Haystack feed to review and merge your Configure Haystack PR:');
|
|
1143
|
+
console.log(` ${chalk.cyan(feedUrl)}\n`);
|
|
1133
1144
|
if (ui.interactive) {
|
|
1134
1145
|
const openFeed = await ui.confirm({ id: 'open_feed', message: 'Open your Haystack feed now?', default: true });
|
|
1135
1146
|
if (openFeed)
|
|
1136
1147
|
tryOpenBrowser(feedUrl);
|
|
1137
1148
|
}
|
|
1138
|
-
// Final structured outcome for non-interactive/agent callers.
|
|
1149
|
+
// Final structured outcome for non-interactive/agent callers. `summary` is the
|
|
1150
|
+
// ready-to-print Next steps text so a driving agent can surface it verbatim.
|
|
1151
|
+
// `pullRequests` stays as structured metadata (programmatic use), but the
|
|
1152
|
+
// human-facing copy points only to the feed.
|
|
1139
1153
|
ui.result({
|
|
1140
1154
|
status: 'complete',
|
|
1141
1155
|
repos: selectedRepos,
|
|
1142
|
-
pullRequests:
|
|
1156
|
+
pullRequests: prs,
|
|
1143
1157
|
feedUrl,
|
|
1158
|
+
summary,
|
|
1144
1159
|
});
|
|
1145
1160
|
}
|
|
1146
1161
|
// =============================================================================
|
package/dist/commands/triage.js
CHANGED
|
@@ -69,38 +69,16 @@ function parsePRIdentifier(identifier) {
|
|
|
69
69
|
// Output formatting
|
|
70
70
|
// ============================================================================
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
73
|
-
* shouldn't be re-fixed).
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* agent/cloudflare/src/auto-fix-post-synthesis.ts (normalizeText / categoriesCompatible).
|
|
72
|
+
* True if the finding is one the auto-fixer is already handling (and thus
|
|
73
|
+
* shouldn't be re-fixed). Correlation is by exact finding id — the same stable id
|
|
74
|
+
* analysis assigns and the fixer echoes back in its manifest. We never compare
|
|
75
|
+
* summary/category text.
|
|
77
76
|
*/
|
|
78
|
-
function normalizeText(value) {
|
|
79
|
-
return value.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
80
|
-
}
|
|
81
|
-
/** Categories match if equal, or if one's tokens are a subset of the other's. */
|
|
82
|
-
function categoriesCompatible(left, right) {
|
|
83
|
-
const a = normalizeText(left);
|
|
84
|
-
const b = normalizeText(right);
|
|
85
|
-
if (a === b)
|
|
86
|
-
return true;
|
|
87
|
-
const aTokens = a.split(/[^a-z0-9]+/).filter(Boolean);
|
|
88
|
-
const bTokens = b.split(/[^a-z0-9]+/).filter(Boolean);
|
|
89
|
-
const aSet = new Set(aTokens);
|
|
90
|
-
const bSet = new Set(bTokens);
|
|
91
|
-
return ((bTokens.length > 0 && bTokens.every((t) => aSet.has(t))) ||
|
|
92
|
-
(aTokens.length > 0 && aTokens.every((t) => bSet.has(t))));
|
|
93
|
-
}
|
|
94
|
-
/** True if the finding matches any item the auto-fixer is handling. */
|
|
95
77
|
function isAutoFixing(finding, manifest) {
|
|
96
|
-
|
|
97
|
-
if (items.length === 0)
|
|
78
|
+
if (!finding.id)
|
|
98
79
|
return false;
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
return false;
|
|
102
|
-
return items.some((item) => normalizeText(item.summary) === summaryKey &&
|
|
103
|
-
categoriesCompatible(finding.category, item.category));
|
|
80
|
+
const items = manifest?.fixedItems ?? [];
|
|
81
|
+
return items.some((item) => item.id === finding.id);
|
|
104
82
|
}
|
|
105
83
|
function printRichOutput(pr, synthesis, manifest) {
|
|
106
84
|
const reviewUrl = `https://haystackeditor.com/review/${pr.owner}/${pr.repo}/${pr.prNumber}`;
|
package/dist/triage/prompts.js
CHANGED
|
@@ -259,6 +259,7 @@ ${traceFileList}
|
|
|
259
259
|
2. From each file, identify:
|
|
260
260
|
- The user's original instruction/prompt (the first message)
|
|
261
261
|
- Any follow-up instructions or corrections from the user
|
|
262
|
+
- The final effective scope after later corrections or reframes
|
|
262
263
|
|
|
263
264
|
3. ${precomputedDiff ? 'Review the diff below' : `Run \`git diff ${baseBranch}...HEAD\``} to see what was actually implemented.
|
|
264
265
|
|
|
@@ -274,7 +275,11 @@ The agent implemented something DIFFERENT from what was asked:
|
|
|
274
275
|
### Incomplete Fulfillment
|
|
275
276
|
The agent didn't finish everything that was asked:
|
|
276
277
|
- User requested 3 things, agent only did 2
|
|
278
|
+
- User requested a general/systematic guardrail or end-to-end behavior, but the diff only handles one known instance, one special case, or one side of the required wiring
|
|
279
|
+
- User initially mentioned a known example, then clarified "not the specific case" / "the general class"; the diff still implements only the known example or category-specific check
|
|
277
280
|
- Interface fields declared but never populated
|
|
281
|
+
- A new keyed capability, event, route, config value, enum variant, or serialized field is referenced on one side of a boundary but the required registry, producer, consumer, schema, handler, persistence path, or delivery surface is missing
|
|
282
|
+
- A change appears to work through local/dev/test defaults, mocks, or overrides, but the production wiring path needed to deliver the requested behavior was not updated
|
|
278
283
|
- Functions stubbed with TODO/placeholder comments
|
|
279
284
|
- Agent said "I'll skip X for now" for something the user explicitly requested
|
|
280
285
|
- Tests not written when user asked for tests
|
|
@@ -297,6 +302,7 @@ Do NOT flag internal implementation choices with no observable effect (naming, f
|
|
|
297
302
|
### Ignored Correction
|
|
298
303
|
The user gave an explicit correction or redirection and the final code does NOT honor it:
|
|
299
304
|
- User said "don't use a global / use X instead / that's racy, do Y" and the agent shipped the thing it was told not to
|
|
305
|
+
- User said the target is the general class rather than the specific instance, but the agent still shipped only the instance-specific implementation
|
|
300
306
|
- Agent applied the correction, then quietly reverted it in a later step
|
|
301
307
|
- A general later "looks good" does NOT cancel a specific earlier correction
|
|
302
308
|
This is high severity by default: the user actively steered and was overridden.
|
|
@@ -322,6 +328,10 @@ Compare each concrete claim the agent made to the user against what the diff act
|
|
|
322
328
|
- TODO, FIXME, placeholder, stub comments in new code
|
|
323
329
|
- Empty function bodies or early returns
|
|
324
330
|
- Interface fields that are declared but never assigned anywhere
|
|
331
|
+
- Feature-specific or instance-specific checks added after the user asked for a general guardrail against a broader failure mode
|
|
332
|
+
- Earlier narrow examples treated as the whole task even though later user messages broadened or generalized the requested scope
|
|
333
|
+
- New string-keyed names, enum variants, serialized fields, action types, routes, events, or config keys that have no matching entry in the surrounding registry, allow-list, schema, handler, producer, consumer, or template path
|
|
334
|
+
- Comments or neighboring code saying "must also register/list/wire this" where the diff updated only the reference side
|
|
325
335
|
- New mechanisms (cooldowns, retries, caches, rate limits) not requested by the user
|
|
326
336
|
- Decisions that drop, limit, or reshape data flowing to the output, or pick a default that changes what end-users see, with no instruction specifying it
|
|
327
337
|
|
|
@@ -26,6 +26,9 @@ export interface AnalysisIssue {
|
|
|
26
26
|
source: 'bug-detection' | 'rule-violation';
|
|
27
27
|
}
|
|
28
28
|
export interface SynthesisDisplayIssue {
|
|
29
|
+
/** Stable finding id (assigned at synthesis). Used to correlate with the
|
|
30
|
+
* auto-fix manifest by exact match instead of comparing text. */
|
|
31
|
+
id?: string;
|
|
29
32
|
category: string;
|
|
30
33
|
summary: string;
|
|
31
34
|
detail: string;
|
|
@@ -110,10 +113,12 @@ export interface AutoFixManifest {
|
|
|
110
113
|
repo: string;
|
|
111
114
|
prNumber: number;
|
|
112
115
|
fixedItems: Array<{
|
|
116
|
+
id?: string;
|
|
113
117
|
category: string;
|
|
114
118
|
summary: string;
|
|
115
119
|
}>;
|
|
116
120
|
leftForReview: Array<{
|
|
121
|
+
id?: string;
|
|
117
122
|
category: string;
|
|
118
123
|
summary: string;
|
|
119
124
|
reason: string;
|