@h-rig/standard-plugin 0.0.6-alpha.15 → 0.0.6-alpha.151

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.
Files changed (45) hide show
  1. package/dist/src/blocker-classifier.d.ts +1 -0
  2. package/dist/src/blocker-classifier.js +18 -0
  3. package/dist/src/bundle.d.ts +7 -0
  4. package/dist/src/bundle.js +1859 -0
  5. package/dist/src/cli-surface.d.ts +1 -0
  6. package/dist/src/cli-surface.js +12 -0
  7. package/dist/src/default-lifecycle.d.ts +2 -0
  8. package/dist/src/default-lifecycle.js +12 -0
  9. package/dist/src/dependency-graph.d.ts +1 -0
  10. package/dist/src/dependency-graph.js +22 -0
  11. package/dist/src/drift/__fixtures__/temp-repo.d.ts +9 -0
  12. package/dist/src/drift/__fixtures__/temp-repo.js +41 -0
  13. package/dist/src/drift/detect.d.ts +11 -0
  14. package/dist/src/drift/detect.js +299 -0
  15. package/dist/src/drift/extract-refs.d.ts +7 -0
  16. package/dist/src/drift/extract-refs.js +60 -0
  17. package/dist/src/drift/git-adapter.d.ts +7 -0
  18. package/dist/src/drift/git-adapter.js +63 -0
  19. package/dist/src/drift/judge.d.ts +19 -0
  20. package/dist/src/drift/judge.js +16 -0
  21. package/dist/src/drift/metadata.d.ts +13 -0
  22. package/dist/src/drift/metadata.js +33 -0
  23. package/dist/src/drift/plugin.d.ts +53 -0
  24. package/dist/src/drift/plugin.js +507 -0
  25. package/dist/src/files-source.d.ts +18 -0
  26. package/dist/src/files-source.js +4 -3
  27. package/dist/src/github-issues-source.d.ts +80 -0
  28. package/dist/src/github-issues-source.js +482 -53
  29. package/dist/src/index.d.ts +13 -0
  30. package/dist/src/index.js +1369 -68
  31. package/dist/src/lifecycle-closeout.d.ts +2 -0
  32. package/dist/src/lifecycle-closeout.js +6 -0
  33. package/dist/src/planning.d.ts +1 -0
  34. package/dist/src/planning.js +14 -0
  35. package/dist/src/plugin.d.ts +24 -0
  36. package/dist/src/plugin.js +1814 -0
  37. package/dist/src/product-plugin.d.ts +3 -0
  38. package/dist/src/product-plugin.js +18 -0
  39. package/dist/src/run-worker-panels.d.ts +15 -0
  40. package/dist/src/run-worker-panels.js +53 -0
  41. package/dist/src/supervisor.d.ts +1 -0
  42. package/dist/src/supervisor.js +12 -0
  43. package/dist/src/task-cli.d.ts +1 -0
  44. package/dist/src/task-cli.js +14 -0
  45. package/package.json +67 -5
package/dist/src/index.js CHANGED
@@ -1,6 +1,610 @@
1
1
  // @bun
2
- // packages/standard-plugin/src/index.ts
3
- import { definePlugin } from "@rig/core";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
+
18
+ // packages/standard-plugin/src/drift/metadata.ts
19
+ import { Schema } from "effect";
20
+ import { StageMutation as StageMutationSchema } from "@rig/contracts";
21
+ var DOCS_DRIFT_VALIDATOR_ID = "std:docs-drift", DOCS_DRIFT_CLI_ID = "std:drift", DOCS_DRIFT_STAGE_ID = "docs-drift", DOCS_DRIFT_CAPABILITY_ID = "std:docs-drift-capability", DOCS_DRIFT_VALIDATOR, DOCS_DRIFT_STAGE_MUTATION, DOCS_DRIFT_CLI_COMMAND = "rig drift [--docs <csv>] [--ignore <csv>] [--fail-on-drift] [--json]";
22
+ var init_metadata = __esm(() => {
23
+ DOCS_DRIFT_VALIDATOR = {
24
+ id: DOCS_DRIFT_VALIDATOR_ID,
25
+ category: "regression",
26
+ description: "Detect documentation references that drifted from the source tree."
27
+ };
28
+ DOCS_DRIFT_STAGE_MUTATION = Schema.decodeUnknownSync(StageMutationSchema)({
29
+ op: "insert",
30
+ stage: {
31
+ id: DOCS_DRIFT_STAGE_ID,
32
+ kind: "gate",
33
+ before: ["merge-gate"],
34
+ after: ["open-pr"]
35
+ },
36
+ contributedBy: DOCS_DRIFT_STAGE_ID
37
+ });
38
+ });
39
+
40
+ // packages/standard-plugin/src/drift/extract-refs.ts
41
+ function stripFenceLines(markdown) {
42
+ const lines = markdown.split(/\r?\n/);
43
+ let fenced = false;
44
+ return lines.map((line) => {
45
+ if (/^\s*(```|~~~)/.test(line)) {
46
+ fenced = !fenced;
47
+ return "";
48
+ }
49
+ return fenced ? "" : line;
50
+ });
51
+ }
52
+ function normalizeToken(raw) {
53
+ return raw.trim().replace(/^['"]|['"]$/g, "").replace(/[),.;:]+$/g, "").replace(/#L\d+(?:-L\d+)?$/i, "");
54
+ }
55
+ function classifyReference(raw) {
56
+ if (raw.startsWith("@"))
57
+ return null;
58
+ if (PATH_REF.test(raw))
59
+ return "path";
60
+ if (SYMBOL_REF.test(raw))
61
+ return "symbol";
62
+ return null;
63
+ }
64
+ function pushReference(refs, seen, raw, line) {
65
+ const value = normalizeToken(raw);
66
+ if (!value)
67
+ return;
68
+ const kind = classifyReference(value);
69
+ if (!kind)
70
+ return;
71
+ const key = `${kind}:${value}:${line}`;
72
+ if (seen.has(key))
73
+ return;
74
+ seen.add(key);
75
+ refs.push({ kind, value, line });
76
+ }
77
+ function extractDriftReferences(markdown) {
78
+ const refs = [];
79
+ const seen = new Set;
80
+ const lines = stripFenceLines(markdown);
81
+ for (const [index, line] of lines.entries()) {
82
+ const lineNumber = index + 1;
83
+ for (const match of line.matchAll(INLINE_CODE)) {
84
+ pushReference(refs, seen, match[1] ?? "", lineNumber);
85
+ }
86
+ for (const match of line.matchAll(MARKDOWN_LINK)) {
87
+ pushReference(refs, seen, match[1] ?? "", lineNumber);
88
+ }
89
+ }
90
+ return refs;
91
+ }
92
+ var INLINE_CODE, MARKDOWN_LINK, SYMBOL_REF, PATH_REF;
93
+ var init_extract_refs = __esm(() => {
94
+ INLINE_CODE = /`([^`\n]+)`/g;
95
+ MARKDOWN_LINK = /\[[^\]]+\]\(([^)\s]+)\)/g;
96
+ SYMBOL_REF = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)?$/;
97
+ PATH_REF = /^(?:\.\.?\/)?(?:[A-Za-z0-9_.-]+\/)+[A-Za-z0-9_.-]+$|^[A-Za-z0-9_.-]+\.(?:ts|tsx|js|jsx|mjs|cjs|json|md|mdx|css|scss|html|yml|yaml|toml|rs|go|py|rb|java|kt|swift|c|cc|cpp|h|hpp)$/;
98
+ });
99
+
100
+ // packages/standard-plugin/src/drift/git-adapter.ts
101
+ import { execFile } from "child_process";
102
+ import { promisify } from "util";
103
+ function processError(value) {
104
+ return value && typeof value === "object" ? value : null;
105
+ }
106
+ function lineCount(output) {
107
+ const trimmed = output.trim();
108
+ return trimmed ? trimmed.split(/\r?\n/).length : 0;
109
+ }
110
+ function makeDriftGit(projectRoot) {
111
+ async function git(args) {
112
+ const result = await execFileAsync("git", [...args], {
113
+ cwd: projectRoot,
114
+ encoding: "utf8",
115
+ maxBuffer: 10 * 1024 * 1024
116
+ });
117
+ return String(result.stdout);
118
+ }
119
+ async function grepCountAt(symbolOrPath, commit) {
120
+ try {
121
+ return lineCount(await git(["grep", "-F", "-n", "-e", symbolOrPath, commit, "--"]));
122
+ } catch (error) {
123
+ const detail = processError(error);
124
+ if (detail?.code === 1)
125
+ return 0;
126
+ throw error;
127
+ }
128
+ }
129
+ return {
130
+ async lastCommitTouching(path) {
131
+ const commit = (await git(["log", "-n", "1", "--format=%H", "--", path])).trim();
132
+ return commit || "HEAD";
133
+ },
134
+ async grepCount(symbolOrPath) {
135
+ return grepCountAt(symbolOrPath, "HEAD");
136
+ },
137
+ async grepCountAtCommit(symbolOrPath, commit) {
138
+ return grepCountAt(symbolOrPath, commit);
139
+ },
140
+ async wasRenamed(symbolOrPath, sinceCommit) {
141
+ if (!symbolOrPath.includes("/") && !symbolOrPath.includes("."))
142
+ return false;
143
+ try {
144
+ const output = await git(["log", "--name-status", "--format=", `${sinceCommit}..HEAD`]);
145
+ return output.split(/\r?\n/).some((line) => {
146
+ const match = line.match(/^R\d*\s+(.+?)\s+(.+)$/);
147
+ return Boolean(match && (match[1] === symbolOrPath || match[2] === symbolOrPath));
148
+ });
149
+ } catch (error) {
150
+ const detail = processError(error);
151
+ if (detail?.code === 128)
152
+ return false;
153
+ throw error;
154
+ }
155
+ }
156
+ };
157
+ }
158
+ var execFileAsync;
159
+ var init_git_adapter = __esm(() => {
160
+ execFileAsync = promisify(execFile);
161
+ });
162
+
163
+ // packages/standard-plugin/src/drift/detect.ts
164
+ var exports_detect = {};
165
+ __export(exports_detect, {
166
+ detectStaleAnchors: () => detectStaleAnchors,
167
+ detectDrift: () => detectDrift,
168
+ detectDeletedReferences: () => detectDeletedReferences
169
+ });
170
+ import { existsSync as existsSync3 } from "fs";
171
+ import { readdir, readFile, stat } from "fs/promises";
172
+ import { basename as basename2, extname, relative, resolve as resolve3 } from "path";
173
+ function globLikeMatch(path, pattern) {
174
+ if (pattern === path)
175
+ return true;
176
+ if (pattern.startsWith("**/*"))
177
+ return path.endsWith(pattern.slice(4));
178
+ if (pattern.endsWith("/**"))
179
+ return path.startsWith(pattern.slice(0, -3));
180
+ if (pattern.includes("*")) {
181
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
182
+ return new RegExp(`^${escaped}$`).test(path);
183
+ }
184
+ return path.startsWith(pattern);
185
+ }
186
+ function isDefaultDoc(path) {
187
+ const lower = basename2(path).toLowerCase();
188
+ return (path.endsWith(".md") || path.endsWith(".mdx")) && !lower.startsWith("changelog") && !lower.includes("generated");
189
+ }
190
+ function isIgnored(path, patterns) {
191
+ return (patterns ?? []).some((pattern) => globLikeMatch(path, pattern));
192
+ }
193
+ async function collectFiles(root, options) {
194
+ const files = [];
195
+ async function visit(dir) {
196
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
197
+ if (entry.isDirectory() && DEFAULT_IGNORED_DIRS[entry.name])
198
+ continue;
199
+ const absolute = resolve3(dir, entry.name);
200
+ const rel = relative(root, absolute).replace(/\\/g, "/");
201
+ if (isIgnored(rel, options.ignore))
202
+ continue;
203
+ if (entry.isDirectory()) {
204
+ await visit(absolute);
205
+ continue;
206
+ }
207
+ if (!entry.isFile())
208
+ continue;
209
+ if (options.docs) {
210
+ const matchesConfigured = options.patterns && options.patterns.length > 0 ? options.patterns.some((pattern) => globLikeMatch(rel, pattern)) : isDefaultDoc(rel);
211
+ if (matchesConfigured)
212
+ files.push(rel);
213
+ continue;
214
+ }
215
+ if (SOURCE_EXTENSIONS[extname(entry.name)])
216
+ files.push(rel);
217
+ }
218
+ }
219
+ await visit(root);
220
+ return files.sort();
221
+ }
222
+ async function sourceReferenceCount(projectRoot, reference, docPath) {
223
+ if (reference.kind === "path")
224
+ return existsSync3(resolve3(projectRoot, reference.value)) ? 1 : 0;
225
+ let count = 0;
226
+ const sourceFiles = await collectFiles(projectRoot, { docs: false });
227
+ for (const sourceFile of sourceFiles) {
228
+ if (sourceFile === docPath)
229
+ continue;
230
+ const text = await readFile(resolve3(projectRoot, sourceFile), "utf8").catch(() => "");
231
+ if (text.includes(reference.value))
232
+ count += 1;
233
+ }
234
+ return count;
235
+ }
236
+ function deletedReferenceFinding(docPath, reference) {
237
+ return {
238
+ kind: "deleted-reference",
239
+ docPath,
240
+ line: reference.line,
241
+ reference: reference.value,
242
+ detail: `Documented reference "${reference.value}" no longer exists in the source tree.`,
243
+ confidence: "high"
244
+ };
245
+ }
246
+ function staleAnchorFinding(docPath, reference) {
247
+ return {
248
+ kind: "stale-anchor",
249
+ docPath,
250
+ line: reference.line,
251
+ reference: reference.value,
252
+ detail: `Documented path "${reference.value}" changed after this doc was last updated.`,
253
+ confidence: "medium"
254
+ };
255
+ }
256
+ async function detectDeletedReferences(projectRoot, docPath, git = makeDriftGit(projectRoot)) {
257
+ const markdown = await readFile(resolve3(projectRoot, docPath), "utf8");
258
+ const docCommit = await git.lastCommitTouching(docPath);
259
+ const findings = [];
260
+ for (const reference of extractDriftReferences(markdown)) {
261
+ if (await sourceReferenceCount(projectRoot, reference, docPath) > 0)
262
+ continue;
263
+ if (await git.wasRenamed(reference.value, docCommit))
264
+ continue;
265
+ findings.push(deletedReferenceFinding(docPath, reference));
266
+ }
267
+ return findings;
268
+ }
269
+ async function detectStaleAnchors(projectRoot, docPath, git = makeDriftGit(projectRoot)) {
270
+ const markdown = await readFile(resolve3(projectRoot, docPath), "utf8");
271
+ const docCommit = await git.lastCommitTouching(docPath);
272
+ const findings = [];
273
+ for (const reference of extractDriftReferences(markdown).filter((ref) => ref.kind === "path")) {
274
+ if (!existsSync3(resolve3(projectRoot, reference.value)))
275
+ continue;
276
+ const sourceStat = await stat(resolve3(projectRoot, reference.value)).catch(() => null);
277
+ if (!sourceStat?.isFile())
278
+ continue;
279
+ const sourceCommit = await git.lastCommitTouching(reference.value);
280
+ if (sourceCommit !== docCommit && !await git.wasRenamed(reference.value, docCommit)) {
281
+ findings.push(staleAnchorFinding(docPath, reference));
282
+ }
283
+ }
284
+ return findings;
285
+ }
286
+ async function detectDrift(options) {
287
+ const git = options.git ?? makeDriftGit(options.projectRoot);
288
+ const docs = await collectFiles(options.projectRoot, {
289
+ docs: true,
290
+ ...options.docsGlobs !== undefined ? { patterns: options.docsGlobs } : {},
291
+ ...options.ignoreGlobs !== undefined ? { ignore: options.ignoreGlobs } : {}
292
+ });
293
+ const findings = [];
294
+ let degraded = false;
295
+ for (const docPath of docs) {
296
+ try {
297
+ findings.push(...await detectDeletedReferences(options.projectRoot, docPath, git));
298
+ findings.push(...await detectStaleAnchors(options.projectRoot, docPath, git));
299
+ } catch {
300
+ degraded = true;
301
+ }
302
+ }
303
+ return {
304
+ generatedAt: new Date().toISOString(),
305
+ scanned: docs.length,
306
+ degraded,
307
+ findings
308
+ };
309
+ }
310
+ var DEFAULT_IGNORED_DIRS, SOURCE_EXTENSIONS;
311
+ var init_detect = __esm(() => {
312
+ init_extract_refs();
313
+ init_git_adapter();
314
+ DEFAULT_IGNORED_DIRS = {
315
+ ".git": true,
316
+ node_modules: true,
317
+ dist: true,
318
+ build: true,
319
+ coverage: true,
320
+ ".next": true,
321
+ vendor: true
322
+ };
323
+ SOURCE_EXTENSIONS = {
324
+ ".ts": true,
325
+ ".tsx": true,
326
+ ".js": true,
327
+ ".jsx": true,
328
+ ".mjs": true,
329
+ ".cjs": true,
330
+ ".rs": true,
331
+ ".go": true,
332
+ ".py": true,
333
+ ".rb": true,
334
+ ".java": true,
335
+ ".kt": true,
336
+ ".swift": true,
337
+ ".c": true,
338
+ ".cc": true,
339
+ ".cpp": true,
340
+ ".h": true,
341
+ ".hpp": true,
342
+ ".json": true,
343
+ ".toml": true,
344
+ ".yml": true,
345
+ ".yaml": true
346
+ };
347
+ });
348
+
349
+ // packages/standard-plugin/src/drift/plugin.ts
350
+ var exports_plugin = {};
351
+ __export(exports_plugin, {
352
+ runDriftCli: () => runDriftCli,
353
+ runDocsDriftValidation: () => runDocsDriftValidation,
354
+ highConfidenceDriftFindings: () => highConfidenceDriftFindings,
355
+ executeDrift: () => executeDrift,
356
+ driftGateResult: () => driftGateResult,
357
+ createDocsDriftValidator: () => createDocsDriftValidator,
358
+ createDocsDriftRuntimeCliCommand: () => createDocsDriftRuntimeCliCommand,
359
+ createDocsDriftGateStage: () => createDocsDriftGateStage,
360
+ DOCS_DRIFT_VALIDATOR_ID: () => DOCS_DRIFT_VALIDATOR_ID,
361
+ DOCS_DRIFT_VALIDATOR: () => DOCS_DRIFT_VALIDATOR,
362
+ DOCS_DRIFT_STAGE_MUTATION: () => DOCS_DRIFT_STAGE_MUTATION,
363
+ DOCS_DRIFT_STAGE_ID: () => DOCS_DRIFT_STAGE_ID,
364
+ DOCS_DRIFT_RUNTIME_CLI_COMMAND: () => DOCS_DRIFT_RUNTIME_CLI_COMMAND,
365
+ DOCS_DRIFT_CLI_ID: () => DOCS_DRIFT_CLI_ID,
366
+ DOCS_DRIFT_CLI_COMMAND: () => DOCS_DRIFT_CLI_COMMAND,
367
+ DOCS_DRIFT_CAPABILITY_ID: () => DOCS_DRIFT_CAPABILITY_ID
368
+ });
369
+ function highConfidenceDriftFindings(report) {
370
+ return report.findings.filter((finding) => finding.confidence === "high");
371
+ }
372
+ function driftGateResult(report, mode = "enforce") {
373
+ const high = highConfidenceDriftFindings(report);
374
+ if (mode === "enforce" && high.length > 0) {
375
+ return { kind: "block", reason: `${high.length} high-confidence documentation drift finding(s).` };
376
+ }
377
+ return { kind: "allow" };
378
+ }
379
+ function createDocsDriftGateStage(options = {}) {
380
+ return async (ctx) => {
381
+ const projectRoot = typeof ctx.metadata?.projectRoot === "string" ? ctx.metadata.projectRoot : process.cwd();
382
+ const report = await detectDrift({
383
+ projectRoot,
384
+ ...options.docsGlobs !== undefined ? { docsGlobs: options.docsGlobs } : {},
385
+ ...options.ignoreGlobs !== undefined ? { ignoreGlobs: options.ignoreGlobs } : {}
386
+ });
387
+ return driftGateResult(report, options.failOnDrift ? "enforce" : "observe");
388
+ };
389
+ }
390
+ async function runDocsDriftValidation(options) {
391
+ const report = await detectDrift(options);
392
+ const high = highConfidenceDriftFindings(report);
393
+ const passed = options.failOnDrift ? high.length === 0 : true;
394
+ const findingWord = report.findings.length === 1 ? "finding" : "findings";
395
+ return {
396
+ id: DOCS_DRIFT_VALIDATOR_ID,
397
+ passed,
398
+ summary: `docs drift scanned ${report.scanned} doc(s), ${report.findings.length} ${findingWord}`,
399
+ details: JSON.stringify(report)
400
+ };
401
+ }
402
+ function createDocsDriftValidator(options = {}) {
403
+ return {
404
+ ...DOCS_DRIFT_VALIDATOR,
405
+ async run(ctx) {
406
+ return runDocsDriftValidation({
407
+ projectRoot: ctx.workspaceRoot,
408
+ ...options.docsGlobs !== undefined ? { docsGlobs: options.docsGlobs } : {},
409
+ ...options.ignoreGlobs !== undefined ? { ignoreGlobs: options.ignoreGlobs } : {},
410
+ ...options.failOnDrift !== undefined ? { failOnDrift: options.failOnDrift } : {}
411
+ });
412
+ }
413
+ };
414
+ }
415
+ function takeOptionValue(args, index, flag) {
416
+ const value = args[index + 1];
417
+ if (!value)
418
+ throw new Error(`${flag} requires a value`);
419
+ return value;
420
+ }
421
+ function takeFlag(args, flag) {
422
+ const rest = [...args];
423
+ const index = rest.indexOf(flag);
424
+ if (index < 0)
425
+ return { value: false, rest };
426
+ rest.splice(index, 1);
427
+ return { value: true, rest };
428
+ }
429
+ function takeOption(args, flag) {
430
+ const rest = [...args];
431
+ const index = rest.indexOf(flag);
432
+ if (index < 0)
433
+ return { rest };
434
+ const value = rest[index + 1];
435
+ if (!value || value.startsWith("-"))
436
+ throw new Error(`${flag} requires a value.`);
437
+ rest.splice(index, 2);
438
+ return { value, rest };
439
+ }
440
+ function requireNoExtraArgs(args, usage) {
441
+ if (args.length > 0)
442
+ throw new Error(`Unexpected argument: ${args[0]}
443
+ Usage: ${usage}`);
444
+ }
445
+ function parseCsv(value) {
446
+ return value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? [];
447
+ }
448
+ function driftSummary(report) {
449
+ const highConfidence = highConfidenceDriftFindings(report).length;
450
+ return { total: report.findings.length, highConfidence, degraded: report.degraded };
451
+ }
452
+ async function executeDrift(context, args, options = {}) {
453
+ const json = takeFlag(args, "--json");
454
+ const docs = takeOption(json.rest, "--docs");
455
+ const ignore = takeOption(docs.rest, "--ignore");
456
+ const failOnDrift = takeFlag(ignore.rest, "--fail-on-drift");
457
+ requireNoExtraArgs(failOnDrift.rest, "rig drift [--docs <csv>] [--ignore <csv>] [--fail-on-drift] [--json]");
458
+ const docsGlobs = parseCsv(docs.value);
459
+ const ignoreGlobs = parseCsv(ignore.value);
460
+ const effectiveDocsGlobs = docsGlobs.length > 0 ? docsGlobs : options.docsGlobs;
461
+ const effectiveIgnoreGlobs = ignoreGlobs.length > 0 ? ignoreGlobs : options.ignoreGlobs;
462
+ const effectiveFailOnDrift = failOnDrift.value || options.failOnDrift === true;
463
+ const report = await detectDrift({
464
+ projectRoot: context.projectRoot,
465
+ ...effectiveDocsGlobs !== undefined ? { docsGlobs: effectiveDocsGlobs } : {},
466
+ ...effectiveIgnoreGlobs !== undefined ? { ignoreGlobs: effectiveIgnoreGlobs } : {}
467
+ });
468
+ const failed = effectiveFailOnDrift && highConfidenceDriftFindings(report).length > 0;
469
+ const details = { report, summary: driftSummary(report), failOnDrift: effectiveFailOnDrift, failed };
470
+ if (context.outputMode === "text") {
471
+ if (json.value)
472
+ console.log(JSON.stringify(details, null, 2));
473
+ else
474
+ console.log(report.findings.length === 0 ? `No drift findings across ${report.scanned} documents.` : report.findings.map((finding) => `${finding.docPath}:${finding.line ?? "?"} ${finding.kind} ${finding.confidence} ${finding.detail}`).join(`
475
+ `));
476
+ }
477
+ return { ok: !failed, group: "drift", command: "scan", details };
478
+ }
479
+ function createDocsDriftRuntimeCliCommand(options = {}) {
480
+ return {
481
+ id: DOCS_DRIFT_CLI_ID,
482
+ family: "drift",
483
+ command: DOCS_DRIFT_CLI_COMMAND,
484
+ description: "Scan documentation for stale code references.",
485
+ usage: DOCS_DRIFT_CLI_COMMAND,
486
+ projectRequired: true,
487
+ run: (context, args) => executeDrift(context, args, options)
488
+ };
489
+ }
490
+ async function runDriftCli(args, options = {}) {
491
+ const docsGlobs = [];
492
+ const ignoreGlobs = [];
493
+ let json = false;
494
+ let failOnDrift = false;
495
+ for (let index = 0;index < args.length; index += 1) {
496
+ const arg = args[index];
497
+ if (arg === "--json") {
498
+ json = true;
499
+ continue;
500
+ }
501
+ if (arg === "--fail-on-drift") {
502
+ failOnDrift = true;
503
+ continue;
504
+ }
505
+ if (arg === "--docs") {
506
+ docsGlobs.push(takeOptionValue(args, index, arg));
507
+ index += 1;
508
+ continue;
509
+ }
510
+ if (arg === "--ignore") {
511
+ ignoreGlobs.push(takeOptionValue(args, index, arg));
512
+ index += 1;
513
+ continue;
514
+ }
515
+ throw new Error(`Unknown rig drift argument: ${arg}`);
516
+ }
517
+ const report = await detectDrift({
518
+ projectRoot: options.projectRoot ?? process.cwd(),
519
+ ...docsGlobs.length > 0 ? { docsGlobs } : {},
520
+ ...ignoreGlobs.length > 0 ? { ignoreGlobs } : {}
521
+ });
522
+ const write = options.write ?? ((message) => console.log(message));
523
+ if (json) {
524
+ write(JSON.stringify(report));
525
+ } else {
526
+ write(`Scanned ${report.scanned} doc(s); ${report.findings.length} drift finding(s).`);
527
+ for (const finding of report.findings) {
528
+ write(`${finding.confidence.toUpperCase()} ${finding.kind} ${finding.docPath}${finding.line ? `:${finding.line}` : ""} ${finding.reference ?? ""} \u2014 ${finding.detail}`);
529
+ }
530
+ }
531
+ const high = highConfidenceDriftFindings(report);
532
+ if (failOnDrift && high.length > 0) {
533
+ options.writeError?.(`${high.length} high-confidence drift finding(s).`);
534
+ return 2;
535
+ }
536
+ return 0;
537
+ }
538
+ var DOCS_DRIFT_RUNTIME_CLI_COMMAND;
539
+ var init_plugin = __esm(() => {
540
+ init_detect();
541
+ init_metadata();
542
+ init_metadata();
543
+ DOCS_DRIFT_RUNTIME_CLI_COMMAND = createDocsDriftRuntimeCliCommand();
544
+ });
545
+
546
+ // packages/standard-plugin/src/bundle.ts
547
+ import { createBlockerClassifierPlugin } from "@rig/blocker-classifier-plugin/plugin";
548
+ import { createDefaultLifecyclePlugin } from "@rig/bundle-default-lifecycle/plugin";
549
+ import { createDependencyGraphPlugin } from "@rig/dependency-graph-plugin/plugin";
550
+ import { createPlanningPlugin } from "@rig/planning-plugin/plugin";
551
+ import { createSupervisorPlugin } from "@rig/supervisor-plugin/plugin";
552
+
553
+ // packages/standard-plugin/src/run-worker-panels.ts
554
+ var RIG_RUN_STOP_PANEL_ACTION = "rig-run:stop";
555
+ var RIG_CAPABILITY_PANEL_SLOT = "capability";
556
+ var RIG_SUPERVISOR_PANEL_ID = "supervisor";
557
+ var RUN_SUPERVISOR_PANEL_REGISTRATION = {
558
+ id: RIG_SUPERVISOR_PANEL_ID,
559
+ slot: RIG_CAPABILITY_PANEL_SLOT,
560
+ title: "Supervisor",
561
+ capabilityId: "run.supervisor",
562
+ description: "Live run status, closeout progress, and operator stop control."
563
+ };
564
+ function buildSupervisorPanelPayload(context) {
565
+ const status = context.folded.status ?? "unknown";
566
+ const taskId = context.folded.record.taskId ?? context.taskIdAtStart;
567
+ const operatorActive = status === "running" || status === "validating" || status === "closing-out" || status === "needs-attention";
568
+ return {
569
+ status,
570
+ currentTask: taskId ? { id: taskId, title: context.runDisplayTitle } : null,
571
+ processed: context.folded.closeoutPhases.length,
572
+ succeeded: context.folded.closeoutPhases.filter((phase) => phase.outcome === "completed").length,
573
+ failed: context.folded.closeoutPhases.filter((phase) => phase.outcome === "failed").length,
574
+ skipped: 0,
575
+ plannedOrder: taskId ? [{ id: taskId, title: context.runDisplayTitle, status }] : [],
576
+ idleReason: operatorActive ? null : status,
577
+ stopActionId: operatorActive ? RIG_RUN_STOP_PANEL_ACTION : null,
578
+ closures: []
579
+ };
580
+ }
581
+ var RUN_SUPERVISOR_PANEL_PRODUCER = {
582
+ ...RUN_SUPERVISOR_PANEL_REGISTRATION,
583
+ produce(context) {
584
+ return buildSupervisorPanelPayload(context);
585
+ }
586
+ };
587
+ var RUN_WORKER_PANEL_PLUGIN = {
588
+ name: "@rig/standard-plugin:run-worker-panels",
589
+ version: "0.0.0-alpha.1",
590
+ contributes: {
591
+ panels: [RUN_SUPERVISOR_PANEL_REGISTRATION]
592
+ },
593
+ __runtime: {
594
+ panels: [RUN_SUPERVISOR_PANEL_PRODUCER]
595
+ }
596
+ };
597
+
598
+ // packages/standard-plugin/src/cli-surface.ts
599
+ import {
600
+ STANDARD_CLI_SURFACE_PLUGIN_NAME,
601
+ createStandardCliSurfacePlugin,
602
+ standardCliSurfacePlugin
603
+ } from "@rig/cli-surface-plugin/plugin";
604
+
605
+ // packages/standard-plugin/src/plugin.ts
606
+ import { resolve as resolve4 } from "path";
607
+ import { definePlugin } from "@rig/core/config";
4
608
 
5
609
  // packages/standard-plugin/src/github-issues-source.ts
6
610
  import { spawnSync } from "child_process";
@@ -16,7 +620,7 @@ function createEnvGitHubCredentialProvider() {
16
620
  if (input.purpose === "selected-repo") {
17
621
  return { token: cleanToken(process.env.RIG_GITHUB_SELECTED_TOKEN ?? null) ?? "", source: "signed-in-user" };
18
622
  }
19
- const token = cleanToken(process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? null);
623
+ const token = cleanToken(process.env.RIG_GITHUB_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? null);
20
624
  if (!token) {
21
625
  throw new Error("No host GitHub token is configured for admin fallback.");
22
626
  }
@@ -25,34 +629,60 @@ function createEnvGitHubCredentialProvider() {
25
629
  };
26
630
  }
27
631
  function createStateGitHubCredentialProvider(options = {}) {
28
- const resolveStateFile = () => {
29
- const explicitFile = options.stateFile ?? process.env.RIG_GITHUB_AUTH_STATE_FILE;
30
- if (explicitFile?.trim())
31
- return resolve(explicitFile.trim());
32
- const stateDir = options.stateDir ?? process.env.RIG_STATE_DIR;
33
- return stateDir?.trim() ? resolve(stateDir.trim(), "github-auth.json") : null;
632
+ const addCandidate = (candidates, path) => {
633
+ const trimmed = path?.trim();
634
+ if (!trimmed)
635
+ return;
636
+ const resolved = resolve(trimmed);
637
+ if (!candidates.includes(resolved))
638
+ candidates.push(resolved);
639
+ };
640
+ const addStateDir = (candidates, dir) => {
641
+ const trimmed = dir?.trim();
642
+ if (!trimmed)
643
+ return;
644
+ addCandidate(candidates, resolve(trimmed, "github-auth.json"));
645
+ };
646
+ const addProjectStateDir = (candidates, root) => {
647
+ const trimmed = root?.trim();
648
+ if (!trimmed)
649
+ return;
650
+ addStateDir(candidates, resolve(trimmed, ".rig", "state"));
651
+ };
652
+ const stateFileCandidates = () => {
653
+ const candidates = [];
654
+ addCandidate(candidates, options.stateFile ?? process.env.RIG_GITHUB_AUTH_STATE_FILE);
655
+ addStateDir(candidates, options.stateDir);
656
+ addStateDir(candidates, process.env.RIG_STATE_DIR);
657
+ addProjectStateDir(candidates, process.env.PROJECT_RIG_ROOT);
658
+ addProjectStateDir(candidates, process.env.RIG_PROJECT_ROOT);
659
+ addProjectStateDir(candidates, process.env.RIG_HOST_PROJECT_ROOT);
660
+ addProjectStateDir(candidates, process.cwd());
661
+ return candidates;
34
662
  };
35
663
  const readToken = () => {
36
- const stateFile = resolveStateFile();
37
- if (!stateFile || !existsSync(stateFile))
38
- return null;
39
- try {
40
- const parsed = JSON.parse(readFileSync(stateFile, "utf8"));
41
- return typeof parsed.token === "string" ? cleanToken(parsed.token) : null;
42
- } catch {
43
- return null;
664
+ for (const stateFile of stateFileCandidates()) {
665
+ if (!existsSync(stateFile))
666
+ continue;
667
+ try {
668
+ const parsed = JSON.parse(readFileSync(stateFile, "utf8"));
669
+ const token = typeof parsed.token === "string" ? cleanToken(parsed.token) : null;
670
+ if (token)
671
+ return token;
672
+ } catch {}
44
673
  }
674
+ return null;
45
675
  };
46
676
  return {
47
677
  async resolveGitHubToken(input) {
48
678
  const token = readToken();
49
679
  if (input.purpose === "selected-repo") {
50
- return { token: token ?? "", source: "signed-in-user" };
680
+ return { token: token ?? cleanToken(process.env.RIG_GITHUB_SELECTED_TOKEN ?? null) ?? "", source: "signed-in-user" };
51
681
  }
52
682
  if (token) {
53
683
  return { token, source: "signed-in-user" };
54
684
  }
55
- const fallback = cleanToken(process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? null);
685
+ const fallback = cleanToken(process.env.RIG_GITHUB_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? null);
56
686
  if (!fallback) {
57
687
  throw new Error("No signed-in GitHub token is stored for Rig and no host admin fallback token is configured.");
58
688
  }
@@ -86,17 +716,57 @@ function statusFor(issue) {
86
716
  return "cancelled";
87
717
  return "open";
88
718
  }
89
- function parseDeps(body) {
90
- const match = body.match(/^depends-on:\s*([^\n]+)/im);
91
- if (!match)
719
+ function parseIssueRefs(raw) {
720
+ const refs = [...raw.matchAll(/(?:^|[^\w/.-])(?:[\w.-]+\/[\w.-]+#|#)?(\d+)\b/g)].map((match) => match[1]).filter((value) => Boolean(value));
721
+ return [...new Set(refs)];
722
+ }
723
+ function metadataKeyPattern(keys) {
724
+ return new RegExp(`^(?:${keys.map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")}):\\s*(.*)$`, "i");
725
+ }
726
+ function parseMetadataList(body, keys) {
727
+ const block = body.match(/<!-- rig:metadata:start -->\s*([\s\S]*?)\s*<!-- rig:metadata:end -->/);
728
+ if (!block)
92
729
  return [];
93
- return match[1].split(",").map((s) => s.trim()).map((s) => s.replace(/^#/, "").match(/^(\d+)/)?.[1] ?? "").filter((s) => s.length > 0);
730
+ const lines = block[1].split(/\r?\n/);
731
+ const values = [];
732
+ const keyPattern = metadataKeyPattern(keys);
733
+ for (let index = 0;index < lines.length; index += 1) {
734
+ const line = lines[index];
735
+ const sameLine = line.match(keyPattern);
736
+ if (!sameLine)
737
+ continue;
738
+ const inlineValue = sameLine[1]?.trim() ?? "";
739
+ if (inlineValue) {
740
+ values.push(...parseIssueRefs(inlineValue));
741
+ continue;
742
+ }
743
+ for (let cursor = index + 1;cursor < lines.length; cursor += 1) {
744
+ const item = lines[cursor].match(/^\s*-\s*(.+)$/);
745
+ if (!item)
746
+ break;
747
+ values.push(...parseIssueRefs(item[1]));
748
+ }
749
+ }
750
+ return [...new Set(values)];
751
+ }
752
+ function bodyWithoutRigMetadataBlock(body) {
753
+ return body.replace(/<!-- rig:metadata:start -->\s*[\s\S]*?\s*<!-- rig:metadata:end -->/g, "");
754
+ }
755
+ function parseBodyKeyRefs(body, keys) {
756
+ const keyPattern = metadataKeyPattern(keys);
757
+ const values = bodyWithoutRigMetadataBlock(body).split(/\r?\n/).flatMap((line) => {
758
+ const match = line.match(keyPattern);
759
+ return match?.[1] ? parseIssueRefs(match[1]) : [];
760
+ });
761
+ return [...new Set(values)];
762
+ }
763
+ function parseDeps(body) {
764
+ const keys = ["depends-on", "deps", "blocked-by", "blocked_by"];
765
+ return [...new Set([...parseBodyKeyRefs(body, keys), ...parseMetadataList(body, keys)])];
94
766
  }
95
767
  function parseParents(body) {
96
- const match = body.match(/^parents?:\s*([^\n]+)/im);
97
- if (!match)
98
- return [];
99
- return match[1].split(",").map((s) => s.trim()).map((s) => s.replace(/^#/, "").match(/^(\d+)/)?.[1] ?? "").filter((s) => s.length > 0);
768
+ const keys = ["parents", "parent"];
769
+ return [...new Set([...parseBodyKeyRefs(body, keys), ...parseMetadataList(body, keys)])];
100
770
  }
101
771
  function issueTypeFor(issue) {
102
772
  const labels = labelNamesFor(issue);
@@ -107,7 +777,7 @@ function issueTypeFor(issue) {
107
777
  return "epic";
108
778
  return "task";
109
779
  }
110
- function issueToTask(issue, repo) {
780
+ function issueToTask(issue, repo, nativeDependencies) {
111
781
  const labelNames = labelNamesFor(issue);
112
782
  const scope = labelNames.filter((l) => l.startsWith("scope:")).map((l) => l.slice("scope:".length));
113
783
  const roleLabel = labelNames.find((l) => l.startsWith("role:"));
@@ -115,10 +785,12 @@ function issueToTask(issue, repo) {
115
785
  const validators = labelNames.filter((l) => l.startsWith("validator:")).map((l) => l.slice("validator:".length));
116
786
  const body = issue.body ?? "";
117
787
  const issueNodeId = issue.id ?? issue.nodeId ?? issue.node_id;
788
+ const parsedDeps = parseDeps(body);
789
+ const deps = nativeDependencies?.deps ? [...new Set([...parsedDeps, ...nativeDependencies.deps])] : parsedDeps;
118
790
  return {
119
791
  id: String(issue.number),
120
792
  ...typeof issueNodeId === "string" && issueNodeId.trim() ? { issueNodeId: issueNodeId.trim() } : {},
121
- deps: parseDeps(body),
793
+ deps,
122
794
  status: statusFor(issue),
123
795
  title: issue.title,
124
796
  body,
@@ -130,6 +802,7 @@ function issueToTask(issue, repo) {
130
802
  sourceIssueId: `${repo}#${issue.number}`,
131
803
  parentChildDeps: parseParents(body),
132
804
  labels: labelNames,
805
+ ...nativeDependencies?.degraded ? { nativeDependenciesDegraded: true, nativeDependenciesError: nativeDependencies.degraded } : {},
133
806
  raw: issue
134
807
  };
135
808
  }
@@ -166,17 +839,49 @@ ${rendered}
166
839
  ` : `${rendered}
167
840
  `;
168
841
  }
842
+ function buildRigStickyStatusComment(input) {
843
+ const lines = [
844
+ RIG_STATUS_COMMENT_MARKER,
845
+ `### Rig status: ${input.status}`,
846
+ "",
847
+ input.summary
848
+ ];
849
+ if (input.runId)
850
+ lines.push("", `- Run: ${input.runId}`);
851
+ if (input.prUrl)
852
+ lines.push(`- PR: ${input.prUrl}`);
853
+ for (const detail of input.details ?? [])
854
+ lines.push(`- ${detail}`);
855
+ return lines.join(`
856
+ `);
857
+ }
169
858
  function isRigStickyStatusComment(body) {
170
859
  return body.includes(RIG_STATUS_COMMENT_MARKER);
171
860
  }
172
861
  function ghSpawnOptions(extraEnv, timeoutMs) {
173
- if (!extraEnv)
174
- return { encoding: "utf-8", timeout: timeoutMs };
175
- return { encoding: "utf-8", timeout: timeoutMs, env: { ...process.env, ...extraEnv } };
862
+ const env = {
863
+ ...process.env,
864
+ ...process.env.GH_TOKEN !== undefined ? { GH_TOKEN: process.env.GH_TOKEN } : {},
865
+ ...process.env.GITHUB_TOKEN !== undefined ? { GITHUB_TOKEN: process.env.GITHUB_TOKEN } : {},
866
+ ...process.env.RIG_GITHUB_TOKEN !== undefined ? { RIG_GITHUB_TOKEN: process.env.RIG_GITHUB_TOKEN } : {}
867
+ };
868
+ for (const [key, value] of Object.entries(extraEnv ?? {})) {
869
+ if (value === undefined)
870
+ delete env[key];
871
+ else
872
+ env[key] = value;
873
+ }
874
+ return {
875
+ encoding: "utf-8",
876
+ timeout: timeoutMs,
877
+ env
878
+ };
176
879
  }
177
880
  function credentialEnv(token) {
178
881
  const clean = token?.trim() ?? "";
179
- return { GH_TOKEN: clean, GITHUB_TOKEN: clean };
882
+ if (clean)
883
+ return { GH_TOKEN: clean, GITHUB_TOKEN: clean, RIG_GITHUB_TOKEN: clean };
884
+ return { GH_TOKEN: undefined, GITHUB_TOKEN: undefined, RIG_GITHUB_TOKEN: undefined };
180
885
  }
181
886
  async function resolveCredentialEnv(opts, purpose) {
182
887
  if (!opts.credentialProvider)
@@ -191,28 +896,319 @@ async function resolveCredentialEnv(opts, purpose) {
191
896
  const resolved = await opts.credentialProvider.resolveGitHubToken(input);
192
897
  return credentialEnv(resolved.token);
193
898
  }
899
+ function tokenDiagnostic(value) {
900
+ const clean = value?.trim() ?? "";
901
+ return clean ? `present(len=${clean.length})` : "missing";
902
+ }
194
903
  function runGh(bin, args, spawn, extraEnv, timeoutMs) {
195
- const res = spawn(bin, [...args], ghSpawnOptions(extraEnv, timeoutMs));
196
- assertGhSuccess(args, res);
904
+ const options = ghSpawnOptions(extraEnv, timeoutMs);
905
+ const res = spawn(bin, [...args], options);
906
+ assertGhSuccess(args, res, options.env);
197
907
  if (!res.stdout || res.stdout.trim() === "")
198
908
  return [];
199
909
  return JSON.parse(res.stdout);
200
910
  }
201
911
  function runGhVoid(bin, args, spawn, extraEnv, timeoutMs) {
202
- const res = spawn(bin, [...args], ghSpawnOptions(extraEnv, timeoutMs));
203
- assertGhSuccess(args, res);
912
+ const options = ghSpawnOptions(extraEnv, timeoutMs);
913
+ const res = spawn(bin, [...args], options);
914
+ assertGhSuccess(args, res, options.env);
204
915
  }
205
- function assertGhSuccess(args, res) {
916
+ function assertGhSuccess(args, res, env) {
206
917
  if (res.error) {
207
918
  const msg = res.error.message ?? String(res.error);
208
919
  throw new Error(`gh CLI not available \u2014 install gh (brew install gh / apt install gh): ${msg}`);
209
920
  }
210
921
  if (res.status !== 0) {
211
- throw new Error(`gh ${args.join(" ")} failed (exit ${res.status}): ${res.stderr}`);
922
+ throw new Error(`gh ${args.join(" ")} failed (exit ${res.status}): ${res.stderr}
923
+ [rig gh env:standard-plugin] GH_TOKEN=${tokenDiagnostic(env.GH_TOKEN)} GITHUB_TOKEN=${tokenDiagnostic(env.GITHUB_TOKEN)} RIG_GITHUB_TOKEN=${tokenDiagnostic(env.RIG_GITHUB_TOKEN)}`);
924
+ }
925
+ }
926
+ var DEFAULT_PROJECT_STATUSES = {
927
+ todo: "Todo",
928
+ running: "In Progress",
929
+ prOpen: "In Review",
930
+ ciFixing: "In Review",
931
+ merging: "In Review",
932
+ done: "Done",
933
+ needsAttention: "Needs Attention"
934
+ };
935
+ function asProjectRecord(value) {
936
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
937
+ }
938
+ function projectString(value) {
939
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
940
+ }
941
+ function projectLifecycleStatusForTaskStatus(status) {
942
+ const normalized = status?.trim().toLowerCase().replace(/[-\s]+/g, "_");
943
+ switch (normalized) {
944
+ case "draft":
945
+ case "open":
946
+ case "queued":
947
+ case "ready":
948
+ return "todo";
949
+ case "running":
950
+ case "in_progress":
951
+ return "running";
952
+ case "under_review":
953
+ case "review":
954
+ case "pr_open":
955
+ return "prOpen";
956
+ case "ci_fixing":
957
+ case "fixing":
958
+ return "ciFixing";
959
+ case "merging":
960
+ case "merge":
961
+ return "merging";
962
+ case "closed":
963
+ case "completed":
964
+ case "done":
965
+ return "done";
966
+ case "blocked":
967
+ case "cancelled":
968
+ case "failed":
969
+ case "needs_attention":
970
+ return "needsAttention";
971
+ default:
972
+ return null;
973
+ }
974
+ }
975
+ function ghGraphQLFetch(bin, spawnFn, extraEnv, timeoutMs) {
976
+ return async (query, variables) => {
977
+ const args = ["api", "graphql", "-f", `query=${query}`];
978
+ for (const [key, value] of Object.entries(variables)) {
979
+ if (value === undefined || value === null)
980
+ continue;
981
+ args.push("-f", `${key}=${String(value)}`);
982
+ }
983
+ const response = runGh(bin, args, spawnFn, extraEnv, timeoutMs);
984
+ return asProjectRecord(response)?.data ?? response;
985
+ };
986
+ }
987
+ function issueNodeIdFor(issue) {
988
+ const id = issue.id ?? issue.nodeId ?? issue.node_id;
989
+ return typeof id === "string" && id.trim().length > 0 ? id.trim() : null;
990
+ }
991
+ function nativeIssueDependencyRef(value, currentRepo) {
992
+ const record = asProjectRecord(value);
993
+ const number = typeof record?.number === "number" ? String(record.number) : projectString(record?.number);
994
+ if (!number)
995
+ return null;
996
+ const repository = asProjectRecord(record?.repository);
997
+ const owner = projectString(asProjectRecord(repository?.owner)?.login);
998
+ const name = projectString(repository?.name);
999
+ if (!owner || !name || `${owner}/${name}` === currentRepo)
1000
+ return number;
1001
+ return `${owner}/${name}#${number}`;
1002
+ }
1003
+ function nativeDependencyRefsFrom(data, currentRepo) {
1004
+ const issue = asProjectRecord(asProjectRecord(data)?.node);
1005
+ const blockedBy = asProjectRecord(issue?.blockedBy);
1006
+ const nodes = Array.isArray(blockedBy?.nodes) ? blockedBy.nodes : [];
1007
+ return [...new Set(nodes.flatMap((node) => {
1008
+ const ref = nativeIssueDependencyRef(node, currentRepo);
1009
+ return ref ? [ref] : [];
1010
+ }))];
1011
+ }
1012
+ async function readNativeDependenciesForIssue(input) {
1013
+ const issueId = issueNodeIdFor(input.issue);
1014
+ if (!issueId)
1015
+ return { deps: [], degraded: "GitHub issue node id is unavailable." };
1016
+ const query = `
1017
+ query RigIssueNativeDependencies($issueId: ID!) {
1018
+ node(id: $issueId) {
1019
+ ... on Issue {
1020
+ blockedBy(first: 100) {
1021
+ nodes {
1022
+ number
1023
+ repository { name owner { login } }
1024
+ }
1025
+ }
1026
+ }
1027
+ }
1028
+ }
1029
+ `;
1030
+ try {
1031
+ return {
1032
+ deps: nativeDependencyRefsFrom(await input.fetchGraphQL(query, { issueId }, "gh-cli"), input.repo)
1033
+ };
1034
+ } catch (error) {
1035
+ const detail = error instanceof Error ? error.message : String(error);
1036
+ return { deps: [], degraded: detail };
1037
+ }
1038
+ }
1039
+ function formatIssueReference(ref) {
1040
+ const clean = ref.trim().replace(/^#/, "");
1041
+ return /^\d+$/.test(clean) ? `#${clean}` : clean;
1042
+ }
1043
+ function appendReferenceLines(body, deps, parents) {
1044
+ const lines = [];
1045
+ const cleanDeps = (deps ?? []).map(formatIssueReference).filter((ref) => ref.length > 0);
1046
+ const cleanParents = (parents ?? []).map(formatIssueReference).filter((ref) => ref.length > 0);
1047
+ if (cleanDeps.length > 0)
1048
+ lines.push(`depends-on: ${cleanDeps.join(", ")}`);
1049
+ if (cleanParents.length > 0)
1050
+ lines.push(`parents: ${cleanParents.join(", ")}`);
1051
+ if (lines.length === 0)
1052
+ return body;
1053
+ return body.trim().length > 0 ? `${body.trimEnd()}
1054
+
1055
+ ${lines.join(`
1056
+ `)}` : lines.join(`
1057
+ `);
1058
+ }
1059
+ function bodyForCreatedTask(input) {
1060
+ const metadata = { ...input.metadata ?? {} };
1061
+ if (input.deps && input.deps.length > 0)
1062
+ metadata["depends-on"] = input.deps.map(formatIssueReference);
1063
+ if (input.parents && input.parents.length > 0)
1064
+ metadata.parents = input.parents.map(formatIssueReference);
1065
+ return updateRigOwnedMetadataBlock(appendReferenceLines(input.body, input.deps, input.parents), metadata);
1066
+ }
1067
+ function projectStatusFieldFrom(data, projectId) {
1068
+ const fields = asProjectRecord(asProjectRecord(asProjectRecord(data)?.node)?.fields)?.nodes;
1069
+ for (const node of Array.isArray(fields) ? fields : []) {
1070
+ const record = asProjectRecord(node);
1071
+ if (projectString(record?.name)?.toLowerCase() !== "status")
1072
+ continue;
1073
+ const id = projectString(record?.id);
1074
+ if (!id)
1075
+ continue;
1076
+ const options = Array.isArray(record?.options) ? record.options.flatMap((option) => {
1077
+ const optionRecord = asProjectRecord(option);
1078
+ const optionId = projectString(optionRecord?.id);
1079
+ const name = projectString(optionRecord?.name);
1080
+ return optionId && name ? [{ id: optionId, name }] : [];
1081
+ }) : [];
1082
+ return { id, name: "Status", options };
1083
+ }
1084
+ throw new Error(`GitHub Project ${projectId} does not expose a Status single-select field.`);
1085
+ }
1086
+ async function resolveProjectStatusField(input) {
1087
+ const query = `
1088
+ query RigProjectStatusField($projectId: ID!) {
1089
+ node(id: $projectId) {
1090
+ ... on ProjectV2 {
1091
+ fields(first: 50) {
1092
+ nodes {
1093
+ ... on ProjectV2FieldCommon { id name }
1094
+ ... on ProjectV2SingleSelectField { id name options { id name } }
1095
+ }
1096
+ }
1097
+ }
1098
+ }
1099
+ }
1100
+ `;
1101
+ return projectStatusFieldFrom(await input.fetchGraphQL(query, { projectId: input.projectId }, input.token), input.projectId);
1102
+ }
1103
+ async function ensureIssueProjectItem(input) {
1104
+ const query = `
1105
+ query RigFindProjectIssueItem($projectId: ID!) {
1106
+ node(id: $projectId) {
1107
+ ... on ProjectV2 {
1108
+ items(first: 100) { nodes { id content { ... on Issue { id } } } }
1109
+ }
1110
+ }
1111
+ }
1112
+ `;
1113
+ const data = await input.fetchGraphQL(query, { projectId: input.projectId }, input.token);
1114
+ const nodes = asProjectRecord(asProjectRecord(asProjectRecord(data)?.node)?.items)?.nodes;
1115
+ for (const node of Array.isArray(nodes) ? nodes : []) {
1116
+ const record = asProjectRecord(node);
1117
+ const content = asProjectRecord(record?.content);
1118
+ if (projectString(content?.id) === input.issueNodeId) {
1119
+ const id2 = projectString(record?.id);
1120
+ if (id2)
1121
+ return { id: id2, created: false };
1122
+ }
212
1123
  }
1124
+ const mutation = `
1125
+ mutation RigAddIssueToProject($projectId: ID!, $contentId: ID!) {
1126
+ addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) { item { id } }
1127
+ }
1128
+ `;
1129
+ const created = await input.fetchGraphQL(mutation, { projectId: input.projectId, contentId: input.issueNodeId }, input.token);
1130
+ const addResult = asProjectRecord(asProjectRecord(created)?.addProjectV2ItemById);
1131
+ const id = projectString(asProjectRecord(addResult?.item)?.id);
1132
+ if (!id)
1133
+ throw new Error("GitHub Project item creation did not return an item id.");
1134
+ return { id, created: true };
1135
+ }
1136
+ async function updateIssueProjectStatus(input) {
1137
+ const mutation = `
1138
+ mutation RigUpdateProjectStatus($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
1139
+ updateProjectV2ItemFieldValue(input: {
1140
+ projectId: $projectId,
1141
+ itemId: $itemId,
1142
+ fieldId: $fieldId,
1143
+ value: { singleSelectOptionId: $optionId }
1144
+ }) { projectV2Item { id } }
1145
+ }
1146
+ `;
1147
+ await input.fetchGraphQL(mutation, {
1148
+ projectId: input.projectId,
1149
+ itemId: input.itemId,
1150
+ fieldId: input.fieldId,
1151
+ optionId: input.optionId
1152
+ }, input.token);
1153
+ }
1154
+ function fetchIssueNodeId(bin, repo, spawnFn, id, extraEnv, timeoutMs) {
1155
+ const issue = runGh(bin, ["issue", "view", String(id), "--repo", repo, "--json", "id"], spawnFn, extraEnv, timeoutMs);
1156
+ return projectString(issue.id) ?? projectString(issue.nodeId) ?? projectString(issue.node_id);
1157
+ }
1158
+ async function syncGitHubProjectStatus(bin, repo, spawnFn, id, status, projects, extraEnv, timeoutMs) {
1159
+ if (!projects?.enabled)
1160
+ return;
1161
+ const projectId = projectString(projects.projectId);
1162
+ if (!projectId)
1163
+ throw new Error("GitHub Projects status sync is enabled but projectId is missing.");
1164
+ const lifecycleStatus = projectLifecycleStatusForTaskStatus(status);
1165
+ if (!lifecycleStatus)
1166
+ return;
1167
+ const issueNodeId = fetchIssueNodeId(bin, repo, spawnFn, id, extraEnv, timeoutMs);
1168
+ if (!issueNodeId)
1169
+ throw new Error(`GitHub issue ${repo}#${id} did not expose a node id for Projects status sync.`);
1170
+ const projectStatus = projectString(projects.statuses?.[lifecycleStatus]) ?? DEFAULT_PROJECT_STATUSES[lifecycleStatus];
1171
+ const fetchGraphQL = ghGraphQLFetch(bin, spawnFn, extraEnv, timeoutMs);
1172
+ const field = await resolveProjectStatusField({ projectId, token: "gh-cli", fetchGraphQL });
1173
+ const option = field.options.find((candidate) => candidate.name.toLowerCase() === projectStatus.toLowerCase() || candidate.id === projectStatus);
1174
+ if (!option)
1175
+ throw new Error(`GitHub Project ${projectId} Status field does not contain option "${projectStatus}".`);
1176
+ const item = await ensureIssueProjectItem({ projectId, issueNodeId, token: "gh-cli", fetchGraphQL });
1177
+ await updateIssueProjectStatus({
1178
+ projectId,
1179
+ itemId: item.id,
1180
+ fieldId: projectString(projects.statusFieldId) ?? field.id,
1181
+ optionId: option.id,
1182
+ token: "gh-cli",
1183
+ fetchGraphQL
1184
+ });
1185
+ }
1186
+ var TERMINAL_TASK_STATUSES = new Set(["closed", "completed", "merged", "cancelled", "resolved", "done"]);
1187
+ function normalizeTaskStatusToken(status) {
1188
+ return status?.trim().toLowerCase().replace(/[-\s]+/g, "_") ?? "";
1189
+ }
1190
+ function issueUpdatesMode(value) {
1191
+ return value === "off" || value === "minimal" || value === "lifecycle" ? value : "lifecycle";
1192
+ }
1193
+ function isTerminalTaskStatus(status) {
1194
+ return TERMINAL_TASK_STATUSES.has(normalizeTaskStatusToken(status));
1195
+ }
1196
+ function shouldWriteIssueUpdate(mode, status) {
1197
+ if (mode === "off")
1198
+ return false;
1199
+ if (mode === "lifecycle")
1200
+ return true;
1201
+ return isTerminalTaskStatus(status);
1202
+ }
1203
+ function isRunningStatus(status) {
1204
+ return normalizeTaskStatusToken(status) === "running";
1205
+ }
1206
+ function assignRunningIssue(bin, repo, spawnFn, id, assignee, extraEnv, timeoutMs) {
1207
+ runGhVoid(bin, ["issue", "edit", String(id), "--repo", repo, "--add-assignee", assignee?.trim() || "@me"], spawnFn, extraEnv, timeoutMs);
213
1208
  }
214
1209
  function statusLabelFor(status) {
215
1210
  switch (status) {
1211
+ case "running":
216
1212
  case "in_progress":
217
1213
  return "in-progress";
218
1214
  case "blocked":
@@ -231,6 +1227,8 @@ function statusLabelFor(status) {
231
1227
  return "under-review";
232
1228
  case "needs_attention":
233
1229
  return "blocked";
1230
+ case "closed":
1231
+ case "completed":
234
1232
  case "open":
235
1233
  return null;
236
1234
  default:
@@ -239,11 +1237,13 @@ function statusLabelFor(status) {
239
1237
  }
240
1238
  function rigStatusLabelFor(status) {
241
1239
  switch (status) {
1240
+ case "running":
242
1241
  case "in_progress":
243
1242
  return "rig:running";
244
1243
  case "under_review":
245
1244
  return "rig:pr-open";
246
1245
  case "closed":
1246
+ case "completed":
247
1247
  return "rig:done";
248
1248
  case "ci_fixing":
249
1249
  return "rig:ci-fixing";
@@ -261,9 +1261,10 @@ function rigStatusLabelFor(status) {
261
1261
  return null;
262
1262
  }
263
1263
  }
264
- function applyIssueStatus(bin, repo, spawnFn, id, status, extraEnv, timeoutMs) {
265
- const targetLabel = status === "closed" ? null : statusLabelFor(status);
1264
+ async function applyIssueStatus(bin, repo, spawnFn, id, status, projects, runningAssignee, issueUpdates, extraEnv, timeoutMs) {
1265
+ const targetLabel = status === "closed" || status === "completed" ? null : statusLabelFor(status);
266
1266
  const targetRigLabel = rigStatusLabelFor(status);
1267
+ const shouldSyncLifecycle = shouldWriteIssueUpdate(issueUpdates, status);
267
1268
  for (const l of [...STATUS_LABELS, ...RIG_STATUS_LABELS]) {
268
1269
  if (targetLabel !== null && l === targetLabel)
269
1270
  continue;
@@ -285,7 +1286,19 @@ function applyIssueStatus(bin, repo, spawnFn, id, status, extraEnv, timeoutMs) {
285
1286
  runGhVoid(bin, ["issue", "edit", String(id), "--repo", repo, "--add-label", label], spawnFn, extraEnv, timeoutMs);
286
1287
  }
287
1288
  }
288
- if (status === "closed") {
1289
+ if (isRunningStatus(status)) {
1290
+ assignRunningIssue(bin, repo, spawnFn, id, runningAssignee, extraEnv, timeoutMs);
1291
+ if (shouldSyncLifecycle) {
1292
+ upsertRigStickyComment(bin, repo, spawnFn, String(id), buildRigStickyStatusComment({
1293
+ status: "running",
1294
+ summary: "Rig run started."
1295
+ }), extraEnv, timeoutMs);
1296
+ }
1297
+ }
1298
+ if (shouldSyncLifecycle) {
1299
+ await syncGitHubProjectStatus(bin, repo, spawnFn, id, status, projects, extraEnv, timeoutMs);
1300
+ }
1301
+ if (status === "closed" || status === "completed") {
289
1302
  runGhVoid(bin, ["issue", "close", String(id), "--repo", repo], spawnFn, extraEnv, timeoutMs);
290
1303
  }
291
1304
  }
@@ -355,11 +1368,11 @@ function applyLabels(bin, repo, spawnFn, id, labels, action, extraEnv, timeoutMs
355
1368
  } catch {}
356
1369
  }
357
1370
  }
358
- function applyIssueUpdate(bin, repo, spawnFn, id, update, extraEnv, timeoutMs) {
1371
+ async function applyIssueUpdate(bin, repo, spawnFn, id, update, projects, runningAssignee, issueUpdates, extraEnv, timeoutMs) {
359
1372
  if (update.status) {
360
- applyIssueStatus(bin, repo, spawnFn, id, update.status, extraEnv, timeoutMs);
1373
+ await applyIssueStatus(bin, repo, spawnFn, id, update.status, projects, runningAssignee, issueUpdates, extraEnv, timeoutMs);
361
1374
  }
362
- if (update.comment?.trim()) {
1375
+ if (update.comment?.trim() && shouldWriteIssueUpdate(issueUpdates, update.status)) {
363
1376
  if (isRigStickyStatusComment(update.comment)) {
364
1377
  upsertRigStickyComment(bin, repo, spawnFn, String(id), update.comment, extraEnv, timeoutMs);
365
1378
  } else {
@@ -385,6 +1398,17 @@ function createGitHubIssuesTaskSource(opts) {
385
1398
  const spawnFn = opts.spawn ?? spawnSync;
386
1399
  const timeoutMs = Math.max(1000, Math.trunc(opts.timeoutMs ?? DEFAULT_GH_TIMEOUT_MS));
387
1400
  const listLimit = Math.max(1, Math.trunc(opts.listLimit ?? DEFAULT_GITHUB_ISSUE_LIST_LIMIT));
1401
+ const issueUpdates = issueUpdatesMode(opts.issueUpdates);
1402
+ async function issueToTaskWithOptionalNativeDependencies(issue, env) {
1403
+ if (!opts.useNativeDependencies)
1404
+ return issueToTask(issue, repo);
1405
+ const nativeDependencies = await readNativeDependenciesForIssue({
1406
+ issue,
1407
+ repo,
1408
+ fetchGraphQL: ghGraphQLFetch(bin, spawnFn, env, timeoutMs)
1409
+ });
1410
+ return issueToTask(issue, repo, nativeDependencies);
1411
+ }
388
1412
  return {
389
1413
  id: "std:github-issues",
390
1414
  kind: "github-issues",
@@ -411,12 +1435,13 @@ function createGitHubIssuesTaskSource(opts) {
411
1435
  throw new Error(`GitHub issue list for ${repo} reached the configured limit (${listLimit}); refusing to silently truncate matching issues. Increase taskSource.options.listLimit or narrow labels/state/assignee.`);
412
1436
  }
413
1437
  const issues = rawIssues.filter((issue) => !issue.pull_request);
414
- return issues.map((i) => issueToTask(i, repo));
1438
+ return Promise.all(issues.map((issue) => issueToTaskWithOptionalNativeDependencies(issue, env)));
415
1439
  },
416
1440
  async get(id) {
1441
+ const env = await resolveCredentialEnv(opts, "selected-repo");
1442
+ let issue;
417
1443
  try {
418
- const env = await resolveCredentialEnv(opts, "selected-repo");
419
- const issue = runGh(bin, [
1444
+ issue = runGh(bin, [
420
1445
  "issue",
421
1446
  "view",
422
1447
  String(id),
@@ -425,19 +1450,23 @@ function createGitHubIssuesTaskSource(opts) {
425
1450
  "--json",
426
1451
  "number,title,body,labels,state,url,assignees,id"
427
1452
  ], spawnFn, env, timeoutMs);
428
- return issueToTask(issue, repo);
429
- } catch {
430
- return;
1453
+ } catch (error) {
1454
+ const detail = error instanceof Error ? error.message : String(error);
1455
+ if (/could not resolve to (an? )?(issue|pullrequest)|no issues? (found|matched)|404 not found|gh: not found|gh issue view\b[\s\S]*failed \(exit \d+\): not found\b/i.test(detail)) {
1456
+ return;
1457
+ }
1458
+ throw new Error(`Failed to read task ${id} from GitHub repo ${repo}: ${detail}`);
431
1459
  }
1460
+ return issueToTaskWithOptionalNativeDependencies(issue, env);
432
1461
  },
433
1462
  async updateStatus(id, status) {
434
1463
  const env = await resolveCredentialEnv(opts, "selected-repo");
435
- applyIssueStatus(bin, repo, spawnFn, id, status, env, timeoutMs);
1464
+ await applyIssueStatus(bin, repo, spawnFn, id, status, opts.projects, opts.assignee, issueUpdates, env, timeoutMs);
436
1465
  notifyTaskChanged(opts.onTaskChanged, repo, id, status);
437
1466
  },
438
1467
  async updateTask(id, update) {
439
1468
  const env = await resolveCredentialEnv(opts, "selected-repo");
440
- applyIssueUpdate(bin, repo, spawnFn, id, update, env, timeoutMs);
1469
+ await applyIssueUpdate(bin, repo, spawnFn, id, update, opts.projects, opts.assignee, issueUpdates, env, timeoutMs);
441
1470
  notifyTaskChanged(opts.onTaskChanged, repo, id, update.status);
442
1471
  },
443
1472
  async addLabels(id, labels) {
@@ -452,6 +1481,7 @@ function createGitHubIssuesTaskSource(opts) {
452
1481
  },
453
1482
  async createIssue(input) {
454
1483
  const env = await resolveCredentialEnv(opts, "selected-repo");
1484
+ const body = input.body ?? "";
455
1485
  const args = [
456
1486
  "api",
457
1487
  "-X",
@@ -460,12 +1490,31 @@ function createGitHubIssuesTaskSource(opts) {
460
1490
  "-f",
461
1491
  `title=${input.title}`,
462
1492
  "-f",
463
- `body=${input.body ?? ""}`,
1493
+ `body=${body}`,
464
1494
  ...(input.labels ?? []).flatMap((label) => ["-f", `labels[]=${label}`])
465
1495
  ];
466
1496
  const issue = runGh(bin, args, spawnFn, env, timeoutMs);
467
1497
  notifyTaskChanged(opts.onTaskChanged, repo, String(issue.number));
468
- return issueToTask(issue, repo);
1498
+ return issueToTask({ ...issue, body: issue.body ?? body }, repo);
1499
+ },
1500
+ async create(input) {
1501
+ const env = await resolveCredentialEnv(opts, "selected-repo");
1502
+ const body = bodyForCreatedTask(input);
1503
+ const args = [
1504
+ "api",
1505
+ "-X",
1506
+ "POST",
1507
+ `repos/${repo}/issues`,
1508
+ "-f",
1509
+ `title=${input.title}`,
1510
+ "-f",
1511
+ `body=${body}`,
1512
+ "-f",
1513
+ "labels[]=rig:generated"
1514
+ ];
1515
+ const issue = runGh(bin, args, spawnFn, env, timeoutMs);
1516
+ notifyTaskChanged(opts.onTaskChanged, repo, String(issue.number));
1517
+ return issueToTask({ ...issue, body: issue.body ?? body }, repo);
469
1518
  },
470
1519
  async getIssueBody(id) {
471
1520
  const env = await resolveCredentialEnv(opts, "selected-repo");
@@ -476,7 +1525,7 @@ function createGitHubIssuesTaskSource(opts) {
476
1525
 
477
1526
  // packages/standard-plugin/src/files-source.ts
478
1527
  import { readFileSync as readFileSync2, readdirSync, existsSync as existsSync2, statSync, writeFileSync } from "fs";
479
- import { join, basename } from "path";
1528
+ import { join, basename, isAbsolute, resolve as resolve2 } from "path";
480
1529
  var DEFAULT_PATTERN = /\.(task\.)?json$/;
481
1530
  function readTaskFile(file, pattern) {
482
1531
  const raw = JSON.parse(readFileSync2(file, "utf-8"));
@@ -497,10 +1546,11 @@ function readTaskFile(file, pattern) {
497
1546
  }
498
1547
  function createFilesTaskSource(opts) {
499
1548
  const pattern = opts.pattern ?? DEFAULT_PATTERN;
500
- const directory = opts.path ?? opts.dir;
501
- if (!directory) {
1549
+ const configured = opts.path ?? opts.dir;
1550
+ if (!configured) {
502
1551
  throw new Error("createFilesTaskSource: either `path` or `dir` must be provided");
503
1552
  }
1553
+ const directory = isAbsolute(configured) ? configured : resolve2(opts.projectRoot ?? process.cwd(), configured);
504
1554
  const findTaskFile = (id) => {
505
1555
  if (!existsSync2(directory))
506
1556
  return;
@@ -577,7 +1627,12 @@ function createFilesTaskSource(opts) {
577
1627
  };
578
1628
  }
579
1629
 
580
- // packages/standard-plugin/src/index.ts
1630
+ // packages/standard-plugin/src/plugin.ts
1631
+ init_metadata();
1632
+ init_metadata();
1633
+ import { createStandardProductEntrypointPlugin, standardProductEntrypointPlugin } from "@rig/product-entrypoint-plugin/plugin";
1634
+ import { createStandardTaskCliPlugin, standardTaskCliPlugin } from "@rig/task-cli-plugin/plugin";
1635
+ var DOCS_HEALTH_PANEL_ID = "docs-health";
581
1636
  function requireStringField(config, field, kind) {
582
1637
  const value = config[field];
583
1638
  if (!value) {
@@ -585,9 +1640,145 @@ function requireStringField(config, field, kind) {
585
1640
  }
586
1641
  return value;
587
1642
  }
588
- function standardPlugin(opts = {}) {
1643
+ function isRecord(value) {
1644
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
1645
+ }
1646
+ function optionalString(value) {
1647
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
1648
+ }
1649
+ function parseGitHubProjectsOptions(value) {
1650
+ if (!isRecord(value))
1651
+ return;
1652
+ const statusesSource = isRecord(value.statuses) ? value.statuses : undefined;
1653
+ const statuses = {};
1654
+ for (const key of ["todo", "running", "prOpen", "ciFixing", "merging", "done", "needsAttention"]) {
1655
+ const status = optionalString(statusesSource?.[key]);
1656
+ if (status)
1657
+ statuses[key] = status;
1658
+ }
1659
+ const parsed = {};
1660
+ if (typeof value.enabled === "boolean")
1661
+ parsed.enabled = value.enabled;
1662
+ const projectId = optionalString(value.projectId);
1663
+ if (projectId)
1664
+ parsed.projectId = projectId;
1665
+ const statusFieldId = optionalString(value.statusFieldId);
1666
+ if (statusFieldId)
1667
+ parsed.statusFieldId = statusFieldId;
1668
+ if (Object.keys(statuses).length > 0)
1669
+ parsed.statuses = statuses;
1670
+ return parsed;
1671
+ }
1672
+ function githubProjectsOptionsFromConfig(config, context) {
1673
+ const rigConfig = isRecord(context?.rigConfig) ? context.rigConfig : undefined;
1674
+ const github = isRecord(rigConfig?.github) ? rigConfig.github : undefined;
1675
+ return parseGitHubProjectsOptions(config.options?.projects) ?? parseGitHubProjectsOptions(github?.projects);
1676
+ }
1677
+ function booleanOption(value) {
1678
+ return typeof value === "boolean" ? value : undefined;
1679
+ }
1680
+ function panelProjectRoot(context) {
1681
+ return isRecord(context) && typeof context.projectRoot === "string" && context.projectRoot.length > 0 ? context.projectRoot : null;
1682
+ }
1683
+ function driftFindingPanelId(finding, index) {
1684
+ return `${finding.docPath}:${finding.line ?? index}:${finding.kind}`;
1685
+ }
1686
+ function createDocsHealthPanelProducer(options = {}) {
1687
+ return async (context) => {
1688
+ const projectRoot = panelProjectRoot(context);
1689
+ if (!projectRoot)
1690
+ return;
1691
+ const { detectDrift: detectDrift2 } = await Promise.resolve().then(() => (init_detect(), exports_detect));
1692
+ const report = await detectDrift2({
1693
+ projectRoot,
1694
+ ...options.docsGlobs !== undefined ? { docsGlobs: options.docsGlobs } : {},
1695
+ ...options.ignoreGlobs !== undefined ? { ignoreGlobs: options.ignoreGlobs } : {}
1696
+ });
1697
+ return {
1698
+ findings: report.findings.map((finding, index) => ({
1699
+ id: driftFindingPanelId(finding, index),
1700
+ docPath: finding.docPath,
1701
+ kind: finding.kind,
1702
+ confidence: finding.confidence,
1703
+ summary: finding.detail,
1704
+ taskId: null
1705
+ })),
1706
+ degraded: report.degraded ? "drift scan degraded" : null
1707
+ };
1708
+ };
1709
+ }
1710
+ function createLazyDocsDriftValidator(options = {}) {
1711
+ return {
1712
+ ...DOCS_DRIFT_VALIDATOR,
1713
+ async run(ctx) {
1714
+ const { runDocsDriftValidation: runDocsDriftValidation2 } = await Promise.resolve().then(() => (init_plugin(), exports_plugin));
1715
+ return runDocsDriftValidation2({
1716
+ projectRoot: ctx.workspaceRoot,
1717
+ ...options.docsGlobs !== undefined ? { docsGlobs: options.docsGlobs } : {},
1718
+ ...options.ignoreGlobs !== undefined ? { ignoreGlobs: options.ignoreGlobs } : {},
1719
+ ...options.failOnDrift !== undefined ? { failOnDrift: options.failOnDrift } : {}
1720
+ });
1721
+ }
1722
+ };
1723
+ }
1724
+ function createLazyDocsDriftGateStage(options = {}) {
1725
+ return async (ctx) => {
1726
+ const { createDocsDriftGateStage: createDocsDriftGateStage2 } = await Promise.resolve().then(() => (init_plugin(), exports_plugin));
1727
+ return createDocsDriftGateStage2(options)(ctx);
1728
+ };
1729
+ }
1730
+ function createLazyDocsDriftRuntimeCliCommand(options = {}) {
1731
+ return {
1732
+ id: DOCS_DRIFT_CLI_ID,
1733
+ family: "drift",
1734
+ command: DOCS_DRIFT_CLI_COMMAND,
1735
+ description: "Scan documentation for stale code references.",
1736
+ usage: DOCS_DRIFT_CLI_COMMAND,
1737
+ projectRequired: true,
1738
+ run: async (context, args) => {
1739
+ const { executeDrift: executeDrift2 } = await Promise.resolve().then(() => (init_plugin(), exports_plugin));
1740
+ return executeDrift2(context, args, options);
1741
+ }
1742
+ };
1743
+ }
1744
+ function createStandardDocsDriftPlugin(opts = {}) {
1745
+ return definePlugin({
1746
+ name: "@rig/standard-plugin:docs-drift",
1747
+ version: "0.1.0",
1748
+ contributes: {
1749
+ validators: [DOCS_DRIFT_VALIDATOR],
1750
+ capabilities: [
1751
+ { id: DOCS_DRIFT_CAPABILITY_ID, title: "Documentation drift detection", commandId: DOCS_DRIFT_CLI_ID, panelId: DOCS_HEALTH_PANEL_ID }
1752
+ ],
1753
+ panels: [
1754
+ { id: DOCS_HEALTH_PANEL_ID, slot: "capability", title: "Documentation drift", capabilityId: DOCS_DRIFT_CAPABILITY_ID }
1755
+ ],
1756
+ cliCommands: [
1757
+ {
1758
+ id: DOCS_DRIFT_CLI_ID,
1759
+ family: "drift",
1760
+ command: DOCS_DRIFT_CLI_COMMAND,
1761
+ description: "Scan documentation for stale code references.",
1762
+ projectRequired: true
1763
+ }
1764
+ ],
1765
+ stageMutations: [DOCS_DRIFT_STAGE_MUTATION]
1766
+ }
1767
+ }, {
1768
+ validators: [createLazyDocsDriftValidator(opts)],
1769
+ stages: { [DOCS_DRIFT_STAGE_ID]: createLazyDocsDriftGateStage(opts) },
1770
+ featureCapabilities: [
1771
+ { id: DOCS_DRIFT_CAPABILITY_ID, title: "Documentation drift detection", commandId: DOCS_DRIFT_CLI_ID, panelId: DOCS_HEALTH_PANEL_ID }
1772
+ ],
1773
+ panels: [
1774
+ { id: DOCS_HEALTH_PANEL_ID, slot: "capability", title: "Documentation drift", capabilityId: DOCS_DRIFT_CAPABILITY_ID, produce: createDocsHealthPanelProducer(opts) }
1775
+ ],
1776
+ cliCommands: [createLazyDocsDriftRuntimeCliCommand(opts)]
1777
+ });
1778
+ }
1779
+ function createStandardTaskSourcesPlugin(opts = {}) {
589
1780
  return definePlugin({
590
- name: "rig-standard",
1781
+ name: "@rig/standard-plugin:task-sources",
591
1782
  version: "0.1.0",
592
1783
  contributes: {
593
1784
  taskSources: [
@@ -609,13 +1800,13 @@ function standardPlugin(opts = {}) {
609
1800
  id: "std:github-issues",
610
1801
  kind: "github-issues",
611
1802
  description: "GitHub Issues via gh CLI",
612
- factory(config) {
1803
+ factory(config, context) {
613
1804
  const options = {
614
1805
  owner: requireStringField(config, "owner", "github-issues"),
615
1806
  repo: requireStringField(config, "repo", "github-issues")
616
1807
  };
617
- if (opts.githubCredentialProvider)
618
- options.credentialProvider = opts.githubCredentialProvider;
1808
+ const credentialProviderOptions = context?.projectRoot ? { stateDir: resolve4(context.projectRoot, ".rig", "state") } : {};
1809
+ options.credentialProvider = opts.githubCredentialProvider ?? createStateGitHubCredentialProvider(credentialProviderOptions);
619
1810
  if (opts.githubWorkspaceId)
620
1811
  options.workspaceId = opts.githubWorkspaceId;
621
1812
  if (opts.githubUserId)
@@ -637,6 +1828,12 @@ function standardPlugin(opts = {}) {
637
1828
  const listLimit = typeof config.options?.listLimit === "number" ? config.options.listLimit : undefined;
638
1829
  if (listLimit !== undefined)
639
1830
  options.listLimit = listLimit;
1831
+ const projects = githubProjectsOptionsFromConfig(config, context);
1832
+ if (projects)
1833
+ options.projects = projects;
1834
+ const useNativeDependencies = booleanOption(config.options?.useNativeDependencies);
1835
+ if (useNativeDependencies !== undefined)
1836
+ options.useNativeDependencies = useNativeDependencies;
640
1837
  return createGitHubIssuesTaskSource(options);
641
1838
  }
642
1839
  },
@@ -644,19 +1841,123 @@ function standardPlugin(opts = {}) {
644
1841
  id: "std:files",
645
1842
  kind: "files",
646
1843
  description: "JSON files in a local directory",
647
- factory(config) {
1844
+ factory(config, context) {
648
1845
  return createFilesTaskSource({
649
- path: requireStringField(config, "path", "files")
1846
+ path: requireStringField(config, "path", "files"),
1847
+ ...context?.projectRoot ? { projectRoot: context.projectRoot } : {}
650
1848
  });
651
1849
  }
652
1850
  }
653
1851
  ]
654
1852
  });
655
1853
  }
1854
+
1855
+ // packages/standard-plugin/src/bundle.ts
1856
+ function standardPlugins(options = {}) {
1857
+ return [
1858
+ createDefaultLifecyclePlugin(),
1859
+ createDependencyGraphPlugin(),
1860
+ createBlockerClassifierPlugin(),
1861
+ createPlanningPlugin(),
1862
+ createSupervisorPlugin(),
1863
+ standardCliSurfacePlugin,
1864
+ RUN_WORKER_PANEL_PLUGIN,
1865
+ createStandardTaskSourcesPlugin(options.taskSources),
1866
+ createStandardTaskCliPlugin(),
1867
+ createStandardDocsDriftPlugin(options.drift),
1868
+ createStandardProductEntrypointPlugin()
1869
+ ];
1870
+ }
1871
+ // packages/standard-plugin/src/default-lifecycle.ts
1872
+ import {
1873
+ DEFAULT_LIFECYCLE_PLUGIN_ID,
1874
+ createDefaultLifecyclePlugin as createDefaultLifecyclePlugin2,
1875
+ defaultLifecyclePlugin
1876
+ } from "@rig/bundle-default-lifecycle/plugin";
1877
+ // packages/standard-plugin/src/dependency-graph.ts
1878
+ import {
1879
+ DEPENDENCY_GRAPH_CLI_ID,
1880
+ DEPENDENCY_GRAPH_PANEL_ID,
1881
+ EPICS_PANEL_ID,
1882
+ PEOPLE_PANEL_ID,
1883
+ WORKSPACE_STATUS_CLI_ID,
1884
+ WORKSPACE_SUMMARY_CLI_ID,
1885
+ createDependencyGraphPlugin as createDependencyGraphPlugin2,
1886
+ dependencyGraphPlugin
1887
+ } from "@rig/dependency-graph-plugin/plugin";
1888
+ // packages/standard-plugin/src/blocker-classifier.ts
1889
+ import {
1890
+ BLOCKERS_CLI_ID,
1891
+ BLOCKER_CLASSIFIER_PLUGIN_NAME,
1892
+ DEFAULT_BLOCKER_CLASSIFIER_ID,
1893
+ HUMAN_BLOCKERS_PANEL_ID,
1894
+ blockerClassifierPlugin,
1895
+ createBlockerClassifierPlugin as createBlockerClassifierPlugin2
1896
+ } from "@rig/blocker-classifier-plugin/plugin";
1897
+ // packages/standard-plugin/src/planning.ts
1898
+ import {
1899
+ PLANNING_PLAN_PANEL_ID,
1900
+ PLANNING_PLUGIN_NAME,
1901
+ createPlanningPlugin as createPlanningPlugin2,
1902
+ planningPlugin
1903
+ } from "@rig/planning-plugin/plugin";
1904
+ // packages/standard-plugin/src/supervisor.ts
1905
+ import {
1906
+ SUPERVISOR_PLUGIN_NAME,
1907
+ createSupervisorPlugin as createSupervisorPlugin2,
1908
+ supervisorPlugin
1909
+ } from "@rig/supervisor-plugin/plugin";
656
1910
  export {
657
- standardPlugin as default,
1911
+ supervisorPlugin,
1912
+ standardTaskCliPlugin,
1913
+ standardProductEntrypointPlugin,
1914
+ standardPlugins,
1915
+ standardCliSurfacePlugin,
1916
+ planningPlugin,
1917
+ dependencyGraphPlugin,
1918
+ defaultLifecyclePlugin,
1919
+ createSupervisorPlugin2 as createSupervisorPlugin,
658
1920
  createStateGitHubCredentialProvider,
1921
+ createStandardTaskSourcesPlugin,
1922
+ createStandardTaskCliPlugin,
1923
+ createStandardProductEntrypointPlugin,
1924
+ createStandardDocsDriftPlugin,
1925
+ createStandardCliSurfacePlugin,
1926
+ createPlanningPlugin2 as createPlanningPlugin,
659
1927
  createGitHubIssuesTaskSource,
660
1928
  createFilesTaskSource,
661
- createEnvGitHubCredentialProvider
1929
+ createEnvGitHubCredentialProvider,
1930
+ createDependencyGraphPlugin2 as createDependencyGraphPlugin,
1931
+ createDefaultLifecyclePlugin2 as createDefaultLifecyclePlugin,
1932
+ createBlockerClassifierPlugin2 as createBlockerClassifierPlugin,
1933
+ blockerClassifierPlugin,
1934
+ WORKSPACE_SUMMARY_CLI_ID,
1935
+ WORKSPACE_STATUS_CLI_ID,
1936
+ SUPERVISOR_PLUGIN_NAME,
1937
+ STANDARD_CLI_SURFACE_PLUGIN_NAME,
1938
+ RUN_WORKER_PANEL_PLUGIN,
1939
+ RUN_SUPERVISOR_PANEL_REGISTRATION,
1940
+ RUN_SUPERVISOR_PANEL_PRODUCER,
1941
+ RIG_SUPERVISOR_PANEL_ID,
1942
+ RIG_RUN_STOP_PANEL_ACTION,
1943
+ RIG_CAPABILITY_PANEL_SLOT,
1944
+ PLANNING_PLUGIN_NAME,
1945
+ PLANNING_PLAN_PANEL_ID,
1946
+ PEOPLE_PANEL_ID,
1947
+ HUMAN_BLOCKERS_PANEL_ID,
1948
+ EPICS_PANEL_ID,
1949
+ DOCS_HEALTH_PANEL_ID,
1950
+ DOCS_DRIFT_VALIDATOR_ID,
1951
+ DOCS_DRIFT_VALIDATOR,
1952
+ DOCS_DRIFT_STAGE_MUTATION,
1953
+ DOCS_DRIFT_STAGE_ID,
1954
+ DOCS_DRIFT_CLI_ID,
1955
+ DOCS_DRIFT_CLI_COMMAND,
1956
+ DOCS_DRIFT_CAPABILITY_ID,
1957
+ DEPENDENCY_GRAPH_PANEL_ID,
1958
+ DEPENDENCY_GRAPH_CLI_ID,
1959
+ DEFAULT_LIFECYCLE_PLUGIN_ID,
1960
+ DEFAULT_BLOCKER_CLASSIFIER_ID,
1961
+ BLOCKER_CLASSIFIER_PLUGIN_NAME,
1962
+ BLOCKERS_CLI_ID
662
1963
  };