@deftai/directive-core 0.69.0 → 0.70.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/dist/deposit/contain.d.ts +41 -0
- package/dist/deposit/contain.js +113 -0
- package/dist/deposit/copy-tree.js +8 -2
- package/dist/doctor/checks.d.ts +11 -0
- package/dist/doctor/checks.js +75 -2
- package/dist/doctor/main.js +54 -1
- package/dist/doctor/manifest.d.ts +20 -0
- package/dist/doctor/manifest.js +22 -0
- package/dist/doctor/types.d.ts +8 -0
- package/dist/init-deposit/init-deposit.js +6 -0
- package/dist/init-deposit/refresh.js +5 -0
- package/dist/intake/issue-ingest.d.ts +11 -0
- package/dist/intake/issue-ingest.js +86 -11
- package/dist/policy/plan-extensions.d.ts +31 -0
- package/dist/policy/plan-extensions.js +45 -0
- package/dist/policy/wip.d.ts +2 -2
- package/dist/policy/wip.js +2 -2
- package/dist/swarm/routing.js +10 -3
- package/dist/triage/bulk/index.d.ts +11 -2
- package/dist/triage/bulk/index.js +41 -4
- package/dist/triage/help/registry-data.d.ts +5 -5
- package/dist/triage/help/registry-data.js +18 -5
- package/dist/triage/scope/cli.d.ts +2 -1
- package/dist/triage/scope/cli.js +30 -6
- package/dist/triage/scope/mutations.d.ts +10 -0
- package/dist/triage/scope/mutations.js +22 -0
- package/dist/triage/welcome/constants.d.ts +1 -2
- package/dist/triage/welcome/constants.js +1 -2
- package/dist/triage/welcome/index.d.ts +1 -0
- package/dist/triage/welcome/index.js +1 -0
- package/dist/triage/welcome/onboard.d.ts +35 -0
- package/dist/triage/welcome/onboard.js +94 -0
- package/dist/umbrella-current-shape/index.d.ts +25 -1
- package/dist/umbrella-current-shape/index.js +84 -4
- package/dist/vbrief-build/project-definition-io.d.ts +6 -0
- package/dist/vbrief-build/project-definition-io.js +7 -2
- package/dist/vbrief-validate/precutover.js +7 -3
- package/package.json +3 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
3
|
import { cacheGet } from "../cache/operations.js";
|
|
4
|
+
import { scan } from "../cache/scanner.js";
|
|
4
5
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
5
6
|
import { call } from "../scm/call.js";
|
|
6
7
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
@@ -11,6 +12,27 @@ import { LEGACY_ARTIFACT_SUFFIX, LEGACY_INFO_ROOT_KEY, LEGACY_VBRIEF_VERSION, MI
|
|
|
11
12
|
import { findAcHeading, parseCheckboxItems, parseListItems, sliceAcSection, stripCodeBlocks, stripFencedCodeBlocks, } from "./markdown-scanners.js";
|
|
12
13
|
import { detectRepo, extractReferencesFromVbrief, fetchOpenIssues, GITHUB_ISSUE_REF_TYPES, LIFECYCLE_FOLDERS, parseIssueNumber, } from "./reconcile-issues.js";
|
|
13
14
|
export const INGEST_STATUSES = ["proposed", "pending", "active"];
|
|
15
|
+
/**
|
|
16
|
+
* Thrown when the quarantine scanner hard-fails (credential-shaped content) on
|
|
17
|
+
* an ingested issue body/comment thread (#2306). Ingest MUST fail closed: emit
|
|
18
|
+
* nothing and propagate a non-zero exit rather than persisting the xBRIEF.
|
|
19
|
+
*/
|
|
20
|
+
export class ScannerHardFailError extends Error {
|
|
21
|
+
issueNumber;
|
|
22
|
+
flags;
|
|
23
|
+
constructor(issueNumber, flags) {
|
|
24
|
+
const details = flags
|
|
25
|
+
.filter((f) => f.severity === "hard-fail")
|
|
26
|
+
.map((f) => f.detail)
|
|
27
|
+
.join("; ");
|
|
28
|
+
super(`issue:ingest refused #${issueNumber}: quarantine scanner hard-fail` +
|
|
29
|
+
(details.length > 0 ? ` (${details})` : "") +
|
|
30
|
+
" -- nothing written.");
|
|
31
|
+
this.name = "ScannerHardFailError";
|
|
32
|
+
this.issueNumber = issueNumber;
|
|
33
|
+
this.flags = flags;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
14
36
|
/** Enriched on issues after `fetchIssue` when the comment thread is non-empty (#2143). */
|
|
15
37
|
export const ISSUE_COMMENT_THREAD_KEY = "issueCommentThread";
|
|
16
38
|
const STATUS_MAP = {
|
|
@@ -311,7 +333,14 @@ export function buildIssueVbrief(issue, status, repoUrl, options = {}) {
|
|
|
311
333
|
};
|
|
312
334
|
if (overviewSource.length > 0) {
|
|
313
335
|
warnBodyControlCharacters(number, overviewSource);
|
|
314
|
-
|
|
336
|
+
// #2306: quarantine-scan untrusted body + comment-thread content before it
|
|
337
|
+
// is persisted as agent-facing scope authority. Fail closed on a credential
|
|
338
|
+
// hard-fail; otherwise persist the fenced/quarantined transform.
|
|
339
|
+
const scanResult = scan(overviewSource);
|
|
340
|
+
if (!scanResult.passed) {
|
|
341
|
+
throw new ScannerHardFailError(number, scanResult.flags);
|
|
342
|
+
}
|
|
343
|
+
narratives.Overview = scanResult.transformed_content;
|
|
315
344
|
}
|
|
316
345
|
if (labelNames.length > 0) {
|
|
317
346
|
narratives.Labels = labelNames.join(", ");
|
|
@@ -369,6 +398,19 @@ export function fetchFromCache(repo, number, options = {}) {
|
|
|
369
398
|
if (typeof issue.html_url === "string" && issue.html_url.length > 0) {
|
|
370
399
|
issue.url = issue.html_url;
|
|
371
400
|
}
|
|
401
|
+
// #2306: consume the cache entry's SCANNED content.md (fenced/quarantined at
|
|
402
|
+
// cache-put) rather than the raw body, so the cache read path cannot bypass
|
|
403
|
+
// the quarantine transform. When content.md is absent (e.g. a credential
|
|
404
|
+
// hard-fail deleted it), the raw body falls through and is re-scanned in
|
|
405
|
+
// buildIssueVbrief.
|
|
406
|
+
if (result.contentPath !== null) {
|
|
407
|
+
try {
|
|
408
|
+
issue.body = readFileSync(result.contentPath, "utf8");
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
// fall back to the raw body (re-scanned downstream)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
372
414
|
return issue;
|
|
373
415
|
}
|
|
374
416
|
catch {
|
|
@@ -514,9 +556,25 @@ export function ingestBulk(issues, options) {
|
|
|
514
556
|
created: [],
|
|
515
557
|
duplicate: [],
|
|
516
558
|
dryrun: [],
|
|
559
|
+
failed: [],
|
|
517
560
|
};
|
|
518
561
|
for (const issue of filtered) {
|
|
519
|
-
|
|
562
|
+
let ingested;
|
|
563
|
+
try {
|
|
564
|
+
ingested = ingestOne(issue, { ...options, existingRefs: refs });
|
|
565
|
+
}
|
|
566
|
+
catch (exc) {
|
|
567
|
+
// #2306: a per-issue quarantine hard-fail must not sink the whole batch;
|
|
568
|
+
// record it, emit nothing for that issue, and surface a non-zero exit
|
|
569
|
+
// upstream via the `failed` bucket.
|
|
570
|
+
if (exc instanceof ScannerHardFailError) {
|
|
571
|
+
summary.failed.push(`#${exc.issueNumber}`);
|
|
572
|
+
process.stderr.write(`${exc.message}\n`);
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
throw exc;
|
|
576
|
+
}
|
|
577
|
+
const [result, path, _msg] = ingested;
|
|
520
578
|
const rel = path !== null ? path.replace(`${options.vbriefDir}/`, "").replace(/\\/g, "/") : "";
|
|
521
579
|
summary[result].push(rel);
|
|
522
580
|
if (result === "created" && path !== null) {
|
|
@@ -581,7 +639,8 @@ export function issueIngestMain(args) {
|
|
|
581
639
|
const created = summary.created;
|
|
582
640
|
const duplicate = summary.duplicate;
|
|
583
641
|
const dryrun = summary.dryrun;
|
|
584
|
-
|
|
642
|
+
const failed = summary.failed ?? [];
|
|
643
|
+
process.stdout.write(`issue:ingest bulk summary: ${created.length} created, ${duplicate.length} duplicate, ${dryrun.length} dry-run, ${failed.length} refused (total considered: ${summary.total})\n`);
|
|
585
644
|
for (const entry of created) {
|
|
586
645
|
process.stdout.write(` CREATED ${entry}\n`);
|
|
587
646
|
}
|
|
@@ -591,19 +650,35 @@ export function issueIngestMain(args) {
|
|
|
591
650
|
for (const entry of duplicate) {
|
|
592
651
|
process.stdout.write(` SKIP ${entry} (already has scope vBRIEF)\n`);
|
|
593
652
|
}
|
|
594
|
-
|
|
653
|
+
for (const entry of failed) {
|
|
654
|
+
process.stdout.write(` REFUSED ${entry} (quarantine scanner hard-fail; nothing written)\n`);
|
|
655
|
+
}
|
|
656
|
+
// #2306: fail closed on any quarantine hard-fail in the batch.
|
|
657
|
+
return failed.length > 0 ? 2 : 0;
|
|
595
658
|
}
|
|
596
659
|
const issue = fetchIssue(repo, args.number, { cwd: projectRoot });
|
|
597
660
|
if (issue === null) {
|
|
598
661
|
return 2;
|
|
599
662
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
663
|
+
let result;
|
|
664
|
+
let msg;
|
|
665
|
+
try {
|
|
666
|
+
[result, , msg] = ingestOne(issue, {
|
|
667
|
+
vbriefDir,
|
|
668
|
+
status,
|
|
669
|
+
repoUrl,
|
|
670
|
+
dryRun: args.dryRun,
|
|
671
|
+
cwd: projectRoot,
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
catch (exc) {
|
|
675
|
+
// #2306: fail closed -- emit nothing, non-zero exit on a quarantine hard-fail.
|
|
676
|
+
if (exc instanceof ScannerHardFailError) {
|
|
677
|
+
process.stderr.write(`${exc.message}\n`);
|
|
678
|
+
return 2;
|
|
679
|
+
}
|
|
680
|
+
throw exc;
|
|
681
|
+
}
|
|
607
682
|
process.stdout.write(`${msg}\n`);
|
|
608
683
|
return result === "duplicate" ? 1 : 0;
|
|
609
684
|
}
|
|
@@ -32,4 +32,35 @@ export declare function readPlanCompletedNote(plan: unknown): unknown;
|
|
|
32
32
|
* bare block alongside the namespaced one.
|
|
33
33
|
*/
|
|
34
34
|
export declare function migrateLegacyPolicyKey(plan: Record<string, unknown>): void;
|
|
35
|
+
/** The namespaced/legacy key pairs a bare block can silently shadow (#2301). */
|
|
36
|
+
export declare const SHADOWABLE_PLAN_EXTENSIONS: ReadonlyArray<{
|
|
37
|
+
readonly namespacedKey: string;
|
|
38
|
+
readonly legacyKey: string;
|
|
39
|
+
}>;
|
|
40
|
+
/** A plan-extension key whose bare form is silently shadowed by the namespaced form. */
|
|
41
|
+
export interface ShadowedPlanExtension {
|
|
42
|
+
/** The namespaced key that wins the read (e.g. `x-directive/policy`). */
|
|
43
|
+
readonly namespacedKey: string;
|
|
44
|
+
/** The bare legacy key that is silently ignored (e.g. `policy`). */
|
|
45
|
+
readonly legacyKey: string;
|
|
46
|
+
/**
|
|
47
|
+
* Best-effort list of sub-keys present in the shadowed bare object (e.g.
|
|
48
|
+
* `["triageScope", "wipCap"]`). Empty when the bare value is not an object.
|
|
49
|
+
*/
|
|
50
|
+
readonly shadowedSubKeys: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Detect every plan-extension key where a bare (legacy) block coexists with the
|
|
54
|
+
* namespaced form (#2301). Because `readPlanExtension` is namespace-first, the
|
|
55
|
+
* bare block is never read once the namespaced key exists -- edits to it take no
|
|
56
|
+
* effect. Detecting the coexistence lets callers emit a loud diagnostic instead
|
|
57
|
+
* of the silent no-op that the #2295 onboarding trap exhibited.
|
|
58
|
+
*/
|
|
59
|
+
export declare function detectShadowedPlanExtensions(plan: unknown): ShadowedPlanExtension[];
|
|
60
|
+
/**
|
|
61
|
+
* Render a human-readable, loud diagnostic for a single shadowed plan-extension
|
|
62
|
+
* key. The message is surface-agnostic (no leading tag) so each caller can
|
|
63
|
+
* prefix it (`[policy:show]`, a doctor finding, ...).
|
|
64
|
+
*/
|
|
65
|
+
export declare function describeShadowedPlanExtension(shadow: ShadowedPlanExtension): string;
|
|
35
66
|
//# sourceMappingURL=plan-extensions.d.ts.map
|
|
@@ -54,4 +54,49 @@ export function migrateLegacyPolicyKey(plan) {
|
|
|
54
54
|
delete plan[LEGACY_PLAN_POLICY_KEY];
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
/** The namespaced/legacy key pairs a bare block can silently shadow (#2301). */
|
|
58
|
+
export const SHADOWABLE_PLAN_EXTENSIONS = [
|
|
59
|
+
{ namespacedKey: PLAN_POLICY_KEY, legacyKey: LEGACY_PLAN_POLICY_KEY },
|
|
60
|
+
{ namespacedKey: PLAN_COMPLETED_NOTE_KEY, legacyKey: LEGACY_PLAN_COMPLETED_NOTE_KEY },
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* Detect every plan-extension key where a bare (legacy) block coexists with the
|
|
64
|
+
* namespaced form (#2301). Because `readPlanExtension` is namespace-first, the
|
|
65
|
+
* bare block is never read once the namespaced key exists -- edits to it take no
|
|
66
|
+
* effect. Detecting the coexistence lets callers emit a loud diagnostic instead
|
|
67
|
+
* of the silent no-op that the #2295 onboarding trap exhibited.
|
|
68
|
+
*/
|
|
69
|
+
export function detectShadowedPlanExtensions(plan) {
|
|
70
|
+
const planObj = asPlanObject(plan);
|
|
71
|
+
if (planObj === null) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
const shadows = [];
|
|
75
|
+
for (const { namespacedKey, legacyKey } of SHADOWABLE_PLAN_EXTENSIONS) {
|
|
76
|
+
if (planObj[namespacedKey] === undefined || planObj[legacyKey] === undefined) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const legacyValue = planObj[legacyKey];
|
|
80
|
+
const shadowedSubKeys = typeof legacyValue === "object" && legacyValue !== null && !Array.isArray(legacyValue)
|
|
81
|
+
? Object.keys(legacyValue)
|
|
82
|
+
: [];
|
|
83
|
+
shadows.push({ namespacedKey, legacyKey, shadowedSubKeys });
|
|
84
|
+
}
|
|
85
|
+
return shadows;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Render a human-readable, loud diagnostic for a single shadowed plan-extension
|
|
89
|
+
* key. The message is surface-agnostic (no leading tag) so each caller can
|
|
90
|
+
* prefix it (`[policy:show]`, a doctor finding, ...).
|
|
91
|
+
*/
|
|
92
|
+
export function describeShadowedPlanExtension(shadow) {
|
|
93
|
+
const subKeys = shadow.shadowedSubKeys.length > 0
|
|
94
|
+
? ` Shadowed field(s): ${shadow.shadowedSubKeys
|
|
95
|
+
.map((k) => `plan.${shadow.legacyKey}.${k}`)
|
|
96
|
+
.join(", ")}.`
|
|
97
|
+
: "";
|
|
98
|
+
return (`bare \`plan.${shadow.legacyKey}\` coexists with namespaced \`plan.${shadow.namespacedKey}\`; ` +
|
|
99
|
+
`the bare block is IGNORED (namespaced-first read) so edits to it silently take no effect.${subKeys} ` +
|
|
100
|
+
`Fold its values into \`plan.${shadow.namespacedKey}\` and delete \`plan.${shadow.legacyKey}\`.`);
|
|
101
|
+
}
|
|
57
102
|
//# sourceMappingURL=plan-extensions.js.map
|
package/dist/policy/wip.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** Framework default WIP cap (#1124 / umbrella #1119). */
|
|
2
|
-
export declare const DEFAULT_WIP_CAP =
|
|
1
|
+
/** Framework default WIP cap (#2319; raised from 10 per #1124 / umbrella #1119). */
|
|
2
|
+
export declare const DEFAULT_WIP_CAP = 20;
|
|
3
3
|
/** vBRIEF lifecycle folders that count toward the WIP set. */
|
|
4
4
|
export declare const WIP_LIFECYCLE_DIRS: readonly ["pending", "active"];
|
|
5
5
|
export type WipCapSource = "typed" | "default" | "default-on-error";
|
package/dist/policy/wip.js
CHANGED
|
@@ -3,8 +3,8 @@ import { join } from "node:path";
|
|
|
3
3
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
4
4
|
import { readPlanPolicy } from "./plan-extensions.js";
|
|
5
5
|
import { loadProjectDefinition } from "./resolve.js";
|
|
6
|
-
/** Framework default WIP cap (#1124 / umbrella #1119). */
|
|
7
|
-
export const DEFAULT_WIP_CAP =
|
|
6
|
+
/** Framework default WIP cap (#2319; raised from 10 per #1124 / umbrella #1119). */
|
|
7
|
+
export const DEFAULT_WIP_CAP = 20;
|
|
8
8
|
/** vBRIEF lifecycle folders that count toward the WIP set. */
|
|
9
9
|
export const WIP_LIFECYCLE_DIRS = ["pending", "active"];
|
|
10
10
|
function pythonTypeName(value) {
|
package/dist/swarm/routing.js
CHANGED
|
@@ -186,7 +186,9 @@ export function resolveDispatchProvider(environ = process.env) {
|
|
|
186
186
|
* Keys that would mutate the prototype chain rather than set an own property
|
|
187
187
|
* if used as a computed object key. Rejected for provider/role names so a
|
|
188
188
|
* malicious routing input cannot pollute `Object.prototype` (CodeQL
|
|
189
|
-
* js/prototype-polluting-assignment).
|
|
189
|
+
* js/prototype-polluting-assignment). This validation guard produces the
|
|
190
|
+
* caller-facing error; the null-prototype write targets in
|
|
191
|
+
* `writeModelDecision` are the structural backstop CodeQL recognizes.
|
|
190
192
|
*/
|
|
191
193
|
const FORBIDDEN_ROUTING_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
192
194
|
function assertSafeRoutingKey(kind, key) {
|
|
@@ -203,9 +205,14 @@ export function writeModelDecision(path, provider, role, decision) {
|
|
|
203
205
|
assertSafeRoutingKey("provider", provider);
|
|
204
206
|
assertSafeRoutingKey("role", role);
|
|
205
207
|
const { data } = loadRoutingFile(path);
|
|
206
|
-
|
|
208
|
+
// Null-prototype write targets so a computed provider/role key can only ever
|
|
209
|
+
// set an own property and can never reach `Object.prototype`, even if the
|
|
210
|
+
// validation guard above regresses. CodeQL's js/prototype-polluting-assignment
|
|
211
|
+
// barrier does not track the interprocedural `assertSafeRoutingKey` guard, so
|
|
212
|
+
// this structural sink is what closes alert #52.
|
|
213
|
+
const file = Object.assign(Object.create(null), data ?? {});
|
|
207
214
|
const existing = providerBlockOf(file, provider);
|
|
208
|
-
const block = existing ?? {};
|
|
215
|
+
const block = Object.assign(Object.create(null), existing ?? {});
|
|
209
216
|
block[role] = {
|
|
210
217
|
model: decision.model,
|
|
211
218
|
mode: decision.mode ??
|
|
@@ -88,12 +88,21 @@ export declare function excludeLogged(candidates: Iterable<IssuePayload>, option
|
|
|
88
88
|
export declare function bulkAction(actionKey: string, repo: string, options?: BulkActionOptions): number;
|
|
89
89
|
export declare function createFilesystemCacheModule(): CacheModule;
|
|
90
90
|
export declare function createFilesystemCandidatesLogModule(logPath?: string): CandidatesLogModule;
|
|
91
|
+
/**
|
|
92
|
+
* Native TypeScript bulk actions module (#2279).
|
|
93
|
+
*
|
|
94
|
+
* The bulk path defaults to this factory instead of {@link createPythonActionsModule}
|
|
95
|
+
* so `triage:bulk-*` never spawns a project-local `scripts/triage_actions.py`
|
|
96
|
+
* (an arbitrary-code-execution trust boundary a malicious checkout could plant).
|
|
97
|
+
* Each verb delegates to the already-native per-issue triage actions
|
|
98
|
+
* (`@deftai/directive-core/triage/actions`, #1725) bound to `createDefaultDeps(projectRoot)`.
|
|
99
|
+
*/
|
|
100
|
+
export declare function createNativeActionsModule(projectRoot: string): TriageActionsModule;
|
|
91
101
|
export declare function createPythonActionsModule(scriptsDir: string): TriageActionsModule;
|
|
92
102
|
export interface DefaultBulkDepsOptions {
|
|
93
103
|
readonly cacheRoot?: string;
|
|
94
104
|
readonly candidatesLogPath?: string;
|
|
95
|
-
readonly
|
|
96
|
-
readonly deftRoot?: string;
|
|
105
|
+
readonly projectRoot?: string;
|
|
97
106
|
}
|
|
98
107
|
export declare function bulkActionWithDefaults(actionKey: string, repo: string, options?: BulkActionOptions & DefaultBulkDepsOptions): number;
|
|
99
108
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { resolveEvalPath } from "../../layout/resolve.js";
|
|
5
|
+
import { createDefaultDeps, accept as nativeAccept, deferAction as nativeDefer, needsAc as nativeNeedsAc, reject as nativeReject, } from "../actions/index.js";
|
|
5
6
|
export const ACTION_FN_NAMES = {
|
|
6
7
|
accept: "accept",
|
|
7
8
|
reject: "reject",
|
|
@@ -402,6 +403,40 @@ export function createFilesystemCandidatesLogModule(logPath = resolveEvalPath(pr
|
|
|
402
403
|
},
|
|
403
404
|
};
|
|
404
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* Native TypeScript bulk actions module (#2279).
|
|
408
|
+
*
|
|
409
|
+
* The bulk path defaults to this factory instead of {@link createPythonActionsModule}
|
|
410
|
+
* so `triage:bulk-*` never spawns a project-local `scripts/triage_actions.py`
|
|
411
|
+
* (an arbitrary-code-execution trust boundary a malicious checkout could plant).
|
|
412
|
+
* Each verb delegates to the already-native per-issue triage actions
|
|
413
|
+
* (`@deftai/directive-core/triage/actions`, #1725) bound to `createDefaultDeps(projectRoot)`.
|
|
414
|
+
*/
|
|
415
|
+
export function createNativeActionsModule(projectRoot) {
|
|
416
|
+
const deps = createDefaultDeps(projectRoot);
|
|
417
|
+
return {
|
|
418
|
+
accept(n, repo) {
|
|
419
|
+
nativeAccept(n, repo, deps, { projectRoot });
|
|
420
|
+
},
|
|
421
|
+
reject(n, repo, ...args) {
|
|
422
|
+
let reason = "";
|
|
423
|
+
const first = args[0];
|
|
424
|
+
if (typeof first === "object" && first !== null && "reason" in first) {
|
|
425
|
+
reason = String(first.reason);
|
|
426
|
+
}
|
|
427
|
+
else if (typeof first === "string") {
|
|
428
|
+
reason = first;
|
|
429
|
+
}
|
|
430
|
+
nativeReject(n, repo, reason, deps, { projectRoot });
|
|
431
|
+
},
|
|
432
|
+
defer(n, repo) {
|
|
433
|
+
nativeDefer(n, repo, "bulk defer", deps, { projectRoot });
|
|
434
|
+
},
|
|
435
|
+
needs_ac(n, repo) {
|
|
436
|
+
nativeNeedsAc(n, repo, deps, { projectRoot });
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
}
|
|
405
440
|
export function createPythonActionsModule(scriptsDir) {
|
|
406
441
|
const runAction = (cmd, issueNumber, repo, extra = []) => {
|
|
407
442
|
const result = spawnSync("uv", [
|
|
@@ -442,14 +477,16 @@ export function createPythonActionsModule(scriptsDir) {
|
|
|
442
477
|
};
|
|
443
478
|
}
|
|
444
479
|
export function bulkActionWithDefaults(actionKey, repo, options = {}) {
|
|
445
|
-
|
|
446
|
-
|
|
480
|
+
// #2279: the bulk path no longer resolves a `scripts/` dir from DEFT_ROOT /
|
|
481
|
+
// the project cwd. It defaults to the native TypeScript actions module so a
|
|
482
|
+
// malicious project-local `scripts/triage_actions.py` can never be executed.
|
|
483
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
447
484
|
return bulkAction(actionKey, repo, {
|
|
448
485
|
...options,
|
|
449
|
-
cacheRoot: options.cacheRoot ?? join(
|
|
486
|
+
cacheRoot: options.cacheRoot ?? join(projectRoot, ".deft-cache"),
|
|
450
487
|
cacheModule: options.cacheModule ?? createFilesystemCacheModule(),
|
|
451
488
|
candidatesLogModule: options.candidatesLogModule ?? createFilesystemCandidatesLogModule(options.candidatesLogPath),
|
|
452
|
-
actionsModule: options.actionsModule ??
|
|
489
|
+
actionsModule: options.actionsModule ?? createNativeActionsModule(projectRoot),
|
|
453
490
|
});
|
|
454
491
|
}
|
|
455
492
|
//# sourceMappingURL=index.js.map
|
|
@@ -191,11 +191,11 @@ export declare const registryData: {
|
|
|
191
191
|
readonly "task triage:welcome": {
|
|
192
192
|
readonly name: "task triage:welcome";
|
|
193
193
|
readonly summary: "Single-entry-point upgrade ritual";
|
|
194
|
-
readonly refs: "(N3 / #1143)";
|
|
195
|
-
readonly description: "6-phase onboarding ritual: detect prior state, prompt for subscription scope, run triage:bootstrap, prompt for wipCap, offer WIP relief, print triage:summary. Idempotent on re-run; safe entrypoint for fresh consumers.";
|
|
196
|
-
readonly usage: "task triage:welcome [-- --no-subprocess]";
|
|
197
|
-
readonly flags: readonly [readonly ["--no-subprocess", "(off)", "Dry-mode: don't shell out to sibling tasks."]];
|
|
198
|
-
readonly examples: readonly ["task triage:welcome"];
|
|
194
|
+
readonly refs: "(N3 / #1143, #2295)";
|
|
195
|
+
readonly description: "6-phase onboarding ritual: detect prior state, prompt for subscription scope, run triage:bootstrap, prompt for wipCap, offer WIP relief, print triage:summary. Idempotent on re-run; safe entrypoint for fresh consumers. Pass --onboard for the non-interactive path that applies a triage-scope preset (and optional --wip-cap) without prompting -- the form agents/CI use.";
|
|
196
|
+
readonly usage: "task triage:welcome [-- --onboard [--preset small|mid|mega] [--wip-cap N]] [--no-subprocess]";
|
|
197
|
+
readonly flags: readonly [readonly ["--onboard", "(off)", "Non-interactive onboarding: apply a triage-scope preset (and optional WIP cap) without prompting."], readonly ["--preset small|mid|mega", "small", "Subscription-scope preset applied by --onboard."], readonly ["--wip-cap N", "(policy default)", "Persist an explicit in-flight scope cap during --onboard (positive integer)."], readonly ["--no-subprocess", "(off)", "Dry-mode: don't shell out to sibling tasks."]];
|
|
198
|
+
readonly examples: readonly ["task triage:welcome", "task triage:welcome -- --onboard --preset small"];
|
|
199
199
|
readonly see_also: readonly ["task triage:bootstrap", "task triage:summary", "#1119 / N3"];
|
|
200
200
|
readonly placeholder: false;
|
|
201
201
|
};
|
|
@@ -296,11 +296,24 @@ export const registryData = {
|
|
|
296
296
|
"task triage:welcome": {
|
|
297
297
|
name: "task triage:welcome",
|
|
298
298
|
summary: "Single-entry-point upgrade ritual",
|
|
299
|
-
refs: "(N3 / #1143)",
|
|
300
|
-
description: "6-phase onboarding ritual: detect prior state, prompt for subscription scope, run triage:bootstrap, prompt for wipCap, offer WIP relief, print triage:summary. Idempotent on re-run; safe entrypoint for fresh consumers.",
|
|
301
|
-
usage: "task triage:welcome [-- --no-subprocess]",
|
|
302
|
-
flags: [
|
|
303
|
-
|
|
299
|
+
refs: "(N3 / #1143, #2295)",
|
|
300
|
+
description: "6-phase onboarding ritual: detect prior state, prompt for subscription scope, run triage:bootstrap, prompt for wipCap, offer WIP relief, print triage:summary. Idempotent on re-run; safe entrypoint for fresh consumers. Pass --onboard for the non-interactive path that applies a triage-scope preset (and optional --wip-cap) without prompting -- the form agents/CI use.",
|
|
301
|
+
usage: "task triage:welcome [-- --onboard [--preset small|mid|mega] [--wip-cap N]] [--no-subprocess]",
|
|
302
|
+
flags: [
|
|
303
|
+
[
|
|
304
|
+
"--onboard",
|
|
305
|
+
"(off)",
|
|
306
|
+
"Non-interactive onboarding: apply a triage-scope preset (and optional WIP cap) without prompting.",
|
|
307
|
+
],
|
|
308
|
+
["--preset small|mid|mega", "small", "Subscription-scope preset applied by --onboard."],
|
|
309
|
+
[
|
|
310
|
+
"--wip-cap N",
|
|
311
|
+
"(policy default)",
|
|
312
|
+
"Persist an explicit in-flight scope cap during --onboard (positive integer).",
|
|
313
|
+
],
|
|
314
|
+
["--no-subprocess", "(off)", "Dry-mode: don't shell out to sibling tasks."],
|
|
315
|
+
],
|
|
316
|
+
examples: ["task triage:welcome", "task triage:welcome -- --onboard --preset small"],
|
|
304
317
|
see_also: ["task triage:bootstrap", "task triage:summary", "#1119 / N3"],
|
|
305
318
|
placeholder: false,
|
|
306
319
|
},
|
|
@@ -6,13 +6,14 @@ export interface ParsedCliArgs {
|
|
|
6
6
|
addLabel: string | undefined;
|
|
7
7
|
addMilestone: string | undefined;
|
|
8
8
|
ignoreLabel: string | undefined;
|
|
9
|
+
setPreset: string | undefined;
|
|
9
10
|
diffFromUpstream: boolean;
|
|
10
11
|
source: string;
|
|
11
12
|
cacheRoot: string | undefined;
|
|
12
13
|
count: number | undefined;
|
|
13
14
|
}
|
|
14
15
|
export declare function parseCliArgs(argv: string[]): ParsedCliArgs;
|
|
15
|
-
export declare const CLI_HELP = "usage: triage_scope.py [-h] [--project-root PROJECT_ROOT] [--list]\n [--refresh-denominator] [--repo REPO]\n [--add-label ADD_LABEL] [--add-milestone ADD_MILESTONE]\n [--ignore-label IGNORE_LABEL] [--diff-from-upstream]\n [--source SOURCE] [--cache-root CACHE_ROOT]\n [--count COUNT]\n\nInspect, mutate, and diff the typed plan.policy.triageScope[] subscription +\nplan.policy.triageScopeIgnores[] (#1131 / D12, #1133 / D14, #1182 / D14c).\nRead paths never trigger a recompute; use --refresh-denominator to update the\ncoverage cache. Mutation flags --add-label / --add-milestone / --ignore-label
|
|
16
|
+
export declare const CLI_HELP = "usage: triage_scope.py [-h] [--project-root PROJECT_ROOT] [--list]\n [--refresh-denominator] [--repo REPO]\n [--add-label ADD_LABEL] [--add-milestone ADD_MILESTONE]\n [--ignore-label IGNORE_LABEL] [--set-preset PRESET]\n [--diff-from-upstream]\n [--source SOURCE] [--cache-root CACHE_ROOT]\n [--count COUNT]\n\nInspect, mutate, and diff the typed plan.policy.triageScope[] subscription +\nplan.policy.triageScopeIgnores[] (#1131 / D12, #1133 / D14, #1182 / D14c).\nRead paths never trigger a recompute; use --refresh-denominator to update the\ncoverage cache. Mutation flags --add-label / --add-milestone / --ignore-label /\n--set-preset are idempotent and atomic; every mutation appends a\nsubscription-change audit entry to vbrief/.eval/subscription-history.jsonl.\n--set-preset small|mid|mega overwrites plan.policy.triageScope[] with a named\nsubscription preset via the same writer that backs triage:welcome --onboard (#2301).\n";
|
|
16
17
|
/** Run triage:scope CLI; returns exit code and captured output for parity. */
|
|
17
18
|
export declare function runCliCapture(argv: string[]): {
|
|
18
19
|
code: number;
|
package/dist/triage/scope/cli.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { coveragePath, writeCoverageDenominator } from "./coverage.js";
|
|
5
|
-
import { addLabelToIgnores, addLabelToScope, addMilestoneToScope, computeDiffFromUpstream, fetchUpstreamLabelsAndMilestones, renderDiffReport, } from "./mutations.js";
|
|
5
|
+
import { addLabelToIgnores, addLabelToScope, addMilestoneToScope, computeDiffFromUpstream, fetchUpstreamLabelsAndMilestones, renderDiffReport, setScopePreset, } from "./mutations.js";
|
|
6
6
|
import { subscriptionHash } from "./normalize.js";
|
|
7
7
|
import { pyListRepr } from "./python-repr.js";
|
|
8
8
|
import { renderIgnores, renderList } from "./renderers.js";
|
|
@@ -17,6 +17,7 @@ export function parseCliArgs(argv) {
|
|
|
17
17
|
addLabel: undefined,
|
|
18
18
|
addMilestone: undefined,
|
|
19
19
|
ignoreLabel: undefined,
|
|
20
|
+
setPreset: undefined,
|
|
20
21
|
diffFromUpstream: false,
|
|
21
22
|
source: "github-issue",
|
|
22
23
|
cacheRoot: undefined,
|
|
@@ -60,6 +61,12 @@ export function parseCliArgs(argv) {
|
|
|
60
61
|
}
|
|
61
62
|
else if (arg?.startsWith("--ignore-label="))
|
|
62
63
|
parsed.ignoreLabel = arg.slice("--ignore-label=".length);
|
|
64
|
+
else if (arg === "--set-preset") {
|
|
65
|
+
parsed.setPreset = argv[i + 1];
|
|
66
|
+
i += 1;
|
|
67
|
+
}
|
|
68
|
+
else if (arg?.startsWith("--set-preset="))
|
|
69
|
+
parsed.setPreset = arg.slice("--set-preset=".length);
|
|
63
70
|
else if (arg === "--source") {
|
|
64
71
|
parsed.source = argv[i + 1] ?? "github-issue";
|
|
65
72
|
i += 1;
|
|
@@ -86,16 +93,19 @@ export function parseCliArgs(argv) {
|
|
|
86
93
|
export const CLI_HELP = `usage: triage_scope.py [-h] [--project-root PROJECT_ROOT] [--list]
|
|
87
94
|
[--refresh-denominator] [--repo REPO]
|
|
88
95
|
[--add-label ADD_LABEL] [--add-milestone ADD_MILESTONE]
|
|
89
|
-
[--ignore-label IGNORE_LABEL] [--
|
|
96
|
+
[--ignore-label IGNORE_LABEL] [--set-preset PRESET]
|
|
97
|
+
[--diff-from-upstream]
|
|
90
98
|
[--source SOURCE] [--cache-root CACHE_ROOT]
|
|
91
99
|
[--count COUNT]
|
|
92
100
|
|
|
93
101
|
Inspect, mutate, and diff the typed plan.policy.triageScope[] subscription +
|
|
94
102
|
plan.policy.triageScopeIgnores[] (#1131 / D12, #1133 / D14, #1182 / D14c).
|
|
95
103
|
Read paths never trigger a recompute; use --refresh-denominator to update the
|
|
96
|
-
coverage cache. Mutation flags --add-label / --add-milestone / --ignore-label
|
|
97
|
-
are idempotent and atomic; every mutation appends a
|
|
98
|
-
entry to vbrief/.eval/subscription-history.jsonl.
|
|
104
|
+
coverage cache. Mutation flags --add-label / --add-milestone / --ignore-label /
|
|
105
|
+
--set-preset are idempotent and atomic; every mutation appends a
|
|
106
|
+
subscription-change audit entry to vbrief/.eval/subscription-history.jsonl.
|
|
107
|
+
--set-preset small|mid|mega overwrites plan.policy.triageScope[] with a named
|
|
108
|
+
subscription preset via the same writer that backs triage:welcome --onboard (#2301).
|
|
99
109
|
`;
|
|
100
110
|
function handleMutation(projectRoot, args) {
|
|
101
111
|
const out = { stdout: [], stderr: [] };
|
|
@@ -115,6 +125,10 @@ function handleMutation(projectRoot, args) {
|
|
|
115
125
|
[changed, message] = addLabelToIgnores(projectRoot, args.ignoreLabel);
|
|
116
126
|
verb = "ignore-label";
|
|
117
127
|
}
|
|
128
|
+
else if (args.setPreset !== undefined) {
|
|
129
|
+
[changed, message] = setScopePreset(projectRoot, args.setPreset);
|
|
130
|
+
verb = "set-preset";
|
|
131
|
+
}
|
|
118
132
|
else {
|
|
119
133
|
throw new Error("internal: mutation flag set but no handler matched");
|
|
120
134
|
}
|
|
@@ -148,12 +162,13 @@ export function runCliCapture(argv) {
|
|
|
148
162
|
args.addLabel !== undefined ? "--add-label" : null,
|
|
149
163
|
args.addMilestone !== undefined ? "--add-milestone" : null,
|
|
150
164
|
args.ignoreLabel !== undefined ? "--ignore-label" : null,
|
|
165
|
+
args.setPreset !== undefined ? "--set-preset" : null,
|
|
151
166
|
].filter((f) => f !== null);
|
|
152
167
|
if (mutationFlags.length > 1) {
|
|
153
168
|
return {
|
|
154
169
|
code: 2,
|
|
155
170
|
stdout: "",
|
|
156
|
-
stderr: "triage:scope: --add-label / --add-milestone / --ignore-label " +
|
|
171
|
+
stderr: "triage:scope: --add-label / --add-milestone / --ignore-label / --set-preset " +
|
|
157
172
|
`are mutually exclusive (got ${pyListRepr(mutationFlags)}).\n`,
|
|
158
173
|
};
|
|
159
174
|
}
|
|
@@ -171,6 +186,15 @@ export function runCliCapture(argv) {
|
|
|
171
186
|
if (mutation.code !== 0) {
|
|
172
187
|
return { code: mutation.code, stdout: stdout.join(""), stderr: stderr.join("") };
|
|
173
188
|
}
|
|
189
|
+
// A preset write comes from the trusted SUBSCRIPTION_PRESETS constant (the
|
|
190
|
+
// same source the onboard path writes). The `mega` preset intentionally
|
|
191
|
+
// ships an `explicit-watch` scaffold with an empty `issues` list for the
|
|
192
|
+
// operator to fill in, which the strict hand-edit validator below flags.
|
|
193
|
+
// Bypass that re-validation for a preset write so `--set-preset` reports the
|
|
194
|
+
// successful, persisted mutation instead of a spurious exit 1 (#2301).
|
|
195
|
+
if (args.setPreset !== undefined) {
|
|
196
|
+
return { code: 0, stdout: stdout.join(""), stderr: stderr.join("") };
|
|
197
|
+
}
|
|
174
198
|
}
|
|
175
199
|
const data = loadProjectDefinition(projectRoot);
|
|
176
200
|
const rules = resolveScopeRules(projectRoot, data);
|
|
@@ -18,4 +18,14 @@ export declare function fetchUpstreamLabelsAndMilestones(repo: string, binary?:
|
|
|
18
18
|
export declare function addLabelToScope(projectRoot: string, label: string, actor?: string | null): [boolean, string];
|
|
19
19
|
export declare function addMilestoneToScope(projectRoot: string, milestone: string, actor?: string | null): [boolean, string];
|
|
20
20
|
export declare function addLabelToIgnores(projectRoot: string, label: string): [boolean, string];
|
|
21
|
+
/** Valid subscription preset keys for `triage:scope --set-preset` (#2301). */
|
|
22
|
+
export declare function scopePresetKeys(): string[];
|
|
23
|
+
/**
|
|
24
|
+
* Overwrite plan.policy.triageScope with a named subscription preset (#2301).
|
|
25
|
+
* Reuses the shared `subscriptionPreset` + `writeTriageScope` helper that backs
|
|
26
|
+
* `triage:welcome --onboard`, so the write path (namespaced key, legacy-key
|
|
27
|
+
* migration, audit trail, mutation lock) stays identical across surfaces rather
|
|
28
|
+
* than being duplicated here.
|
|
29
|
+
*/
|
|
30
|
+
export declare function setScopePreset(projectRoot: string, presetKey: string): [boolean, string];
|
|
21
31
|
//# sourceMappingURL=mutations.d.ts.map
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { SUBPROCESS_MAX_BUFFER } from "../../subprocess/max-buffer.js";
|
|
3
|
+
import { SUBSCRIPTION_PRESETS } from "../welcome/constants.js";
|
|
4
|
+
import { subscriptionPreset, writeTriageScope } from "../welcome/writers.js";
|
|
3
5
|
import { collectMilestoneSubscribedNames, rulesRequestIsOpen } from "./milestone.js";
|
|
4
6
|
import { addIgnore, subscribe } from "./mutations-core.js";
|
|
5
7
|
import { resolveScopeIgnores, resolveScopeRules } from "./resolve.js";
|
|
@@ -200,4 +202,24 @@ export function addLabelToIgnores(projectRoot, label) {
|
|
|
200
202
|
throw new Error(`label must be a non-empty string; got ${JSON.stringify(label)}`);
|
|
201
203
|
return addIgnore(projectRoot, label);
|
|
202
204
|
}
|
|
205
|
+
/** Valid subscription preset keys for `triage:scope --set-preset` (#2301). */
|
|
206
|
+
export function scopePresetKeys() {
|
|
207
|
+
return Object.keys(SUBSCRIPTION_PRESETS);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Overwrite plan.policy.triageScope with a named subscription preset (#2301).
|
|
211
|
+
* Reuses the shared `subscriptionPreset` + `writeTriageScope` helper that backs
|
|
212
|
+
* `triage:welcome --onboard`, so the write path (namespaced key, legacy-key
|
|
213
|
+
* migration, audit trail, mutation lock) stays identical across surfaces rather
|
|
214
|
+
* than being duplicated here.
|
|
215
|
+
*/
|
|
216
|
+
export function setScopePreset(projectRoot, presetKey) {
|
|
217
|
+
const key = presetKey.trim();
|
|
218
|
+
const validKeys = scopePresetKeys();
|
|
219
|
+
if (!validKeys.includes(key)) {
|
|
220
|
+
throw new Error(`unknown preset ${JSON.stringify(presetKey)}; valid presets: ${validKeys.join(", ")}`);
|
|
221
|
+
}
|
|
222
|
+
const rules = subscriptionPreset(key);
|
|
223
|
+
return writeTriageScope(projectRoot, rules, { presetLabel: key });
|
|
224
|
+
}
|
|
203
225
|
//# sourceMappingURL=mutations.js.map
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
export declare const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
2
1
|
export declare const CACHE_DIR_NAME = ".deft-cache";
|
|
3
2
|
export declare const CACHE_SOURCE = "github-issue";
|
|
4
3
|
export declare const CANDIDATES_RELPATH: readonly ["vbrief", ".eval", "candidates.jsonl"];
|
|
5
4
|
export declare const WIP_LIFECYCLE_DIRS: readonly ["pending", "active"];
|
|
6
5
|
export declare const AUDIT_LOG_REL_PATH = "meta/policy-changes.log";
|
|
7
|
-
export declare const DEFAULT_WIP_CAP =
|
|
6
|
+
export declare const DEFAULT_WIP_CAP = 20;
|
|
8
7
|
export declare const DEFAULT_RELIEF_AGE_DAYS = 30;
|
|
9
8
|
export declare const TRIAGE_SKILL_PATH = "skills/deft-directive-triage/SKILL.md";
|
|
10
9
|
export declare const WELCOME_AUDIT_TAG = "triage-welcome";
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
export const PROJECT_DEFINITION_REL_PATH = "vbrief/PROJECT-DEFINITION.vbrief.json";
|
|
2
1
|
export const CACHE_DIR_NAME = ".deft-cache";
|
|
3
2
|
export const CACHE_SOURCE = "github-issue";
|
|
4
3
|
export const CANDIDATES_RELPATH = ["vbrief", ".eval", "candidates.jsonl"];
|
|
5
4
|
export const WIP_LIFECYCLE_DIRS = ["pending", "active"];
|
|
6
5
|
export const AUDIT_LOG_REL_PATH = "meta/policy-changes.log";
|
|
7
|
-
export const DEFAULT_WIP_CAP =
|
|
6
|
+
export const DEFAULT_WIP_CAP = 20;
|
|
8
7
|
export const DEFAULT_RELIEF_AGE_DAYS = 30;
|
|
9
8
|
export const TRIAGE_SKILL_PATH = "skills/deft-directive-triage/SKILL.md";
|
|
10
9
|
export const WELCOME_AUDIT_TAG = "triage-welcome";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./constants.js";
|
|
2
2
|
export { type DefaultModeOptions, formatWelcomeCommand, normalizeTaskPrefix, runDefaultMode, type WelcomeOutcome, } from "./default-mode.js";
|
|
3
|
+
export { DEFAULT_ONBOARD_PRESET, type OnboardOptions, type OnboardOutcome, runOnboardMode, } from "./onboard.js";
|
|
3
4
|
export { candidatesLogPath, classifyOnboarding, detectPriorState, type PriorState, pendingDecisionsNudgeLine, } from "./prior-state.js";
|
|
4
5
|
export { computeSummary, emitOneliner, formatOneLiner, formatSummary, type SummaryResult, } from "./summary.js";
|
|
5
6
|
export { appendAuditEntry, previewWipRelief, type ReliefPreview, subscriptionPreset, writeTriageScope, writeWipCap, } from "./writers.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./constants.js";
|
|
2
2
|
export { formatWelcomeCommand, normalizeTaskPrefix, runDefaultMode, } from "./default-mode.js";
|
|
3
|
+
export { DEFAULT_ONBOARD_PRESET, runOnboardMode, } from "./onboard.js";
|
|
3
4
|
export { candidatesLogPath, classifyOnboarding, detectPriorState, pendingDecisionsNudgeLine, } from "./prior-state.js";
|
|
4
5
|
export { computeSummary, emitOneliner, formatOneLiner, formatSummary, } from "./summary.js";
|
|
5
6
|
export { appendAuditEntry, previewWipRelief, subscriptionPreset, writeTriageScope, writeWipCap, } from "./writers.js";
|