@sabaiway/agent-workflow-kit 5.11.0 → 5.11.2
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/CHANGELOG.md +111 -0
- package/SKILL.md +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/hooks/gate-approve.mjs +13 -2
- package/references/hooks/state-block-guard.mjs +14 -2
- package/references/scripts/archive-changelog.mjs +14 -3
- package/references/scripts/archive-decisions.mjs +14 -3
- package/references/scripts/archive-issues.mjs +14 -3
- package/references/scripts/check-docs-size.mjs +14 -3
- package/references/scripts/migrate-gates.mjs +13 -2
- package/tools/ack-write.mjs +3 -3
- package/tools/autonomy-doctor.mjs +2 -3
- package/tools/bridge-settings.mjs +2 -3
- package/tools/cheap-agents.mjs +3 -3
- package/tools/commands.mjs +2 -3
- package/tools/commit-guard.mjs +3 -3
- package/tools/core-evidence.mjs +2 -3
- package/tools/coverage-check.mjs +2 -3
- package/tools/delegation.mjs +2 -3
- package/tools/detect-backends.mjs +2 -3
- package/tools/dispatch-record.mjs +1 -1
- package/tools/doc-parity.mjs +2 -3
- package/tools/family-registry.mjs +3 -3
- package/tools/flow-adoption-mint.mjs +70 -0
- package/tools/flow-append.mjs +309 -0
- package/tools/flow-chain-state.mjs +91 -0
- package/tools/flow-check.mjs +2 -3
- package/tools/flow-delta-proof.mjs +307 -0
- package/tools/flow-finding-manifest.mjs +70 -0
- package/tools/flow-legality.mjs +248 -0
- package/tools/flow-record-identity.mjs +115 -0
- package/tools/flow-record-shape.mjs +283 -0
- package/tools/flow-record.mjs +49 -789
- package/tools/flow-store-read.mjs +3 -3
- package/tools/flow-store.mjs +35 -812
- package/tools/flow-subset-budget.mjs +81 -0
- package/tools/flow-vocabulary.mjs +96 -0
- package/tools/flow-writer.mjs +3 -3
- package/tools/gate-hook.mjs +3 -3
- package/tools/gates-init.mjs +3 -3
- package/tools/grounding.mjs +2 -3
- package/tools/hide-footprint.mjs +2 -3
- package/tools/inject-methodology.mjs +2 -3
- package/tools/lens-region.mjs +2 -3
- package/tools/manifest/validate.mjs +2 -3
- package/tools/migrate-adr-store.mjs +3 -3
- package/tools/path-inventory.mjs +2 -3
- package/tools/procedures.mjs +3 -3
- package/tools/receipt-deadline.mjs +2 -3
- package/tools/recipes.mjs +2 -3
- package/tools/recommendations.mjs +3 -3
- package/tools/release-scan.mjs +2 -3
- package/tools/repo-search.mjs +2 -3
- package/tools/review-state.mjs +3 -3
- package/tools/run-gates.mjs +2 -3
- package/tools/sandbox-masks.mjs +3 -3
- package/tools/set-autonomy.mjs +2 -3
- package/tools/set-flow.mjs +3 -3
- package/tools/set-recipe.mjs +2 -3
- package/tools/setup-backends.mjs +3 -3
- package/tools/store-append.mjs +2 -2
- package/tools/uninstall.mjs +2 -3
- package/tools/velocity-profile.mjs +3 -3
- package/tools/worktrees.mjs +3 -3
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// flow-subset-budget.mjs — the Decision-7/8 counting-context budget (Plan 4): the two caps, the ONE
|
|
2
|
+
// budget walk both consumers share (subsetAttemptState), the exhaustion remedy prose, and the
|
|
3
|
+
// under-lock gate the append lane runs. Split out of flow-store.mjs unchanged (baseline-practices
|
|
4
|
+
// tranche 2); the facade re-exports everything here except subsetAttemptGate, which crosses to
|
|
5
|
+
// flow-append.mjs alone and stays off the store's public surface.
|
|
6
|
+
//
|
|
7
|
+
// PURE over read results, beside flow-chain-state.mjs at the bottom of the store's five leaves: no
|
|
8
|
+
// store IO, no git, no fs, and the record vocabulary is its only tools sibling.
|
|
9
|
+
|
|
10
|
+
import { flowRecordKey, subsetFoldBatchDigest } from './flow-record.mjs';
|
|
11
|
+
|
|
12
|
+
// The waste bound is the CAP, not the prose (Decision 8): a counting context allows at most
|
|
13
|
+
// THREE red attempts (two blind + one diagnosed) on its own; past that, no diagnosis reopens it —
|
|
14
|
+
// only a recorded FRESH-EYES consult verdict does, one further attempt per consult.
|
|
15
|
+
export const SUBSET_ATTEMPT_MAX_REDS = 3;
|
|
16
|
+
|
|
17
|
+
// Past the SECOND red every further attempt at the key rides a recorded diagnosis — the
|
|
18
|
+
// obligation keys on REDS, never on the attempt index (a green history stays blind-legal).
|
|
19
|
+
export const SUBSET_ATTEMPT_DIAGNOSIS_REDS = 2;
|
|
20
|
+
|
|
21
|
+
// subsetAttemptState(records, probe) → { attempts, nextIndex, reds, credits, exhausted } — the
|
|
22
|
+
// ONE Decision-7/8 budget walk both consumers share (the locked gate below re-runs it under the
|
|
23
|
+
// lock; run-gates' pre-gate check reads it lock-free). The exhaustion ladder is PERMIT-based
|
|
24
|
+
// and foldBatch-GLOBAL (round-3 disposition): red counts stay per key, but permits and their
|
|
25
|
+
// consumption span EVERY subsetDigest of the round context — one consult verdict is exactly
|
|
26
|
+
// ONE further attempt across the whole foldBatch, whichever subset spends it. Consult identity
|
|
27
|
+
// {backend, nonce} is tracked STORE-WIDE before any relevance filtering, so a replay from
|
|
28
|
+
// another round (or any seen identity — pre-exhaustion or spent) never credits; a credit is
|
|
29
|
+
// granted only for a NEW identity whose {planId, cycle, stepId, round} digests to this
|
|
30
|
+
// foldBatch while SOME key of the foldBatch is base-exhausted at that point in raw order.
|
|
31
|
+
// EVERY attempt recorded past its own key's base budget consumes one credit, whatever its
|
|
32
|
+
// status; a tampered store that drove credits negative stays exhausted (fail closed).
|
|
33
|
+
export const subsetAttemptState = (records, probe) => {
|
|
34
|
+
const key = flowRecordKey({ kind: 'subset-attempt', ...probe });
|
|
35
|
+
const attempts = [];
|
|
36
|
+
const seenConsults = new Set();
|
|
37
|
+
const redsByKey = new Map();
|
|
38
|
+
let credits = 0;
|
|
39
|
+
const someKeyExhausted = () => [...redsByKey.values()].some((n) => n >= SUBSET_ATTEMPT_MAX_REDS);
|
|
40
|
+
for (const r of records) {
|
|
41
|
+
if (r.kind === 'subset-attempt' && r.foldBatch === probe.foldBatch) {
|
|
42
|
+
const rKey = flowRecordKey(r);
|
|
43
|
+
const priorReds = redsByKey.get(rKey) ?? 0;
|
|
44
|
+
if (priorReds >= SUBSET_ATTEMPT_MAX_REDS) credits -= 1;
|
|
45
|
+
if (r.status === 'red') redsByKey.set(rKey, priorReds + 1);
|
|
46
|
+
if (rKey === key) attempts.push(r);
|
|
47
|
+
} else if (r.kind === 'consult-attestation') {
|
|
48
|
+
const identity = JSON.stringify([r.backend, r.nonce]);
|
|
49
|
+
const relevant = subsetFoldBatchDigest({ planId: r.planId, cycle: r.cycle, stepId: r.stepId, round: r.round }) === probe.foldBatch;
|
|
50
|
+
if (relevant && !seenConsults.has(identity) && someKeyExhausted()) credits += 1;
|
|
51
|
+
seenConsults.add(identity);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const reds = redsByKey.get(key) ?? 0;
|
|
55
|
+
return {
|
|
56
|
+
attempts,
|
|
57
|
+
nextIndex: attempts.reduce((m, r) => Math.max(m, r.attemptIndex), 0) + 1,
|
|
58
|
+
reds,
|
|
59
|
+
credits,
|
|
60
|
+
exhausted: reds >= SUBSET_ATTEMPT_MAX_REDS && credits <= 0,
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const subsetExhaustionRemedy = 'the fresh-eyes lane reopens it (Decision 8 — never a human wait-state): dispatch a MANDATORY grounded bridge consult (a different model) carrying the full attempt/diagnosis trail; its recorded consult-attestation at this round context reopens exactly ONE further diagnosed attempt. Otherwise park the stuck work with its trail and switch to independent work; a fresh context opens with the next round (new foldBatch) or a declared pregateExclude change (new subsetDigest)';
|
|
65
|
+
|
|
66
|
+
// The under-lock rules the factory does NOT already enforce itself: the exhaustion ladder and
|
|
67
|
+
// the byte-distinct diagnosis (blind thrashing refuses; a NEW hypothesis proceeds). The
|
|
68
|
+
// monotonic index and the reds-based diagnosis REQUIREMENT live in the factory alone — it is
|
|
69
|
+
// the ONLY entry for this kind (the generic lane refuses it by name, round-9 fold), computes
|
|
70
|
+
// the index from the SAME locked snapshot this gate sees, and throws its own named stops first.
|
|
71
|
+
export const subsetAttemptGate = (records, snapshot) => {
|
|
72
|
+
const { attempts, reds, exhausted } = subsetAttemptState(records, snapshot);
|
|
73
|
+
if (exhausted) {
|
|
74
|
+
return { ok: false, reason: `this counting context already holds ${reds} red attempts — EXHAUSTED (two blind + one diagnosed, Decision 8) and no diagnosis reopens it; ${subsetExhaustionRemedy}` };
|
|
75
|
+
}
|
|
76
|
+
const prior = attempts.find((r) => r.attemptIndex === snapshot.attemptIndex - 1);
|
|
77
|
+
if (typeof snapshot.diagnosis === 'string' && prior != null && prior.diagnosis === snapshot.diagnosis) {
|
|
78
|
+
return { ok: false, reason: "the diagnosis is byte-identical to the prior attempt's — a diagnosed continuation states a NEW hypothesis (Decision 8); blind thrashing refuses" };
|
|
79
|
+
}
|
|
80
|
+
return { ok: true };
|
|
81
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// flow-vocabulary.mjs — the CLOSED flow-record vocabulary (kinds, purposes, terminal lanes, the
|
|
2
|
+
// design seed assignment, the allowed-transition table) plus the five shared form bindings every
|
|
3
|
+
// validator states its refusals in: HEX64_RE, isPlainObject, isNonEmptyString, isHex64 and refuse.
|
|
4
|
+
// Split out of flow-record.mjs unchanged (baseline-practices tranche 3), which now re-exports the
|
|
5
|
+
// ten public names here; the five shared bindings stay OFF that surface (plan D3) — the leaves that
|
|
6
|
+
// need them import this module, so the record family's named grammars have exactly one home and no
|
|
7
|
+
// copy can drift.
|
|
8
|
+
//
|
|
9
|
+
// The LOWEST leaf of the family and pure form: no filesystem, no git, no CLI, no side effects on
|
|
10
|
+
// import, and orchestration-config.mjs (the schema version) is its only tools sibling. Imports run
|
|
11
|
+
// ONE way — the shape, identity, legality and manifest leaves compose this module; nothing here
|
|
12
|
+
// reaches back up to the facade.
|
|
13
|
+
|
|
14
|
+
import { FLOW_SCHEMA_VERSION } from './orchestration-config.mjs';
|
|
15
|
+
|
|
16
|
+
export { FLOW_SCHEMA_VERSION };
|
|
17
|
+
|
|
18
|
+
const deepFreeze = (value) => {
|
|
19
|
+
if (value !== null && typeof value === 'object') {
|
|
20
|
+
Object.values(value).forEach(deepFreeze);
|
|
21
|
+
Object.freeze(value);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ── the closed vocabulary ─────────────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
export const CHAIN_KIND = 'chain';
|
|
29
|
+
export const CHAIN_PURPOSES = deepFreeze(['adoption', 'round', 'refresh', 're-baseline', 'freeze', 'unfreeze', 'park', 'resume', 'converged', 'complete']);
|
|
30
|
+
export const STEP_SCOPED_PURPOSES = deepFreeze(['round', 'refresh', 're-baseline', 'freeze', 'unfreeze', 'converged']);
|
|
31
|
+
export const PLAN_LANE_PURPOSES = deepFreeze(['adoption', 'park', 'resume', 'complete']);
|
|
32
|
+
export const GLOBAL_KINDS = deepFreeze(['internal-attestation', 'down-mark', 'down-mark-up', 'down-mark-clear', 'degrade-justification', 'rerun-cause', 'bookkeeping-delta', 'maintainer-override', 'consult-attestation', 'subset-attempt']);
|
|
33
|
+
export const FLOW_KINDS = deepFreeze([CHAIN_KIND, ...GLOBAL_KINDS]);
|
|
34
|
+
|
|
35
|
+
// Reserved lane-typed terminals (#16): converged terminates a CYCLE (its step's sequence), complete
|
|
36
|
+
// terminates the PLAN. Park is a resumable suspension, never a terminal (#59).
|
|
37
|
+
export const TERMINAL_LANES = deepFreeze({ converged: 'cycle', complete: 'plan' });
|
|
38
|
+
|
|
39
|
+
// Design §5 seed member → family assignment; the drift-guard test binds this map to the verbatim
|
|
40
|
+
// seed list on one side and to the shipped CHAIN_PURPOSES/GLOBAL_KINDS on the other.
|
|
41
|
+
export const DESIGN_SEED_ASSIGNMENT = deepFreeze({
|
|
42
|
+
adoption: { family: 'chain', purpose: 'adoption' },
|
|
43
|
+
'round-chain': { family: 'chain', purpose: 'round' },
|
|
44
|
+
refresh: { family: 'chain', purpose: 'refresh' },
|
|
45
|
+
're-baseline': { family: 'chain', purpose: 're-baseline' },
|
|
46
|
+
unfreeze: { family: 'chain', purpose: 'unfreeze' },
|
|
47
|
+
freeze: { family: 'chain', purpose: 'freeze' },
|
|
48
|
+
converged: { family: 'chain', purpose: 'converged' },
|
|
49
|
+
park: { family: 'chain', purpose: 'park' },
|
|
50
|
+
resume: { family: 'chain', purpose: 'resume' },
|
|
51
|
+
complete: { family: 'chain', purpose: 'complete' },
|
|
52
|
+
'internal-attestation': { family: 'global', kind: 'internal-attestation' },
|
|
53
|
+
'down-mark': { family: 'global', kind: 'down-mark' },
|
|
54
|
+
'down-mark up': { family: 'global', kind: 'down-mark-up' },
|
|
55
|
+
'down-mark clear': { family: 'global', kind: 'down-mark-clear' },
|
|
56
|
+
'degrade-justification': { family: 'global', kind: 'degrade-justification' },
|
|
57
|
+
'rerun-cause': { family: 'global', kind: 'rerun-cause' },
|
|
58
|
+
'bookkeeping-delta': { family: 'global', kind: 'bookkeeping-delta' },
|
|
59
|
+
'maintainer-override': { family: 'global', kind: 'maintainer-override' },
|
|
60
|
+
'consult-attestation': { family: 'global', kind: 'consult-attestation' },
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// The allowed-transition table — an exported frozen structure, never prose. Within a step:
|
|
64
|
+
// converged ends the sequence (only the unfreeze lane reopens it, and only in its own cycle);
|
|
65
|
+
// freeze admits only unfreeze/converged. Plan lane: park admits only resume (and both preserve the
|
|
66
|
+
// pre-park cycle/round); complete admits nothing; adoption is only ever the chain's first record.
|
|
67
|
+
// The boundary lane (between steps): the opener round, the unfreeze reopen, and a re-baseline for
|
|
68
|
+
// disjoint base motion that reopens nothing. The cross-step edge (the opener's prior-terminal
|
|
69
|
+
// reference) is enforced by validateChainSequence, distinct from the within-step successor rule.
|
|
70
|
+
export const ALLOWED_TRANSITIONS = deepFreeze({
|
|
71
|
+
stepOpening: 'round',
|
|
72
|
+
withinStep: {
|
|
73
|
+
round: ['round', 'refresh', 're-baseline', 'freeze', 'converged'],
|
|
74
|
+
refresh: ['round', 'refresh', 're-baseline', 'freeze', 'converged'],
|
|
75
|
+
're-baseline': ['round', 'refresh', 're-baseline', 'freeze', 'converged'],
|
|
76
|
+
freeze: ['unfreeze', 'converged'],
|
|
77
|
+
unfreeze: ['round', 'refresh', 're-baseline', 'freeze', 'converged'],
|
|
78
|
+
converged: ['unfreeze'],
|
|
79
|
+
},
|
|
80
|
+
planLane: {
|
|
81
|
+
adoption: ['round', 'park', 'complete'],
|
|
82
|
+
park: ['resume'],
|
|
83
|
+
resume: ['round', 'park', 'complete'],
|
|
84
|
+
complete: [],
|
|
85
|
+
},
|
|
86
|
+
boundary: ['round', 'unfreeze', 're-baseline'],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ── the shared form bindings (D3) — the record family's named grammars, off the public surface ────
|
|
90
|
+
|
|
91
|
+
export const HEX64_RE = /^[0-9a-f]{64}$/;
|
|
92
|
+
export const isPlainObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
93
|
+
export const isNonEmptyString = (v) => typeof v === 'string' && v.length > 0;
|
|
94
|
+
export const isHex64 = (v) => typeof v === 'string' && HEX64_RE.test(v);
|
|
95
|
+
|
|
96
|
+
export const refuse = (reason) => ({ ok: false, reason });
|
package/tools/flow-writer.mjs
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
|
|
49
49
|
import { readFileSync, lstatSync } from 'node:fs';
|
|
50
50
|
import { join, dirname } from 'node:path';
|
|
51
|
-
import {
|
|
51
|
+
import { fileURLToPath } from 'node:url';
|
|
52
52
|
import { createHash, randomBytes } from 'node:crypto';
|
|
53
53
|
import {
|
|
54
54
|
FLOW_SCHEMA_VERSION, CHAIN_KIND, canonicalFlowDigest, flowTreeIdentity,
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
appendFlowRecord, appendFlowRecordWithPreflight, mintAdoption, readPlanFrontmatterId,
|
|
60
60
|
resolveRecordReference, priorChainTerminal,
|
|
61
61
|
} from './flow-store.mjs';
|
|
62
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
62
63
|
import { readFileBytesNoFollow, gitLine } from './flow-store-read.mjs';
|
|
63
64
|
import {
|
|
64
65
|
resolveBase, computeTreeFingerprint, lexicalRepoRelative,
|
|
@@ -1256,8 +1257,7 @@ export const main = (argv, ctx = {}) => {
|
|
|
1256
1257
|
}
|
|
1257
1258
|
};
|
|
1258
1259
|
|
|
1259
|
-
|
|
1260
|
-
if (isDirectRun) {
|
|
1260
|
+
if (isDirectRun(import.meta.url)) {
|
|
1261
1261
|
const r = main(process.argv.slice(2));
|
|
1262
1262
|
if (r.stdout) console.log(r.stdout);
|
|
1263
1263
|
if (r.stderr) console.error(r.stderr);
|
package/tools/gate-hook.mjs
CHANGED
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
43
43
|
import { dirname, join, resolve } from 'node:path';
|
|
44
|
-
import { fileURLToPath
|
|
44
|
+
import { fileURLToPath } from 'node:url';
|
|
45
45
|
import {
|
|
46
46
|
CLAUDE_DIR,
|
|
47
47
|
EXPECTED_WORKFLOW_VERSION,
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
resolveEffectiveMode,
|
|
55
55
|
} from './velocity-profile.mjs';
|
|
56
56
|
import { assertDocsAiDeployment, writeDocsAiFileAtomic } from './atomic-write.mjs';
|
|
57
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
57
58
|
import { shellQuoteArg } from './review-state.mjs';
|
|
58
59
|
|
|
59
60
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
@@ -572,5 +573,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
|
|
|
572
573
|
}
|
|
573
574
|
};
|
|
574
575
|
|
|
575
|
-
|
|
576
|
-
if (isDirectRun) process.exit(main(process.argv.slice(2)));
|
|
576
|
+
if (isDirectRun(import.meta.url)) process.exit(main(process.argv.slice(2)));
|
package/tools/gates-init.mjs
CHANGED
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
|
|
59
59
|
import { existsSync, lstatSync, readFileSync } from 'node:fs';
|
|
60
60
|
import { join, resolve, dirname } from 'node:path';
|
|
61
|
-
import { fileURLToPath
|
|
61
|
+
import { fileURLToPath } from 'node:url';
|
|
62
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
62
63
|
import { discoverGateCandidates, EXPECTED_WORKFLOW_VERSION } from './velocity-profile.mjs';
|
|
63
64
|
import { GATES_REL, validateDeclaration } from './run-gates.mjs';
|
|
64
65
|
import { canonicalCheckerGates, coverageDeclarationDefects, isKitOwnedCheckerGate } from './gates-declaration.mjs';
|
|
@@ -795,5 +796,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
|
|
|
795
796
|
}
|
|
796
797
|
};
|
|
797
798
|
|
|
798
|
-
|
|
799
|
-
if (isDirectRun) process.exit(main(process.argv.slice(2)));
|
|
799
|
+
if (isDirectRun(import.meta.url)) process.exit(main(process.argv.slice(2)));
|
package/tools/grounding.mjs
CHANGED
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
import { readFileSync, writeFileSync, lstatSync, realpathSync } from 'node:fs';
|
|
32
32
|
import { resolve, join, relative, isAbsolute, dirname, basename, sep } from 'node:path';
|
|
33
33
|
import { tmpdir } from 'node:os';
|
|
34
|
-
import { pathToFileURL } from 'node:url';
|
|
35
34
|
import { spawnSync } from 'node:child_process';
|
|
35
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
36
36
|
import { fail } from './orchestration-config.mjs';
|
|
37
37
|
import { readRegularFileNoFollow } from './fs-read-nofollow.mjs';
|
|
38
38
|
// (f) --autonomy (AD-044 Plan 3): the effective per-project autonomy policy for the facts payload.
|
|
@@ -466,8 +466,7 @@ export const main = (argv, ctx = {}) => {
|
|
|
466
466
|
}
|
|
467
467
|
};
|
|
468
468
|
|
|
469
|
-
|
|
470
|
-
if (isDirectRun) {
|
|
469
|
+
if (isDirectRun(import.meta.url)) {
|
|
471
470
|
const r = main(process.argv.slice(2));
|
|
472
471
|
// Exact writes + a natural exit: console.log would append a stray newline to the
|
|
473
472
|
// byte-exact facts payload, and process.exit() can truncate large piped stdout before Node
|
package/tools/hide-footprint.mjs
CHANGED
|
@@ -24,8 +24,8 @@ import {
|
|
|
24
24
|
} from 'node:fs';
|
|
25
25
|
import { join, resolve, relative, isAbsolute, basename } from 'node:path';
|
|
26
26
|
import { execFileSync } from 'node:child_process';
|
|
27
|
-
import { pathToFileURL } from 'node:url';
|
|
28
27
|
import os from 'node:os';
|
|
28
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
29
29
|
import {
|
|
30
30
|
KIT_OWN_PATHS, KNOWN_FOOTPRINT, FOOTPRINT_STOP, stop,
|
|
31
31
|
normalizeSlashes, isDirPattern, patternToProbe, expandGlob, matchesKnownGlob,
|
|
@@ -584,5 +584,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
|
|
|
584
584
|
}
|
|
585
585
|
};
|
|
586
586
|
|
|
587
|
-
|
|
588
|
-
if (isDirectRun) process.exit(main(process.argv.slice(2)));
|
|
587
|
+
if (isDirectRun(import.meta.url)) process.exit(main(process.argv.slice(2)));
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
// ONLY when its content normalize-matches a KNOWN PRIOR canonical fragment (drift-guarded, append-only
|
|
27
27
|
// per descriptor). A customized slot never matches → preserved verbatim + a read-only upgrade advisory.
|
|
28
28
|
|
|
29
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
29
30
|
import { normalizeCanonical } from './orchestration-config.mjs';
|
|
30
31
|
|
|
31
32
|
export const START_MARKER = '<!-- workflow:methodology:start -->';
|
|
@@ -599,9 +600,7 @@ export const runCli = async (argv, deps = {}) => {
|
|
|
599
600
|
}
|
|
600
601
|
};
|
|
601
602
|
|
|
602
|
-
|
|
603
|
-
const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
604
|
-
if (isDirectRun) {
|
|
603
|
+
if (isDirectRun(import.meta.url)) {
|
|
605
604
|
const { code, stdout, stderr } = await runCli(process.argv.slice(2));
|
|
606
605
|
if (stdout) process.stdout.write(stdout);
|
|
607
606
|
if (stderr) process.stderr.write(stderr);
|
package/tools/lens-region.mjs
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
//
|
|
28
28
|
// Pure string functions (fs only in the CLI); dependency-free, Node >= 22.
|
|
29
29
|
|
|
30
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
30
31
|
import { normalizeCanonical } from './orchestration-config.mjs';
|
|
31
32
|
|
|
32
33
|
// The deployed-heading matcher (prefix, like the historical extractLensBlock: robust to a future
|
|
@@ -386,6 +387,4 @@ export const runCli = async (argv, deps = {}) => {
|
|
|
386
387
|
return 0;
|
|
387
388
|
};
|
|
388
389
|
|
|
389
|
-
|
|
390
|
-
const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
391
|
-
if (isDirectRun) process.exitCode = await runCli(process.argv.slice(2));
|
|
390
|
+
if (isDirectRun(import.meta.url)) process.exitCode = await runCli(process.argv.slice(2));
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import { readFileSync, existsSync } from 'node:fs';
|
|
22
22
|
import { resolve, join, isAbsolute } from 'node:path';
|
|
23
|
-
import {
|
|
23
|
+
import { isDirectRun } from '../direct-run.mjs';
|
|
24
24
|
|
|
25
25
|
export const FAMILY = 'agent-workflow';
|
|
26
26
|
export const SUPPORTED_SCHEMA = 1;
|
|
@@ -765,5 +765,4 @@ const main = (argv) => {
|
|
|
765
765
|
if (strict && notValid > 0) process.exit(1);
|
|
766
766
|
};
|
|
767
767
|
|
|
768
|
-
|
|
769
|
-
if (isDirectRun) main(process.argv.slice(2));
|
|
768
|
+
if (isDirectRun(import.meta.url)) main(process.argv.slice(2));
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync, chmodSync } from 'node:fs';
|
|
35
35
|
import { join, resolve, dirname, relative, isAbsolute } from 'node:path';
|
|
36
|
-
import { fileURLToPath
|
|
36
|
+
import { fileURLToPath } from 'node:url';
|
|
37
37
|
import { spawnSync } from 'node:child_process';
|
|
38
38
|
import { tmpdir } from 'node:os';
|
|
39
39
|
import { writeContainedFileAtomic, assertDocsAiDeployment } from './atomic-write.mjs';
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
defaultRegenerateIndex,
|
|
48
48
|
runCli as runArchiveDecisions,
|
|
49
49
|
} from '../references/scripts/archive-decisions.mjs';
|
|
50
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
50
51
|
import { surveyAdrLayoutStrict, ADR_LAYOUT_PATHS } from './family-registry.mjs';
|
|
51
52
|
|
|
52
53
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
@@ -463,5 +464,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
|
|
|
463
464
|
}
|
|
464
465
|
};
|
|
465
466
|
|
|
466
|
-
|
|
467
|
-
if (isDirectRun) process.exitCode = main();
|
|
467
|
+
if (isDirectRun(import.meta.url)) process.exitCode = main();
|
package/tools/path-inventory.mjs
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
|
|
47
47
|
import { openSync, fstatSync, readSync, closeSync, opendirSync, realpathSync, lstatSync, constants } from 'node:fs';
|
|
48
48
|
import { resolve, relative, isAbsolute, sep, dirname } from 'node:path';
|
|
49
|
-
import {
|
|
49
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
50
50
|
|
|
51
51
|
import {
|
|
52
52
|
UsageError,
|
|
@@ -512,5 +512,4 @@ const emitResult = (r) => {
|
|
|
512
512
|
process.exitCode = r.code;
|
|
513
513
|
};
|
|
514
514
|
|
|
515
|
-
|
|
516
|
-
if (isDirectRun) emitResult(main(process.argv.slice(2)));
|
|
515
|
+
if (isDirectRun(import.meta.url)) emitResult(main(process.argv.slice(2)));
|
package/tools/procedures.mjs
CHANGED
|
@@ -20,11 +20,12 @@
|
|
|
20
20
|
import { readFileSync, lstatSync } from 'node:fs';
|
|
21
21
|
import { homedir } from 'node:os';
|
|
22
22
|
import { join, dirname } from 'node:path';
|
|
23
|
-
import {
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
24
|
import { detectBackends, wrapperCmdFor, wrapperContractFor } from './detect-backends.mjs';
|
|
25
25
|
// The host-level bridge-settings registry (manifest-as-source) + its allowed-value labels. READ-ONLY
|
|
26
26
|
// core only — never the writer — so this read-only advisor never imports the atomic-write core.
|
|
27
27
|
import { loadRegistry, allowedLabel } from './bridge-settings-read.mjs';
|
|
28
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
28
29
|
import { ACTIVITIES, resolveActivityRecipe, planRecipe } from './recipes.mjs';
|
|
29
30
|
import { resolveEngineDir, readEngineFragment, PROCEDURES_FRAGMENT_REL } from './engine-source.mjs';
|
|
30
31
|
// The plan-in-flight detector (AD-038) — imported from the plan-files.mjs LEAF (read-only fs by
|
|
@@ -586,8 +587,7 @@ export const main = (argv, ctx = {}) => {
|
|
|
586
587
|
}
|
|
587
588
|
};
|
|
588
589
|
|
|
589
|
-
|
|
590
|
-
if (isDirectRun) {
|
|
590
|
+
if (isDirectRun(import.meta.url)) {
|
|
591
591
|
const r = main(process.argv.slice(2));
|
|
592
592
|
if (r.stdout) console.log(r.stdout);
|
|
593
593
|
if (r.stderr) console.error(r.stderr);
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
// Node >= 22. No side effects on import (the isDirectRun idiom).
|
|
22
22
|
|
|
23
23
|
import { join, dirname } from 'node:path';
|
|
24
|
-
import { pathToFileURL } from 'node:url';
|
|
25
24
|
import { createHash } from 'node:crypto';
|
|
26
25
|
import { resolveReceiptsPath } from './core-evidence.mjs';
|
|
26
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
27
27
|
import { SAFE_NONCE_RE, findingManifestBasename, decodeFindingManifest } from './flow-record.mjs';
|
|
28
28
|
import { readFileBytesNoFollow } from './flow-store-read.mjs';
|
|
29
29
|
|
|
@@ -254,8 +254,7 @@ export const main = async (argv, ctx = {}) => {
|
|
|
254
254
|
}
|
|
255
255
|
};
|
|
256
256
|
|
|
257
|
-
|
|
258
|
-
if (isDirectRun) {
|
|
257
|
+
if (isDirectRun(import.meta.url)) {
|
|
259
258
|
main(process.argv.slice(2)).then((r) => {
|
|
260
259
|
if (r.stdout) process.stdout.write(r.stdout.endsWith('\n') ? r.stdout : `${r.stdout}\n`);
|
|
261
260
|
if (r.stderr) process.stderr.write(r.stderr.endsWith('\n') ? r.stderr : `${r.stderr}\n`);
|
package/tools/recipes.mjs
CHANGED
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
|
|
17
17
|
import { existsSync, readFileSync } from 'node:fs';
|
|
18
18
|
import { dirname, join, resolve } from 'node:path';
|
|
19
|
-
import { pathToFileURL } from 'node:url';
|
|
20
19
|
// The host-level bridge-settings snapshot (fact-only, best-effort). READ-ONLY core only — never the
|
|
21
20
|
// writer — so this read-only advisor never pulls in the atomic-write core.
|
|
22
21
|
import { settingsSnapshot, DEFAULT_BUNDLE_ROOT } from './bridge-settings-read.mjs';
|
|
22
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
23
23
|
import {
|
|
24
24
|
detectBackends,
|
|
25
25
|
wrapperCmdFor,
|
|
@@ -596,11 +596,10 @@ exclusive. Detection only — never writes, never commits, never runs a subscrip
|
|
|
596
596
|
return 0;
|
|
597
597
|
};
|
|
598
598
|
|
|
599
|
-
const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
600
599
|
// Natural exit via process.exitCode — never process.exit inside the async main (it would drop buffered
|
|
601
600
|
// stdio writes on piped stderr), and never a TOP-LEVEL await here: orchestration-config.mjs statically
|
|
602
601
|
// imports this module, so awaiting the dynamic import during our own evaluation would deadlock the cycle.
|
|
603
|
-
if (isDirectRun) {
|
|
602
|
+
if (isDirectRun(import.meta.url)) {
|
|
604
603
|
main(process.argv.slice(2)).then(
|
|
605
604
|
(code) => {
|
|
606
605
|
process.exitCode = code ?? 0;
|
|
@@ -35,7 +35,7 @@ import { readFileSync, readdirSync, lstatSync, existsSync } from 'node:fs';
|
|
|
35
35
|
import { createHash } from 'node:crypto';
|
|
36
36
|
import { homedir } from 'node:os';
|
|
37
37
|
import { dirname, join, resolve } from 'node:path';
|
|
38
|
-
import { fileURLToPath
|
|
38
|
+
import { fileURLToPath } from 'node:url';
|
|
39
39
|
import {
|
|
40
40
|
preflightVelocityProfile,
|
|
41
41
|
planVelocityProfile,
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
import { loadAutonomy, isSparseSeedConfig, AUTONOMY_REL } from './autonomy-config.mjs';
|
|
52
52
|
import { deriveDoctorPlan } from './autonomy-doctor.mjs';
|
|
53
53
|
import { detectBackends, findOnPath } from './detect-backends.mjs';
|
|
54
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
54
55
|
import { ACTIVITIES, resolveActivityRecipe } from './recipes.mjs';
|
|
55
56
|
import { surveyFamily, surveyGateHook, surveyAdrLayoutStrict } from './family-registry.mjs';
|
|
56
57
|
import { probeSandboxMasks, needsMasksApply } from './sandbox-masks.mjs';
|
|
@@ -1534,8 +1535,7 @@ export const main = (argv, ctx = {}) => {
|
|
|
1534
1535
|
}
|
|
1535
1536
|
};
|
|
1536
1537
|
|
|
1537
|
-
|
|
1538
|
-
if (isDirectRun) {
|
|
1538
|
+
if (isDirectRun(import.meta.url)) {
|
|
1539
1539
|
const r = main(process.argv.slice(2));
|
|
1540
1540
|
if (r.stdout) process.stdout.write(r.stdout.endsWith('\n') ? r.stdout : `${r.stdout}\n`);
|
|
1541
1541
|
if (r.stderr) process.stderr.write(r.stderr.endsWith('\n') ? r.stderr : `${r.stderr}\n`);
|
package/tools/release-scan.mjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
|
|
14
14
|
import { join, extname, basename, resolve } from 'node:path';
|
|
15
|
-
import {
|
|
15
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
16
16
|
|
|
17
17
|
export const ALLOWLIST = [
|
|
18
18
|
'codex-cli-bridge',
|
|
@@ -274,5 +274,4 @@ export const main = (argv) => {
|
|
|
274
274
|
return reportOnly ? 0 : 1;
|
|
275
275
|
};
|
|
276
276
|
|
|
277
|
-
|
|
278
|
-
if (isDirectRun) process.exitCode = main(process.argv.slice(2));
|
|
277
|
+
if (isDirectRun(import.meta.url)) process.exitCode = main(process.argv.slice(2));
|
package/tools/repo-search.mjs
CHANGED
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
import { openSync, fstatSync, readSync, closeSync, opendirSync, realpathSync, lstatSync, constants } from 'node:fs';
|
|
51
51
|
import { createHash } from 'node:crypto';
|
|
52
52
|
import { join, resolve, relative, isAbsolute, sep } from 'node:path';
|
|
53
|
-
import {
|
|
53
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
54
54
|
|
|
55
55
|
export const EXIT_OK = 0;
|
|
56
56
|
export const EXIT_ERROR = 1;
|
|
@@ -590,5 +590,4 @@ const emitResult = (r) => {
|
|
|
590
590
|
process.exitCode = r.code;
|
|
591
591
|
};
|
|
592
592
|
|
|
593
|
-
|
|
594
|
-
if (isDirectRun) emitResult(main(process.argv.slice(2)));
|
|
593
|
+
if (isDirectRun(import.meta.url)) emitResult(main(process.argv.slice(2)));
|
package/tools/review-state.mjs
CHANGED
|
@@ -90,10 +90,11 @@
|
|
|
90
90
|
|
|
91
91
|
import { lstatSync, readFileSync } from 'node:fs';
|
|
92
92
|
import { join, dirname } from 'node:path';
|
|
93
|
-
import {
|
|
93
|
+
import { fileURLToPath } from 'node:url';
|
|
94
94
|
import { spawnSync } from 'node:child_process';
|
|
95
95
|
import { createHash } from 'node:crypto';
|
|
96
96
|
import { detectBackends, READY } from './detect-backends.mjs';
|
|
97
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
97
98
|
import { resolveActivityRecipe, DISPLAY_ALIASES, requiredBackendsForConfiguredRecipe } from './recipes.mjs';
|
|
98
99
|
import { CONFIG_REL, fail, loadConfig } from './orchestration-config.mjs';
|
|
99
100
|
import { resolveFlowStorePath, readFlowStore, deriveFlowOwner, readPlanFrontmatterId } from './flow-store.mjs';
|
|
@@ -819,8 +820,7 @@ const emitResult = (r) => {
|
|
|
819
820
|
process.exitCode = r.code;
|
|
820
821
|
};
|
|
821
822
|
|
|
822
|
-
|
|
823
|
-
if (isDirectRun) {
|
|
823
|
+
if (isDirectRun(import.meta.url)) {
|
|
824
824
|
const argv = process.argv.slice(2);
|
|
825
825
|
if (argv.includes('--await')) mainAwait(argv).then(emitResult);
|
|
826
826
|
else emitResult(main(argv));
|
package/tools/run-gates.mjs
CHANGED
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
import { readFileSync, lstatSync, unlinkSync } from 'node:fs';
|
|
33
33
|
import { join } from 'node:path';
|
|
34
34
|
import { spawnSync } from 'node:child_process';
|
|
35
|
-
import { pathToFileURL } from 'node:url';
|
|
36
35
|
import { createHash, randomUUID } from 'node:crypto';
|
|
36
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
37
37
|
import { computeTreeFingerprint } from './review-state.mjs';
|
|
38
38
|
import { CONFIG_REL, loadConfig } from './orchestration-config.mjs';
|
|
39
39
|
// The D3(a) final receipt rides the core-evidence SOLE WRITER (the sole-writer boundary — this
|
|
@@ -942,5 +942,4 @@ export const runCli = (argv, deps = {}) => {
|
|
|
942
942
|
}
|
|
943
943
|
};
|
|
944
944
|
|
|
945
|
-
|
|
946
|
-
if (isDirectRun) process.exitCode = runCli(process.argv.slice(2));
|
|
945
|
+
if (isDirectRun(import.meta.url)) process.exitCode = runCli(process.argv.slice(2));
|
package/tools/sandbox-masks.mjs
CHANGED
|
@@ -36,8 +36,9 @@
|
|
|
36
36
|
|
|
37
37
|
import { lstatSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
38
38
|
import { join, resolve, dirname } from 'node:path';
|
|
39
|
-
import { fileURLToPath
|
|
39
|
+
import { fileURLToPath } from 'node:url';
|
|
40
40
|
import { spawnSync } from 'node:child_process';
|
|
41
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
41
42
|
import { fail } from './orchestration-config.mjs';
|
|
42
43
|
import { isNeverCommittableStat, shellQuoteArg } from './review-state.mjs';
|
|
43
44
|
import { assertContainedRealPath } from './fs-safe.mjs';
|
|
@@ -366,5 +367,4 @@ const emitResult = (r) => {
|
|
|
366
367
|
process.exitCode = r.code;
|
|
367
368
|
};
|
|
368
369
|
|
|
369
|
-
|
|
370
|
-
if (isDirectRun) emitResult(main(process.argv.slice(2)));
|
|
370
|
+
if (isDirectRun(import.meta.url)) emitResult(main(process.argv.slice(2)));
|
package/tools/set-autonomy.mjs
CHANGED
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
// Dependency-free, Node >= 22. No side effects on import (the isDirectRun idiom).
|
|
23
23
|
|
|
24
24
|
import { readFileSync, lstatSync } from 'node:fs';
|
|
25
|
-
import { pathToFileURL } from 'node:url';
|
|
26
25
|
import {
|
|
27
26
|
AUTONOMY_REL,
|
|
28
27
|
fail,
|
|
@@ -35,6 +34,7 @@ import {
|
|
|
35
34
|
AUTONOMY_README,
|
|
36
35
|
} from './autonomy-config.mjs';
|
|
37
36
|
import { writeAutonomy as writeAutonomyFs } from './autonomy-write.mjs';
|
|
37
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
38
38
|
|
|
39
39
|
// ── argument parsing (usage errors → exit 2) ────────────────────────────────────────
|
|
40
40
|
|
|
@@ -186,8 +186,7 @@ export const main = (argv, ctx = {}) => {
|
|
|
186
186
|
}
|
|
187
187
|
};
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
if (isDirectRun) {
|
|
189
|
+
if (isDirectRun(import.meta.url)) {
|
|
191
190
|
const r = main(process.argv.slice(2));
|
|
192
191
|
if (r.stdout) console.log(r.stdout);
|
|
193
192
|
if (r.stderr) console.error(r.stderr);
|
package/tools/set-flow.mjs
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
import { readFileSync, lstatSync } from 'node:fs';
|
|
32
32
|
import { join, dirname, resolve } from 'node:path';
|
|
33
|
-
import {
|
|
33
|
+
import { fileURLToPath } from 'node:url';
|
|
34
34
|
import { spawnSync } from 'node:child_process';
|
|
35
35
|
import {
|
|
36
36
|
CONFIG_REL,
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
FLOW_CANDIDATE_CLASSES,
|
|
45
45
|
FLOW_SCHEMA_1_FIXTURE,
|
|
46
46
|
} from './orchestration-config.mjs';
|
|
47
|
+
import { isDirectRun } from './direct-run.mjs';
|
|
47
48
|
import { writeConfig as writeConfigFs } from './orchestration-write.mjs';
|
|
48
49
|
import { loadDeclaration } from './run-gates.mjs';
|
|
49
50
|
import { compareSemver } from './semver-lite.mjs';
|
|
@@ -456,8 +457,7 @@ export const main = (argv, ctx = {}) => {
|
|
|
456
457
|
}
|
|
457
458
|
};
|
|
458
459
|
|
|
459
|
-
|
|
460
|
-
if (isDirectRun) {
|
|
460
|
+
if (isDirectRun(import.meta.url)) {
|
|
461
461
|
const r = main(process.argv.slice(2));
|
|
462
462
|
if (r.stdout) console.log(r.stdout);
|
|
463
463
|
if (r.stderr) console.error(r.stderr);
|