@deftai/directive-core 0.107.0 → 0.108.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/architecture/sor-preflight.js +2 -0
- package/dist/check/cached-orchestrator.js +28 -3
- package/dist/check/named-cause.js +18 -3
- package/dist/codebase/provider.js +7 -1
- package/dist/delivery-attempt/handoff.js +1 -1
- package/dist/design-critique/citation-grammar.d.ts +53 -0
- package/dist/design-critique/citation-grammar.js +303 -0
- package/dist/design-critique/completed-arc-record.d.ts +62 -0
- package/dist/design-critique/completed-arc-record.js +232 -0
- package/dist/freshness/bind.d.ts +32 -3
- package/dist/freshness/bind.js +58 -12
- package/dist/hooks/classify/host-session-identity.d.ts +76 -0
- package/dist/hooks/classify/host-session-identity.js +470 -0
- package/dist/hooks/classify/index.d.ts +3 -2
- package/dist/hooks/classify/index.js +3 -2
- package/dist/hooks/classify/paths.d.ts +7 -0
- package/dist/hooks/classify/paths.js +35 -0
- package/dist/hooks/classify/stdin.d.ts +5 -0
- package/dist/hooks/classify/stdin.js +27 -0
- package/dist/hooks/dispatcher.d.ts +52 -1
- package/dist/hooks/dispatcher.js +538 -45
- package/dist/hooks/fixtures/cases.d.ts +16 -2
- package/dist/hooks/fixtures/cases.js +175 -0
- package/dist/hooks/scope.js +3 -1
- package/dist/init-deposit/agent-hooks.d.ts +2 -1
- package/dist/init-deposit/agent-hooks.js +8 -1
- package/dist/init-deposit/hook-runtime-travel.d.ts +77 -0
- package/dist/init-deposit/hook-runtime-travel.js +269 -0
- package/dist/intake/clause-derivation.js +9 -3
- package/dist/intake/issue-ingest.js +62 -2
- package/dist/intake/platform-capabilities.d.ts +9 -2
- package/dist/intake/platform-capabilities.js +38 -9
- package/dist/orphan-active/evaluate.d.ts +25 -0
- package/dist/orphan-active/evaluate.js +193 -128
- package/dist/orphan-active/index.d.ts +1 -0
- package/dist/orphan-active/index.js +1 -0
- package/dist/orphan-active/issue-state.d.ts +121 -0
- package/dist/orphan-active/issue-state.js +278 -0
- package/dist/platform/cursor-managed-runtime.d.ts +95 -0
- package/dist/platform/cursor-managed-runtime.js +241 -0
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.js +1 -0
- package/dist/platform/platform-capabilities.d.ts +23 -1
- package/dist/platform/platform-capabilities.js +55 -15
- package/dist/policy/ceremony-dial.js +5 -9
- package/dist/policy/host-hooks.js +4 -9
- package/dist/policy/org-force-on-migration.js +5 -10
- package/dist/policy/plan-extensions.d.ts +14 -2
- package/dist/policy/plan-extensions.js +24 -3
- package/dist/policy/product-signal.js +5 -10
- package/dist/policy/require-human-merge.js +5 -9
- package/dist/policy/resolve.js +77 -16
- package/dist/policy/value-feedback.js +9 -18
- package/dist/pr-closeout-attestable/evaluate.d.ts +80 -0
- package/dist/pr-closeout-attestable/evaluate.js +306 -0
- package/dist/pr-closeout-attestable/index.d.ts +2 -0
- package/dist/pr-closeout-attestable/index.js +2 -0
- package/dist/pr-protected-issues/gh.d.ts +6 -2
- package/dist/pr-protected-issues/gh.js +7 -3
- package/dist/pr-wait-mergeable/cascade.d.ts +9 -1
- package/dist/pr-wait-mergeable/cascade.js +21 -1
- package/dist/pr-wait-mergeable/types.d.ts +2 -0
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
- package/dist/pr-wait-mergeable/wrappers.js +11 -0
- package/dist/preflight/evaluate.d.ts +15 -0
- package/dist/preflight/evaluate.js +29 -3
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +14 -2
- package/dist/product-first-done-gate/acceptance-resolver.js +13 -2
- package/dist/product-first-done-gate/evaluate.js +17 -7
- package/dist/release/cli-drift-report.d.ts +68 -0
- package/dist/release/cli-drift-report.js +189 -0
- package/dist/release/index.d.ts +1 -0
- package/dist/release/index.js +1 -0
- package/dist/release/issue-state-fetch.d.ts +5 -3
- package/dist/release/issue-state-fetch.js +86 -21
- package/dist/release/native-steps.js +1 -0
- package/dist/release/pipeline.js +19 -0
- package/dist/release/types.d.ts +14 -0
- package/dist/render/constants.d.ts +4 -0
- package/dist/render/constants.js +11 -8
- package/dist/render/export-spec.js +31 -5
- package/dist/render/index.d.ts +1 -1
- package/dist/render/index.js +1 -1
- package/dist/render/prd-render.d.ts +4 -1
- package/dist/render/prd-render.js +82 -23
- package/dist/render/project-render.js +14 -7
- package/dist/render/scope-outlook.d.ts +2 -0
- package/dist/render/scope-outlook.js +3 -0
- package/dist/render/spec-render.js +2 -2
- package/dist/resolution/package-manager.d.ts +33 -4
- package/dist/resolution/package-manager.js +210 -9
- package/dist/review-monitor/constants.d.ts +7 -0
- package/dist/review-monitor/constants.js +7 -0
- package/dist/review-monitor/github-lease.d.ts +98 -1
- package/dist/review-monitor/github-lease.js +182 -1
- package/dist/review-monitor/lease-comment.d.ts +36 -0
- package/dist/review-monitor/lease-comment.js +103 -2
- package/dist/scm/call.js +12 -3
- package/dist/scm/design-critique-chip.d.ts +1 -0
- package/dist/scm/design-critique-chip.js +22 -6
- package/dist/scm/gh-rest.d.ts +9 -1
- package/dist/scm/gh-rest.js +103 -0
- package/dist/scm/readiness.d.ts +2 -0
- package/dist/scm/readiness.js +28 -4
- package/dist/scope/project-definition-sync.js +14 -7
- package/dist/session/git.d.ts +24 -0
- package/dist/session/git.js +138 -2
- package/dist/session/occupancy.d.ts +238 -12
- package/dist/session/occupancy.js +815 -29
- package/dist/session/ritual-sentinel.d.ts +5 -0
- package/dist/session/ritual-sentinel.js +5 -0
- package/dist/session/session-ready.d.ts +5 -1
- package/dist/session/session-ready.js +98 -8
- package/dist/session/session-start.d.ts +5 -1
- package/dist/session/session-start.js +32 -17
- package/dist/session/toolchain-preflight.d.ts +13 -3
- package/dist/session/toolchain-preflight.js +93 -18
- package/dist/session/verify-session-ritual.d.ts +11 -0
- package/dist/session/verify-session-ritual.js +60 -15
- package/dist/slice/constants.d.ts +1 -1
- package/dist/spec-authority/constants.d.ts +10 -2
- package/dist/spec-authority/constants.js +53 -8
- package/dist/spec-authority/resolver.d.ts +3 -0
- package/dist/spec-authority/resolver.js +55 -7
- package/dist/subprocess/max-buffer.d.ts +14 -0
- package/dist/subprocess/max-buffer.js +15 -0
- package/dist/swarm/complete-cohort.d.ts +2 -0
- package/dist/swarm/complete-cohort.js +21 -13
- package/dist/swarm/index.d.ts +1 -0
- package/dist/swarm/index.js +1 -0
- package/dist/swarm/launch-cli.js +53 -33
- package/dist/swarm/launch.d.ts +8 -0
- package/dist/swarm/launch.js +63 -43
- package/dist/swarm/pre-dispatch-cli.js +2 -1
- package/dist/swarm/pre-dispatch.js +10 -1
- package/dist/swarm/subagent-status-dir.d.ts +27 -0
- package/dist/swarm/subagent-status-dir.js +42 -0
- package/dist/swarm/worktrees.d.ts +17 -3
- package/dist/swarm/worktrees.js +48 -4
- package/dist/triage/scope/mutations-core.d.ts +2 -6
- package/dist/triage/scope/mutations-core.js +23 -55
- package/dist/triage/scope-drift/add-ignore.js +5 -39
- package/dist/triage/subscribe/index.d.ts +2 -6
- package/dist/triage/subscribe/index.js +15 -65
- package/dist/triage/welcome/writers.js +15 -74
- package/dist/vbrief-build/index.d.ts +2 -1
- package/dist/vbrief-build/index.js +2 -1
- package/dist/vbrief-build/parity-scenarios.js +5 -5
- package/dist/vbrief-build/project-definition-io.d.ts +63 -3
- package/dist/vbrief-build/project-definition-io.js +357 -31
- package/dist/vbrief-build/project-definition-mutation.d.ts +32 -0
- package/dist/vbrief-build/project-definition-mutation.js +43 -0
- package/dist/vbrief-validate/conformance.d.ts +1 -0
- package/dist/vbrief-validate/conformance.js +85 -8
- package/dist/vbrief-validate/main.js +2 -0
- package/dist/vbrief-validate/precutover.js +5 -10
- package/dist/verify-ac/clauses.d.ts +62 -2
- package/dist/verify-ac/clauses.js +183 -111
- package/dist/verify-ac/index.d.ts +1 -1
- package/dist/verify-ac/index.js +1 -1
- package/dist/verify-env/node-runtime.d.ts +8 -4
- package/dist/verify-env/node-runtime.js +9 -6
- package/dist/verify-env/toolchain-check.d.ts +35 -3
- package/dist/verify-env/toolchain-check.js +155 -36
- package/dist/verify-source/project-definition-mutation-boundary.d.ts +104 -0
- package/dist/verify-source/project-definition-mutation-boundary.js +326 -0
- package/package.json +7 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_STALE_MINUTES, REVIEW_OWNER_MARKER_END, REVIEW_OWNER_MARKER_START, } from "./constants.js";
|
|
1
|
+
import { DEFAULT_STALE_MINUTES, PASS_MARKER_KIND, REVIEW_OWNER_MARKER_END, REVIEW_OWNER_MARKER_START, } from "./constants.js";
|
|
2
2
|
export function parseIso8601Utc(value) {
|
|
3
3
|
if (typeof value !== "string" || value.length === 0) {
|
|
4
4
|
return null;
|
|
@@ -17,7 +17,7 @@ export function parseIso8601Utc(value) {
|
|
|
17
17
|
}
|
|
18
18
|
return parsed;
|
|
19
19
|
}
|
|
20
|
-
const FIELD_RE = /^(owner|monitor_agent_id|head_sha|started_at|expires_at|platform_primitive|ended_at):\s*(.*)$/;
|
|
20
|
+
const FIELD_RE = /^(kind|owner|monitor_agent_id|agent_id|pass_kind|ceiling|head_sha|started_at|expires_at|platform_primitive|ended_at):\s*(.*)$/;
|
|
21
21
|
/** True when the comment body contains the review-owner marker block. */
|
|
22
22
|
export function hasReviewOwnerMarker(body) {
|
|
23
23
|
return body.includes(REVIEW_OWNER_MARKER_START);
|
|
@@ -42,8 +42,17 @@ function parseFieldBlock(body) {
|
|
|
42
42
|
}
|
|
43
43
|
return fields;
|
|
44
44
|
}
|
|
45
|
+
/** Marker `kind:` value, or "" when the block declares none (ownership-lease shape). */
|
|
46
|
+
function markerKind(body) {
|
|
47
|
+
return parseFieldBlock(body).kind ?? "";
|
|
48
|
+
}
|
|
45
49
|
export function parseReviewOwnerLease(body) {
|
|
46
50
|
const fields = parseFieldBlock(body);
|
|
51
|
+
// An advisory pass mark shares the marker but must never satisfy the
|
|
52
|
+
// review-monitor ownership gate (#3607).
|
|
53
|
+
if ((fields.kind ?? "") === PASS_MARKER_KIND) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
47
56
|
const owner = fields.owner ?? "";
|
|
48
57
|
const monitorAgentId = fields.monitor_agent_id ?? "";
|
|
49
58
|
const startedAt = fields.started_at ?? "";
|
|
@@ -128,6 +137,11 @@ export function mapCommentEntry(entry) {
|
|
|
128
137
|
if (!hasReviewOwnerMarker(rec.body)) {
|
|
129
138
|
return null;
|
|
130
139
|
}
|
|
140
|
+
// Pass marks are a disjoint population on the same marker; excluding them here
|
|
141
|
+
// keeps `registerReviewMonitor` from overwriting one as its lease anchor (#3607).
|
|
142
|
+
if (markerKind(rec.body) === PASS_MARKER_KIND) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
131
145
|
return {
|
|
132
146
|
id: rec.id,
|
|
133
147
|
body: rec.body,
|
|
@@ -149,4 +163,91 @@ export function findActiveLeaseComment(comments, options = {}) {
|
|
|
149
163
|
}
|
|
150
164
|
return selectWinningReviewOwnerComment(active);
|
|
151
165
|
}
|
|
166
|
+
export function parsePassOpenMarker(body) {
|
|
167
|
+
if (!hasReviewOwnerMarker(body)) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const fields = parseFieldBlock(body);
|
|
171
|
+
if ((fields.kind ?? "") !== PASS_MARKER_KIND) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
const owner = fields.owner ?? "";
|
|
175
|
+
const passKind = fields.pass_kind ?? "";
|
|
176
|
+
const startedAt = fields.started_at ?? "";
|
|
177
|
+
const expiresAt = fields.expires_at ?? "";
|
|
178
|
+
if (owner.length === 0 ||
|
|
179
|
+
passKind.length === 0 ||
|
|
180
|
+
startedAt.length === 0 ||
|
|
181
|
+
expiresAt.length === 0) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const agentId = fields.agent_id ?? "";
|
|
185
|
+
const ceiling = fields.ceiling ?? "";
|
|
186
|
+
const endedAt = fields.ended_at ?? "";
|
|
187
|
+
return {
|
|
188
|
+
kind: PASS_MARKER_KIND,
|
|
189
|
+
pass_kind: passKind,
|
|
190
|
+
owner,
|
|
191
|
+
agent_id: agentId.length > 0 ? agentId : null,
|
|
192
|
+
ceiling: ceiling.length > 0 ? ceiling : null,
|
|
193
|
+
started_at: startedAt,
|
|
194
|
+
expires_at: expiresAt,
|
|
195
|
+
ended_at: endedAt.length > 0 ? endedAt : null,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
export function renderPassOpenComment(marker) {
|
|
199
|
+
const optional = (name, value) => value !== null && value.length > 0 ? `${name}: ${value}\n` : "";
|
|
200
|
+
return (`${REVIEW_OWNER_MARKER_START}\n` +
|
|
201
|
+
`kind: ${PASS_MARKER_KIND}\n` +
|
|
202
|
+
`pass_kind: ${marker.pass_kind}\n` +
|
|
203
|
+
`owner: ${marker.owner}\n` +
|
|
204
|
+
optional("agent_id", marker.agent_id) +
|
|
205
|
+
optional("ceiling", marker.ceiling) +
|
|
206
|
+
`started_at: ${marker.started_at}\n` +
|
|
207
|
+
`expires_at: ${marker.expires_at}\n` +
|
|
208
|
+
optional("ended_at", marker.ended_at) +
|
|
209
|
+
`${REVIEW_OWNER_MARKER_END}`);
|
|
210
|
+
}
|
|
211
|
+
/** Expired or synthesis-cleared marks are ignored on read; a mark self-clears (#3607). */
|
|
212
|
+
export function isPassMarkerActive(marker, now = new Date()) {
|
|
213
|
+
if (marker.ended_at !== null) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
const expires = parseIso8601Utc(marker.expires_at);
|
|
217
|
+
if (expires === null) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return now.getTime() <= expires.getTime();
|
|
221
|
+
}
|
|
222
|
+
export function mapPassCommentEntry(entry) {
|
|
223
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const rec = entry;
|
|
227
|
+
if (typeof rec.id !== "number" || typeof rec.body !== "string") {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const marker = parsePassOpenMarker(rec.body);
|
|
231
|
+
if (marker === null) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
id: rec.id,
|
|
236
|
+
body: rec.body,
|
|
237
|
+
createdAt: typeof rec.created_at === "string" ? rec.created_at : "",
|
|
238
|
+
authorLogin: typeof rec.author_login === "string" ? rec.author_login : "",
|
|
239
|
+
marker,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/** Oldest comment id wins when two marks race, matching the lease (#2814 / #3607). */
|
|
243
|
+
export function selectWinningPassComment(comments) {
|
|
244
|
+
if (comments.length === 0) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
return [...comments].sort((a, b) => a.id - b.id)[0] ?? null;
|
|
248
|
+
}
|
|
249
|
+
export function findActivePassComment(comments, options = {}) {
|
|
250
|
+
const active = comments.filter((comment) => comment.marker !== null && isPassMarkerActive(comment.marker, options.now));
|
|
251
|
+
return selectWinningPassComment(active);
|
|
252
|
+
}
|
|
152
253
|
//# sourceMappingURL=lease-comment.js.map
|
package/dist/scm/call.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { resolveCaptureFailureStderr, SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
|
|
2
3
|
import { resolveBinary } from "./binary.js";
|
|
3
4
|
import { SUPPORTED_CALL_SOURCES } from "./constants.js";
|
|
4
5
|
import { pyRepr } from "./py-format.js";
|
|
@@ -26,18 +27,26 @@ export function call(source, verb, args = null, options = {}) {
|
|
|
26
27
|
input: options.input,
|
|
27
28
|
encoding: (options.text ?? true) ? "utf8" : undefined,
|
|
28
29
|
timeout: timeoutMs,
|
|
30
|
+
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
29
31
|
stdio: captureOutput ? ["pipe", "pipe", "pipe"] : "inherit",
|
|
30
32
|
});
|
|
33
|
+
// A spawn-level failure (ENOBUFS past maxBuffer, timeout kill) yields a null
|
|
34
|
+
// status and empty stderr; surface error.message so callers never report a
|
|
35
|
+
// bare exit 1 with no reason (#1867 / #3903).
|
|
36
|
+
const stderr = resolveCaptureFailureStderr({
|
|
37
|
+
captured: typeof result.stderr === "string" ? result.stderr : "",
|
|
38
|
+
status: result.status,
|
|
39
|
+
message: result.error?.message,
|
|
40
|
+
});
|
|
31
41
|
if (options.check && result.status !== 0) {
|
|
32
|
-
const
|
|
33
|
-
const error = new Error(message || `Process exited with code ${result.status}`);
|
|
42
|
+
const error = new Error(stderr || `Process exited with code ${result.status}`);
|
|
34
43
|
throw error;
|
|
35
44
|
}
|
|
36
45
|
return {
|
|
37
46
|
args: argv,
|
|
38
47
|
returncode: result.status ?? 1,
|
|
39
48
|
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
40
|
-
stderr
|
|
49
|
+
stderr,
|
|
41
50
|
};
|
|
42
51
|
}
|
|
43
52
|
//# sourceMappingURL=call.js.map
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { type DesignCritiqueCatalogChip } from "../design-critique/exclusive-chip.js";
|
|
8
8
|
import type { LabelClient } from "../vbrief-reconcile/types.js";
|
|
9
9
|
export declare const DESIGN_CRITIQUE_CHIP_VERB: "design-critique-chip";
|
|
10
|
+
export declare const CHIP_APPLY_MISS_TOKEN = "chip apply missed (non-blocking convenience)";
|
|
10
11
|
export declare const DESIGN_CRITIQUE_CHIP_USAGE: string;
|
|
11
12
|
export interface DesignCritiqueChipArgs {
|
|
12
13
|
readonly issue: number;
|
|
@@ -7,14 +7,16 @@
|
|
|
7
7
|
import { spawnSync } from "node:child_process";
|
|
8
8
|
import { applyDesignCritiqueCatalogChip, } from "../design-critique/exclusive-chip.js";
|
|
9
9
|
import { parseGithubOwnerRepo } from "../policy/sync-default.js";
|
|
10
|
-
import { ScmLabelClient
|
|
10
|
+
import { ScmLabelClient } from "../vbrief-reconcile/labels.js";
|
|
11
11
|
import { extractFlag, extractValueFlag } from "./argv.js";
|
|
12
12
|
import { InvalidRepoError, splitRepo } from "./gh-rest.js";
|
|
13
13
|
import { pyRepr } from "./py-format.js";
|
|
14
14
|
export const DESIGN_CRITIQUE_CHIP_VERB = "design-critique-chip";
|
|
15
|
+
export const CHIP_APPLY_MISS_TOKEN = "chip apply missed (non-blocking convenience)";
|
|
15
16
|
export const DESIGN_CRITIQUE_CHIP_USAGE = "usage: scm issue design-critique-chip --issue N --chip triage-ready|mechanism-shaped [--repo OWNER/NAME] [--json]\n" +
|
|
16
17
|
" Parent attach of design-critique:triage-ready / recut mechanism-shaped.\n" +
|
|
17
|
-
" Closed catalog remaining-set replace. One write. Other facets stay.\n"
|
|
18
|
+
" Closed catalog remaining-set replace. One write. Other facets stay.\n" +
|
|
19
|
+
" Apply miss is non-blocking convenience; ingest is not blocked.\n";
|
|
18
20
|
const CHIP_ALIASES = {
|
|
19
21
|
"triage-ready": "design-critique:triage-ready",
|
|
20
22
|
"mechanism-shaped": "design-critique:mechanism-shaped",
|
|
@@ -161,11 +163,25 @@ export function runDesignCritiqueChip(extra, seams = {}) {
|
|
|
161
163
|
};
|
|
162
164
|
}
|
|
163
165
|
catch (err) {
|
|
164
|
-
if (err instanceof ScmLabelError) {
|
|
165
|
-
return { exitCode: 1, stdout: "", stderr: `error: ${err.message}\n` };
|
|
166
|
-
}
|
|
167
166
|
const message = err instanceof Error ? err.message : String(err);
|
|
168
|
-
|
|
167
|
+
const payload = {
|
|
168
|
+
repo,
|
|
169
|
+
issue: args.issue,
|
|
170
|
+
chip: args.chip,
|
|
171
|
+
applied: false,
|
|
172
|
+
miss: true,
|
|
173
|
+
blocking: false,
|
|
174
|
+
error: message,
|
|
175
|
+
};
|
|
176
|
+
if (args.json) {
|
|
177
|
+
return { exitCode: 0, stdout: `${JSON.stringify(payload)}\n`, stderr: "" };
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
exitCode: 0,
|
|
181
|
+
stdout: "",
|
|
182
|
+
stderr: `${CHIP_APPLY_MISS_TOKEN}: ${message}\n` +
|
|
183
|
+
"ingest is not blocked; remaining-set hygiene is optional for a write-capable identity\n",
|
|
184
|
+
};
|
|
169
185
|
}
|
|
170
186
|
}
|
|
171
187
|
//# sourceMappingURL=design-critique-chip.js.map
|
package/dist/scm/gh-rest.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export interface RestIssueListOptions {
|
|
|
53
53
|
export declare function restIssueList(repo: string, options?: RestIssueListOptions, seams?: GhRestSeams): Record<string, unknown>[];
|
|
54
54
|
export declare const REST_MAX_PER_PAGE = 100;
|
|
55
55
|
export declare const REST_PAGINATION_MAX_PAGES = 100;
|
|
56
|
-
export declare const PUBLIC_HELPERS: readonly ["restCreateIssue", "restPostComment", "restUpdateComment", "restDeleteComment", "restGetUser", "restUpdateIssue", "restCreateLabel", "restCloseIssue", "restOpenPr", "restMergePr", "restIssueView", "restPrView", "restIssueList", "restIssueListPaginated"];
|
|
56
|
+
export declare const PUBLIC_HELPERS: readonly ["restCreateIssue", "restPostComment", "restUpdateComment", "restDeleteComment", "restGetUser", "restUpdateIssue", "restCreateLabel", "restCloseIssue", "restOpenPr", "restMergePr", "restIssueView", "restPrView", "restIssueList", "restIssueListPaginated", "restIssueListOpenInventory"];
|
|
57
57
|
export declare function restCreateIssue(repo: string, title: string, body: string, labels?: readonly string[], seams?: GhRestSeams): Record<string, unknown>;
|
|
58
58
|
export declare function restPostComment(repo: string, n: number, body: string, seams?: GhRestSeams): Record<string, unknown>;
|
|
59
59
|
/** `PATCH /repos/{owner}/{repo}/issues/comments/{id}` -- edit a PR/issue comment in place. */
|
|
@@ -78,5 +78,13 @@ export interface RestIssueListPaginatedOptions extends RestIssueListOptions {
|
|
|
78
78
|
readonly limit?: number | null;
|
|
79
79
|
readonly excludePulls?: boolean;
|
|
80
80
|
}
|
|
81
|
+
/** Flatten `gh api --paginate --slurp` issue-list output (pages or single page). */
|
|
82
|
+
export declare function flattenOpenInventoryPayload(parsed: unknown[], endpoint: string): Record<string, unknown>[];
|
|
83
|
+
/**
|
|
84
|
+
* Complete open-issue inventory in one `gh api --paginate --slurp` subprocess (#3752).
|
|
85
|
+
* Fail-closed on command failure, non-array JSON, buffer exhaustion, or cap hit.
|
|
86
|
+
* Pull-request rows are excluded.
|
|
87
|
+
*/
|
|
88
|
+
export declare function restIssueListOpenInventory(repo: string, seams?: GhRestSeams): Record<string, unknown>[];
|
|
81
89
|
export declare function restIssueListPaginated(repo: string, options?: RestIssueListPaginatedOptions, seams?: GhRestSeams): Record<string, unknown>[];
|
|
82
90
|
//# sourceMappingURL=gh-rest.d.ts.map
|
package/dist/scm/gh-rest.js
CHANGED
|
@@ -183,6 +183,7 @@ export const PUBLIC_HELPERS = [
|
|
|
183
183
|
"restPrView",
|
|
184
184
|
"restIssueList",
|
|
185
185
|
"restIssueListPaginated",
|
|
186
|
+
"restIssueListOpenInventory",
|
|
186
187
|
];
|
|
187
188
|
function writeJsonPayload(payload) {
|
|
188
189
|
const dir = mkdtempSync(join(tmpdir(), "gh_rest_payload_"));
|
|
@@ -331,6 +332,108 @@ export function restPrView(repo, n, seams = {}) {
|
|
|
331
332
|
whichFn: seams.whichFn,
|
|
332
333
|
});
|
|
333
334
|
}
|
|
335
|
+
const OPEN_INVENTORY_MAX_ISSUES = REST_PAGINATION_MAX_PAGES * REST_MAX_PER_PAGE;
|
|
336
|
+
function issueRowOrThrow(item, endpoint) {
|
|
337
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
338
|
+
throw new GhRestError({
|
|
339
|
+
stderr: "open-issue inventory row is not an object",
|
|
340
|
+
exitCode: 0,
|
|
341
|
+
endpoint,
|
|
342
|
+
payload: null,
|
|
343
|
+
hint: "REST issue list rows must be objects",
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
return item;
|
|
347
|
+
}
|
|
348
|
+
/** Flatten `gh api --paginate --slurp` issue-list output (pages or single page). */
|
|
349
|
+
export function flattenOpenInventoryPayload(parsed, endpoint) {
|
|
350
|
+
if (parsed.length === 0) {
|
|
351
|
+
return [];
|
|
352
|
+
}
|
|
353
|
+
const out = [];
|
|
354
|
+
const allPages = parsed.every((item) => Array.isArray(item));
|
|
355
|
+
if (allPages) {
|
|
356
|
+
for (const page of parsed) {
|
|
357
|
+
if (!Array.isArray(page)) {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
for (const item of page) {
|
|
361
|
+
const row = issueRowOrThrow(item, endpoint);
|
|
362
|
+
if ("pull_request" in row) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
out.push(row);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return out;
|
|
369
|
+
}
|
|
370
|
+
for (const item of parsed) {
|
|
371
|
+
const row = issueRowOrThrow(item, endpoint);
|
|
372
|
+
if ("pull_request" in row) {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
out.push(row);
|
|
376
|
+
}
|
|
377
|
+
return out;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Complete open-issue inventory in one `gh api --paginate --slurp` subprocess (#3752).
|
|
381
|
+
* Fail-closed on command failure, non-array JSON, buffer exhaustion, or cap hit.
|
|
382
|
+
* Pull-request rows are excluded.
|
|
383
|
+
*/
|
|
384
|
+
export function restIssueListOpenInventory(repo, seams = {}) {
|
|
385
|
+
const [owner, name] = splitRepo(repo);
|
|
386
|
+
const endpoint = `repos/${owner}/${name}/issues?state=open&per_page=${REST_MAX_PER_PAGE}`;
|
|
387
|
+
const runner = seams.runGhApiFn ?? runGhApi;
|
|
388
|
+
const result = runner(["--paginate", "--slurp", endpoint], { timeout: 120 });
|
|
389
|
+
if (result.returncode !== 0) {
|
|
390
|
+
throw new GhRestError({
|
|
391
|
+
stderr: result.stderr.trim(),
|
|
392
|
+
exitCode: result.returncode,
|
|
393
|
+
endpoint,
|
|
394
|
+
payload: null,
|
|
395
|
+
hint: "verify gh auth and core REST quota; open-issue inventory must be complete",
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
const stdout = result.stdout.trim();
|
|
399
|
+
if (stdout.length === 0) {
|
|
400
|
+
return [];
|
|
401
|
+
}
|
|
402
|
+
let parsed;
|
|
403
|
+
try {
|
|
404
|
+
parsed = JSON.parse(stdout);
|
|
405
|
+
}
|
|
406
|
+
catch (exc) {
|
|
407
|
+
const message = exc instanceof Error ? exc.message : String(exc);
|
|
408
|
+
throw new GhRestError({
|
|
409
|
+
stderr: `non-JSON open-issue inventory: ${message}`,
|
|
410
|
+
exitCode: 0,
|
|
411
|
+
endpoint,
|
|
412
|
+
payload: null,
|
|
413
|
+
hint: "gh api --paginate --slurp must return a JSON array",
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
if (!Array.isArray(parsed)) {
|
|
417
|
+
throw new GhRestError({
|
|
418
|
+
stderr: `unexpected top-level type ${typeof parsed}`,
|
|
419
|
+
exitCode: 0,
|
|
420
|
+
endpoint,
|
|
421
|
+
payload: null,
|
|
422
|
+
hint: "open-issue inventory must be a JSON array",
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
const out = flattenOpenInventoryPayload(parsed, endpoint);
|
|
426
|
+
if (out.length >= OPEN_INVENTORY_MAX_ISSUES) {
|
|
427
|
+
throw new GhRestError({
|
|
428
|
+
stderr: `open-issue inventory reached cap ${OPEN_INVENTORY_MAX_ISSUES}; pagination may be incomplete`,
|
|
429
|
+
exitCode: 0,
|
|
430
|
+
endpoint,
|
|
431
|
+
payload: null,
|
|
432
|
+
hint: "repo may exceed supported open-issue count; fail closed rather than truncate",
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
return out;
|
|
436
|
+
}
|
|
334
437
|
export function restIssueListPaginated(repo, options = {}, seams = {}) {
|
|
335
438
|
const cappedPerPage = Math.min(Math.max(1, options.perPage ?? REST_MAX_PER_PAGE), REST_MAX_PER_PAGE);
|
|
336
439
|
const [owner, name] = splitRepo(repo);
|
package/dist/scm/readiness.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export interface ScmReadinessReport {
|
|
|
41
41
|
readonly githubAuthMode: string;
|
|
42
42
|
/** Runtime mode from the #1557a probe. */
|
|
43
43
|
readonly runtimeMode: string;
|
|
44
|
+
/** Stable id naming why the runtime was classified that way (#3859). */
|
|
45
|
+
readonly runtimeModeReason: string | null;
|
|
44
46
|
/** Whether an injected token env var is present (value never reported). */
|
|
45
47
|
readonly injectedTokenPresent: boolean;
|
|
46
48
|
/** Probe depth used for this report. */
|
package/dist/scm/readiness.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { spawnSync } from "node:child_process";
|
|
22
22
|
import { findInjectedToken, GITHUB_AUTH_MODE_HOST_GH, GITHUB_AUTH_MODE_INJECTED_TOKEN, inferGithubAuthMode, validateGithubAuthForWorker, } from "../intake/github-auth-modes.js";
|
|
23
23
|
import { getPlatformCapabilities, probeRuntimeCapabilities, } from "../intake/platform-capabilities.js";
|
|
24
|
+
import { EXPLICIT_SELECTION_REMEDIATION, GITHUB_AUTH_MODE_ENV, RUNTIME_REASON_CURSOR_MARKER_AMBIGUOUS, } from "../platform/cursor-managed-runtime.js";
|
|
24
25
|
import { defaultWhich } from "./binary.js";
|
|
25
26
|
import { BINARY_PREFERENCE } from "./constants.js";
|
|
26
27
|
import { ScmStubError } from "./errors.js";
|
|
@@ -138,6 +139,7 @@ export function probeScmReadiness(options = {}) {
|
|
|
138
139
|
authState: "binary-absent",
|
|
139
140
|
githubAuthMode,
|
|
140
141
|
runtimeMode: runtimeReport.runtimeMode,
|
|
142
|
+
runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
|
|
141
143
|
injectedTokenPresent,
|
|
142
144
|
depth,
|
|
143
145
|
detail,
|
|
@@ -149,9 +151,11 @@ export function probeScmReadiness(options = {}) {
|
|
|
149
151
|
}
|
|
150
152
|
// Injected-token mode without a token is a hard not-ready even before auth status.
|
|
151
153
|
if (githubAuthMode === GITHUB_AUTH_MODE_INJECTED_TOKEN && !injectedTokenPresent) {
|
|
154
|
+
const ambiguousRuntime = runtimeReport.runtimeModeReason === RUNTIME_REASON_CURSOR_MARKER_AMBIGUOUS;
|
|
152
155
|
const detail = "injected-token mode requires GH_TOKEN, GITHUB_TOKEN, or GH_ENTERPRISE_TOKEN; " +
|
|
153
156
|
`binary=${binary} present but SCM-dependent gates skipped ` +
|
|
154
|
-
`(runtime_mode=${runtimeReport.runtimeMode}
|
|
157
|
+
`(runtime_mode=${runtimeReport.runtimeMode}` +
|
|
158
|
+
`${runtimeReport.runtimeModeReason ? `, reason=${runtimeReport.runtimeModeReason}` : ""})`;
|
|
155
159
|
return {
|
|
156
160
|
ready: false,
|
|
157
161
|
binary,
|
|
@@ -159,10 +163,15 @@ export function probeScmReadiness(options = {}) {
|
|
|
159
163
|
authState: "missing-token",
|
|
160
164
|
githubAuthMode,
|
|
161
165
|
runtimeMode: runtimeReport.runtimeMode,
|
|
166
|
+
runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
|
|
162
167
|
injectedTokenPresent: false,
|
|
163
168
|
depth,
|
|
164
169
|
detail,
|
|
165
|
-
|
|
170
|
+
// An ambiguous Cursor runtime is the #3859 case: name the explicit opt-in
|
|
171
|
+
// here, because this is where the operator actually meets the block.
|
|
172
|
+
remediation: ambiguousRuntime
|
|
173
|
+
? `${EXPLICIT_SELECTION_REMEDIATION}\n\n${REMEDIATION_MISSING_TOKEN}`
|
|
174
|
+
: REMEDIATION_MISSING_TOKEN,
|
|
166
175
|
skippedGates: [...SCM_DEPENDENT_GATES],
|
|
167
176
|
login: null,
|
|
168
177
|
failureKind: "missing_injected_token",
|
|
@@ -209,6 +218,7 @@ export function probeScmReadiness(options = {}) {
|
|
|
209
218
|
authState: "authenticated",
|
|
210
219
|
githubAuthMode: deep.githubAuthMode,
|
|
211
220
|
runtimeMode: runtimeReport.runtimeMode,
|
|
221
|
+
runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
|
|
212
222
|
injectedTokenPresent,
|
|
213
223
|
depth,
|
|
214
224
|
detail: `SCM ready: ${binary} present, ${deep.githubAuthMode} authenticated${loginPart} (deep)`,
|
|
@@ -226,6 +236,7 @@ export function probeScmReadiness(options = {}) {
|
|
|
226
236
|
authState,
|
|
227
237
|
githubAuthMode: deep.githubAuthMode,
|
|
228
238
|
runtimeMode: runtimeReport.runtimeMode,
|
|
239
|
+
runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
|
|
229
240
|
injectedTokenPresent,
|
|
230
241
|
depth,
|
|
231
242
|
detail: `SCM not ready: ${deep.detail}`,
|
|
@@ -247,6 +258,7 @@ export function probeScmReadiness(options = {}) {
|
|
|
247
258
|
authState: "unknown",
|
|
248
259
|
githubAuthMode,
|
|
249
260
|
runtimeMode: runtimeReport.runtimeMode,
|
|
261
|
+
runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
|
|
250
262
|
injectedTokenPresent,
|
|
251
263
|
depth,
|
|
252
264
|
detail,
|
|
@@ -268,6 +280,7 @@ export function probeScmReadiness(options = {}) {
|
|
|
268
280
|
authState: "unauthenticated",
|
|
269
281
|
githubAuthMode,
|
|
270
282
|
runtimeMode: runtimeReport.runtimeMode,
|
|
283
|
+
runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
|
|
271
284
|
injectedTokenPresent,
|
|
272
285
|
depth,
|
|
273
286
|
detail,
|
|
@@ -284,6 +297,7 @@ export function probeScmReadiness(options = {}) {
|
|
|
284
297
|
authState: "authenticated",
|
|
285
298
|
githubAuthMode,
|
|
286
299
|
runtimeMode: runtimeReport.runtimeMode,
|
|
300
|
+
runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
|
|
287
301
|
injectedTokenPresent,
|
|
288
302
|
depth,
|
|
289
303
|
detail: `SCM ready: ${binary} present, ${githubAuthMode} authenticated (shallow)`,
|
|
@@ -302,6 +316,7 @@ export function scmReadinessToDict(report) {
|
|
|
302
316
|
auth_state: report.authState,
|
|
303
317
|
github_auth_mode: report.githubAuthMode,
|
|
304
318
|
runtime_mode: report.runtimeMode,
|
|
319
|
+
runtime_mode_reason: report.runtimeModeReason,
|
|
305
320
|
injected_token_present: report.injectedTokenPresent,
|
|
306
321
|
depth: report.depth,
|
|
307
322
|
detail: report.detail,
|
|
@@ -324,7 +339,14 @@ export function formatScmReadinessLines(report) {
|
|
|
324
339
|
}
|
|
325
340
|
lines.push(`[deft scm] ${report.detail}`);
|
|
326
341
|
if (report.skippedGates.length > 0) {
|
|
327
|
-
|
|
342
|
+
// Name the classification reason next to the skip: a silent skip is what
|
|
343
|
+
// makes this failure costly, because work-selection surfaces go dark (#3859).
|
|
344
|
+
const reason = report.runtimeModeReason ? ` (reason: ${report.runtimeModeReason})` : "";
|
|
345
|
+
lines.push(`[deft scm] skipped gates: ${report.skippedGates.join(", ")}${reason}`);
|
|
346
|
+
if (report.runtimeModeReason === RUNTIME_REASON_CURSOR_MARKER_AMBIGUOUS) {
|
|
347
|
+
lines.push(`[deft scm] runtime is ambiguous; set ${GITHUB_AUTH_MODE_ENV}=host-gh to authorize ` +
|
|
348
|
+
"the host gh credential store on a machine you control (#3859)");
|
|
349
|
+
}
|
|
328
350
|
}
|
|
329
351
|
lines.push("[deft scm] run SCM-dependent gates only after auth is ready, or from a matched env; " +
|
|
330
352
|
"see content/scm/github.md § Mismatched/headless SCM readiness (#2275)");
|
|
@@ -387,13 +409,15 @@ export function requireScmReady(options = {}) {
|
|
|
387
409
|
if (hermeticAuthSkip) {
|
|
388
410
|
// Production path uses assertScmBinaryPresent (not test-only).
|
|
389
411
|
const binary = assertScmBinaryPresent(options.whichFn);
|
|
412
|
+
const hermeticRuntime = options.runtimeReport ?? getPlatformCapabilities();
|
|
390
413
|
const report = {
|
|
391
414
|
ready: true,
|
|
392
415
|
binary,
|
|
393
416
|
binaryPath: options.whichFn?.(binary) ?? null,
|
|
394
417
|
authState: "unknown",
|
|
395
418
|
githubAuthMode: GITHUB_AUTH_MODE_HOST_GH,
|
|
396
|
-
runtimeMode:
|
|
419
|
+
runtimeMode: hermeticRuntime.runtimeMode,
|
|
420
|
+
runtimeModeReason: hermeticRuntime.runtimeModeReason ?? null,
|
|
397
421
|
injectedTokenPresent: findInjectedToken(options.env ?? process.env) !== null,
|
|
398
422
|
depth: "shallow",
|
|
399
423
|
detail: `SCM binary present (${binary}); auth status not checked (hermetic)`,
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { dirname, resolve } from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
import { atomicWriteProjectDefinition, loadProjectDefinitionForMutation, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
2
|
+
import { withProjectDefinitionMutation } from "../vbrief-build/project-definition-mutation.js";
|
|
4
3
|
import { syncRegistryArtifactAfterScopeMove } from "./registry-artifact-sync.js";
|
|
5
4
|
/** Fail-closed sync of PROJECT-DEFINITION after a lifecycle move (#1527 / #2131). */
|
|
6
5
|
export function syncProjectDefinitionAfterScopeMove(scopeData, oldPath, newPath, vbriefRoot, targetStatus) {
|
|
7
6
|
const projectRoot = dirname(resolve(vbriefRoot));
|
|
8
7
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
withProjectDefinitionMutation(projectRoot, (mutation) => {
|
|
9
|
+
syncRegistryArtifactAfterScopeMove(
|
|
10
|
+
// The artifact the lock captured, not an independent re-resolution (#3796).
|
|
11
|
+
mutation.artifactPath, scopeData, oldPath, newPath, vbriefRoot, targetStatus, {
|
|
12
|
+
loadForMutation: () => [mutation.load(), mutation.artifactPath],
|
|
13
|
+
persist: (path, data) => {
|
|
14
|
+
// The registry sync echoes back the path it was handed, so a
|
|
15
|
+
// mismatch means something re-resolved the artifact mid-section.
|
|
16
|
+
if (resolve(path) !== resolve(mutation.artifactPath)) {
|
|
17
|
+
throw new Error("refusing to persist PROJECT-DEFINITION to a path other than the locked artifact");
|
|
18
|
+
}
|
|
19
|
+
mutation.persist(data);
|
|
20
|
+
},
|
|
14
21
|
});
|
|
15
22
|
});
|
|
16
23
|
return null;
|
package/dist/session/git.d.ts
CHANGED
|
@@ -5,11 +5,35 @@ export interface GitRunResult {
|
|
|
5
5
|
}
|
|
6
6
|
export type GitRunner = (projectRoot: string, args: readonly string[]) => GitRunResult;
|
|
7
7
|
export declare const defaultGitRunner: GitRunner;
|
|
8
|
+
/**
|
|
9
|
+
* Serve a dispatch's repeated ref reads from one `git` child (#3736).
|
|
10
|
+
*
|
|
11
|
+
* The host mutation gate resolved HEAD, the worktree root, and the branch with
|
|
12
|
+
* a separate spawn each, twice over; on a loaded Windows box a spawn measured a
|
|
13
|
+
* 2.2s p50, so every concurrent agent's hook slowed every other agent's hook.
|
|
14
|
+
* The probe is lazy on purpose: it only fires for a read it can actually
|
|
15
|
+
* answer, so a caller that never asks for context never pays for it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function memoizeGitRunner(runGit?: GitRunner): GitRunner;
|
|
8
18
|
export declare function gitHead(projectRoot: string, runGit?: GitRunner): {
|
|
9
19
|
head: string | null;
|
|
10
20
|
error: string | null;
|
|
11
21
|
};
|
|
12
22
|
export declare function worktreePath(projectRoot: string, runGit?: GitRunner): string;
|
|
23
|
+
/**
|
|
24
|
+
* Walk from `targetPath` to the nearest existing directory. A write that
|
|
25
|
+
* creates a missing nested file is the common case; git cannot use a
|
|
26
|
+
* nonexistent cwd.
|
|
27
|
+
*/
|
|
28
|
+
export declare function existingAncestorDir(targetPath: string): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Failure-expressing sibling of {@link worktreePath}. Returns null instead of
|
|
31
|
+
* falling back to `startDir` so a missing git tree is distinguishable from
|
|
32
|
+
* success (#3794).
|
|
33
|
+
*/
|
|
34
|
+
export declare function worktreePathOrNull(startDir: string, runGit?: GitRunner): string | null;
|
|
35
|
+
/** Absolute `--git-common-dir` for `projectRoot`, or null on failure. */
|
|
36
|
+
export declare function gitCommonDir(projectRoot: string, runGit?: GitRunner): string | null;
|
|
13
37
|
/** True when `ancestor` is reachable from `descendant` (same commit counts). */
|
|
14
38
|
export declare function gitIsAncestor(projectRoot: string, ancestor: string, descendant: string, runGit?: GitRunner): boolean | null;
|
|
15
39
|
/**
|