@deftai/directive-runtime 0.126.0-beta.1 → 0.126.0-beta.3
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/UPGRADING.md +4 -3
- package/commands.md +9 -6
- package/contracts/design-critique.md +39 -1
- package/contracts/human-origin-authz.md +14 -8
- package/dist/coverage-hotspots.d.ts +3 -0
- package/dist/coverage-hotspots.js +42 -2
- package/dist/doctor-diagnose.d.ts +1 -1
- package/dist/doctor-diagnose.js +29 -3
- package/dist/doctor.js +1 -1
- package/dist/occupancy-release.d.ts +5 -0
- package/dist/occupancy-release.js +11 -3
- package/dist/policy.d.ts +2 -0
- package/dist/policy.js +6 -0
- package/dist/session-start.js +28 -1
- package/dist/triage-actions.js +10 -2
- package/dist/verify-review-monitor.d.ts +7 -0
- package/dist/verify-review-monitor.js +21 -0
- package/docs/framework-gap-filing.md +85 -0
- package/docs/getting-started.md +13 -1
- package/docs/product-signal.md +1 -1
- package/package.json +1 -1
- package/packs/skills/skills-pack-0.1.json +10 -7
- package/packs/strategies/strategies-pack-0.1.json +32 -19
- package/scm/github.md +1 -1
- package/skills/deft-directive-article-review/SKILL.md +104 -24
- package/skills/deft-directive-design-critique/SKILL.md +10 -9
- package/skills/deft-directive-feedback/SKILL.md +37 -21
- package/skills/deft-directive-pre-pr/SKILL.md +14 -0
- package/skills/deft-directive-product-signal/SKILL.md +1 -1
- package/skills/deft-directive-review-cycle/SKILL.md +18 -1
- package/skills/deft-directive-setup/SKILL.md +41 -11
- package/skills/deft-directive-swarm/references/host-grok-build.md +1 -1
- package/skills/deft-directive-tutorial/SKILL.md +1 -1
- package/strategies/README.md +1 -0
- package/strategies/artifact-guards.md +2 -2
- package/strategies/design-thinking.md +182 -0
- package/strategies/discuss.md +2 -0
- package/strategies/interview.md +76 -23
- package/strategies/rapid.md +22 -12
- package/templates/agents-entry.md +4 -3
- package/templates/design-critique-brief.md +5 -1
- package/tutorial/beats.json +2 -2
- package/vendor/directive-content/UPGRADING.md +4 -3
- package/vendor/directive-content/commands.md +9 -6
- package/vendor/directive-content/contracts/design-critique.md +39 -1
- package/vendor/directive-content/contracts/human-origin-authz.md +14 -8
- package/vendor/directive-content/docs/framework-gap-filing.md +85 -0
- package/vendor/directive-content/docs/getting-started.md +13 -1
- package/vendor/directive-content/docs/product-signal.md +1 -1
- package/vendor/directive-content/packs/skills/skills-pack-0.1.json +10 -7
- package/vendor/directive-content/packs/strategies/strategies-pack-0.1.json +32 -19
- package/vendor/directive-content/scm/github.md +1 -1
- package/vendor/directive-content/skills/deft-directive-article-review/SKILL.md +104 -24
- package/vendor/directive-content/skills/deft-directive-design-critique/SKILL.md +10 -9
- package/vendor/directive-content/skills/deft-directive-feedback/SKILL.md +37 -21
- package/vendor/directive-content/skills/deft-directive-pre-pr/SKILL.md +14 -0
- package/vendor/directive-content/skills/deft-directive-product-signal/SKILL.md +1 -1
- package/vendor/directive-content/skills/deft-directive-review-cycle/SKILL.md +18 -1
- package/vendor/directive-content/skills/deft-directive-setup/SKILL.md +41 -11
- package/vendor/directive-content/skills/deft-directive-swarm/references/host-grok-build.md +1 -1
- package/vendor/directive-content/skills/deft-directive-tutorial/SKILL.md +1 -1
- package/vendor/directive-content/strategies/README.md +1 -0
- package/vendor/directive-content/strategies/artifact-guards.md +2 -2
- package/vendor/directive-content/strategies/design-thinking.md +182 -0
- package/vendor/directive-content/strategies/discuss.md +2 -0
- package/vendor/directive-content/strategies/interview.md +76 -23
- package/vendor/directive-content/strategies/rapid.md +22 -12
- package/vendor/directive-content/templates/agents-entry.md +4 -3
- package/vendor/directive-content/templates/design-critique-brief.md +5 -1
- package/vendor/directive-content/tutorial/beats.json +2 -2
- package/vendor/directive-core/dist/.tsbuildinfo +1 -1
- package/vendor/directive-core/dist/content-contracts/skills/helpers.d.ts +11 -1
- package/vendor/directive-core/dist/content-contracts/skills/helpers.d.ts.map +1 -1
- package/vendor/directive-core/dist/content-contracts/skills/helpers.js +18 -5
- package/vendor/directive-core/dist/content-contracts/skills/helpers.js.map +1 -1
- package/vendor/directive-core/dist/coverage-applicability/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/coverage-applicability/evaluate.js +39 -0
- package/vendor/directive-core/dist/coverage-applicability/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.d.ts +24 -5
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.js +89 -8
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/design-critique/auto-stamp-path1.d.ts +40 -0
- package/vendor/directive-core/dist/design-critique/auto-stamp-path1.d.ts.map +1 -1
- package/vendor/directive-core/dist/design-critique/auto-stamp-path1.js +116 -0
- package/vendor/directive-core/dist/design-critique/auto-stamp-path1.js.map +1 -1
- package/vendor/directive-core/dist/design-critique/citation-grammar.d.ts +36 -0
- package/vendor/directive-core/dist/design-critique/citation-grammar.d.ts.map +1 -1
- package/vendor/directive-core/dist/design-critique/citation-grammar.js +60 -3
- package/vendor/directive-core/dist/design-critique/citation-grammar.js.map +1 -1
- package/vendor/directive-core/dist/design-critique/completed-arc-record.d.ts +103 -3
- package/vendor/directive-core/dist/design-critique/completed-arc-record.d.ts.map +1 -1
- package/vendor/directive-core/dist/design-critique/completed-arc-record.js +127 -5
- package/vendor/directive-core/dist/design-critique/completed-arc-record.js.map +1 -1
- package/vendor/directive-core/dist/design-critique/ingest-posture.d.ts +27 -0
- package/vendor/directive-core/dist/design-critique/ingest-posture.d.ts.map +1 -0
- package/vendor/directive-core/dist/design-critique/ingest-posture.js +66 -0
- package/vendor/directive-core/dist/design-critique/ingest-posture.js.map +1 -0
- package/vendor/directive-core/dist/design-critique/panel-handback-acceptance.d.ts +106 -7
- package/vendor/directive-core/dist/design-critique/panel-handback-acceptance.d.ts.map +1 -1
- package/vendor/directive-core/dist/design-critique/panel-handback-acceptance.js +284 -17
- package/vendor/directive-core/dist/design-critique/panel-handback-acceptance.js.map +1 -1
- package/vendor/directive-core/dist/docs/docs-impact.d.ts +13 -0
- package/vendor/directive-core/dist/docs/docs-impact.d.ts.map +1 -1
- package/vendor/directive-core/dist/docs/docs-impact.js +49 -4
- package/vendor/directive-core/dist/docs/docs-impact.js.map +1 -1
- package/vendor/directive-core/dist/doctor/main.d.ts +48 -0
- package/vendor/directive-core/dist/doctor/main.d.ts.map +1 -1
- package/vendor/directive-core/dist/doctor/main.js +221 -4
- package/vendor/directive-core/dist/doctor/main.js.map +1 -1
- package/vendor/directive-core/dist/hooks/dispatcher.d.ts.map +1 -1
- package/vendor/directive-core/dist/hooks/dispatcher.js +160 -14
- package/vendor/directive-core/dist/hooks/dispatcher.js.map +1 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts +4 -0
- package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts.map +1 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.js +25 -4
- package/vendor/directive-core/dist/init-deposit/agent-hooks.js.map +1 -1
- package/vendor/directive-core/dist/init-deposit/skill-discovery-deposit.d.ts.map +1 -1
- package/vendor/directive-core/dist/init-deposit/skill-discovery-deposit.js +15 -0
- package/vendor/directive-core/dist/init-deposit/skill-discovery-deposit.js.map +1 -1
- package/vendor/directive-core/dist/intake/issue-ingest.d.ts +15 -0
- package/vendor/directive-core/dist/intake/issue-ingest.d.ts.map +1 -1
- package/vendor/directive-core/dist/intake/issue-ingest.js +190 -22
- package/vendor/directive-core/dist/intake/issue-ingest.js.map +1 -1
- package/vendor/directive-core/dist/pr-closing-keywords/detect.d.ts.map +1 -1
- package/vendor/directive-core/dist/pr-closing-keywords/detect.js +52 -7
- package/vendor/directive-core/dist/pr-closing-keywords/detect.js.map +1 -1
- package/vendor/directive-core/dist/pr-watch/main.d.ts +5 -0
- package/vendor/directive-core/dist/pr-watch/main.d.ts.map +1 -1
- package/vendor/directive-core/dist/pr-watch/main.js.map +1 -1
- package/vendor/directive-core/dist/pr-watch/watch.d.ts.map +1 -1
- package/vendor/directive-core/dist/pr-watch/watch.js +5 -0
- package/vendor/directive-core/dist/pr-watch/watch.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/controller.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/controller.js +118 -12
- package/vendor/directive-core/dist/pre-pr-controller/controller.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/index.d.ts +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/index.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/index.js +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/index.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/phases.d.ts +5 -0
- package/vendor/directive-core/dist/pre-pr-controller/phases.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/phases.js +10 -0
- package/vendor/directive-core/dist/pre-pr-controller/phases.js.map +1 -1
- package/vendor/directive-core/dist/presentation-ceiling/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/presentation-ceiling/evaluate.js +3 -1
- package/vendor/directive-core/dist/presentation-ceiling/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/release/auto-hatch.d.ts +6 -0
- package/vendor/directive-core/dist/release/auto-hatch.d.ts.map +1 -1
- package/vendor/directive-core/dist/release/auto-hatch.js +13 -4
- package/vendor/directive-core/dist/release/auto-hatch.js.map +1 -1
- package/vendor/directive-core/dist/review-monitor/record.d.ts +11 -0
- package/vendor/directive-core/dist/review-monitor/record.d.ts.map +1 -1
- package/vendor/directive-core/dist/review-monitor/record.js +23 -0
- package/vendor/directive-core/dist/review-monitor/record.js.map +1 -1
- package/vendor/directive-core/dist/run-summary/emit.d.ts +2 -1
- package/vendor/directive-core/dist/run-summary/emit.d.ts.map +1 -1
- package/vendor/directive-core/dist/run-summary/emit.js +3 -0
- package/vendor/directive-core/dist/run-summary/emit.js.map +1 -1
- package/vendor/directive-core/dist/run-summary/index.d.ts +1 -1
- package/vendor/directive-core/dist/run-summary/index.d.ts.map +1 -1
- package/vendor/directive-core/dist/run-summary/index.js.map +1 -1
- package/vendor/directive-core/dist/run-summary/types.d.ts +8 -2
- package/vendor/directive-core/dist/run-summary/types.d.ts.map +1 -1
- package/vendor/directive-core/dist/run-summary/types.js +1 -0
- package/vendor/directive-core/dist/run-summary/types.js.map +1 -1
- package/vendor/directive-core/dist/scope/delivery-evidence.d.ts +11 -0
- package/vendor/directive-core/dist/scope/delivery-evidence.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope/delivery-evidence.js +34 -0
- package/vendor/directive-core/dist/scope/delivery-evidence.js.map +1 -1
- package/vendor/directive-core/dist/scope/promote-from-issue.d.ts +2 -1
- package/vendor/directive-core/dist/scope/promote-from-issue.d.ts.map +1 -1
- package/vendor/directive-core/dist/scope/promote-from-issue.js +17 -61
- package/vendor/directive-core/dist/scope/promote-from-issue.js.map +1 -1
- package/vendor/directive-core/dist/session/occupancy-recovery.d.ts +10 -0
- package/vendor/directive-core/dist/session/occupancy-recovery.d.ts.map +1 -1
- package/vendor/directive-core/dist/session/occupancy-recovery.js +43 -7
- package/vendor/directive-core/dist/session/occupancy-recovery.js.map +1 -1
- package/vendor/directive-core/dist/session/occupancy.d.ts +148 -1
- package/vendor/directive-core/dist/session/occupancy.d.ts.map +1 -1
- package/vendor/directive-core/dist/session/occupancy.js +516 -34
- package/vendor/directive-core/dist/session/occupancy.js.map +1 -1
- package/vendor/directive-core/dist/session/session-start.d.ts.map +1 -1
- package/vendor/directive-core/dist/session/session-start.js +6 -1
- package/vendor/directive-core/dist/session/session-start.js.map +1 -1
- package/vendor/directive-core/dist/swarm/cohort-review-monitors-cli.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/cohort-review-monitors-cli.js +5 -1
- package/vendor/directive-core/dist/swarm/cohort-review-monitors-cli.js.map +1 -1
- package/vendor/directive-core/dist/swarm/cohort-review-monitors.d.ts +71 -0
- package/vendor/directive-core/dist/swarm/cohort-review-monitors.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/cohort-review-monitors.js +125 -2
- package/vendor/directive-core/dist/swarm/cohort-review-monitors.js.map +1 -1
- package/vendor/directive-core/dist/telemetry-coverage/fake-trial.d.ts.map +1 -1
- package/vendor/directive-core/dist/telemetry-coverage/fake-trial.js +9 -0
- package/vendor/directive-core/dist/telemetry-coverage/fake-trial.js.map +1 -1
- package/vendor/directive-core/dist/telemetry-coverage/kinds.d.ts.map +1 -1
- package/vendor/directive-core/dist/telemetry-coverage/kinds.js +2 -0
- package/vendor/directive-core/dist/telemetry-coverage/kinds.js.map +1 -1
- package/vendor/directive-core/dist/triage/actions/index.d.ts +8 -5
- package/vendor/directive-core/dist/triage/actions/index.d.ts.map +1 -1
- package/vendor/directive-core/dist/triage/actions/index.js +248 -22
- package/vendor/directive-core/dist/triage/actions/index.js.map +1 -1
- package/vendor/directive-core/dist/triage/actions/types.d.ts +16 -2
- package/vendor/directive-core/dist/triage/actions/types.d.ts.map +1 -1
- package/vendor/directive-core/dist/triage/bulk/index.d.ts +8 -1
- package/vendor/directive-core/dist/triage/bulk/index.d.ts.map +1 -1
- package/vendor/directive-core/dist/triage/bulk/index.js +30 -11
- package/vendor/directive-core/dist/triage/bulk/index.js.map +1 -1
- package/vendor/directive-core/dist/value/feedback-file.d.ts +54 -6
- package/vendor/directive-core/dist/value/feedback-file.d.ts.map +1 -1
- package/vendor/directive-core/dist/value/feedback-file.js +470 -43
- package/vendor/directive-core/dist/value/feedback-file.js.map +1 -1
|
@@ -3,12 +3,14 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { isFrameworkRepoRoot } from "../check/orchestrator.js";
|
|
4
4
|
import { policyColonInvocation } from "../policy/policy-invocation.js";
|
|
5
5
|
import { isValueFeedbackPathAllowed, resolveValueFeedback } from "../policy/value-feedback.js";
|
|
6
|
-
import { GhRestError, restCreateIssue,
|
|
6
|
+
import { GhRestError, REST_MAX_PER_PAGE, REST_PAGINATION_MAX_PAGES, REST_SHARED_ROW_BUDGET, restCreateIssue, restIssueListOpenInventory, restPostComment, runGhApi, splitRepo, } from "../scm/gh-rest.js";
|
|
7
7
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
8
8
|
export const DEFAULT_UPSTREAM_REPO = "deftai/directive";
|
|
9
9
|
export const FRAMEWORK_GAP_TITLE_PREFIX = "[framework-gap]";
|
|
10
10
|
/** Sole sanctioned title classification (#3713). Does not write `adoption-blocker`. */
|
|
11
11
|
export const ADOPTION_BLOCKER_TITLE_TOKEN = "BLOCKER";
|
|
12
|
+
/** Retained closed-issue count window for feedback:file dedup (#5099 Prefer-A Recut-2). */
|
|
13
|
+
export const FEEDBACK_CLOSED_DEDUP_WINDOW = Number.parseInt("500", 10);
|
|
12
14
|
const FRAMEWORK_GAP_PREFIX_RE = /^\[framework-gap\]\s*/i;
|
|
13
15
|
const BLOCKER_TITLE_TOKEN_RE = /^BLOCKER\b[:\s-]*/i;
|
|
14
16
|
/** Strip source prefix and the optional BLOCKER token so marked/unmarked titles dedup. */
|
|
@@ -25,9 +27,27 @@ export function stripTitleClassificationTokens(title) {
|
|
|
25
27
|
function sanitizeOneLine(value) {
|
|
26
28
|
return value.replace(/\r?\n/g, " ").trim();
|
|
27
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Fence free-text so line-leading `#` / `---` cannot break issue markdown (#4375).
|
|
32
|
+
* Indent those lines before insertion into structured sections.
|
|
33
|
+
*/
|
|
34
|
+
export function fenceFreeTextSection(value) {
|
|
35
|
+
return value
|
|
36
|
+
.split(/\r?\n/)
|
|
37
|
+
.map((line) => {
|
|
38
|
+
if (/^#{1,6}(\s|$)/.test(line) || /^---\s*$/.test(line)) {
|
|
39
|
+
return ` ${line}`;
|
|
40
|
+
}
|
|
41
|
+
return line;
|
|
42
|
+
})
|
|
43
|
+
.join("\n");
|
|
44
|
+
}
|
|
28
45
|
function sectionOrPlaceholder(value, placeholder) {
|
|
29
46
|
const trimmed = value?.trim() ?? "";
|
|
30
|
-
|
|
47
|
+
if (trimmed.length === 0) {
|
|
48
|
+
return placeholder;
|
|
49
|
+
}
|
|
50
|
+
return fenceFreeTextSection(trimmed);
|
|
31
51
|
}
|
|
32
52
|
/** True when the resolved project root is the directive maintainer source checkout. */
|
|
33
53
|
export function isMaintainerFrameworkRepo(projectRoot) {
|
|
@@ -81,9 +101,18 @@ function buildAdoptionImpactSection(input) {
|
|
|
81
101
|
"",
|
|
82
102
|
];
|
|
83
103
|
}
|
|
84
|
-
/** Build the structured framework-gap issue body. */
|
|
85
|
-
export function buildFrameworkGapBody(input) {
|
|
104
|
+
/** Build the structured framework-gap issue or evidence-comment body. */
|
|
105
|
+
export function buildFrameworkGapBody(input, options) {
|
|
86
106
|
const summary = sanitizeOneLine(input.summary);
|
|
107
|
+
const mode = options?.mode ?? "issue";
|
|
108
|
+
const footer = mode === "comment"
|
|
109
|
+
? "_Evidence appended via `deft feedback:file` from a directive consumer project (Refs #1709 value-feedback gap escalation)._"
|
|
110
|
+
: "_Filed via `deft feedback:file` from a directive consumer project (Refs #1709 value-feedback gap escalation)._";
|
|
111
|
+
const related = typeof options?.relatedClosedNumber === "number" &&
|
|
112
|
+
Number.isFinite(options.relatedClosedNumber) &&
|
|
113
|
+
options.relatedClosedNumber > 0
|
|
114
|
+
? [`Related: closed #${options.relatedClosedNumber}`, ""]
|
|
115
|
+
: [];
|
|
87
116
|
const lines = [
|
|
88
117
|
"## Summary",
|
|
89
118
|
"",
|
|
@@ -106,29 +135,213 @@ export function buildFrameworkGapBody(input) {
|
|
|
106
135
|
sectionOrPlaceholder(input.sessionNotes, "_(not provided)_"),
|
|
107
136
|
"",
|
|
108
137
|
...buildAdoptionImpactSection(input),
|
|
138
|
+
...related,
|
|
109
139
|
"---",
|
|
110
|
-
|
|
140
|
+
footer,
|
|
111
141
|
"",
|
|
112
142
|
];
|
|
113
143
|
return lines.join("\n");
|
|
114
144
|
}
|
|
115
|
-
|
|
145
|
+
function matchTitleRow(row, needle) {
|
|
146
|
+
const issueTitle = typeof row.title === "string" ? row.title : "";
|
|
147
|
+
if (normalizeForDedup(issueTitle) !== needle) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const url = typeof row.html_url === "string" ? row.html_url : "";
|
|
151
|
+
const state = typeof row.state === "string" ? row.state : "";
|
|
152
|
+
const number = row.number;
|
|
153
|
+
if (typeof number !== "number" ||
|
|
154
|
+
!Number.isFinite(number) ||
|
|
155
|
+
number <= 0 ||
|
|
156
|
+
!Number.isInteger(number) ||
|
|
157
|
+
url.length === 0 ||
|
|
158
|
+
state.length === 0) {
|
|
159
|
+
return "malformed";
|
|
160
|
+
}
|
|
161
|
+
return { number, state, url, title: issueTitle };
|
|
162
|
+
}
|
|
163
|
+
function fetchClosedIssuePage(repo, page, perPage, seams) {
|
|
164
|
+
const [owner, name] = splitRepo(repo);
|
|
165
|
+
const endpoint = `repos/${owner}/${name}/issues`;
|
|
166
|
+
const runner = seams.runGhApiFn ?? runGhApi;
|
|
167
|
+
const args = [
|
|
168
|
+
endpoint,
|
|
169
|
+
"--method",
|
|
170
|
+
"GET",
|
|
171
|
+
"--raw-field",
|
|
172
|
+
"state=closed",
|
|
173
|
+
"--raw-field",
|
|
174
|
+
"sort=updated",
|
|
175
|
+
"--raw-field",
|
|
176
|
+
"direction=desc",
|
|
177
|
+
"--raw-field",
|
|
178
|
+
`per_page=${perPage}`,
|
|
179
|
+
"--raw-field",
|
|
180
|
+
`page=${page}`,
|
|
181
|
+
];
|
|
182
|
+
const result = runner(args, { whichFn: seams.whichFn });
|
|
183
|
+
if (result.returncode !== 0) {
|
|
184
|
+
const detail = result.stderr.trim();
|
|
185
|
+
return {
|
|
186
|
+
ok: false,
|
|
187
|
+
message: detail.length > 0
|
|
188
|
+
? detail
|
|
189
|
+
: `closed-issue walk gh exit ${String(result.returncode)} (${endpoint})`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const stdout = result.stdout.trim();
|
|
193
|
+
if (stdout.length === 0) {
|
|
194
|
+
return { ok: true, rows: [] };
|
|
195
|
+
}
|
|
196
|
+
let parsed;
|
|
197
|
+
try {
|
|
198
|
+
parsed = JSON.parse(stdout);
|
|
199
|
+
}
|
|
200
|
+
catch (exc) {
|
|
201
|
+
const message = exc instanceof Error ? exc.message : String(exc);
|
|
202
|
+
return { ok: false, message: `non-JSON closed-issue page: ${message}` };
|
|
203
|
+
}
|
|
204
|
+
if (!Array.isArray(parsed)) {
|
|
205
|
+
return {
|
|
206
|
+
ok: false,
|
|
207
|
+
message: `unexpected top-level type ${typeof parsed}`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return { ok: true, rows: parsed };
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Closed-issue sibling of `restWalkClosedPullsUpdatedDesc` for feedback:file dedup (#5099).
|
|
214
|
+
* Retained-count window; pull rows consume raw budget but are excluded from match/count.
|
|
215
|
+
*/
|
|
216
|
+
export function walkClosedIssuesForFeedbackDedup(repo, title, options = {}, seams = {}) {
|
|
217
|
+
const needle = normalizeForDedup(title);
|
|
218
|
+
if (needle.length === 0) {
|
|
219
|
+
return {
|
|
220
|
+
outcome: "list-exhausted",
|
|
221
|
+
inspectedIssueCount: 0,
|
|
222
|
+
rawRows: 0,
|
|
223
|
+
match: null,
|
|
224
|
+
malformedMatch: false,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const window = Math.max(1, options.window ?? FEEDBACK_CLOSED_DEDUP_WINDOW);
|
|
228
|
+
const cappedPerPage = Math.min(Math.max(1, options.perPage ?? REST_MAX_PER_PAGE), REST_MAX_PER_PAGE);
|
|
229
|
+
const budget = Math.max(0, options.remainingBudget ?? REST_SHARED_ROW_BUDGET);
|
|
230
|
+
let rawRows = 0;
|
|
231
|
+
let inspectedIssueCount = 0;
|
|
232
|
+
for (let page = 1; page <= REST_PAGINATION_MAX_PAGES; page += 1) {
|
|
233
|
+
if (rawRows >= budget) {
|
|
234
|
+
return {
|
|
235
|
+
outcome: "cap",
|
|
236
|
+
inspectedIssueCount,
|
|
237
|
+
rawRows,
|
|
238
|
+
match: null,
|
|
239
|
+
malformedMatch: false,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const pageResult = fetchClosedIssuePage(repo, page, cappedPerPage, seams);
|
|
243
|
+
if (!pageResult.ok) {
|
|
244
|
+
return {
|
|
245
|
+
outcome: "error",
|
|
246
|
+
inspectedIssueCount,
|
|
247
|
+
rawRows,
|
|
248
|
+
match: null,
|
|
249
|
+
malformedMatch: false,
|
|
250
|
+
errorMessage: pageResult.message,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const pagePayload = pageResult.rows;
|
|
254
|
+
if (pagePayload.length === 0) {
|
|
255
|
+
return {
|
|
256
|
+
outcome: "list-exhausted",
|
|
257
|
+
inspectedIssueCount,
|
|
258
|
+
rawRows,
|
|
259
|
+
match: null,
|
|
260
|
+
malformedMatch: false,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
for (const item of pagePayload) {
|
|
264
|
+
if (rawRows >= budget) {
|
|
265
|
+
return {
|
|
266
|
+
outcome: "cap",
|
|
267
|
+
inspectedIssueCount,
|
|
268
|
+
rawRows,
|
|
269
|
+
match: null,
|
|
270
|
+
malformedMatch: false,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
rawRows += 1;
|
|
274
|
+
if ("pull_request" in item) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
inspectedIssueCount += 1;
|
|
278
|
+
const matched = matchTitleRow(item, needle);
|
|
279
|
+
if (matched === "malformed") {
|
|
280
|
+
return {
|
|
281
|
+
outcome: "error",
|
|
282
|
+
inspectedIssueCount,
|
|
283
|
+
rawRows,
|
|
284
|
+
match: null,
|
|
285
|
+
malformedMatch: true,
|
|
286
|
+
errorMessage: "closed-issue title match lacks usable numeric number/state/url",
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
if (matched !== null) {
|
|
290
|
+
return {
|
|
291
|
+
outcome: "list-exhausted",
|
|
292
|
+
inspectedIssueCount,
|
|
293
|
+
rawRows,
|
|
294
|
+
match: matched,
|
|
295
|
+
malformedMatch: false,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
if (inspectedIssueCount >= window) {
|
|
299
|
+
return {
|
|
300
|
+
outcome: "window-exhausted",
|
|
301
|
+
inspectedIssueCount,
|
|
302
|
+
rawRows,
|
|
303
|
+
match: null,
|
|
304
|
+
malformedMatch: false,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (pagePayload.length < cappedPerPage) {
|
|
309
|
+
return {
|
|
310
|
+
outcome: "list-exhausted",
|
|
311
|
+
inspectedIssueCount,
|
|
312
|
+
rawRows,
|
|
313
|
+
match: null,
|
|
314
|
+
malformedMatch: false,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
outcome: "cap",
|
|
320
|
+
inspectedIssueCount,
|
|
321
|
+
rawRows,
|
|
322
|
+
match: null,
|
|
323
|
+
malformedMatch: false,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
116
326
|
export function findDuplicateIssue(repo, title, seams = {}) {
|
|
117
327
|
const needle = normalizeForDedup(title);
|
|
118
328
|
if (needle.length === 0) {
|
|
119
|
-
return null;
|
|
329
|
+
return { ok: true, match: null };
|
|
120
330
|
}
|
|
121
|
-
const issues =
|
|
331
|
+
const issues = restIssueListOpenInventory(repo, seams);
|
|
122
332
|
for (const issue of issues) {
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
333
|
+
const matched = matchTitleRow(issue, needle);
|
|
334
|
+
if (matched === "malformed") {
|
|
335
|
+
return {
|
|
336
|
+
ok: false,
|
|
337
|
+
error: "open-issue title match lacks usable numeric number/state/url",
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
if (matched !== null) {
|
|
341
|
+
return { ok: true, match: matched };
|
|
129
342
|
}
|
|
130
343
|
}
|
|
131
|
-
return null;
|
|
344
|
+
return { ok: true, match: null };
|
|
132
345
|
}
|
|
133
346
|
function resolveRepo(options) {
|
|
134
347
|
const explicit = options.repo?.trim();
|
|
@@ -147,7 +360,40 @@ function buildConfirmationPrompt(title, body) {
|
|
|
147
360
|
`${body}\n` +
|
|
148
361
|
"Re-run with --confirm to file this issue upstream after reviewing the draft.\n");
|
|
149
362
|
}
|
|
150
|
-
|
|
363
|
+
function buildOpenMatchPreview(match, commentBody) {
|
|
364
|
+
return (`[deft feedback] Open duplicate: ${match.url} ("${match.title}").\n` +
|
|
365
|
+
"Proposed evidence comment (not posted):\n\n" +
|
|
366
|
+
`${commentBody}\n` +
|
|
367
|
+
"Re-run with --confirm to append this evidence as a comment (never creates a new issue).\n");
|
|
368
|
+
}
|
|
369
|
+
function buildClosedMatchBlock(match) {
|
|
370
|
+
return (`[deft feedback] Closed duplicate: ${match.url} ("${match.title}").\n` +
|
|
371
|
+
"Re-run with an explicit disposition before any write:\n" +
|
|
372
|
+
" --on-closed=comment append evidence on the closed issue (no reopen)\n" +
|
|
373
|
+
" --on-closed=create-anyway create a new issue with Related: closed #<n>\n");
|
|
374
|
+
}
|
|
375
|
+
function incompleteClosedSearchNote(stopReason, inspectedIssueCount, boundary) {
|
|
376
|
+
if (stopReason === "window-exhausted") {
|
|
377
|
+
return (`[deft feedback] Note: closed-issue search incomplete ` +
|
|
378
|
+
`(stopReason=window-exhausted, inspectedIssueCount=${inspectedIssueCount}, window=${boundary}).\n`);
|
|
379
|
+
}
|
|
380
|
+
return (`[deft feedback] Note: closed-issue search incomplete ` +
|
|
381
|
+
`(stopReason=cap, inspectedIssueCount=${inspectedIssueCount}, budget=${boundary}).\n`);
|
|
382
|
+
}
|
|
383
|
+
function extractCommentUrl(repo, issueNumber, created) {
|
|
384
|
+
if (typeof created.html_url === "string" && created.html_url.length > 0) {
|
|
385
|
+
return created.html_url;
|
|
386
|
+
}
|
|
387
|
+
const id = created.id;
|
|
388
|
+
if (typeof id === "number" && Number.isFinite(id)) {
|
|
389
|
+
return `https://github.com/${repo}/issues/${issueNumber}#issuecomment-${id}`;
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
function mutationsAllowed(options, offline, dryRun) {
|
|
394
|
+
return options.confirm === true && !dryRun && !offline;
|
|
395
|
+
}
|
|
396
|
+
/** Core feedback:file workflow (#1709 / #5099 Prefer-A Recut-2). */
|
|
151
397
|
export function runFeedbackFile(options) {
|
|
152
398
|
const summary = sanitizeOneLine(options.summary);
|
|
153
399
|
if (summary.length === 0) {
|
|
@@ -174,60 +420,218 @@ export function runFeedbackFile(options) {
|
|
|
174
420
|
const projectRoot = resolve(projectRootRaw);
|
|
175
421
|
const repo = resolveRepo(options);
|
|
176
422
|
const title = buildFrameworkGapTitle(summary, { adoptionBlocker: options.adoptionBlocker });
|
|
177
|
-
const
|
|
423
|
+
const issueBody = buildFrameworkGapBody(options);
|
|
424
|
+
const commentBody = buildFrameworkGapBody(options, { mode: "comment" });
|
|
178
425
|
if (isMaintainerFrameworkRepo(projectRoot)) {
|
|
179
426
|
return {
|
|
180
427
|
outcome: "skipped-maintainer",
|
|
181
428
|
exitCode: 0,
|
|
182
429
|
title,
|
|
183
|
-
body,
|
|
430
|
+
body: issueBody,
|
|
184
431
|
repo,
|
|
185
432
|
message: "[deft feedback] Skipped: maintainer framework repo detected; gap escalation targets consumer projects only.\n",
|
|
186
433
|
};
|
|
187
434
|
}
|
|
188
435
|
const policy = resolveValueFeedback(projectRoot);
|
|
189
436
|
if (!isValueFeedbackPathAllowed("upstreamPrompt", policy)) {
|
|
437
|
+
const readout = options.dryRun === true || options.json === true
|
|
438
|
+
? `${buildConfirmationPrompt(title, issueBody)}\n`
|
|
439
|
+
: "";
|
|
190
440
|
return {
|
|
191
441
|
outcome: "blocked-policy",
|
|
192
442
|
exitCode: 1,
|
|
193
443
|
title,
|
|
194
|
-
body,
|
|
444
|
+
body: issueBody,
|
|
195
445
|
repo,
|
|
196
446
|
message: "[deft feedback] Blocked: plan.policy.valueFeedback upstreamPrompt is OFF. " +
|
|
197
|
-
`Enable with \`${policyColonInvocation("enable-value-feedback", " -- --confirm")}
|
|
198
|
-
`or inspect via \`${policyColonInvocation("show", " --field=valueFeedback")}\`.\n
|
|
447
|
+
`Enable with \`${policyColonInvocation("enable-value-feedback", " -- --confirm --upstream-prompt")}\`, ` +
|
|
448
|
+
`or inspect via \`${policyColonInvocation("show", " --field=valueFeedback")}\`.\n` +
|
|
449
|
+
readout,
|
|
199
450
|
};
|
|
200
451
|
}
|
|
201
452
|
const offline = isOffline();
|
|
202
453
|
const dryRun = options.dryRun ?? false;
|
|
203
454
|
const seams = options.seams ?? {};
|
|
204
455
|
let dedupSkippedNote = "";
|
|
456
|
+
let incompleteNote = "";
|
|
457
|
+
let createBody = issueBody;
|
|
205
458
|
if (!offline) {
|
|
206
459
|
try {
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
460
|
+
const openSearch = findDuplicateIssue(repo, title, seams);
|
|
461
|
+
if (!openSearch.ok) {
|
|
209
462
|
return {
|
|
210
|
-
outcome: "
|
|
211
|
-
exitCode:
|
|
463
|
+
outcome: "error-network",
|
|
464
|
+
exitCode: 2,
|
|
212
465
|
title,
|
|
213
|
-
body,
|
|
466
|
+
body: issueBody,
|
|
214
467
|
repo,
|
|
215
|
-
|
|
216
|
-
message: `[deft feedback] Duplicate detected: open issue ${duplicate.url} ` +
|
|
217
|
-
`("${duplicate.title}") matches this report.\n`,
|
|
468
|
+
message: networkErrorMessage("Open-issue inventory / duplicate search failed", openSearch.error),
|
|
218
469
|
};
|
|
219
470
|
}
|
|
471
|
+
const openMatch = openSearch.match;
|
|
472
|
+
if (openMatch !== null) {
|
|
473
|
+
if (!options.confirm) {
|
|
474
|
+
return {
|
|
475
|
+
outcome: "draft",
|
|
476
|
+
exitCode: 1,
|
|
477
|
+
title,
|
|
478
|
+
body: commentBody,
|
|
479
|
+
repo,
|
|
480
|
+
duplicateUrl: openMatch.url,
|
|
481
|
+
issueUrl: openMatch.url,
|
|
482
|
+
message: buildOpenMatchPreview(openMatch, commentBody),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
if (!mutationsAllowed(options, offline, dryRun)) {
|
|
486
|
+
return {
|
|
487
|
+
outcome: "draft",
|
|
488
|
+
exitCode: 0,
|
|
489
|
+
title,
|
|
490
|
+
body: commentBody,
|
|
491
|
+
repo,
|
|
492
|
+
duplicateUrl: openMatch.url,
|
|
493
|
+
issueUrl: openMatch.url,
|
|
494
|
+
message: `[deft feedback] Dry run: would comment on open duplicate ${openMatch.url}.\n` +
|
|
495
|
+
`${buildOpenMatchPreview(openMatch, commentBody)}`,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
try {
|
|
499
|
+
const created = restPostComment(repo, openMatch.number, commentBody, seams);
|
|
500
|
+
const commentUrl = extractCommentUrl(repo, openMatch.number, created);
|
|
501
|
+
return {
|
|
502
|
+
outcome: "commented-open-duplicate",
|
|
503
|
+
exitCode: 0,
|
|
504
|
+
title,
|
|
505
|
+
body: commentBody,
|
|
506
|
+
repo,
|
|
507
|
+
issueUrl: openMatch.url,
|
|
508
|
+
duplicateUrl: openMatch.url,
|
|
509
|
+
commentUrl,
|
|
510
|
+
message: commentUrl !== null
|
|
511
|
+
? `[deft feedback] Commented on open duplicate ${openMatch.url}\nComment: ${commentUrl}\n`
|
|
512
|
+
: `[deft feedback] Commented on open duplicate ${openMatch.url}\n`,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
catch (err) {
|
|
516
|
+
return {
|
|
517
|
+
outcome: "error-network",
|
|
518
|
+
exitCode: 2,
|
|
519
|
+
title,
|
|
520
|
+
body: commentBody,
|
|
521
|
+
repo,
|
|
522
|
+
duplicateUrl: openMatch.url,
|
|
523
|
+
message: networkErrorMessage("Open-duplicate comment failed", err),
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
}
|
|
220
527
|
}
|
|
221
528
|
catch (err) {
|
|
222
529
|
return {
|
|
223
530
|
outcome: "error-network",
|
|
224
531
|
exitCode: 2,
|
|
225
532
|
title,
|
|
226
|
-
body,
|
|
533
|
+
body: issueBody,
|
|
227
534
|
repo,
|
|
228
|
-
message: networkErrorMessage("
|
|
535
|
+
message: networkErrorMessage("Open-issue inventory / duplicate search failed", err),
|
|
229
536
|
};
|
|
230
537
|
}
|
|
538
|
+
const closedWalk = walkClosedIssuesForFeedbackDedup(repo, title, {
|
|
539
|
+
window: options.closedWindow,
|
|
540
|
+
remainingBudget: options.closedRowBudget,
|
|
541
|
+
}, seams);
|
|
542
|
+
if (closedWalk.malformedMatch || closedWalk.outcome === "error") {
|
|
543
|
+
return {
|
|
544
|
+
outcome: "error-network",
|
|
545
|
+
exitCode: 2,
|
|
546
|
+
title,
|
|
547
|
+
body: issueBody,
|
|
548
|
+
repo,
|
|
549
|
+
message: networkErrorMessage("Closed-issue search failed", closedWalk.errorMessage ?? "closed walk error"),
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
if (closedWalk.match !== null) {
|
|
553
|
+
const closedMatch = closedWalk.match;
|
|
554
|
+
if (options.onClosed === undefined) {
|
|
555
|
+
return {
|
|
556
|
+
outcome: "blocked-closed-duplicate",
|
|
557
|
+
exitCode: 1,
|
|
558
|
+
title,
|
|
559
|
+
body: issueBody,
|
|
560
|
+
repo,
|
|
561
|
+
duplicateUrl: closedMatch.url,
|
|
562
|
+
issueUrl: closedMatch.url,
|
|
563
|
+
message: buildClosedMatchBlock(closedMatch),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
if (options.onClosed === "comment") {
|
|
567
|
+
if (!options.confirm) {
|
|
568
|
+
return {
|
|
569
|
+
outcome: "draft",
|
|
570
|
+
exitCode: 1,
|
|
571
|
+
title,
|
|
572
|
+
body: commentBody,
|
|
573
|
+
repo,
|
|
574
|
+
duplicateUrl: closedMatch.url,
|
|
575
|
+
issueUrl: closedMatch.url,
|
|
576
|
+
message: buildClosedMatchBlock(closedMatch) +
|
|
577
|
+
"\nProposed evidence comment (not posted):\n\n" +
|
|
578
|
+
`${commentBody}\n` +
|
|
579
|
+
"Re-run with --confirm --on-closed=comment to append evidence (no reopen).\n",
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
if (!mutationsAllowed(options, offline, dryRun)) {
|
|
583
|
+
return {
|
|
584
|
+
outcome: "draft",
|
|
585
|
+
exitCode: 0,
|
|
586
|
+
title,
|
|
587
|
+
body: commentBody,
|
|
588
|
+
repo,
|
|
589
|
+
duplicateUrl: closedMatch.url,
|
|
590
|
+
issueUrl: closedMatch.url,
|
|
591
|
+
message: `[deft feedback] Dry run: would comment on closed duplicate ${closedMatch.url} (no reopen).\n` +
|
|
592
|
+
`${commentBody}\n`,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
const created = restPostComment(repo, closedMatch.number, commentBody, seams);
|
|
597
|
+
const commentUrl = extractCommentUrl(repo, closedMatch.number, created);
|
|
598
|
+
return {
|
|
599
|
+
outcome: "commented-open-duplicate",
|
|
600
|
+
exitCode: 0,
|
|
601
|
+
title,
|
|
602
|
+
body: commentBody,
|
|
603
|
+
repo,
|
|
604
|
+
issueUrl: closedMatch.url,
|
|
605
|
+
duplicateUrl: closedMatch.url,
|
|
606
|
+
commentUrl,
|
|
607
|
+
message: commentUrl !== null
|
|
608
|
+
? `[deft feedback] Commented on closed duplicate ${closedMatch.url} (no reopen)\nComment: ${commentUrl}\n`
|
|
609
|
+
: `[deft feedback] Commented on closed duplicate ${closedMatch.url} (no reopen)\n`,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
catch (err) {
|
|
613
|
+
return {
|
|
614
|
+
outcome: "error-network",
|
|
615
|
+
exitCode: 2,
|
|
616
|
+
title,
|
|
617
|
+
body: commentBody,
|
|
618
|
+
repo,
|
|
619
|
+
duplicateUrl: closedMatch.url,
|
|
620
|
+
message: networkErrorMessage("Closed-duplicate comment failed", err),
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
createBody = buildFrameworkGapBody(options, {
|
|
625
|
+
mode: "issue",
|
|
626
|
+
relatedClosedNumber: closedMatch.number,
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
else if (closedWalk.outcome === "window-exhausted") {
|
|
630
|
+
incompleteNote = incompleteClosedSearchNote("window-exhausted", closedWalk.inspectedIssueCount, options.closedWindow ?? FEEDBACK_CLOSED_DEDUP_WINDOW);
|
|
631
|
+
}
|
|
632
|
+
else if (closedWalk.outcome === "cap") {
|
|
633
|
+
incompleteNote = incompleteClosedSearchNote("cap", closedWalk.inspectedIssueCount, options.closedRowBudget ?? REST_SHARED_ROW_BUDGET);
|
|
634
|
+
}
|
|
231
635
|
}
|
|
232
636
|
else {
|
|
233
637
|
dedupSkippedNote = "[deft feedback] Note: duplicate detection skipped (DEFT_NO_NETWORK=1).\n\n";
|
|
@@ -237,24 +641,24 @@ export function runFeedbackFile(options) {
|
|
|
237
641
|
outcome: "draft",
|
|
238
642
|
exitCode: 1,
|
|
239
643
|
title,
|
|
240
|
-
body,
|
|
644
|
+
body: createBody,
|
|
241
645
|
repo,
|
|
242
|
-
message: `${dedupSkippedNote}${buildConfirmationPrompt(title,
|
|
646
|
+
message: `${dedupSkippedNote}${incompleteNote}${buildConfirmationPrompt(title, createBody)}`,
|
|
243
647
|
};
|
|
244
648
|
}
|
|
245
|
-
if (
|
|
649
|
+
if (!mutationsAllowed(options, offline, dryRun)) {
|
|
246
650
|
return {
|
|
247
651
|
outcome: "draft",
|
|
248
652
|
exitCode: 0,
|
|
249
653
|
title,
|
|
250
|
-
body,
|
|
654
|
+
body: createBody,
|
|
251
655
|
repo,
|
|
252
|
-
message: `${dedupSkippedNote}[deft feedback] Dry run: would file upstream issue in ${repo}.\n` +
|
|
253
|
-
`${buildConfirmationPrompt(title,
|
|
656
|
+
message: `${dedupSkippedNote}${incompleteNote}[deft feedback] Dry run: would file upstream issue in ${repo}.\n` +
|
|
657
|
+
`${buildConfirmationPrompt(title, createBody)}`,
|
|
254
658
|
};
|
|
255
659
|
}
|
|
256
660
|
try {
|
|
257
|
-
const created = restCreateIssue(repo, title,
|
|
661
|
+
const created = restCreateIssue(repo, title, createBody, [], seams);
|
|
258
662
|
const issueUrl = typeof created.html_url === "string" && created.html_url.length > 0 ? created.html_url : null;
|
|
259
663
|
const number = created.number;
|
|
260
664
|
const urlLine = issueUrl ??
|
|
@@ -263,12 +667,13 @@ export function runFeedbackFile(options) {
|
|
|
263
667
|
outcome: "filed",
|
|
264
668
|
exitCode: 0,
|
|
265
669
|
title,
|
|
266
|
-
body,
|
|
670
|
+
body: createBody,
|
|
267
671
|
repo,
|
|
268
672
|
issueUrl: urlLine,
|
|
269
|
-
message:
|
|
270
|
-
|
|
271
|
-
|
|
673
|
+
message: `${incompleteNote}` +
|
|
674
|
+
(urlLine !== null
|
|
675
|
+
? `[deft feedback] Filed upstream issue: ${urlLine}\n`
|
|
676
|
+
: "[deft feedback] Issue filed upstream (URL unavailable in API response).\n"),
|
|
272
677
|
};
|
|
273
678
|
}
|
|
274
679
|
catch (err) {
|
|
@@ -276,7 +681,7 @@ export function runFeedbackFile(options) {
|
|
|
276
681
|
outcome: "error-network",
|
|
277
682
|
exitCode: 2,
|
|
278
683
|
title,
|
|
279
|
-
body,
|
|
684
|
+
body: createBody,
|
|
280
685
|
repo,
|
|
281
686
|
message: networkErrorMessage("Upstream filing failed", err),
|
|
282
687
|
};
|
|
@@ -288,6 +693,12 @@ function appendContextValue(collected, value) {
|
|
|
288
693
|
if (typeof value === "string")
|
|
289
694
|
collected.push(value);
|
|
290
695
|
}
|
|
696
|
+
function parseOnClosedValue(value) {
|
|
697
|
+
if (value === "comment" || value === "create-anyway") {
|
|
698
|
+
return value;
|
|
699
|
+
}
|
|
700
|
+
return "bad";
|
|
701
|
+
}
|
|
291
702
|
/** Parse argv for feedback:file. */
|
|
292
703
|
export function parseFeedbackFileArgs(argv) {
|
|
293
704
|
const out = {};
|
|
@@ -357,6 +768,20 @@ export function parseFeedbackFileArgs(argv) {
|
|
|
357
768
|
else if (arg?.startsWith("--recovery-cost=")) {
|
|
358
769
|
out.recoveryCost = arg.slice("--recovery-cost=".length);
|
|
359
770
|
}
|
|
771
|
+
else if (arg === "--on-closed") {
|
|
772
|
+
const parsed = parseOnClosedValue(argv[++i]);
|
|
773
|
+
if (parsed === "bad") {
|
|
774
|
+
return { ...out, error: "invalid --on-closed value (expected comment|create-anyway)" };
|
|
775
|
+
}
|
|
776
|
+
out.onClosed = parsed;
|
|
777
|
+
}
|
|
778
|
+
else if (arg?.startsWith("--on-closed=")) {
|
|
779
|
+
const parsed = parseOnClosedValue(arg.slice("--on-closed=".length));
|
|
780
|
+
if (parsed === "bad") {
|
|
781
|
+
return { ...out, error: "invalid --on-closed value (expected comment|create-anyway)" };
|
|
782
|
+
}
|
|
783
|
+
out.onClosed = parsed;
|
|
784
|
+
}
|
|
360
785
|
else if (arg === "--repo") {
|
|
361
786
|
out.repo = argv[++i];
|
|
362
787
|
}
|
|
@@ -404,6 +829,7 @@ export function feedbackFileMain(argv) {
|
|
|
404
829
|
confirm: args.confirm,
|
|
405
830
|
dryRun: args.dryRun,
|
|
406
831
|
json: args.json,
|
|
832
|
+
onClosed: args.onClosed,
|
|
407
833
|
repo: args.repo,
|
|
408
834
|
projectRoot: args.projectRoot,
|
|
409
835
|
});
|
|
@@ -415,6 +841,7 @@ export function feedbackFileMain(argv) {
|
|
|
415
841
|
body: result.body,
|
|
416
842
|
repo: result.repo,
|
|
417
843
|
issue_url: result.issueUrl ?? null,
|
|
844
|
+
comment_url: result.commentUrl ?? null,
|
|
418
845
|
duplicate_url: result.duplicateUrl ?? null,
|
|
419
846
|
message: result.message.trim(),
|
|
420
847
|
}, null, 2)}\n`);
|