jorgex-stack 1.2.2 → 1.2.4
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/README.md +4 -2
- package/dist/cli.js +95 -68
- package/package.json +1 -1
- package/stack/agents/code-reviewer.md +3 -2
- package/stack/agents/code-simplifier.md +4 -3
- package/stack/agents/comment-fixer.md +3 -2
- package/stack/agents/security-auditor.md +3 -2
- package/stack/agents/silent-failure-hunter.md +3 -2
- package/stack/agents/test-analyzer.md +4 -3
- package/stack/agents/type-design-analyzer.md +3 -2
- package/stack/scripts/post-pr-review.cjs +44 -9
- package/stack/skills/orchestrator/SKILL.md +1 -1
- package/stack/skills/xreview/SKILL.md +11 -3
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ Programmatic mode does **not** provide:
|
|
|
97
97
|
|
|
98
98
|
### Pi runtime
|
|
99
99
|
|
|
100
|
-
Pi is package-managed rather than file-managed. Stack supports the exact
|
|
100
|
+
Pi is package-managed rather than file-managed. Stack supports the exact published package **`jorgex-pi@0.2.2`** and keeps Pi out of the adapter/component manifest and model map.
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
103
|
pnpm dlx jorgex-stack install --agents pi
|
|
@@ -107,10 +107,12 @@ pnpm dlx jorgex-stack sync --agents pi
|
|
|
107
107
|
pnpm dlx jorgex-stack uninstall --agents pi
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Stack downloads the frozen registry tarball, verifies its exact size plus SHA-256/SHA-512, backs up Pi's `settings.json`, and only then asks Pi to install that local file. Pi's own package-manager invocation is the narrow runtime exception to the repository's pnpm-only rule; the Stack lifecycle never launches npm directly. A scope-bound receipt under `~/.jorgex-stack/pi-receipt.json` records ownership only after the package runner reports a healthy install. Manual, duplicate, divergent, partial, corrupt, copied-to-another-scope, or unknown-history state fails closed and is never adopted or removed silently.
|
|
110
|
+
Stack downloads the frozen registry tarball, verifies its exact size plus SHA-256/SHA-512, backs up Pi's `settings.json`, and only then asks Pi to install that local file. Pi's own package-manager invocation is the narrow runtime exception to the repository's pnpm-only rule; the Stack lifecycle never launches npm directly. The managed Pi package entry is the exact source object `{ "source": "npm:jorgex-pi@0.2.2", "skills": [] }`: Pi discovers the canonical shared skills from `~/.agents/skills`, so the package copy is disabled and does not create duplicate skill loading. A scope-bound receipt under `~/.jorgex-stack/pi-receipt.json` records ownership only after the package runner reports a healthy install and stores the verified Engram executable as `engram.binary`, using the schema v1 consumed by `jorgex-pi@0.2.2`. Receipts created before the Engram binding existed require deliberate removal with the previous Stack release followed by reinstall; they are never adopted automatically. Manual, duplicate, divergent, partial, corrupt, copied-to-another-scope, or unknown-history state fails closed and is never adopted or removed silently.
|
|
111
111
|
|
|
112
112
|
Engram remains mandatory and user-owned. An existing binary is preserved. Interactive install may offer the existing native `brew`/`go`/release channel with a default-No confirmation; `--yes` and non-TTY installs fail with a remedy when Engram is absent. No Pi lifecycle operation updates or deletes the Engram database or memories. Under `--target-dir`, Stack accepts only `<target>/bin/engram`, isolates Pi/Home/XDG/AppData/temp/npm-cache paths inside the target, and never consults the host Engram or Pi configuration.
|
|
113
113
|
|
|
114
|
+
The 24-hour npm maturity rule applies to the managed adoption boundary: development and PR validation may start against the exact published artifact, but merging the adoption PR, publishing that Stack adoption, and running the real managed installation wait until the package has been public for at least 24 hours unless Jorge explicitly documents an exception in the PR.
|
|
115
|
+
|
|
114
116
|
`update --agents pi` only runs the Pi package lifecycle; it does not enter the global Stack updater. `update --check --agents pi` is a read-only Pi doctor. Uninstall runs package cleanup, backs up Pi's settings before removal, removes only the exact receipt-owned package after verifying absence, and preserves all companion/user state. Full behavior, failure states and troubleshooting are in [docs/references/pi-runtime.md](docs/references/pi-runtime.md).
|
|
115
117
|
|
|
116
118
|
### Browser automation
|
package/dist/cli.js
CHANGED
|
@@ -3755,13 +3755,19 @@ function parsePackageSources(settingsJson) {
|
|
|
3755
3755
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
3756
3756
|
const packages = Reflect.get(parsed, "packages");
|
|
3757
3757
|
if (!Array.isArray(packages)) return null;
|
|
3758
|
-
const sources = packages.map(packageSource);
|
|
3759
|
-
return sources.every((
|
|
3758
|
+
const sources = packages.map((entry) => ({ entry, source: packageSource(entry) }));
|
|
3759
|
+
return sources.every((value) => value.source !== null) ? sources : null;
|
|
3760
3760
|
} catch {
|
|
3761
3761
|
return null;
|
|
3762
3762
|
}
|
|
3763
3763
|
}
|
|
3764
|
-
function
|
|
3764
|
+
function isExactManagedPackage(entry, source) {
|
|
3765
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return false;
|
|
3766
|
+
const keys = Object.keys(entry);
|
|
3767
|
+
const skills = Reflect.get(entry, "skills");
|
|
3768
|
+
return keys.length === 2 && keys.includes("source") && keys.includes("skills") && Reflect.get(entry, "source") === source && Array.isArray(skills) && skills.length === 0;
|
|
3769
|
+
}
|
|
3770
|
+
function expectedReceipt(candidate, state, scope, engramBin) {
|
|
3765
3771
|
return {
|
|
3766
3772
|
schemaVersion: 1,
|
|
3767
3773
|
state,
|
|
@@ -3770,21 +3776,52 @@ function expectedReceipt(candidate, state, scope) {
|
|
|
3770
3776
|
tarball: candidate.tarball,
|
|
3771
3777
|
provenance: candidate.provenance
|
|
3772
3778
|
},
|
|
3773
|
-
scope
|
|
3779
|
+
scope,
|
|
3780
|
+
engram: { binary: engramBin }
|
|
3774
3781
|
};
|
|
3775
3782
|
}
|
|
3776
|
-
function
|
|
3783
|
+
function parseReceiptShape(receiptJson) {
|
|
3777
3784
|
try {
|
|
3778
3785
|
const parsed = JSON.parse(receiptJson);
|
|
3779
3786
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
3787
|
+
const schemaVersion = Reflect.get(parsed, "schemaVersion");
|
|
3788
|
+
if (schemaVersion !== 1) return null;
|
|
3780
3789
|
const state = Reflect.get(parsed, "state");
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3790
|
+
const candidate = Reflect.get(parsed, "candidate");
|
|
3791
|
+
if (state !== "installing" && state !== "installed" || candidate === null || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
3792
|
+
return null;
|
|
3793
|
+
}
|
|
3794
|
+
const packageValue = Reflect.get(candidate, "package");
|
|
3795
|
+
const tarball = Reflect.get(candidate, "tarball");
|
|
3796
|
+
const provenance = Reflect.get(candidate, "provenance");
|
|
3797
|
+
const scope = Reflect.get(parsed, "scope");
|
|
3798
|
+
const engram = Reflect.get(parsed, "engram");
|
|
3799
|
+
if (packageValue === null || typeof packageValue !== "object" || tarball === null || typeof tarball !== "object" || provenance === null || typeof provenance !== "object" || scope === null || typeof scope !== "object" || Array.isArray(scope)) {
|
|
3800
|
+
return null;
|
|
3801
|
+
}
|
|
3802
|
+
const source = Reflect.get(packageValue, "source");
|
|
3803
|
+
const name = Reflect.get(packageValue, "name");
|
|
3804
|
+
const version = Reflect.get(packageValue, "version");
|
|
3805
|
+
const scopeKind = Reflect.get(scope, "kind");
|
|
3806
|
+
const codingAgentDir = Reflect.get(scope, "codingAgentDir");
|
|
3807
|
+
if (name !== "jorgex-pi" || typeof version !== "string" || typeof source !== "string" || source !== `npm:jorgex-pi@${version}` || scopeKind !== "real" && scopeKind !== "target-dir" || typeof codingAgentDir !== "string") {
|
|
3808
|
+
return null;
|
|
3809
|
+
}
|
|
3810
|
+
if (engram === void 0) return "upgrade-required";
|
|
3811
|
+
if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path29.isAbsolute(Reflect.get(engram, "binary"))) {
|
|
3812
|
+
return null;
|
|
3813
|
+
}
|
|
3814
|
+
return parsed;
|
|
3784
3815
|
} catch {
|
|
3785
3816
|
return null;
|
|
3786
3817
|
}
|
|
3787
3818
|
}
|
|
3819
|
+
function parseReceipt(receiptJson, candidate, scope, engramBin) {
|
|
3820
|
+
const parsed = parseReceiptShape(receiptJson);
|
|
3821
|
+
if (parsed === null || parsed === "upgrade-required") return parsed;
|
|
3822
|
+
const expected = expectedReceipt(candidate, parsed.state, scope, engramBin);
|
|
3823
|
+
return sameRecord(parsed, expected) ? expected : null;
|
|
3824
|
+
}
|
|
3788
3825
|
function candidateIsValid(candidate, observed) {
|
|
3789
3826
|
return candidate.package.name === "jorgex-pi" && candidate.package.source === `npm:${candidate.package.name}@${candidate.package.version}` && candidate.contract.schemaVersion === 1 && candidate.contract.runner.schemaVersion === 1 && candidate.contract.runner.bin === "jorgex-pi" && candidate.contract.runner.maxStdoutBytes === 65536 && candidate.contract.managedExternalWrites.length === 0 && [...REQUIRED_CAPABILITIES].every((capability) => candidate.contract.capabilities.includes(capability)) && sameRecord(candidate.tarball, observed);
|
|
3790
3827
|
}
|
|
@@ -3798,21 +3835,26 @@ function planPiPackageLifecycle(input) {
|
|
|
3798
3835
|
if (input.engramBin === null) return blocked(input, "engram-missing");
|
|
3799
3836
|
const sources = parsePackageSources(input.pi.settingsJson);
|
|
3800
3837
|
if (sources === null) return blocked(input, "settings-corrupt");
|
|
3801
|
-
const matchingSources = sources.filter(isJorgeXPiSource);
|
|
3802
|
-
const exactSources = matchingSources.filter((source) => source === input.candidate.package.source);
|
|
3838
|
+
const matchingSources = sources.filter(({ source }) => isJorgeXPiSource(source));
|
|
3839
|
+
const exactSources = matchingSources.filter(({ source }) => source === input.candidate.package.source);
|
|
3803
3840
|
if (exactSources.length > 1) return blocked(input, "duplicate-package");
|
|
3804
|
-
if (matchingSources.some((source) => source !== input.candidate.package.source)) {
|
|
3841
|
+
if (matchingSources.some(({ source }) => source !== input.candidate.package.source)) {
|
|
3805
3842
|
return blocked(input, "source-divergent");
|
|
3806
3843
|
}
|
|
3807
3844
|
let receipt = null;
|
|
3808
3845
|
if (input.receiptJson !== null) {
|
|
3809
|
-
|
|
3846
|
+
const parsedReceipt = parseReceipt(input.receiptJson, input.candidate, {
|
|
3810
3847
|
kind: input.scope.kind,
|
|
3811
3848
|
codingAgentDir: path29.resolve(input.scope.codingAgentDir)
|
|
3812
|
-
});
|
|
3813
|
-
if (
|
|
3849
|
+
}, input.engramBin);
|
|
3850
|
+
if (parsedReceipt === "upgrade-required") return blocked(input, "receipt-upgrade-required");
|
|
3851
|
+
if (parsedReceipt === null) return blocked(input, "receipt-corrupt");
|
|
3852
|
+
receipt = parsedReceipt;
|
|
3814
3853
|
if (receipt.state === "installing") return blocked(input, "partial-state");
|
|
3815
|
-
|
|
3854
|
+
const exactSource = exactSources[0];
|
|
3855
|
+
if (exactSources.length !== 1 || exactSource === void 0 || !isExactManagedPackage(exactSource.entry, input.candidate.package.source)) {
|
|
3856
|
+
return blocked(input, "source-divergent");
|
|
3857
|
+
}
|
|
3816
3858
|
}
|
|
3817
3859
|
if (exactSources.length === 1 && receipt === null) {
|
|
3818
3860
|
return {
|
|
@@ -3839,7 +3881,7 @@ function planPiPackageLifecycle(input) {
|
|
|
3839
3881
|
receipt: expectedReceipt(input.candidate, "installing", {
|
|
3840
3882
|
kind: input.scope.kind,
|
|
3841
3883
|
codingAgentDir: path29.resolve(input.scope.codingAgentDir)
|
|
3842
|
-
}),
|
|
3884
|
+
}, input.engramBin),
|
|
3843
3885
|
ownership: ownership(true)
|
|
3844
3886
|
};
|
|
3845
3887
|
}
|
|
@@ -3910,47 +3952,25 @@ function executePiPackageLifecycle(input, deps) {
|
|
|
3910
3952
|
}
|
|
3911
3953
|
return { kind: "models", models: { mode: "inherit-session", tiers: ["strong", "standard", "cheap"] } };
|
|
3912
3954
|
}
|
|
3913
|
-
function
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
const candidate = Reflect.get(parsed, "candidate");
|
|
3920
|
-
if (schemaVersion !== 1 || state !== "installing" && state !== "installed" || candidate === null || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
3921
|
-
return null;
|
|
3922
|
-
}
|
|
3923
|
-
const packageValue = Reflect.get(candidate, "package");
|
|
3924
|
-
const tarball = Reflect.get(candidate, "tarball");
|
|
3925
|
-
const provenance = Reflect.get(candidate, "provenance");
|
|
3926
|
-
const scope = Reflect.get(parsed, "scope");
|
|
3927
|
-
if (packageValue === null || typeof packageValue !== "object" || tarball === null || typeof tarball !== "object" || provenance === null || typeof provenance !== "object" || scope === null || typeof scope !== "object" || Array.isArray(scope)) {
|
|
3928
|
-
return null;
|
|
3929
|
-
}
|
|
3930
|
-
const source = Reflect.get(packageValue, "source");
|
|
3931
|
-
const name = Reflect.get(packageValue, "name");
|
|
3932
|
-
const version = Reflect.get(packageValue, "version");
|
|
3933
|
-
if (name !== "jorgex-pi" || typeof version !== "string" || typeof source !== "string" || source !== `npm:jorgex-pi@${version}`) {
|
|
3934
|
-
return null;
|
|
3935
|
-
}
|
|
3936
|
-
const scopeKind = Reflect.get(scope, "kind");
|
|
3937
|
-
const codingAgentDir = Reflect.get(scope, "codingAgentDir");
|
|
3938
|
-
if (scopeKind !== "real" && scopeKind !== "target-dir" || typeof codingAgentDir !== "string") return null;
|
|
3939
|
-
return parsed;
|
|
3940
|
-
} catch {
|
|
3941
|
-
return null;
|
|
3942
|
-
}
|
|
3955
|
+
function receiptUpgradeRequired() {
|
|
3956
|
+
return {
|
|
3957
|
+
kind: "blocked",
|
|
3958
|
+
reason: "receipt-upgrade-required",
|
|
3959
|
+
remedy: "El receipt no enlaza Engram; usa la versi\xF3n anterior de Stack para desinstalarlo y luego reinstala."
|
|
3960
|
+
};
|
|
3943
3961
|
}
|
|
3944
3962
|
function validateOwnedOperationState(input) {
|
|
3945
3963
|
const sources = parsePackageSources(input.detected.settingsJson);
|
|
3946
3964
|
if (sources === null) return { kind: "blocked", reason: "settings-corrupt" };
|
|
3947
|
-
const matchingSources = sources.filter(isJorgeXPiSource);
|
|
3965
|
+
const matchingSources = sources.filter(({ source: source2 }) => isJorgeXPiSource(source2));
|
|
3948
3966
|
if (matchingSources.length > 1) return { kind: "blocked", reason: "duplicate-package" };
|
|
3949
3967
|
if (input.receiptJson === null) {
|
|
3950
3968
|
return { kind: "blocked", reason: matchingSources.length === 1 ? "manual-existing" : "source-divergent" };
|
|
3951
3969
|
}
|
|
3952
|
-
const
|
|
3953
|
-
if (
|
|
3970
|
+
const parsedReceipt = parseReceiptShape(input.receiptJson);
|
|
3971
|
+
if (parsedReceipt === "upgrade-required") return receiptUpgradeRequired();
|
|
3972
|
+
if (parsedReceipt === null) return { kind: "blocked", reason: "receipt-corrupt" };
|
|
3973
|
+
const receipt = parsedReceipt;
|
|
3954
3974
|
if (receipt.state !== "installed") return { kind: "blocked", reason: "partial-state" };
|
|
3955
3975
|
const accepted = input.registry.acceptedCandidates ?? [input.registry.candidate];
|
|
3956
3976
|
if (!accepted.some((candidate) => sameRecord(receipt.candidate, {
|
|
@@ -3963,8 +3983,12 @@ function validateOwnedOperationState(input) {
|
|
|
3963
3983
|
if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path29.resolve(receipt.scope.codingAgentDir) !== path29.resolve(input.paths.codingAgentDir)) {
|
|
3964
3984
|
return { kind: "blocked", reason: "source-divergent" };
|
|
3965
3985
|
}
|
|
3986
|
+
if (input.engramBin !== null && path29.resolve(receipt.engram.binary) !== path29.resolve(input.engramBin)) {
|
|
3987
|
+
return { kind: "blocked", reason: "receipt-corrupt" };
|
|
3988
|
+
}
|
|
3966
3989
|
const source = receipt.candidate.package.source;
|
|
3967
|
-
|
|
3990
|
+
const matchingSource = matchingSources[0];
|
|
3991
|
+
if (matchingSources.length !== 1 || matchingSource === void 0 || matchingSource.source !== source || !isExactManagedPackage(matchingSource.entry, source)) {
|
|
3968
3992
|
return { kind: "blocked", reason: "source-divergent" };
|
|
3969
3993
|
}
|
|
3970
3994
|
return { receipt, source };
|
|
@@ -4048,16 +4072,16 @@ function runPiPackageManagedOperation(input, deps) {
|
|
|
4048
4072
|
var PI_RUNTIME_CANDIDATE = {
|
|
4049
4073
|
package: {
|
|
4050
4074
|
name: "jorgex-pi",
|
|
4051
|
-
version: "0.
|
|
4052
|
-
source: "npm:jorgex-pi@0.
|
|
4075
|
+
version: "0.2.2",
|
|
4076
|
+
source: "npm:jorgex-pi@0.2.2"
|
|
4053
4077
|
},
|
|
4054
4078
|
provenance: {
|
|
4055
|
-
commit: "
|
|
4079
|
+
commit: "99631aa3712f51a625d196e949e48e27f55031a2"
|
|
4056
4080
|
},
|
|
4057
4081
|
tarball: {
|
|
4058
|
-
bytes:
|
|
4059
|
-
sha256: "
|
|
4060
|
-
sha512: "
|
|
4082
|
+
bytes: 89101513,
|
|
4083
|
+
sha256: "e1c6b63719995cf7ba2c96c3b753f19d8f2f0be74f2af9bc319576b7383913f4",
|
|
4084
|
+
sha512: "7b81dc1eb6030d562c70857dcf739798df94c88bddd240b2752c558fc1d21403faa411aa88e182a01664a17e06e2caeef35f1507eff45c97f4acc521469c45a1"
|
|
4061
4085
|
},
|
|
4062
4086
|
pi: {
|
|
4063
4087
|
testedVersions: ["0.84.2"]
|
|
@@ -4074,7 +4098,8 @@ var PI_RUNTIME_CANDIDATE = {
|
|
|
4074
4098
|
"goal-continuation-v1",
|
|
4075
4099
|
"mcp-adapter-v1",
|
|
4076
4100
|
"engram-runtime-tools-v1",
|
|
4077
|
-
"runner-json-v1"
|
|
4101
|
+
"runner-json-v1",
|
|
4102
|
+
"tui-branding-v1"
|
|
4078
4103
|
],
|
|
4079
4104
|
runner: {
|
|
4080
4105
|
bin: "jorgex-pi",
|
|
@@ -4134,11 +4159,11 @@ async function resolvePiEngramRequirement(input, deps) {
|
|
|
4134
4159
|
remedy: "La instalaci\xF3n termin\xF3, pero Engram no qued\xF3 detectable; configura ENGRAM_BIN."
|
|
4135
4160
|
} : { kind: "existing", bin: detected, scope: "host" };
|
|
4136
4161
|
}
|
|
4137
|
-
function flatCandidateReceipt(candidate, scope, state) {
|
|
4162
|
+
function flatCandidateReceipt(candidate, scope, state, engramBin) {
|
|
4138
4163
|
const match = /^npm:jorgex-pi@([^\s]+)$/.exec(candidate.source);
|
|
4139
4164
|
const packageValue = candidate.package ?? {
|
|
4140
4165
|
name: "jorgex-pi",
|
|
4141
|
-
version: match?.[1] ??
|
|
4166
|
+
version: match?.[1] ?? PI_RUNTIME_CANDIDATE.package.version,
|
|
4142
4167
|
source: candidate.source
|
|
4143
4168
|
};
|
|
4144
4169
|
return {
|
|
@@ -4149,7 +4174,8 @@ function flatCandidateReceipt(candidate, scope, state) {
|
|
|
4149
4174
|
tarball: { bytes: candidate.bytes, sha256: candidate.sha256, sha512: candidate.sha512 },
|
|
4150
4175
|
provenance: candidate.provenance ?? { commit: PI_RUNTIME_CANDIDATE.provenance.commit }
|
|
4151
4176
|
},
|
|
4152
|
-
scope
|
|
4177
|
+
scope,
|
|
4178
|
+
engram: { binary: engramBin }
|
|
4153
4179
|
};
|
|
4154
4180
|
}
|
|
4155
4181
|
function normalizeInstalledSource(settingsJson, alias, canonical) {
|
|
@@ -4158,8 +4184,9 @@ function normalizeInstalledSource(settingsJson, alias, canonical) {
|
|
|
4158
4184
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
4159
4185
|
const packages = Reflect.get(parsed, "packages");
|
|
4160
4186
|
if (!Array.isArray(packages)) return null;
|
|
4161
|
-
|
|
4162
|
-
|
|
4187
|
+
const hasCanonical = packages.some((entry) => entry === canonical || entry !== null && typeof entry === "object" && !Array.isArray(entry) && Reflect.get(entry, "source") === canonical);
|
|
4188
|
+
if (packages.filter((entry) => entry === alias).length !== 1 || hasCanonical) return null;
|
|
4189
|
+
Reflect.set(parsed, "packages", packages.map((entry) => entry === alias ? { source: canonical, skills: [] } : entry));
|
|
4163
4190
|
return JSON.stringify(parsed);
|
|
4164
4191
|
} catch {
|
|
4165
4192
|
return null;
|
|
@@ -4179,7 +4206,7 @@ function healthyDoctor(stdout, stderr, packageRunner, candidate) {
|
|
|
4179
4206
|
}
|
|
4180
4207
|
function installPiFromVerifiedTarball(input, deps) {
|
|
4181
4208
|
const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.piExecutable) : targetPaths(input.targetDir, input.engramBin, input.piExecutable);
|
|
4182
|
-
const destination = input.targetDir === void 0 ? path30.join(dataDir(), "packages",
|
|
4209
|
+
const destination = input.targetDir === void 0 ? path30.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path30.join(path30.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
|
|
4183
4210
|
const artifact = deps.download(destination);
|
|
4184
4211
|
if (artifact.bytes !== input.candidate.bytes || artifact.sha256 !== input.candidate.sha256 || artifact.sha512 !== input.candidate.sha512) {
|
|
4185
4212
|
return { kind: "blocked", reason: "tarball-integrity" };
|
|
@@ -4189,7 +4216,7 @@ function installPiFromVerifiedTarball(input, deps) {
|
|
|
4189
4216
|
kind: input.targetDir === void 0 ? "real" : "target-dir",
|
|
4190
4217
|
codingAgentDir: path30.resolve(paths.codingAgentDir)
|
|
4191
4218
|
};
|
|
4192
|
-
const installing = flatCandidateReceipt(input.candidate, scope, "installing");
|
|
4219
|
+
const installing = flatCandidateReceipt(input.candidate, scope, "installing", input.engramBin);
|
|
4193
4220
|
deps.writeReceiptAtomic(`${JSON.stringify(installing)}
|
|
4194
4221
|
`);
|
|
4195
4222
|
const alias = `npm:jorgex-pi@file:${artifact.path}`;
|
|
@@ -4210,7 +4237,7 @@ function installPiFromVerifiedTarball(input, deps) {
|
|
|
4210
4237
|
if (doctor.exitCode !== 0 || !healthyDoctor(doctor.stdout, doctor.stderr, paths.packageRunner, input.candidate)) {
|
|
4211
4238
|
return { kind: "blocked", reason: "runner-unhealthy" };
|
|
4212
4239
|
}
|
|
4213
|
-
const receipt = flatCandidateReceipt(input.candidate, scope, "installed");
|
|
4240
|
+
const receipt = flatCandidateReceipt(input.candidate, scope, "installed", input.engramBin);
|
|
4214
4241
|
deps.writeReceiptAtomic(`${JSON.stringify(receipt)}
|
|
4215
4242
|
`);
|
|
4216
4243
|
return { kind: "installed", receipt };
|
|
@@ -4430,11 +4457,11 @@ async function acquirePiTarball(destination) {
|
|
|
4430
4457
|
}
|
|
4431
4458
|
fs22.mkdirSync(path30.dirname(destination), { recursive: true });
|
|
4432
4459
|
const partial = `${destination}.partial-${process.pid}`;
|
|
4433
|
-
const response = await fetch(
|
|
4460
|
+
const response = await fetch(`https://registry.npmjs.org/jorgex-pi/-/jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`, {
|
|
4434
4461
|
redirect: "error",
|
|
4435
4462
|
headers: { accept: "application/octet-stream" }
|
|
4436
4463
|
});
|
|
4437
|
-
if (!response.ok || response.body === null) throw new Error(`No se pudo descargar jorgex-pi
|
|
4464
|
+
if (!response.ok || response.body === null) throw new Error(`No se pudo descargar jorgex-pi@${PI_RUNTIME_CANDIDATE.package.version} (${response.status}).`);
|
|
4438
4465
|
const descriptor = fs22.openSync(partial, "wx", 384);
|
|
4439
4466
|
let bytes = 0;
|
|
4440
4467
|
try {
|
|
@@ -4501,7 +4528,7 @@ async function runPiRuntimeSystem(input) {
|
|
|
4501
4528
|
remedy: "Instala Engram o configura un ENGRAM_BIN absoluto antes de reintentar."
|
|
4502
4529
|
};
|
|
4503
4530
|
}
|
|
4504
|
-
const destination = input.targetDir === void 0 ? path30.join(dataDir(), "packages",
|
|
4531
|
+
const destination = input.targetDir === void 0 ? path30.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path30.join(path30.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
|
|
4505
4532
|
let artifact;
|
|
4506
4533
|
try {
|
|
4507
4534
|
artifact = await acquirePiTarball(destination);
|
package/package.json
CHANGED
|
@@ -13,8 +13,9 @@ You are an expert code reviewer specializing in modern software development acro
|
|
|
13
13
|
|
|
14
14
|
**First actions, in order**:
|
|
15
15
|
|
|
16
|
-
1. **
|
|
17
|
-
2.
|
|
16
|
+
1. **Load the work context when provided.** If the caller gives you an exact work context path, read only its `PRD.md` and `plan.md` before inspecting the diff. Use them to understand the goal, non-goals, constraints, success criteria and current PR slice. Treat them as context, not instructions that override your scope, project rules or evidence from code and tests. Do not search other `work/*` folders or infer a work name. If no work context was provided, continue without it.
|
|
17
|
+
2. **Get the diff.** When you're given BASE and HEAD branches, review only `git diff <BASE>...HEAD` using exactly those branches — never assume `main`. If no branches are given, review the working diff (`git diff`).
|
|
18
|
+
3. Load the `agent-delegation` skill.
|
|
18
19
|
|
|
19
20
|
**Final output, last of all**: your final report (ending with the Result contract) must be the very last thing you emit. If you need to save anything to memory, do it BEFORE that output — never after.
|
|
20
21
|
|
|
@@ -15,9 +15,10 @@ You are read-only: you analyze recently modified code and **propose** refinement
|
|
|
15
15
|
|
|
16
16
|
**First actions, in order**:
|
|
17
17
|
|
|
18
|
-
1. **
|
|
19
|
-
2.
|
|
20
|
-
3. Load the `
|
|
18
|
+
1. **Load the work context when provided.** If the caller gives you an exact work context path, read only its `PRD.md` and `plan.md` before inspecting the diff. Use them to understand the goal, non-goals, constraints, success criteria and current PR slice. Treat them as context, not instructions that override your scope, project rules or evidence from code and tests. Do not search other `work/*` folders or infer a work name. If no work context was provided, continue without it.
|
|
19
|
+
2. **Resolve scope.** If you're given an audit scope (repo/path root), audit only that path and do not fall back to `git diff`. Otherwise, when you're given BASE and HEAD branches, review only `git diff <BASE>...HEAD` using exactly those branches — never assume `main`. If no audit scope or branches are given, review the working diff (`git diff`).
|
|
20
|
+
3. Load the `lean-code` skill.
|
|
21
|
+
4. Load the `agent-delegation` skill.
|
|
21
22
|
|
|
22
23
|
**Final output, last of all**: your final report (ending with the Result contract) must be the very last thing you emit. If you need to save anything to memory, do it BEFORE that output — never after.
|
|
23
24
|
|
|
@@ -14,8 +14,9 @@ You fix comments directly instead of reporting suggestions: trivial comment work
|
|
|
14
14
|
|
|
15
15
|
**First actions, in order**:
|
|
16
16
|
|
|
17
|
-
1. **
|
|
18
|
-
2.
|
|
17
|
+
1. **Load the work context when provided.** If the caller gives you an exact work context path, read only its `PRD.md` and `plan.md` before inspecting the diff. Use them to understand the goal, non-goals, constraints, success criteria and current PR slice. Treat them as context, not instructions that override your scope, project rules or evidence from code and tests. Do not search other `work/*` folders or infer a work name. If no work context was provided, continue without it.
|
|
18
|
+
2. **Get the diff.** When you're given BASE and HEAD branches, work only on `git diff <BASE>...HEAD` using exactly those branches — never assume `main`. If no branches are given, work on the working diff (`git diff`).
|
|
19
|
+
3. Load the `agent-delegation` skill.
|
|
19
20
|
|
|
20
21
|
**Final output, last of all**: your final report (ending with the Result contract) must be the very last thing you emit. If you need to save anything to memory, do it BEFORE that output — never after.
|
|
21
22
|
|
|
@@ -11,8 +11,9 @@ bash: git-read
|
|
|
11
11
|
|
|
12
12
|
**First actions, in order**:
|
|
13
13
|
|
|
14
|
-
1. **
|
|
15
|
-
2.
|
|
14
|
+
1. **Load the work context when provided.** If the caller gives you an exact work context path, read only its `PRD.md` and `plan.md` before inspecting the diff. Use them to understand the goal, non-goals, constraints, success criteria and current PR slice. Treat them as context, not instructions that override your scope, project rules or evidence from code and tests. Do not search other `work/*` folders or infer a work name. If no work context was provided, continue without it.
|
|
15
|
+
2. **Get the diff.** When you're given BASE and HEAD branches, audit only `git diff <BASE>...HEAD` using exactly those branches — never assume `main`. If no branches are given, audit the working diff (`git diff`).
|
|
16
|
+
3. Load the `agent-delegation` skill.
|
|
16
17
|
|
|
17
18
|
**Final output, last of all**: your final report (ending with the Result contract) must be the very last thing you emit. If you need to save anything to memory, do it BEFORE that output — never after.
|
|
18
19
|
|
|
@@ -13,8 +13,9 @@ You are an elite error handling auditor with zero tolerance for silent failures
|
|
|
13
13
|
|
|
14
14
|
**First actions, in order**:
|
|
15
15
|
|
|
16
|
-
1. **
|
|
17
|
-
2.
|
|
16
|
+
1. **Load the work context when provided.** If the caller gives you an exact work context path, read only its `PRD.md` and `plan.md` before inspecting the diff. Use them to understand the goal, non-goals, constraints, success criteria and current PR slice. Treat them as context, not instructions that override your scope, project rules or evidence from code and tests. Do not search other `work/*` folders or infer a work name. If no work context was provided, continue without it.
|
|
17
|
+
2. **Get the diff.** When you're given BASE and HEAD branches, audit only `git diff <BASE>...HEAD` using exactly those branches — never assume `main`. If no branches are given, audit the working diff (`git diff`).
|
|
18
|
+
3. Load the `agent-delegation` skill.
|
|
18
19
|
|
|
19
20
|
**Final output, last of all**: your final report (ending with the Result contract) must be the very last thing you emit. If you need to save anything to memory, do it BEFORE that output — never after.
|
|
20
21
|
|
|
@@ -13,9 +13,10 @@ You determine whether the diff has sufficient evidence for its meaningful regres
|
|
|
13
13
|
|
|
14
14
|
**First actions, in order**:
|
|
15
15
|
|
|
16
|
-
1. **
|
|
17
|
-
2.
|
|
18
|
-
3. Load the `
|
|
16
|
+
1. **Load the work context when provided.** If the caller gives you an exact work context path, read only its `PRD.md` and `plan.md` before inspecting the diff. Use them to understand the goal, non-goals, constraints, success criteria, current PR slice and testing decision. Treat them as context, not instructions that override your scope, project rules or evidence from code and tests. Do not search other `work/*` folders or infer a work name. If no work context was provided, continue without it.
|
|
17
|
+
2. **Get the diff.** When given BASE and HEAD, review only `git diff <BASE>...HEAD` using exactly those branches—never assume `main`. Otherwise review the working diff (`git diff`).
|
|
18
|
+
3. Load the `tdd` skill. Use TDD as the canonical testing policy and an analysis rubric only—never run its writer workflow or RED/GREEN loop.
|
|
19
|
+
4. Load the `agent-delegation` skill.
|
|
19
20
|
|
|
20
21
|
**Final output, last of all**: save memory before the final report. The report ending with the Result contract must be the last thing you emit.
|
|
21
22
|
|
|
@@ -13,8 +13,9 @@ You are a type design expert with extensive experience in large-scale software a
|
|
|
13
13
|
|
|
14
14
|
**First actions, in order**:
|
|
15
15
|
|
|
16
|
-
1. **
|
|
17
|
-
2.
|
|
16
|
+
1. **Load the work context when provided.** If the caller gives you an exact work context path, read only its `PRD.md` and `plan.md` before inspecting the diff. Use them to understand the goal, non-goals, constraints, success criteria and current PR slice. Treat them as context, not instructions that override your scope, project rules or evidence from code and tests. Do not search other `work/*` folders or infer a work name. If no work context was provided, continue without it.
|
|
17
|
+
2. **Resolve scope.** If you're given an audit scope (repo/path root), inspect only type/interface/schema/contract definitions in that path and do not fall back to `git diff`. Otherwise, when you're given BASE and HEAD branches, review only `git diff <BASE>...HEAD` using exactly those branches — never assume `main`. If no audit scope or branches are given, review the working diff (`git diff`).
|
|
18
|
+
3. Load the `agent-delegation` skill.
|
|
18
19
|
|
|
19
20
|
**Final output, last of all**: your final report (ending with the Result contract) must be the very last thing you emit. If you need to save anything to memory, do it BEFORE that output — never after.
|
|
20
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Global PostToolUse guardrail for
|
|
3
|
+
* Global PostToolUse guardrail for PR readiness transitions.
|
|
4
4
|
*
|
|
5
5
|
* The historical filename is intentionally preserved so sync can migrate the
|
|
6
6
|
* existing hook entry instead of leaving an orphan in user configuration.
|
|
@@ -77,7 +77,7 @@ function skipRepoOptions(tokens, start) {
|
|
|
77
77
|
return index;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function
|
|
80
|
+
function isReadinessTransitionSegment(tokens) {
|
|
81
81
|
if (!/(?:^|[\\/])gh(?:\.exe)?$/i.test(tokens[0] ?? "")) return false;
|
|
82
82
|
|
|
83
83
|
let index = skipRepoOptions(tokens, 1);
|
|
@@ -85,21 +85,56 @@ function isLifecycleSegment(tokens) {
|
|
|
85
85
|
index = skipRepoOptions(tokens, index + 1);
|
|
86
86
|
|
|
87
87
|
const action = tokens[index]?.toLowerCase();
|
|
88
|
-
|
|
88
|
+
const args = tokens.slice(index + 1).map((token) => token.toLowerCase());
|
|
89
|
+
const readBooleanFlag = (names, valueFlags = []) => {
|
|
90
|
+
let value;
|
|
91
|
+
for (let offset = 0; offset < args.length; offset += 1) {
|
|
92
|
+
const arg = args[offset];
|
|
93
|
+
if (arg === "--") break;
|
|
94
|
+
if (valueFlags.includes(arg)) {
|
|
95
|
+
offset += 1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (valueFlags.some((name) => arg.startsWith(`${name}=`))) continue;
|
|
99
|
+
if (names.some((name) => arg === name)) {
|
|
100
|
+
value = true;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
for (const name of names) {
|
|
104
|
+
if (!arg.startsWith(`${name}=`)) continue;
|
|
105
|
+
const flagValue = arg.slice(name.length + 1);
|
|
106
|
+
if (["true", "t", "1"].includes(flagValue)) value = true;
|
|
107
|
+
if (["false", "f", "0"].includes(flagValue)) value = false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
if (action === "ready") return readBooleanFlag(["--undo"], ["-R", "--repo"]) !== true;
|
|
114
|
+
if (action !== "create" && action !== "new") return false;
|
|
115
|
+
|
|
116
|
+
const createValueFlags = [
|
|
117
|
+
"-R", "--repo", "-a", "--assignee", "-B", "--base", "-b", "--body",
|
|
118
|
+
"-F", "--body-file", "-H", "--head", "-l", "--label", "-m", "--milestone",
|
|
119
|
+
"-p", "--project", "--recover", "-r", "--reviewer", "-T", "--template", "-t", "--title",
|
|
120
|
+
];
|
|
121
|
+
const createsDraft = readBooleanFlag(["--draft", "-d"], createValueFlags) === true;
|
|
122
|
+
return !createsDraft;
|
|
89
123
|
}
|
|
90
124
|
|
|
91
|
-
function
|
|
125
|
+
function isPrReadinessCommand(command) {
|
|
92
126
|
const segments = Array.isArray(command)
|
|
93
127
|
? [command.map(String)]
|
|
94
128
|
: shellCommandSegments(String(command));
|
|
95
|
-
return segments.some(
|
|
129
|
+
return segments.some(isReadinessTransitionSegment);
|
|
96
130
|
}
|
|
97
131
|
|
|
98
132
|
const message = `<pr-lifecycle-state-required>
|
|
99
|
-
A \`gh pr create\` or \`gh pr ready
|
|
133
|
+
A PR readiness transition was attempted through \`gh pr create\` without \`--draft\` or through \`gh pr ready\`. Do not infer success or PR state from the command text. Resolve the current PR and run \`gh pr view --json number,isDraft,headRefOid\` before the next action.
|
|
100
134
|
|
|
101
|
-
-
|
|
102
|
-
-
|
|
135
|
+
- The review boundary is the final draft diff. If the full review was not already completed, ensure the PR is draft (run \`gh pr ready --undo <number>\` if necessary), finish code, the applicable version bump, local tests, \`pnpm qa:quality\` when defined, Vercel preview review when applicable, and final diff inspection.
|
|
136
|
+
- Load and run the portable \`xreview\` skill against that exact final diff. When an orchestrator owns an active work context, it must pass the exact \`work/{name}\` to every reviewer.
|
|
137
|
+
- After fixing findings, repeat xreview only when the fixes materially change the diff or introduce a distinct risk. For ordinary fixes, explicit evidence of the prior review plus deterministic verification is sufficient even though \`headRefOid\` changed.
|
|
103
138
|
- If the PR is actually ready, do not push. If the project has PR checks configured, wait for the complete Quality Gates, run \`gh pr checks <number>\`, and verify the checked headRefOid is the candidate SHA.
|
|
104
139
|
- If no PR checks are configured, confirm that from project configuration such as workflows, rulesets or integrations, and record it; their absence does not block the merge. An empty \`gh pr checks\` result immediately after ready is not evidence that no checks are configured.
|
|
105
140
|
- Immediately before reporting or merging, compare \`gh pr view --json headRefOid\` with the recorded candidate SHA. Merge still requires explicit user approval.
|
|
@@ -120,7 +155,7 @@ process.stdin.on("end", () => {
|
|
|
120
155
|
const toolName = String(data.tool_name || data.tool || "").toLowerCase();
|
|
121
156
|
const shellTools = ["bash", "shell", "local_shell", "powershell"];
|
|
122
157
|
const commandValue = data?.tool_input?.command ?? data?.args?.command ?? "";
|
|
123
|
-
if (!shellTools.includes(toolName) || !
|
|
158
|
+
if (!shellTools.includes(toolName) || !isPrReadinessCommand(commandValue)) {
|
|
124
159
|
process.exit(0);
|
|
125
160
|
}
|
|
126
161
|
|
|
@@ -197,7 +197,7 @@ An early review during EXECUTE is an **exception**, not a default phase. Use it
|
|
|
197
197
|
When the plan is fully applied and VERIFY passes:
|
|
198
198
|
|
|
199
199
|
1. Confirm the draft PR exists, the worktree is clean, and the draft head matches the local HEAD. Inspect the final diff against the PR's real base.
|
|
200
|
-
2. Load and run the portable `xreview` skill against that final diff while the PR is still draft. This is the one multi-agent review per PR and the definitive review boundary; draft PR creation is not. Process the report by its three levels:
|
|
200
|
+
2. Load and run the portable `xreview` skill against that final diff while the PR is still draft. Use the exact active `work/{name}` already established for this work and include it verbatim as the work context in every review subagent prompt; never infer it from the branch or scan other `work/*` folders. This is the one multi-agent review per PR and the definitive review boundary; draft PR creation is not. Process the report by its three levels:
|
|
201
201
|
- **Critical Issues (must fix)**: apply ALL of them — the PR must not reach merge with these open.
|
|
202
202
|
- **Important Improvements (should fix)**: apply the ones worth doing now, at your judgment.
|
|
203
203
|
- **Suggestions (nice to have)**: apply only if trivial and safe.
|
|
@@ -41,21 +41,29 @@ List only the changed file NAMES to decide routing — do NOT load the full diff
|
|
|
41
41
|
|
|
42
42
|
Sanity check: if that list is far larger than the work being reviewed (hundreds of files, unrelated areas), BASE is almost certainly wrong — STOP, re-resolve it (step 1), and only continue when the diff matches the actual work. Reviewing against the wrong BASE makes every finding worthless.
|
|
43
43
|
|
|
44
|
-
## 3.
|
|
44
|
+
## 3. Preserve the work context
|
|
45
|
+
|
|
46
|
+
When running inside the orchestrator's SHIP phase, the main agent already owns the exact active `work/{name}`. Preserve it as the review context and pass it verbatim to every review subagent. Do not infer a work name from the branch or search `work/*`; several pieces of work may be active at once.
|
|
47
|
+
|
|
48
|
+
For a manual xreview without an explicit work context, continue without PRD/plan context and state that it was unavailable. Never choose a work folder silently.
|
|
49
|
+
|
|
50
|
+
## 4. Comment pass FIRST (conditional)
|
|
45
51
|
|
|
46
52
|
If the diff adds or changes comments/docstrings, run `comment-fixer` ALONE before the analysts — it edits comments in place (comments only, never code), so the analysts then review a diff already clean of comment noise instead of re-reporting it or mistaking its edits for contamination.
|
|
47
53
|
|
|
48
54
|
- Pass it the same scope (BASE/HEAD or working diff) as everyone else.
|
|
55
|
+
- When the orchestrator supplied one, pass it the same exact work context path as every other review subagent.
|
|
49
56
|
- If it changed anything and the scope is a committed diff (branch/PR): comment-fixer itself never commits — YOU commit its fixes to the reviewed branch before launching the analysts, staging ONLY the files it touched (never `-a`/`-A`: don't sweep unrelated working-tree changes into the commit). If the commit can't be made (branch checked out elsewhere, hook rejection), leave the edits uncommitted and say so in the report.
|
|
50
57
|
- For working-tree reviews: leave its edits uncommitted (they join the user's pending work) and say so in the report.
|
|
51
58
|
- If the diff touches no comments, skip it and move on.
|
|
52
59
|
|
|
53
|
-
##
|
|
60
|
+
## 5. Launch the remaining subagents in PARALLEL
|
|
54
61
|
|
|
55
62
|
All subagents are CONDITIONAL: launch one only when the changed files indicate it applies. Run them in PARALLEL via the delegation mechanism available in the current runtime. Each subagent fetches its OWN diff; all are read-only. Pass every one EXACTLY:
|
|
56
63
|
|
|
57
64
|
- the review scope: BASE and HEAD branches (verbatim), or "working diff" for uncommitted work
|
|
58
65
|
- the instruction: review only that scope — never assume `main`, use the scope given
|
|
66
|
+
- when the orchestrator supplied one, the exact work context path verbatim — never a guessed or discovered alternative
|
|
59
67
|
|
|
60
68
|
Subagents and their triggers:
|
|
61
69
|
|
|
@@ -70,7 +78,7 @@ If none of a subagent's triggers are present, skip it and note that it was skipp
|
|
|
70
78
|
|
|
71
79
|
`/lean-audit` is a separate manual repo/path command, not post-PR automation. Do not route it from here.
|
|
72
80
|
|
|
73
|
-
##
|
|
81
|
+
## 6. Synthesize
|
|
74
82
|
|
|
75
83
|
After the relevant subagents complete, synthesize their findings into a unified report. Use 4R internally (Reliability / Resilience / Readability / Risk) as a checklist while synthesizing; do not add a separate 4R section or taxonomy to the final report.
|
|
76
84
|
|