@dadado/agent-kit-cli 5.15.0 → 5.16.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/dashboard/dashboard-data.mjs +69 -65
- package/dashboard/lib/guards.d.mts +5 -0
- package/dashboard/lib/guards.mjs +31 -0
- package/dashboard/lib/identity-probe.mjs +56 -0
- package/dashboard/lib/pipeline-cache.mjs +58 -0
- package/dashboard/lib/processes.mjs +71 -0
- package/dashboard/serve.mjs +43 -1
- package/dashboard/start-broadcast.mjs +2 -12
- package/dashboard/start.mjs +2 -13
- package/dist/add-7UMSGVWC.js +150 -0
- package/dist/chunk-2IPXQFJE.js +253 -0
- package/dist/chunk-2YU2H7I6.js +42 -0
- package/dist/chunk-4IWG5CVE.js +214 -0
- package/dist/chunk-5OXPNVCF.js +106 -0
- package/dist/chunk-5VCOWTTP.js +6 -0
- package/dist/chunk-6QV2BFGA.js +341 -0
- package/dist/chunk-77V75A7O.js +39 -0
- package/dist/chunk-7HDQEK6T.js +26 -0
- package/dist/chunk-CBCW5R5J.js +484 -0
- package/dist/chunk-CGTE7DHN.js +410 -0
- package/dist/chunk-DE3LQFSJ.js +18 -0
- package/dist/chunk-F6C3GFKV.js +57 -0
- package/dist/chunk-HFUH3HVS.js +282 -0
- package/dist/chunk-HS7LVVLO.js +74 -0
- package/dist/chunk-IHROBTNM.js +135 -0
- package/dist/chunk-KD2MYBB6.js +213 -0
- package/dist/chunk-KI6ZHBPV.js +332 -0
- package/dist/chunk-MAPOQEA2.js +43 -0
- package/dist/chunk-NQ7IQ5XT.js +26 -0
- package/dist/chunk-OUQZC744.js +1315 -0
- package/dist/chunk-OZ4QV6LM.js +1078 -0
- package/dist/chunk-PCCQUWE5.js +817 -0
- package/dist/chunk-PNFF6OU3.js +273 -0
- package/dist/chunk-QTON2GQX.js +203 -0
- package/dist/chunk-TFZ2JUXH.js +1081 -0
- package/dist/chunk-VGZBARQC.js +318 -0
- package/dist/chunk-VS2JD2XC.js +1453 -0
- package/dist/chunk-VY7MTPJ2.js +205 -0
- package/dist/chunk-WW3YZKHW.js +165 -0
- package/dist/chunk-XWPO3VYQ.js +53 -0
- package/dist/chunk-XZZAVHH4.js +503 -0
- package/dist/chunk-YGBBHKBE.js +82 -0
- package/dist/chunk-ZB43YMHW.js +20 -0
- package/dist/chunk-ZCTSUQRR.js +874 -0
- package/dist/chunk-ZXKZBTXE.js +17 -0
- package/dist/contribute-YKK6YCSI.js +367 -0
- package/dist/cursor-awareness-KTL2RGKD.js +77 -0
- package/dist/dashboard-YSNUGBFY.js +21 -0
- package/dist/dashboard-broadcast-M4CMW7RX.js +119 -0
- package/dist/diff-YTR7VOAN.js +74 -0
- package/dist/doctor-TA2FWREU.js +497 -0
- package/dist/dogfood-KKFVRWT6.js +366 -0
- package/dist/guard-EHXF76ID.js +530 -0
- package/dist/handoff-3UI6VKVG.js +302 -0
- package/dist/hook-MUV6Y7UW.js +1074 -0
- package/dist/index.js +62 -14068
- package/dist/init-TTTKGC4C.js +127 -0
- package/dist/install-ZMFJBPOI.js +34 -0
- package/dist/mission-control-LJACEVVN.js +89 -0
- package/dist/monitors-QV2IS3MP.js +176 -0
- package/dist/plan-index-JMC7Z4DW.js +44 -0
- package/dist/public-inbound-radar-WP3XF7GS.js +68 -0
- package/dist/remote-issues-SZ4HDQL5.js +44 -0
- package/dist/run-I3BFGULT.js +243 -0
- package/dist/run-plan-FO7CL4M3.js +118 -0
- package/dist/scan-NI2SWJ2E.js +33 -0
- package/dist/setup-global-JUVMJXJ5.js +340 -0
- package/dist/status-MVCUNCWX.js +138 -0
- package/dist/update-Q4EBIRUB.js +260 -0
- package/dist/validate-V2CW7J4B.js +201 -0
- package/package.json +1 -1
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import {
|
|
2
|
+
REMOTE_ISSUE_TITLE_PREFIX,
|
|
3
|
+
parseRemoteUrl
|
|
4
|
+
} from "./chunk-VY7MTPJ2.js";
|
|
5
|
+
import {
|
|
6
|
+
PUBLIC_INBOUND_REPO
|
|
7
|
+
} from "./chunk-2IPXQFJE.js";
|
|
8
|
+
import {
|
|
9
|
+
logger
|
|
10
|
+
} from "./chunk-ZB43YMHW.js";
|
|
11
|
+
import "./chunk-NQ7IQ5XT.js";
|
|
12
|
+
import "./chunk-77V75A7O.js";
|
|
13
|
+
|
|
14
|
+
// src/commands/dogfood.ts
|
|
15
|
+
import { defineCommand } from "citty";
|
|
16
|
+
|
|
17
|
+
// src/lifecycle/dogfood-file.ts
|
|
18
|
+
import { execFile } from "child_process";
|
|
19
|
+
import { access, mkdir, readFile, writeFile } from "fs/promises";
|
|
20
|
+
import path from "path";
|
|
21
|
+
import { promisify } from "util";
|
|
22
|
+
var execFileAsync = promisify(execFile);
|
|
23
|
+
var TRANSIENT_PHRASES = [/\bas I mentioned\b/gi, /\bdear user\b/gi, /\bconforme falamos\b/gi];
|
|
24
|
+
function hygieneStrip(input) {
|
|
25
|
+
const redacted = /* @__PURE__ */ new Set();
|
|
26
|
+
let text = input;
|
|
27
|
+
const apply = (name, pattern, replacement) => {
|
|
28
|
+
text = text.replace(pattern, () => {
|
|
29
|
+
redacted.add(name);
|
|
30
|
+
return replacement;
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
apply(
|
|
34
|
+
"secret",
|
|
35
|
+
/\b(?:gh[pousr]_[A-Za-z0-9]{20,}|glpat-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|xox[baprs]-[A-Za-z0-9-]{10,})\b/g,
|
|
36
|
+
"[redacted-secret]"
|
|
37
|
+
);
|
|
38
|
+
apply(
|
|
39
|
+
"secret",
|
|
40
|
+
/\b(?:api[_-]?key|token|secret|password|passwd)\s*[:=]\s*\S+/gi,
|
|
41
|
+
"[redacted-secret]"
|
|
42
|
+
);
|
|
43
|
+
apply("secret", /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/g, "[redacted-secret]");
|
|
44
|
+
apply("email", /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g, "[redacted-email]");
|
|
45
|
+
apply("path", /(?:\/Users|\/home)\/[^\s/]+(?:\/[^\s`'")]*)?/g, "[redacted-path]");
|
|
46
|
+
for (const phrase of TRANSIENT_PHRASES) apply("transient", phrase, "");
|
|
47
|
+
text = text.replace(/[ \t]{2,}/g, " ");
|
|
48
|
+
const needsAnonymization = ["secret", "email", "path"].some((kind) => redacted.has(kind));
|
|
49
|
+
return { text: text.trim(), redacted: [...redacted], needsAnonymization };
|
|
50
|
+
}
|
|
51
|
+
function normalizeTopic(topic) {
|
|
52
|
+
const normalized = topic.toLowerCase().replace(/\d{4}[-_]?\d{2}[-_]?\d{2}\s*$/, "").replace(/[\s_-]+/g, "_").replace(/[^a-z0-9_]/g, "").replace(/^_+|_+$/g, "").slice(0, 40).replace(/_+$/g, "");
|
|
53
|
+
return normalized || "note";
|
|
54
|
+
}
|
|
55
|
+
function dogfoodFilename(topic, date) {
|
|
56
|
+
const stamp = date.toISOString().slice(0, 10).replaceAll("-", "_");
|
|
57
|
+
return `cursor_${normalizeTopic(topic)}_${stamp}.md`;
|
|
58
|
+
}
|
|
59
|
+
function renderDogfoodNote(input) {
|
|
60
|
+
const tags = (input.tags ?? []).map((tag) => tag.trim().toLowerCase()).filter(Boolean);
|
|
61
|
+
const lines = [
|
|
62
|
+
`# Dogfood: ${input.topic}`,
|
|
63
|
+
"",
|
|
64
|
+
`- **Date:** ${input.date.toISOString().slice(0, 10)}`,
|
|
65
|
+
`- **Lane:** ${input.lane}`,
|
|
66
|
+
`- **Source:** ${input.source ?? "explicit dogfood args"}`
|
|
67
|
+
];
|
|
68
|
+
if (input.needsAnonymization) lines.push("- **Flag:** needs-anonymization");
|
|
69
|
+
lines.push(
|
|
70
|
+
"",
|
|
71
|
+
"## Observation",
|
|
72
|
+
"",
|
|
73
|
+
input.observation || "(no description)",
|
|
74
|
+
"",
|
|
75
|
+
"## Impact",
|
|
76
|
+
"",
|
|
77
|
+
input.impact || "(to be assessed at triage)",
|
|
78
|
+
"",
|
|
79
|
+
"## Triage (initial)",
|
|
80
|
+
"",
|
|
81
|
+
"- Fix now / Park / Ignore",
|
|
82
|
+
`- **Tags:** ${tags.join(", ")}`,
|
|
83
|
+
""
|
|
84
|
+
);
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
87
|
+
async function exists(target) {
|
|
88
|
+
try {
|
|
89
|
+
await access(target);
|
|
90
|
+
return true;
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function defaultGetRemoteUrl(cwd, name) {
|
|
96
|
+
try {
|
|
97
|
+
const { stdout } = await execFileAsync("git", ["remote", "get-url", name], {
|
|
98
|
+
cwd,
|
|
99
|
+
encoding: "utf8",
|
|
100
|
+
timeout: 5e3
|
|
101
|
+
});
|
|
102
|
+
return stdout.trim() || null;
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function defaultRunCli(bin, args, options) {
|
|
108
|
+
const { stdout } = await execFileAsync(bin, args, {
|
|
109
|
+
cwd: options?.cwd,
|
|
110
|
+
encoding: "utf8",
|
|
111
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
112
|
+
timeout: 3e4
|
|
113
|
+
});
|
|
114
|
+
return stdout;
|
|
115
|
+
}
|
|
116
|
+
async function detectDogfoodLane(root, deps = {}) {
|
|
117
|
+
const getRemoteUrl = deps.getRemoteUrl ?? defaultGetRemoteUrl;
|
|
118
|
+
const origin = await getRemoteUrl(root, "origin");
|
|
119
|
+
if (origin && /agent-kit-dev/i.test(origin)) return "factory";
|
|
120
|
+
if (await exists(path.join(root, "dogfood", "README.md"))) return "factory";
|
|
121
|
+
if (await exists(path.join(root, ".cursor", "agent-kit.json"))) return "consumer";
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
function inboxDir(root, lane) {
|
|
125
|
+
return lane === "factory" ? path.join(root, "dogfood") : path.join(root, ".cursor", "dogfood");
|
|
126
|
+
}
|
|
127
|
+
var UNPROCESSED_HEADING = /^(#{2,3})\s+Unprocessed Files\s*$/m;
|
|
128
|
+
var NEW_INDEX = "# Dogfood inbox\n\n### Unprocessed Files\n\n### Processed Files\n";
|
|
129
|
+
function appendUnprocessedEntry(readme, entry) {
|
|
130
|
+
const match = UNPROCESSED_HEADING.exec(readme);
|
|
131
|
+
if (!match) {
|
|
132
|
+
const base = readme.trimEnd();
|
|
133
|
+
return `${base}${base ? "\n\n" : ""}### Unprocessed Files
|
|
134
|
+
|
|
135
|
+
${entry}
|
|
136
|
+
`;
|
|
137
|
+
}
|
|
138
|
+
const level = (match[1] ?? "###").length;
|
|
139
|
+
const start = match.index + match[0].length;
|
|
140
|
+
const rest = readme.slice(start);
|
|
141
|
+
const next = new RegExp(`^#{1,${level}}\\s`, "m").exec(rest);
|
|
142
|
+
const sectionEnd = next ? start + next.index : readme.length;
|
|
143
|
+
const section = readme.slice(start, sectionEnd).replace(/\s+$/, "");
|
|
144
|
+
const tail = readme.slice(sectionEnd);
|
|
145
|
+
return `${readme.slice(0, start)}${section}
|
|
146
|
+
${entry}
|
|
147
|
+
${tail ? `
|
|
148
|
+
${tail}` : ""}`;
|
|
149
|
+
}
|
|
150
|
+
async function addDogfoodNote(root, input, deps = {}) {
|
|
151
|
+
const lane = await detectDogfoodLane(root, deps);
|
|
152
|
+
if (!lane) return { status: "unknown-lane" };
|
|
153
|
+
const now = deps.now?.() ?? /* @__PURE__ */ new Date();
|
|
154
|
+
const topic = hygieneStrip(input.topic);
|
|
155
|
+
const observation = hygieneStrip(input.summary ?? input.topic);
|
|
156
|
+
const impact = hygieneStrip(input.impact ?? "");
|
|
157
|
+
const redacted = [.../* @__PURE__ */ new Set([...topic.redacted, ...observation.redacted, ...impact.redacted])];
|
|
158
|
+
const needsAnonymization = topic.needsAnonymization || observation.needsAnonymization || impact.needsAnonymization;
|
|
159
|
+
const dir = inboxDir(root, lane);
|
|
160
|
+
const filename = dogfoodFilename(topic.text || input.topic, now);
|
|
161
|
+
const file = path.join(dir, filename);
|
|
162
|
+
if (await exists(file)) return { status: "exists", lane, file };
|
|
163
|
+
await mkdir(dir, { recursive: true });
|
|
164
|
+
await writeFile(
|
|
165
|
+
file,
|
|
166
|
+
renderDogfoodNote({
|
|
167
|
+
topic: topic.text || input.topic,
|
|
168
|
+
lane,
|
|
169
|
+
observation: observation.text,
|
|
170
|
+
impact: impact.text || void 0,
|
|
171
|
+
tags: input.tags,
|
|
172
|
+
source: input.source,
|
|
173
|
+
date: now,
|
|
174
|
+
needsAnonymization
|
|
175
|
+
}),
|
|
176
|
+
"utf8"
|
|
177
|
+
);
|
|
178
|
+
const indexFile = path.join(dir, "README.md");
|
|
179
|
+
const oneLine = observation.text.split(/\r?\n/)[0]?.slice(0, 200) ?? "";
|
|
180
|
+
const entry = `- \`${filename}\` - ${oneLine || topic.text} (captured ${now.toISOString().slice(0, 10)})${needsAnonymization ? " [needs-anonymization]" : ""}`;
|
|
181
|
+
const current = await exists(indexFile) ? await readFile(indexFile, "utf8") : NEW_INDEX;
|
|
182
|
+
await writeFile(indexFile, appendUnprocessedEntry(current, entry), "utf8");
|
|
183
|
+
return {
|
|
184
|
+
status: "filed",
|
|
185
|
+
lane,
|
|
186
|
+
file,
|
|
187
|
+
relativeFile: path.relative(root, file),
|
|
188
|
+
indexFile,
|
|
189
|
+
needsAnonymization,
|
|
190
|
+
redacted
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function withMarker(title) {
|
|
194
|
+
const trimmed = title.trim();
|
|
195
|
+
return trimmed.toLowerCase().startsWith(REMOTE_ISSUE_TITLE_PREFIX.toLowerCase()) ? trimmed : `${REMOTE_ISSUE_TITLE_PREFIX} ${trimmed}`;
|
|
196
|
+
}
|
|
197
|
+
function classifyCliFailure(error) {
|
|
198
|
+
const err = error;
|
|
199
|
+
const text = `${String(err?.message ?? "")} ${String(err?.stderr ?? "")}`;
|
|
200
|
+
if (err?.code === "ENOENT" || /ENOENT|command not found/i.test(text))
|
|
201
|
+
return "skipped-cli-missing";
|
|
202
|
+
if (/auth|login|401|403|token|credential/i.test(text)) return "skipped-auth";
|
|
203
|
+
return "skipped-error";
|
|
204
|
+
}
|
|
205
|
+
function extractUrl(stdout) {
|
|
206
|
+
const trimmed = stdout.trim();
|
|
207
|
+
if (!trimmed) return null;
|
|
208
|
+
try {
|
|
209
|
+
const parsed = JSON.parse(trimmed);
|
|
210
|
+
const url = parsed.web_url ?? parsed.url;
|
|
211
|
+
if (typeof url === "string" && url) return url;
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
return /https?:\/\/\S+/.exec(trimmed)?.[0] ?? null;
|
|
215
|
+
}
|
|
216
|
+
async function fileDogfoodIssue(root, input, deps = {}) {
|
|
217
|
+
const getRemoteUrl = deps.getRemoteUrl ?? defaultGetRemoteUrl;
|
|
218
|
+
const runCli = deps.runCli ?? defaultRunCli;
|
|
219
|
+
const title = hygieneStrip(withMarker(input.title));
|
|
220
|
+
const body = hygieneStrip(input.body);
|
|
221
|
+
const redacted = [.../* @__PURE__ */ new Set([...title.redacted, ...body.redacted])];
|
|
222
|
+
const base = {
|
|
223
|
+
visibility: input.visibility,
|
|
224
|
+
provider: null,
|
|
225
|
+
repo: null,
|
|
226
|
+
title: title.text,
|
|
227
|
+
confidential: false,
|
|
228
|
+
url: null,
|
|
229
|
+
redacted
|
|
230
|
+
};
|
|
231
|
+
const stop = (status, message, extra = {}) => ({ ...base, status, message, ...extra });
|
|
232
|
+
const lane = await detectDogfoodLane(root, deps);
|
|
233
|
+
if (!lane)
|
|
234
|
+
return stop("refused-unknown-lane", "Lane unknown: not the factory nor a kit install.");
|
|
235
|
+
let target;
|
|
236
|
+
if (input.visibility === "public") {
|
|
237
|
+
if (lane !== "factory") {
|
|
238
|
+
return stop("refused-consumer-public", "Consumers never file upstream; use a private issue.");
|
|
239
|
+
}
|
|
240
|
+
if (!input.approvedPublic) {
|
|
241
|
+
return stop(
|
|
242
|
+
"refused-not-approved",
|
|
243
|
+
"Public issues need explicit operator approval (--approve-public)."
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
if (title.needsAnonymization || body.needsAnonymization) {
|
|
247
|
+
return stop(
|
|
248
|
+
"refused-hygiene",
|
|
249
|
+
`Hygiene strip removed ${redacted.join(", ")}; anonymize the text and retry.`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
target = `https://github.com/${PUBLIC_INBOUND_REPO}.git`;
|
|
253
|
+
} else {
|
|
254
|
+
target = await getRemoteUrl(root, "origin");
|
|
255
|
+
}
|
|
256
|
+
const remote = target ? parseRemoteUrl(target) : null;
|
|
257
|
+
if (!remote)
|
|
258
|
+
return stop("skipped-no-remote", `No ${input.visibility} remote to file the issue in.`);
|
|
259
|
+
const found = { provider: remote.provider, repo: remote.repo };
|
|
260
|
+
const finalTitle = title.text;
|
|
261
|
+
const finalBody = input.visibility === "public" ? body.text : input.body.trim();
|
|
262
|
+
try {
|
|
263
|
+
if (remote.provider === "gitlab") {
|
|
264
|
+
const args = ["api", "--method", "POST"];
|
|
265
|
+
if (remote.host !== "gitlab.com") args.push("--hostname", remote.host);
|
|
266
|
+
args.push(
|
|
267
|
+
`projects/${encodeURIComponent(remote.repo)}/issues`,
|
|
268
|
+
"-f",
|
|
269
|
+
`title=${finalTitle}`,
|
|
270
|
+
"-f",
|
|
271
|
+
`description=${finalBody}`,
|
|
272
|
+
"-f",
|
|
273
|
+
`confidential=${input.visibility === "private" ? "true" : "false"}`
|
|
274
|
+
);
|
|
275
|
+
const url2 = extractUrl(await runCli("glab", args, { cwd: root }));
|
|
276
|
+
return stop("filed", `Filed ${input.visibility} GitLab issue in ${remote.repo}.`, {
|
|
277
|
+
...found,
|
|
278
|
+
confidential: input.visibility === "private",
|
|
279
|
+
url: url2
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
const url = extractUrl(
|
|
283
|
+
await runCli(
|
|
284
|
+
"gh",
|
|
285
|
+
["issue", "create", "--repo", remote.repo, "--title", finalTitle, "--body", finalBody],
|
|
286
|
+
{ cwd: root }
|
|
287
|
+
)
|
|
288
|
+
);
|
|
289
|
+
return stop("filed", `Filed ${input.visibility} GitHub issue in ${remote.repo}.`, {
|
|
290
|
+
...found,
|
|
291
|
+
url
|
|
292
|
+
});
|
|
293
|
+
} catch (error) {
|
|
294
|
+
const status = classifyCliFailure(error);
|
|
295
|
+
return stop(status, `Issue not filed (${status}); the local note stays the record.`, found);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/commands/dogfood.ts
|
|
300
|
+
var dogfoodCommand = defineCommand({
|
|
301
|
+
meta: {
|
|
302
|
+
name: "dogfood",
|
|
303
|
+
description: "File dogfood findings headlessly: `add` writes the note + Unprocessed index; `file-issue` opens a private or public issue (GitHub/GitLab)."
|
|
304
|
+
},
|
|
305
|
+
subCommands: {
|
|
306
|
+
add: defineCommand({
|
|
307
|
+
meta: {
|
|
308
|
+
name: "add",
|
|
309
|
+
description: "Write the dogfood template file and append the Unprocessed index for the detected lane (factory dogfood/, consumer .cursor/dogfood/)."
|
|
310
|
+
},
|
|
311
|
+
args: {
|
|
312
|
+
topic: { type: "positional", description: "Short topic", required: true },
|
|
313
|
+
summary: { type: "string", description: "Observation (hygiene-stripped)" },
|
|
314
|
+
impact: { type: "string", description: "Impact on the kit or operator" },
|
|
315
|
+
tags: { type: "string", description: "Comma-separated lowercase tags" },
|
|
316
|
+
cwd: { type: "string", default: process.cwd() }
|
|
317
|
+
},
|
|
318
|
+
async run({ args }) {
|
|
319
|
+
const result = await addDogfoodNote(String(args.cwd), {
|
|
320
|
+
topic: String(args.topic),
|
|
321
|
+
summary: args.summary,
|
|
322
|
+
impact: args.impact,
|
|
323
|
+
tags: args.tags?.split(",").map((tag) => tag.trim()),
|
|
324
|
+
source: "agent-kit dogfood add"
|
|
325
|
+
});
|
|
326
|
+
console.log(JSON.stringify(result, null, 2));
|
|
327
|
+
if (result.status !== "filed") process.exitCode = 1;
|
|
328
|
+
}
|
|
329
|
+
}),
|
|
330
|
+
"file-issue": defineCommand({
|
|
331
|
+
meta: {
|
|
332
|
+
name: "file-issue",
|
|
333
|
+
description: "File an issue with the [Dogfood] marker. private: own origin (GitLab confidential). public: factory only, needs --approve-public and a clean hygiene strip."
|
|
334
|
+
},
|
|
335
|
+
args: {
|
|
336
|
+
visibility: { type: "string", description: "private | public", required: true },
|
|
337
|
+
title: { type: "string", required: true },
|
|
338
|
+
body: { type: "string", required: true },
|
|
339
|
+
"approve-public": {
|
|
340
|
+
type: "boolean",
|
|
341
|
+
description: "Operator approved this public issue (Ask provenance recorded in HANDOFF)",
|
|
342
|
+
default: false
|
|
343
|
+
},
|
|
344
|
+
cwd: { type: "string", default: process.cwd() }
|
|
345
|
+
},
|
|
346
|
+
async run({ args }) {
|
|
347
|
+
if (args.visibility !== "private" && args.visibility !== "public") {
|
|
348
|
+
logger.error("--visibility must be private or public");
|
|
349
|
+
process.exitCode = 1;
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const result = await fileDogfoodIssue(String(args.cwd), {
|
|
353
|
+
visibility: args.visibility,
|
|
354
|
+
title: String(args.title),
|
|
355
|
+
body: String(args.body),
|
|
356
|
+
approvedPublic: args["approve-public"] === true
|
|
357
|
+
});
|
|
358
|
+
console.log(JSON.stringify(result, null, 2));
|
|
359
|
+
if (result.status !== "filed") process.exitCode = 1;
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
export {
|
|
365
|
+
dogfoodCommand
|
|
366
|
+
};
|