jorgex-stack 1.0.6 → 1.0.8
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 +57 -57
- package/dist/cli.js +50 -33
- package/package.json +1 -1
- package/stack/plugins/opencode/goal/opencode-hooks.ts +9 -3
- package/upstreams.json +3 -2
- package/PRD.md +0 -310
package/README.md
CHANGED
|
@@ -1,97 +1,97 @@
|
|
|
1
1
|
# JorgeX Stack
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Portable multi-agent harness: one configuration source — 15 agents, 18 skills, hooks, persistent memory ([Engram](https://github.com/Gentleman-Programming/engram)), MCPs, and system prompt — installable with one command in **Claude Code**, **Codex CLI**, and **OpenCode**.
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> Inspired by [gentle-ai](https://github.com/Gentleman-Programming/gentle-ai), rebuilt for the JorgeX stack.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Usage
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Install and run via npm without cloning the repository:
|
|
10
10
|
|
|
11
11
|
```
|
|
12
|
-
pnpm dlx jorgex-stack install #
|
|
13
|
-
pnpm dlx jorgex-stack models # picker
|
|
14
|
-
pnpm dlx jorgex-stack sync #
|
|
15
|
-
pnpm dlx jorgex-stack doctor #
|
|
16
|
-
pnpm dlx jorgex-stack update #
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
pnpm dlx jorgex-stack restore #
|
|
20
|
-
pnpm dlx jorgex-stack uninstall #
|
|
12
|
+
pnpm dlx jorgex-stack install # interactive: choose runtimes and confirm
|
|
13
|
+
pnpm dlx jorgex-stack models # model picker by runtime and tier (strong/standard/cheap)
|
|
14
|
+
pnpm dlx jorgex-stack sync # reapplies config (idempotent; removes orphans)
|
|
15
|
+
pnpm dlx jorgex-stack doctor # checks that everything is healthy (Engram, drift, hooks, keys)
|
|
16
|
+
pnpm dlx jorgex-stack update # interactive: scans stack + Engram, multiselect, diff/confirm
|
|
17
|
+
# With --check: report only, no changes
|
|
18
|
+
# With --yes: batch mode (report only)
|
|
19
|
+
pnpm dlx jorgex-stack restore # restores a backup
|
|
20
|
+
pnpm dlx jorgex-stack uninstall # uninstalls our files and keeps user data (Engram intact)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
For development from a clone, run the same commands through `pnpm cli <command>` (see [Development](#development)).
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Every command supports `--dry-run`, `--yes`, and `--target-dir <dir>` for testing without touching the real config. Writes create automatic backups and verify idempotency; merges into user config are surgical (marked markdown sections, JSON/TOML upserts), so user-owned content is never touched.
|
|
26
26
|
|
|
27
|
-
### Update:
|
|
27
|
+
### Update: Interactive Flow
|
|
28
28
|
|
|
29
|
-
`update`
|
|
29
|
+
`update` manages two sources for the end user, plus a maintainer-only one:
|
|
30
30
|
|
|
31
|
-
1. **Stack** (jorgex-stack):
|
|
32
|
-
2. **Engram** (
|
|
33
|
-
3. **
|
|
31
|
+
1. **Stack** (jorgex-stack): detects whether it is a git clone or a global install, then offers an update with confirmation.
|
|
32
|
+
2. **Engram** (binary): detects the installed version and offers an update through the **native channel** (brew -> `go install` -> release URL). Nothing needs to be stopped: as in upstream macOS/Linux, live processes keep using the old version until clients restart; on Windows, the in-use `.exe` is rotated by rename before installation. **Automatic DB backup before updating**. The database and memories are never touched.
|
|
33
|
+
3. **Vendored skills** (maintainer only): third-party skills ship **pinned** with the stack version, so the installed package never reaches out to their upstreams. Only when running from a git clone (`pnpm cli update`) does `update` scan the upstreams in `upstreams.json`, download to a temp directory, **show a mandatory diff**, and ask for confirmation — so the review and re-pin persist in the repo and get published. Skills with local changes (`modified: true`) warn and require double confirmation.
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
- `update --check`:
|
|
37
|
-
- `update` (TTY,
|
|
38
|
-
- `update --yes`
|
|
35
|
+
Usage:
|
|
36
|
+
- `update --check`: scans versions without applying changes.
|
|
37
|
+
- `update` (TTY, without `--yes`): interactive multiselect with visible diffs and step-by-step confirmations.
|
|
38
|
+
- `update --yes` or non-TTY: behaves like `--check` (report only).
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
GitHub authentication: requests use `GH_TOKEN`/`GITHUB_TOKEN` from the environment or, if unavailable, the token from your `gh` CLI session (`gh auth token` — local read only, never logged or persisted). Without a token, GitHub limits parallel requests and some upstreams may appear as "offline".
|
|
41
41
|
|
|
42
|
-
### Goal Mode
|
|
42
|
+
### OpenCode Goal Mode
|
|
43
43
|
|
|
44
|
-
Goal Mode
|
|
44
|
+
Goal Mode is an OpenCode plugin for long-running goals: multiple sessions, multiple slices, multiple worktrees, and, when needed, multiple PRs. It is not meant for short tasks. If the change fits without extended autonomy, do not use `/goal`.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
It only exists in OpenCode. Claude Code and Codex do not receive it.
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
Available commands:
|
|
49
49
|
|
|
50
|
-
- `/goal <
|
|
51
|
-
- `/goal status` —
|
|
52
|
-
- `/goal plan` —
|
|
53
|
-
- `/goal history` —
|
|
54
|
-
- `/goal pause` —
|
|
55
|
-
- `/goal resume` —
|
|
56
|
-
- `/goal merged [commit]` —
|
|
57
|
-
- `/goal cancel` —
|
|
50
|
+
- `/goal <goal>` — creates a persistent goal.
|
|
51
|
+
- `/goal status` — shows status and next action.
|
|
52
|
+
- `/goal plan` — shows the goal's master plan / PRD.
|
|
53
|
+
- `/goal history` — lists events and transitions.
|
|
54
|
+
- `/goal pause` — pauses the goal.
|
|
55
|
+
- `/goal resume` — resumes the goal.
|
|
56
|
+
- `/goal merged [commit]` — signals that the pending external PR has been merged.
|
|
57
|
+
- `/goal cancel` — cancels the goal.
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
What does not exist:
|
|
60
60
|
|
|
61
61
|
- `/goal quick`
|
|
62
62
|
- `/goal work`
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
Operational state:
|
|
65
65
|
|
|
66
|
-
- SQLite
|
|
67
|
-
-
|
|
68
|
-
- Engram
|
|
69
|
-
- Goal Mode
|
|
70
|
-
-
|
|
66
|
+
- Separate SQLite database by default at `~/.jorgex-stack/goals/goals.sqlite`.
|
|
67
|
+
- Optional override with `JORGEX_GOAL_DB`, but always inside `~/.jorgex-stack/goals/`.
|
|
68
|
+
- Engram is not the goal's operational store: it remains memory/protocol, not the state database.
|
|
69
|
+
- Goal Mode does not perform automatic merges; when it must wait for an external merge, the state becomes `waiting_for_merge`.
|
|
70
|
+
- The integration uses experimental OpenCode hooks (`experimental.chat.system.transform` and `experimental.session.compacting`), so that surface may change.
|
|
71
71
|
|
|
72
|
-
##
|
|
72
|
+
## Status
|
|
73
73
|
|
|
74
|
-
CLI
|
|
74
|
+
The CLI is complete and the real migration has been executed (F6); the stack is the only configuration source. Versions are published automatically to [npm](https://www.npmjs.com/package/jorgex-stack) according to the flow described in [Publishing](#publishing). The design, decisions (D1-D9), and roadmap are in [PRD.md](PRD.md).
|
|
75
75
|
|
|
76
|
-
##
|
|
76
|
+
## Publishing
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Releases are triggered by push/merge to `main` and GitHub Actions; there is also a recovery `workflow_dispatch` on `main` with an optional `release_sha`. `validate` resolves the target SHA once and exposes it as `target_sha`; `bump` reuses that SHA. If you do not pass `release_sha`, `validate` pins `target_sha` to `origin/main` after `fetch`; if you do pass it, it must be a full 40-hex SHA that belongs to `main` or the workflow fails red with recovery instructions. Running without `release_sha` is only valid to publish `origin/main` when the version does not exist on npm yet; if the version already exists and the tag is missing, the workflow fails and requires `workflow_dispatch` with `release_sha=<published sha>`. If the diff mixes publishable changes with `.github/workflows/*`, auto-release stops before bump/publish because GitHub may reject the tag push without workflow permissions; split the release or use manual publish/tag with elevated permissions. `pnpm publish` is not used and npm login is not required:
|
|
79
79
|
|
|
80
|
-
- **
|
|
81
|
-
- **
|
|
82
|
-
- **
|
|
83
|
-
- **
|
|
84
|
-
- **OIDC / trusted publishing**:
|
|
80
|
+
- **Automatic patch**: if the push to `main` contains publishable changes and the current `package.json` version already exists on npm, the workflow finds the first free patch (`x+1`, `x+2`, ...), commits `chore(release): bump version to v...`, and publishes. If tag `v<package.version>` already exists, it uses that point as the accumulated base; otherwise, it falls back to `github.event.before`. Obsolete runs are aborted after `git fetch origin main --tags` if `origin/main` no longer matches `GITHUB_SHA`.
|
|
81
|
+
- **Manual recovery**: a manual run on `main` with `release_sha` publishes that SHA if it does not exist on npm yet, without bumping again; if the version already exists on npm but tag `v<version>` is missing, the workflow fails and forces a rerun with `release_sha=<published sha>` to avoid tagging `origin/main`. `release_sha` must be a full 40-hex SHA and belong to `main`; mutable refs (`main`, tags, `main~1`) are rejected. If you do not pass `release_sha`, `validate` resolves `origin/main` once, exposes it as `target_sha`, and `bump` uses that validated SHA. Recovery does not bypass the `.github/workflows/*` guard: if the diff mixes workflows with publishable changes, split the release or perform the tag/publish manually with elevated permissions. If there is no reachable previous release tag to reconstruct the range, the workflow fails closed and requires manual intervention.
|
|
82
|
+
- **No release**: changes only in `work/`, `worktrees/`, tests, or files not listed as publishable (`src/`, `stack/`, `upstreams.json`, `package.json`, `pnpm-lock.yaml`, `tsconfig.json`, `tsup.config.ts`, `README.md`, `PRD.md`) do not create a release.
|
|
83
|
+
- **Manual minor and major**: explicit bump in `package.json` in the PR (the workflow detects that the next patch already exists on npm and requires the bump).
|
|
84
|
+
- **OIDC / trusted publishing**: the publishing job uses `id-token: write` and `setup-node` `registry-url`; the bump/push job only has `contents: write`; `tag-release` only writes `contents` and does not use OIDC. There is no `NPM_TOKEN` or `NODE_AUTH_TOKEN` in any secret. `tag-release` only runs if `publish` was `success` or `skipped` with `tag_needed=true`, and keeps its SHA validation as the final defense. The only exception to the "always pnpm" rule is `npm pack --dry-run --ignore-scripts` and `npm publish --ignore-scripts --provenance` in the final step, for registry compatibility and hardening.
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
Design details are in [PRD §7.6](PRD.md).
|
|
87
87
|
|
|
88
|
-
##
|
|
88
|
+
## Development
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
Requirements: Node >= 22.5 and pnpm (never npm). Goal Mode uses `node:sqlite` in tests/Node CLI and OpenCode uses `bun:sqlite` at runtime.
|
|
91
91
|
|
|
92
92
|
```
|
|
93
93
|
pnpm install
|
|
94
|
-
pnpm build # tsup
|
|
94
|
+
pnpm build # tsup -> dist/
|
|
95
95
|
pnpm typecheck
|
|
96
96
|
pnpm test # vitest
|
|
97
97
|
pnpm cli --help
|
package/dist/cli.js
CHANGED
|
@@ -1947,6 +1947,9 @@ function loadUpstreams() {
|
|
|
1947
1947
|
const file = path22.join(path22.dirname(stackRoot()), "upstreams.json");
|
|
1948
1948
|
return JSON.parse(fs16.readFileSync(file, "utf8"));
|
|
1949
1949
|
}
|
|
1950
|
+
function skillsToScan(maintainer, upstreams) {
|
|
1951
|
+
return maintainer ? Object.keys(upstreams.skills) : [];
|
|
1952
|
+
}
|
|
1950
1953
|
async function latestNpmVersion(pkg) {
|
|
1951
1954
|
try {
|
|
1952
1955
|
const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`, {
|
|
@@ -1985,37 +1988,45 @@ async function runUpdateCheck(localVersion) {
|
|
|
1985
1988
|
`engram: ${local} local, ${latest} disponible. Tu instalaci\xF3n NO se toca (D7) \u2014 actualiza t\xFA: github.com/${engramRepo}/releases`
|
|
1986
1989
|
);
|
|
1987
1990
|
}
|
|
1988
|
-
const
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
1991
|
+
const checkSkillNames = skillsToScan(isGitClone(), upstreams);
|
|
1992
|
+
if (checkSkillNames.length === 0) {
|
|
1993
|
+
p4.log.info(
|
|
1994
|
+
"Skills de terceros: pineadas con la versi\xF3n del stack (su revisi\xF3n upstream se hace desde el clon del repo)."
|
|
1995
|
+
);
|
|
1996
|
+
} else {
|
|
1997
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
1998
|
+
for (const name of checkSkillNames) {
|
|
1999
|
+
const info = upstreams.skills[name];
|
|
2000
|
+
const repo = info.source.replace(/^github:/, "");
|
|
2001
|
+
const entry = byRepo.get(repo) ?? { skills: [], pinned: info.commit };
|
|
2002
|
+
entry.skills.push(info.modified ? `${name} (modificada localmente)` : name);
|
|
2003
|
+
byRepo.set(repo, entry);
|
|
2004
|
+
}
|
|
2005
|
+
const heads = await Promise.all(
|
|
2006
|
+
[...byRepo.keys()].map(async (repo) => [repo, await latestGithubCommit(repo)])
|
|
2007
|
+
);
|
|
2008
|
+
let moved = 0;
|
|
2009
|
+
for (const [repo, head] of heads) {
|
|
2010
|
+
const { skills, pinned } = byRepo.get(repo);
|
|
2011
|
+
if (!pinned)
|
|
2012
|
+
p4.log.warn(
|
|
2013
|
+
`${repo}: sin pin en upstreams.json \u2014 a\xF1ade el commit revisado. Skills: ${skills.join(", ")}`
|
|
2014
|
+
);
|
|
2015
|
+
else if (head === null)
|
|
2016
|
+
p4.log.info(`${repo}: pin ${pinned.slice(0, 7)} (no se pudo consultar el upstream).`);
|
|
2017
|
+
else if (head === pinned)
|
|
2018
|
+
p4.log.success(`${repo}: al d\xEDa con el pin ${pinned.slice(0, 7)} (${skills.join(", ")}).`);
|
|
2019
|
+
else {
|
|
2020
|
+
moved++;
|
|
2021
|
+
p4.log.warn(
|
|
2022
|
+
`${repo}: el upstream se movi\xF3 (pin ${pinned.slice(0, 7)} \u2192 ${head.slice(0, 7)}). Skills: ${skills.join(", ")}.
|
|
2013
2023
|
Revisa el diff y, si lo aceptas, actualiza la copia vendorizada y el pin: github.com/${repo}/compare/${pinned.slice(0, 7)}...${head.slice(0, 7)}`
|
|
2014
|
-
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
if (moved === 0 && heads.every(([, head]) => head !== null)) {
|
|
2028
|
+
p4.log.info("Skills de terceros: ning\xFAn upstream se ha movido respecto a su pin.");
|
|
2015
2029
|
}
|
|
2016
|
-
}
|
|
2017
|
-
if (moved === 0 && heads.every(([, head]) => head !== null)) {
|
|
2018
|
-
p4.log.info("Skills de terceros: ning\xFAn upstream se ha movido respecto a su pin.");
|
|
2019
2030
|
}
|
|
2020
2031
|
if (githubRateLimited()) {
|
|
2021
2032
|
p4.log.warn(rateLimitHint("GitHub limit\xF3 algunas consultas (rate limit sin token)."));
|
|
@@ -2046,8 +2057,7 @@ function isEngramRunning() {
|
|
|
2046
2057
|
return null;
|
|
2047
2058
|
}
|
|
2048
2059
|
}
|
|
2049
|
-
function isGitClone() {
|
|
2050
|
-
const projectRoot = path22.dirname(stackRoot());
|
|
2060
|
+
function isGitClone(projectRoot = path22.dirname(stackRoot())) {
|
|
2051
2061
|
return fs16.existsSync(path22.join(projectRoot, ".git"));
|
|
2052
2062
|
}
|
|
2053
2063
|
var STACK_METHOD_CLONE = "git pull + pnpm install + pnpm build";
|
|
@@ -2325,8 +2335,10 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false) {
|
|
|
2325
2335
|
const upstreams = loadUpstreams();
|
|
2326
2336
|
let exitCode = 0;
|
|
2327
2337
|
let appliedUpdates = false;
|
|
2338
|
+
const maintainer = isGitClone();
|
|
2328
2339
|
const spin = p4.spinner();
|
|
2329
2340
|
spin.start("Consultando versiones upstream\u2026");
|
|
2341
|
+
const skillNames = skillsToScan(maintainer, upstreams);
|
|
2330
2342
|
const [npmLatest, engramLatestRaw, ...skillHeads] = await Promise.all([
|
|
2331
2343
|
latestNpmVersion("jorgex-stack"),
|
|
2332
2344
|
(async () => {
|
|
@@ -2334,7 +2346,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false) {
|
|
|
2334
2346
|
if (!repo) return null;
|
|
2335
2347
|
return { repo, version: await latestGithubRelease(repo) };
|
|
2336
2348
|
})(),
|
|
2337
|
-
...
|
|
2349
|
+
...skillNames.map(async (name) => {
|
|
2338
2350
|
const info = upstreams.skills[name];
|
|
2339
2351
|
const repo = info.source.replace(/^github:/, "");
|
|
2340
2352
|
const head = await latestGithubCommit(repo);
|
|
@@ -2354,7 +2366,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false) {
|
|
|
2354
2366
|
p4.log.success(`jorgex-stack: v${localVersion} \u2014 al d\xEDa.`);
|
|
2355
2367
|
} else {
|
|
2356
2368
|
stackNeedsUpdate = true;
|
|
2357
|
-
const mode =
|
|
2369
|
+
const mode = maintainer ? STACK_METHOD_CLONE : `pnpm add -g jorgex-stack@${npmLatest}`;
|
|
2358
2370
|
updateItems.push({
|
|
2359
2371
|
value: "stack",
|
|
2360
2372
|
label: `jorgex-stack: v${localVersion} \u2192 v${npmLatest}`,
|
|
@@ -2406,6 +2418,11 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false) {
|
|
|
2406
2418
|
p4.log.success(`${repo}: al d\xEDa (pin ${pinned.slice(0, 7)}). Skills: ${names.join(", ")}`);
|
|
2407
2419
|
}
|
|
2408
2420
|
}
|
|
2421
|
+
if (!maintainer) {
|
|
2422
|
+
p4.log.info(
|
|
2423
|
+
"Skills de terceros: pineadas con esta versi\xF3n del stack \u2014 su revisi\xF3n upstream se hace desde el clon del repo."
|
|
2424
|
+
);
|
|
2425
|
+
}
|
|
2409
2426
|
const skillUpdates = buildEligibleSkillUpdates(typedSkillHeads);
|
|
2410
2427
|
for (const skillInfo of skillUpdates) {
|
|
2411
2428
|
const modifiedWarning = skillInfo.modified ? " \u26A0 modificada localmente" : "";
|
package/package.json
CHANGED
|
@@ -174,7 +174,7 @@ function replaceGoalCommandPrompt(input: unknown, output: HookOutput, text: stri
|
|
|
174
174
|
|
|
175
175
|
if (Array.isArray(output.parts)) {
|
|
176
176
|
output.parts.splice(0, output.parts.length, {
|
|
177
|
-
id:
|
|
177
|
+
id: createOpenCodeID("prt"),
|
|
178
178
|
sessionID: extractHookSessionID(input, output),
|
|
179
179
|
messageID: extractHookMessageID(input, output),
|
|
180
180
|
type: "text",
|
|
@@ -226,7 +226,7 @@ function extractHookSessionID(input: unknown, output: HookOutput): string {
|
|
|
226
226
|
if (typeof sessionID === "string" && sessionID.trim()) return sessionID;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
return
|
|
229
|
+
return createOpenCodeID("ses");
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
function extractHookMessageID(input: unknown, output: HookOutput): string {
|
|
@@ -241,7 +241,7 @@ function extractHookMessageID(input: unknown, output: HookOutput): string {
|
|
|
241
241
|
if (typeof id === "string" && id.trim()) return id;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
return
|
|
244
|
+
return createOpenCodeID("msg");
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
function upsertMarkedBlock(text: string, block: string): string {
|
|
@@ -288,3 +288,9 @@ function readEventStateSequence(data: unknown): number | undefined {
|
|
|
288
288
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
289
289
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
290
290
|
}
|
|
291
|
+
|
|
292
|
+
// OpenCode validates entity IDs by prefix: parts must start with "prt", sessions with "ses", messages with "msg".
|
|
293
|
+
// The UUID is hex-only (dashes removed) to avoid confusion with uuid format.
|
|
294
|
+
function createOpenCodeID(prefix: "prt" | "ses" | "msg"): string {
|
|
295
|
+
return `${prefix}_${randomUUID().replace(/-/g, "")}`;
|
|
296
|
+
}
|
package/upstreams.json
CHANGED
|
@@ -23,8 +23,9 @@
|
|
|
23
23
|
},
|
|
24
24
|
"diagnose": {
|
|
25
25
|
"source": "github:mattpocock/skills",
|
|
26
|
-
"path": "skills/engineering/
|
|
27
|
-
"commit": "694fa30311e02c2639942308513555e61ee84a6f"
|
|
26
|
+
"path": "skills/engineering/diagnosing-bugs",
|
|
27
|
+
"commit": "694fa30311e02c2639942308513555e61ee84a6f",
|
|
28
|
+
"note": "upstream renombró la carpeta diagnose → diagnosing-bugs; mantenemos el nombre local 'diagnose'. El pin sigue en la última revisión aceptada hasta hacer diff y re-pin deliberado del contenido nuevo."
|
|
28
29
|
},
|
|
29
30
|
"find-skills": {
|
|
30
31
|
"source": "github:vercel-labs/skills",
|
package/PRD.md
DELETED
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
# PRD — JorgeX Stack
|
|
2
|
-
|
|
3
|
-
> Harness multi-agente portable: una sola fuente de configuración (agentes, skills, hooks, memoria Engram, MCPs, system prompt) instalable con un comando en **Claude Code**, **Codex CLI** y **OpenCode**.
|
|
4
|
-
|
|
5
|
-
**Estado**: v0 — documento vivo. Última actualización: 2026-06-09.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Problema
|
|
10
|
-
|
|
11
|
-
La config actual vive solo en `C:\Users\jorge\.config\opencode` y tiene estos problemas:
|
|
12
|
-
|
|
13
|
-
1. **Atada a OpenCode**: 15 agentes, 18 skills, plugins de Engram/hooks y el system prompt no sirven en Claude Code ni Codex sin porte manual.
|
|
14
|
-
2. **Dependencias frágiles**: `hooks.ts` y `photo-heart-worktree.ts` son re-exports a rutas locales (`file:///C:/Users/jorge/Desktop/jorgex-custom-tools/...`) que no existen en otra máquina.
|
|
15
|
-
3. **Sin gestión de terceros**: Engram (Gentleman-Programming) y varias skills open-source no tienen tracking de versión ni vía de actualización.
|
|
16
|
-
4. **Sin instalación reproducible**: montar este setup en otra máquina (o restaurarlo) es trabajo manual.
|
|
17
|
-
5. **Secretos en claro**: `opencode.json` tiene API keys hardcodeadas (Context7, Hostinger) — inaceptable en un repo versionado.
|
|
18
|
-
|
|
19
|
-
## 2. Visión
|
|
20
|
-
|
|
21
|
-
Lo mismo que hace [gentle-ai](https://github.com/Gentleman-Programming/gentle-ai) pero con el stack de Jorge: un repo único con la config canónica + un CLI que la **instala, sincroniza y actualiza** en los tres runtimes, adaptando formatos automáticamente.
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
pnpm dlx jorgex-stack → TUI: detecta agentes instalados, eliges uno/varios/los 3, instala
|
|
25
|
-
pnpm dlx jorgex-stack sync → re-aplica la config (idempotente)
|
|
26
|
-
pnpm dlx jorgex-stack update → actualiza stack + terceros (Engram, skills upstream)
|
|
27
|
-
pnpm dlx jorgex-stack doctor → verifica que todo está sano
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## 3. Decisiones tomadas (cerradas con Jorge, 2026-06-09/10)
|
|
31
|
-
|
|
32
|
-
| # | Decisión | Elección |
|
|
33
|
-
|---|----------|----------|
|
|
34
|
-
| D1 | Tecnología del instalador | **TypeScript + Node (≥22.5)**, bundle único (tsup/esbuild), prompts con `@clack/prompts`, publicado en el registry npm y ejecutado con `pnpm dlx jorgex-stack`. Sin Go, sin binarios propios. |
|
|
35
|
-
| D2 | Plataformas | **Cross-platform desde v1** (Windows + macOS + Linux). Windows es el entorno principal de pruebas. |
|
|
36
|
-
| D3 | Estrategia de despliegue | **Merge idempotente con marcadores** (`<!-- jorgex:seccion -->` en markdown, upsert quirúrgico en JSON/TOML). Backup automático antes de tocar nada + rollback. Nunca machaca contenido manual del usuario. |
|
|
37
|
-
| D4 | Plugins de jorgex-custom-tools | Se **copian** al nuevo repo ahora (los originales NO se tocan porque están en uso). **Cuando el proyecto esté completo e instalado**: se eliminan de `C:\Users\jorge\Desktop\jorgex-custom-tools` y pasan a vivir/instalarse SOLO desde JorgeX Stack. Ver §11 F6. |
|
|
38
|
-
| D5 | MCPs incluidos | Solo **engram** (local, sin key) y **context7** (placeholder vacío: cada usuario conecta su cuenta/key al instalar o después). Hostinger eliminado. **Ninguna key personal de la config actual de Jorge pasa a este proyecto, jamás.** |
|
|
39
|
-
| D6 | Selección de modelos | Por runtime, en el install: Claude Code ofrece solo modelos Claude (alias auto-actualizables: `fable`/`opus`/`sonnet`/`haiku`), Codex solo OpenAI, OpenCode **detecta y ofrece todos los que el usuario tenga conectados** (`opencode models`). Ver §6.1. |
|
|
40
|
-
| D7 | Engram existente | La instalación de Engram (binario + **base de datos de memorias en `~/.engram`**) es **intocable en TODOS los flujos**: `install`/`sync` solo detectan y registran (jamás reinstalan, migran ni escriben en la DB); `update` solo INFORMA de releases (actualizar el binario es acción del usuario); `uninstall` **conserva por defecto** todo lo de Engram (registro MCP, plugin engram.ts) — desregistrarlo exige el sí explícito (`--remove-engram` o confirmación interactiva con default No), y ni con eso se tocan binario o DB. Repo upstream: https://github.com/Gentleman-Programming/engram |
|
|
41
|
-
| D8 | Gestor de paquetes | **pnpm siempre, nunca npm** — desarrollo, scripts, instalación de dependencias y cualquier instalación que haga el CLI (`pnpm dlx`, `pnpm add -g`). |
|
|
42
|
-
| D9 | work/ vs Engram | **Una sola casa por artefacto, cero duplicación** (v2, 2026-06-11): `work/{nombre}/` (gitignorada, SOLO trabajo en curso) contiene `PRD.md` + `plan.md` — el plan es el único tablero de estado (edits quirúrgicos). Engram guarda lo que consumen los agentes y el historial: spec completa de cada tarea (`work/{nombre}/task/{NN}`, el subagente recibe topic_key + título), resultados de fase (`work/{nombre}/{fase}`), cierre (`work/{nombre}/done`) y el backlog del proyecto en la clave ÚNICA `work/backlog`. Al cerrar: PRD a `docs/` solo si tiene valor duradero y la carpeta se borra. Sin `1-TODOs/` ni `3-finalized/`. Ver §9.11. |
|
|
43
|
-
|
|
44
|
-
## 4. Objetivos
|
|
45
|
-
|
|
46
|
-
1. Un comando instala la config completa (o por componentes) en Claude Code, Codex y/u OpenCode, a elección.
|
|
47
|
-
2. Fuente canónica única: cada agente/skill/hook se define UNA vez; los adapters generan el formato de cada runtime.
|
|
48
|
-
3. Paridad funcional con el setup actual de OpenCode (no perder nada en la migración).
|
|
49
|
-
4. Engram funcionando en los tres runtimes (MCP + protocolo de memoria + captura pasiva donde sea posible).
|
|
50
|
-
5. Hooks funcionando en los tres (nativos en Claude Code y Codex; plugin puente en OpenCode).
|
|
51
|
-
6. `update` gestiona: el propio stack, el binario de Engram y las skills de terceros (con fuente y versión registradas).
|
|
52
|
-
7. Mejorar el harness actual, no solo portarlo (ver §9).
|
|
53
|
-
|
|
54
|
-
### No-objetivos (v1)
|
|
55
|
-
|
|
56
|
-
- Soportar más runtimes (Cursor, Gemini CLI, etc.) — la arquitectura adapter lo deja abierto para v2.
|
|
57
|
-
- TUI elaborada tipo Bubbletea — prompts simples de clack bastan.
|
|
58
|
-
- Self-update agresivo en cada invocación (decisión consciente contra el default de gentle-ai): `update --check` manual o aviso no bloqueante.
|
|
59
|
-
- Skill registry con cache por fingerprint (idea buena de gentle-ai → backlog v1.x).
|
|
60
|
-
- Instalación scope-proyecto (v1 solo global/usuario; proyecto en v2).
|
|
61
|
-
|
|
62
|
-
## 5. Arquitectura del repo
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
JorgeX Stack/
|
|
66
|
-
├── PRD.md
|
|
67
|
-
├── README.md
|
|
68
|
-
├── package.json # bin: jorgex-stack
|
|
69
|
-
├── stack/ # ══ FUENTE CANÓNICA (lo que se instala) ══
|
|
70
|
-
│ ├── system-prompt/
|
|
71
|
-
│ │ └── AGENTS.md # system prompt global (hoy: ~/.config/opencode/AGENTS.md, mejorado)
|
|
72
|
-
│ ├── agents/ # 15 agentes en formato canónico (md + frontmatter propio)
|
|
73
|
-
│ │ ├── orchestrator.md
|
|
74
|
-
│ │ ├── backend-analyst.md … type-design-analyzer.md
|
|
75
|
-
│ ├── skills/ # TODAS las skills vendorizadas (terceros con upstream registrado en upstreams.json)
|
|
76
|
-
│ ├── commands/ # xreview.md, lean-audit.md (formato canónico)
|
|
77
|
-
│ ├── hooks/
|
|
78
|
-
│ │ └── hooks.json # definición canónica de hooks (formato Claude Code como base)
|
|
79
|
-
│ ├── scripts/
|
|
80
|
-
│ │ └── post-pr-review.cjs
|
|
81
|
-
│ ├── mcp/
|
|
82
|
-
│ │ └── servers.json # manifiesto MCP canónico (env refs, SIN secretos)
|
|
83
|
-
│ └── plugins/
|
|
84
|
-
│ └── opencode/ # engram.ts, hooks-bridge.ts, worktree.ts (solo OpenCode)
|
|
85
|
-
├── upstreams.json # terceros: fuente, versión instalada, método de update
|
|
86
|
-
├── src/ # ══ CLI ══
|
|
87
|
-
│ ├── cli.ts # entrypoint: install | sync | update | doctor | uninstall | restore
|
|
88
|
-
│ ├── adapters/
|
|
89
|
-
│ │ ├── types.ts # interface Adapter (rutas + estrategias por runtime)
|
|
90
|
-
│ │ ├── claude-code.ts
|
|
91
|
-
│ │ ├── codex.ts
|
|
92
|
-
│ │ └── opencode.ts
|
|
93
|
-
│ ├── components/ # lógica por componente, agnóstica del runtime
|
|
94
|
-
│ │ ├── system-prompt.ts agents.ts skills.ts commands.ts
|
|
95
|
-
│ │ ├── hooks.ts mcp.ts engram.ts plugins.ts
|
|
96
|
-
│ ├── lib/
|
|
97
|
-
│ │ ├── filemerge.ts # merge por marcadores (md) + upsert JSON/JSONC/TOML
|
|
98
|
-
│ │ ├── backup.ts # snapshot tar.gz con retención + restore
|
|
99
|
-
│ │ └── detect.ts # qué runtimes hay instalados (binario en PATH + dir config)
|
|
100
|
-
│ └── …
|
|
101
|
-
└── tests/ # unit (filemerge, adapters) + paridad entre runtimes
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**Patrón central (de gentle-ai)**: `Adapter` por runtime declara *dónde* (rutas) y *cómo* (estrategias: merge de system prompt, formato de agentes, sintaxis MCP…). Los componentes iteran (componente × runtime) sin un solo `switch`. Añadir un runtime nuevo = un archivo adapter.
|
|
105
|
-
|
|
106
|
-
**Pipeline de instalación**: detect → selección → plan (dry-run visible) → **backup** → aplicar componentes → verificar → (si falla) rollback.
|
|
107
|
-
|
|
108
|
-
## 6. Mapeo por runtime
|
|
109
|
-
|
|
110
|
-
| Componente | Canónico | Claude Code | Codex CLI | OpenCode |
|
|
111
|
-
|---|---|---|---|---|
|
|
112
|
-
| System prompt | `stack/system-prompt/AGENTS.md` | sección con marcadores en `~/.claude/CLAUDE.md` | sección en `~/.codex/AGENTS.md` | sección en `~/.config/opencode/AGENTS.md` |
|
|
113
|
-
| Agentes (subagentes) | `stack/agents/*.md` | `~/.claude/agents/*.md` (frontmatter `name/description/tools/model`) | `~/.codex/agents/*.toml` (`developer_instructions`, `model_reasoning_effort`, `sandbox_mode`) | `~/.config/opencode/agents/*.md` (`mode/model/tools/permission`) |
|
|
114
|
-
| **Orchestrator (primary)** | `stack/agents/orchestrator.md` | **Output style** `~/.claude/output-styles/orchestrator.md` (modifica el system prompt del MAIN agent; se elige con `/config` y persiste) + **skill** `~/.claude/skills/orchestrator/` como activación puntual (`/orchestrator` explícito o carga implícita por description) | **Profile** `~/.codex/orchestrator.config.toml` con `developer_instructions` → `codex --profile orchestrator` + **la misma skill** en `~/.agents/skills/orchestrator/` (los commands de Codex están deprecados; skills es la vía oficial) | **Primary agent** nativo: en el ciclo de Tab junto a build/plan |
|
|
115
|
-
| Skills | `stack/skills/` + upstreams | copia espejo en `~/.claude/skills/` (verificado 2026-06: Claude Code NO lee `~/.agents/skills` — sigue agentskills.io solo en formato) | **`~/.agents/skills/`** (estándar agentskills.io — NO `~/.codex/skills`) | **misma copia que Codex**: lee `~/.agents/skills/` global nativo (verificado en código fuente; si una skill existe también en `~/.config/opencode/skills/` esa gana — F6 limpia las legacy de ahí) |
|
|
116
|
-
| Commands | `stack/commands/*.md` | `~/.claude/commands/*.md` | como skills (`~/.codex/prompts/` está deprecated) | `~/.config/opencode/commands/*.md` |
|
|
117
|
-
| Hooks | `stack/hooks/hooks.json` | merge en `~/.claude/settings.json` → clave `hooks` | `~/.codex/hooks.json` (⚠ requiere trust manual vía `/hooks`) | **plugin puente** `hooks-bridge.ts` (OpenCode no tiene hooks declarativos) |
|
|
118
|
-
| MCP | `stack/mcp/servers.json` | `claude mcp add --scope user` o merge en `~/.claude.json` | bloques `[mcp_servers.x]` upsert en `~/.codex/config.toml` | clave `mcp` upsert en `opencode.json` (`command` es **array**, `environment` no `env`) |
|
|
119
|
-
| Engram | binario Go + MCP + protocolo | MCP user-scope + protocolo en sección de CLAUDE.md | MCP en config.toml + protocolo en AGENTS.md | MCP + plugin `engram.ts` completo (captura pasiva, compaction, inyección) |
|
|
120
|
-
| Plugins TS | `stack/plugins/opencode/` | n/a (funcionalidad cubierta por hooks nativos) | n/a (ídem) | `~/.config/opencode/plugins/` |
|
|
121
|
-
|
|
122
|
-
**Notas de skills**: una sola copia física en `~/.agents/skills/` sirve a Codex y OpenCode; para Claude Code el instalador mantiene copia espejo en `~/.claude/skills/` (sin symlinks: en Windows requieren Developer Mode). `sync` mantiene ambas alineadas. Frontmatter común seguro: `name` + `description` (extensiones de Claude como `context: fork` solo en la copia de Claude).
|
|
123
|
-
|
|
124
|
-
### 6.1 Modelos: tiers canónicos + picker por runtime
|
|
125
|
-
|
|
126
|
-
La config actual referencia modelos vía OpenCode multi-provider (`openai/gpt-5.4`, `minimax/MiniMax-M3`). Eso no es portable. El formato canónico asigna a cada agente un **tier** (`strong | standard | cheap`) y el install resuelve cada tier a un modelo concreto **por runtime, con un picker**:
|
|
127
|
-
|
|
128
|
-
| Runtime | Qué ofrece el picker | ¿Se actualiza solo? |
|
|
129
|
-
|---|---|---|
|
|
130
|
-
| Claude Code | Solo modelos Claude: alias `fable` / `opus` / `sonnet` / `haiku` / `inherit` (+ ID concreto opcional). `fable` es el nivel nuevo por encima de opus (Fable 5, `claude-fable-5`, 2026) | **Sí** — los alias apuntan siempre al modelo más reciente de cada familia; cuando Anthropic añade una familia nueva (como fable) basta re-ejecutar `jorgex-stack models` |
|
|
131
|
-
| Codex | Solo OpenAI: `default` (omitir `model` → usa el default vigente del CLI) o ID concreto + `model_reasoning_effort` (high/medium/low) por tier | **Solo si usas `default`** — el CLI lo actualiza con sus releases. Un ID fijado es manual: se cambia re-ejecutando el picker (`jorgex-stack models`) |
|
|
132
|
-
| OpenCode | **Todos los modelos que el usuario tenga conectados**, detectados en vivo con `opencode models` (registry models.dev, verificado en la máquina de Jorge) | **Sí** — la lista refleja providers/modelos conectados en el momento de instalar; nuevos modelos aparecen al re-ejecutar el picker |
|
|
133
|
-
|
|
134
|
-
- Defaults sensatos pre-seleccionados por tier (strong → análisis/review/seguridad/orchestrator; standard → implementer/tester; cheap → translator/docs/comments/engram), confirmables con Enter.
|
|
135
|
-
- La elección se guarda en `model-map.json` (local del usuario, no en el repo) y `sync` la respeta.
|
|
136
|
-
- Comando dedicado `jorgex-stack models` para re-escoger sin reinstalar.
|
|
137
|
-
|
|
138
|
-
**Regla del orchestrator (cerrada con Jorge, 2026-06-10)**: el orchestrator es SIEMPRE un modo del agente principal que el usuario pilota — **nunca un subagente que se invoca**. OpenCode lo soporta nativo (primary + Tab). En Claude Code y Codex, que no tienen primary seleccionable, se instalan dos vías generadas de la misma fuente canónica: (a) el **modo persistente** — output style en Claude Code (`/config`), profile en Codex (`codex --profile orchestrator`, `developer_instructions`); y (b) la **skill `orchestrator`** para activación puntual dentro de una sesión — elegida frente al command porque una misma SKILL.md sirve en ambos runtimes (estándar agentskills.io), permite invocación explícita (`/orchestrator` · `$orchestrator`) e implícita por description, y los custom prompts de Codex están deprecados. Verificado contra docs y código (openai/codex): no hay modos custom seleccionables en caliente en ninguno de los dos; si los añaden, se migra a eso.
|
|
139
|
-
|
|
140
|
-
## 7. Componentes en detalle
|
|
141
|
-
|
|
142
|
-
### 7.1 Hooks — la pieza con más fricción
|
|
143
|
-
|
|
144
|
-
- **Formato canónico**: el de Claude Code (`hooks.json` con eventos `SessionStart`, `PreToolUse`, `PostToolUse`, `Stop`…). Codex usa un formato casi idéntico (mismos eventos núcleo, añade `commandWindows` para Windows — lo usamos).
|
|
145
|
-
- **Claude Code**: merge en `settings.json`. Nativo.
|
|
146
|
-
- **Codex**: escribir `~/.codex/hooks.json`. ⚠ Los hooks no-managed exigen aprobación manual con `/hooks` — el instalador no puede activarlos solo. `doctor` lo detecta y lo recuerda.
|
|
147
|
-
- **OpenCode**: no hay hooks declarativos → `hooks-bridge.ts` (evolución del `hooks.ts` actual): plugin que lee el `hooks.json` canónico y traduce eventos (`PostToolUse` + matcher bash → `tool.execute.after`, `Stop` → `session.idle`, `SessionStart` → init del plugin).
|
|
148
|
-
- **Hook actual a portar**: post-`gh pr create` → ejecuta `post-pr-review.cjs` (routing ligero de subagentes de review sobre `git diff BASE...HEAD`). Debe funcionar igual en los tres.
|
|
149
|
-
|
|
150
|
-
### 7.2 Engram
|
|
151
|
-
|
|
152
|
-
- Binario Go de Gentleman-Programming ([repo](https://github.com/Gentleman-Programming/engram)). Sirve CLI + MCP server (`engram mcp --tools=agent`). **El binario y los datos van separados**: el binario donde lo instale el método elegido (brew · `go install` → `~/go/bin` · zip de Releases) y los DATOS siempre en `~/.engram/engram.db` (override: `ENGRAM_DATA_DIR`) — esa carpeta es la que D7 protege.
|
|
153
|
-
- **Integración oficial por runtime (verificado 2026-06)**: Engram trae `engram setup <agent>` (claude-code, codex, opencode…) y un plugin de marketplace oficial SOLO para Claude Code (`claude plugin marketplace add Gentleman-Programming/engram` + `claude plugin install engram`: MCP + hooks de sesión + skill memory). En OpenCode, `engram setup opencode` escribe el plugin `engram.ts` (el que este stack vendoriza) + MCP en opencode.json; en Codex escribe `[mcp_servers.engram]` + `engram-instructions.md` + compact prompt. No publica marketplace para Codex.
|
|
154
|
-
- **Política del stack**: detectar la integración oficial y respetarla — si existe, NO se registra el MCP (duplicaría las tools `mem_*`) y NO se inyecta la sección `engram-protocol` en el system prompt (el plugin/setup ya inyecta el protocolo). En OpenCode la sección no se inyecta nunca: el plugin `engram.ts` que el propio stack instala la aporta en runtime (consolidación de la duplicación detectada en F1). Donde no haya integración, el stack registra el MCP básico + la sección de protocolo, y `doctor`/install sugieren `engram setup <agent>` para la integración completa.
|
|
155
|
-
- **Regla D7 — instalación existente intocable**: si el instalador detecta un Engram ya instalado (binario en PATH o ruta conocida, p.ej. `C:\Users\jorge\go\bin\engram.exe`, y/o base de datos existente), lo usa tal cual: registra el MCP apuntando al binario detectado y NO descarga, NO reinstala, NO migra y NO toca la DB (que en el caso de Jorge está llena de memorias en uso). Solo si NO hay Engram en la máquina: descarga release de GitHub con **SHA256 fail-closed**.
|
|
156
|
-
- En todos los casos: registra MCP en los runtimes elegidos → inyecta el protocolo de memoria (sección marcada) en el system prompt de cada uno.
|
|
157
|
-
- **Una sola fuente del protocolo**: hoy está duplicado (AGENTS.md + inyección del plugin engram.ts). Se consolida: el texto vive en `stack/system-prompt/` y se inyecta una vez por runtime. En OpenCode el plugin deja de inyectar el bloque largo (o se hace la única vía, pero no ambas).
|
|
158
|
-
- En OpenCode se conserva el plugin completo (captura pasiva, session resilience, compaction handling) — es la integración más rica y se mantiene.
|
|
159
|
-
- `update` trata Engram como tool gestionada (release de GitHub, comparación de versión), pero **solo actualiza el binario con confirmación explícita** y nunca toca la base de datos.
|
|
160
|
-
|
|
161
|
-
### 7.3 Update: política y flujo
|
|
162
|
-
|
|
163
|
-
`upstreams.json` registra cada pieza de terceros (ejemplo de formato):
|
|
164
|
-
|
|
165
|
-
```json
|
|
166
|
-
{
|
|
167
|
-
"tools": {
|
|
168
|
-
"engram": { "kind": "binary", "source": "github:Gentleman-Programming/engram", "verify": "sha256", "policy": "respect-existing — D7" }
|
|
169
|
-
},
|
|
170
|
-
"skills": {
|
|
171
|
-
"skill-name": { "source": "github:org/repo", "commit": "...", "modified": false }
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
**Auditoría (F1, 2026-06-10)**: las 18 skills están vendorizadas en `stack/skills/` con upstream registrado. Solo `agent-delegation`, `work-lifecycle` y `lean-code` son propias; `tdd`, `to-prd`, `to-issues` y `diagnose` de **mattpocock/skills** tienen modificaciones locales (`modified: true`). Resto: anthropics/skills, supabase/agent-skills, vercel(-labs), kepano/obsidian-skills, millionco/react-doctor, safishamsi/graphify.
|
|
177
|
-
|
|
178
|
-
**Política de `update` (F5.x — implementada con flujo interactivo)**:
|
|
179
|
-
|
|
180
|
-
1. **`update --check`** (sin TTY o con `--yes`): compara versión local vs upstream (tags/commits de GitHub) y **solo lista** qué hay nuevo. Respeta D7 y D8: no toca nada sin confirmación explícita.
|
|
181
|
-
|
|
182
|
-
2. **`update` interactivo** (TTY + sin `--yes`):
|
|
183
|
-
- **Escanea 3 fuentes en paralelo**: stack (npm), Engram (GitHub releases), skills (commit pins en upstreams.json por repo único).
|
|
184
|
-
- **Multiselect**: ofrece marcar lo actualizable (stack, Engram, skills por repo con upstream movido).
|
|
185
|
-
- **Stack**: detecta clon git o instalación global; ofrece `git pull + pnpm install + pnpm build` o `pnpm add -g jorgex-stack@latest` con confirmación.
|
|
186
|
-
- **Engram** (D7 reforzado):
|
|
187
|
-
* Detecta si el proceso está en ejecución (bloquea en Windows) y advierte.
|
|
188
|
-
* Ofrece **backup de la DB** (`~/.engram/engram.db`) a `~/.jorgex-stack/` ANTES de actualizar el binario.
|
|
189
|
-
* Usa **canal nativo** replicado: brew → `go install` → URL de releases.
|
|
190
|
-
* La DB y las memorias **jamás** se tocan; solo el binario se puede actualizar con confirmación explícita.
|
|
191
|
-
- **Skills**:
|
|
192
|
-
* Descarga upstream a temporal y **muestra diff SIEMPRE** (obligatorio antes de aplicar).
|
|
193
|
-
* Las skills `modified: true` alertan y exigen doble confirmación (los cambios locales se sobreescriben con backup automático).
|
|
194
|
-
* Reemplaza la copia vendorizada y re-pined el commit en upstreams.json.
|
|
195
|
-
- **Backup automático** de `~/.jorgex-stack/manifest.json` antes de cualquier cambio.
|
|
196
|
-
- **Verificación post-update**: detecta engram actualizado y reporta la nueva versión.
|
|
197
|
-
|
|
198
|
-
3. **Con `--yes` o sin TTY**: se comporta como `--check` (solo informe).
|
|
199
|
-
|
|
200
|
-
### 7.4 MCPs
|
|
201
|
-
|
|
202
|
-
Solo dos MCPs en el stack (D5):
|
|
203
|
-
|
|
204
|
-
1. **engram** — local, apunta al binario detectado. No necesita key.
|
|
205
|
-
2. **context7** — remoto, se instala **vacío** (sin key). Cada usuario conecta su cuenta: el instalador ofrece introducir la key opcionalmente (se escribe SOLO en la config local del runtime) o dejarlo en blanco y configurarla después. Hostinger queda fuera.
|
|
206
|
-
|
|
207
|
-
**Regla dura**: el manifiesto canónico (`stack/mcp/servers.json`) solo contiene referencias de entorno/placeholders. Ninguna key personal de la config actual de Jorge (`opencode.json`) se copia a este repo, al instalador ni a sus artefactos — ni siquiera en ejemplos, tests o fixtures. CI check de secretos en F5.
|
|
208
|
-
|
|
209
|
-
### 7.5 Scripts y plugins de jorgex-custom-tools (D4)
|
|
210
|
-
|
|
211
|
-
- `hooks.ts` (HooksPlugin) y `worktree-plugin.ts` (WorktreePlugin) de `C:\Users\jorge\Desktop\jorgex-custom-tools\plugins\hooks\src\` se **copian** a `stack/plugins/opencode/` y se adaptan (rutas relativas, sin `file:///C:/Users/jorge/...`).
|
|
212
|
-
- Los originales **no se tocan** mientras dure el desarrollo (están en uso).
|
|
213
|
-
- F6 (cierre): eliminar de jorgex-custom-tools, reinstalar todo desde JorgeX Stack.
|
|
214
|
-
|
|
215
|
-
### 7.6 Publicación automática en npm
|
|
216
|
-
|
|
217
|
-
El paquete `jorgex-stack` se publica solo, sin acción del usuario. El workflow `.github/workflows/publish.yml` corre en cada push a `main` y aplica esta política:
|
|
218
|
-
|
|
219
|
-
1. **Detección de cambios publicables**: se compara `HEAD` contra `v<package.version>` si ese tag existe; si no, cae a `github.event.before`. Son publicables los de `src/`, `stack/` y la lista exacta (`upstreams.json`, `package.json`, `pnpm-lock.yaml`, `tsconfig.json`, `tsup.config.ts`, `README.md`, `PRD.md`). **No** son publicables los cambios solo en `work/`, `worktrees/`, tests, ni en archivos fuera de la lista. La política está modelada y testeada en `src/lib/release.ts` (`classifyReleasePaths`); el workflow la replica inline para no ejecutar código generado por el repo en el job privilegiado.
|
|
220
|
-
2. **Patch automático + recuperación manual**: si hay cambios publicables y la versión de `package.json` ya existe en npm, el workflow busca el primer patch libre (`x+1`, `x+2`, …) con `bumpPatch` (`src/lib/release.ts`), commitea `chore(release): bump version to v…` con el actor `github-actions[bot]` y publica la nueva versión. `validate` resuelve una sola vez la SHA objetivo y la expone como `target_sha`; `bump` la reutiliza y solo falla verde en `stale_run` cuando una run de push quedó vieja tras `git fetch origin main --tags` y `origin/main` ya no coincide con la SHA validada. Un `workflow_dispatch` sobre `main` recupera una publicación fallida: si no se pasa `release_sha`, `validate` fija `target_sha` a `origin/main` tras el fetch; si se pasa, debe ser una SHA completa de 40 hex perteneciente a `main` y se valida su `package.json.version` antes de publicar o tagear. Si la `release_sha` no es válida o no pertenece a `main`, el job falla en rojo con mensaje accionable; `workflow_dispatch` nunca emite `skip_reason=stale_run`. Si el diff mezcla cambios publicables con `.github/workflows/*`, el auto-release se aborta antes de bump/publish porque GitHub puede rechazar el push del tag sin permisos para workflows; hay que separar la release o usar una publicación/tag manual con permisos elevados. Si no hay un tag de release previo alcanzable para reconstruir el rango de recovery, el workflow falla cerrado y exige intervención manual. Si no hay cambios publicables en un push normal: no hace nada.
|
|
221
|
-
3. **Minor y major manuales**: cuando el siguiente patch ya existe en npm (p.ej. el workflow detectó que `1.0.3` está ocupado), falla con mensaje claro y exige bump manual de `package.json` en un PR. Minor y major siguen siendo decisiones humanas.
|
|
222
|
-
4. **Guarda anti-loop**: `isReleaseBumpCommit` reconoce solo `chore(release):`, semver puro (`1.0.3`, `v1.0.3`) y commits de actores que terminan en `[bot]` y mencionan release/publish/bump/version. `release:` genérico y `chore:` a secas no cuentan. Cuando detecta uno, no vuelve a bumpear ni a publicar.
|
|
223
|
-
5. **OIDC / trusted publishing**: el job de bump/push usa solo `contents: write`; el job de publish usa `permissions: id-token: write` + `contents: read` y `setup-node` con `registry-url: https://registry.npmjs.org`; el `tag-release` solo usa `contents: write` y no necesita OIDC. **No** se usan `NPM_TOKEN` ni `NODE_AUTH_TOKEN` — los únicos secretos del repo son los de GitHub. La excepción a la regla D8 ("pnpm siempre") son `npm pack --dry-run --ignore-scripts` y el `npm publish --ignore-scripts --provenance` final: el cliente npm permite fijar `--ignore-scripts` y publicar con OIDC/provenance contra el registry oficial.
|
|
224
|
-
6. **Versión del CLI sincronizada**: `src/cli.ts --version` lee `package.json` directamente (`readPackageMetadata` en `src/lib/release.ts`); no hay constante `VERSION` hardcodeada que pueda quedar desincronizada.
|
|
225
|
-
|
|
226
|
-
Los detalles de política (criterios de publicabilidad, lista exacta, semántica del bump commit) se prueban en `src/lib/release.ts`; el YAML mantiene una copia inline mínima por seguridad, separando bump/push (`contents: write`) de publish (`id-token: write` + `contents: read`).
|
|
227
|
-
|
|
228
|
-
## 8. CLI — UX
|
|
229
|
-
|
|
230
|
-
```
|
|
231
|
-
pnpm dlx jorgex-stack # = install interactivo
|
|
232
|
-
✔ Detectados: OpenCode ✓ Claude Code ✓ Codex ✗ (no instalado)
|
|
233
|
-
✔ Engram existente detectado: C:\Users\jorge\go\bin\engram.exe → se respeta (D7)
|
|
234
|
-
? ¿Para qué agentes instalar? [multiselect: los detectados]
|
|
235
|
-
? Componentes: [todos | agentes, skills, hooks, engram, mcp, system-prompt…]
|
|
236
|
-
? Modelos por tier (picker por runtime, §6.1):
|
|
237
|
-
Claude Code → opus / sonnet / haiku (alias)
|
|
238
|
-
Codex → default / ID + reasoning effort
|
|
239
|
-
OpenCode → lista en vivo de `opencode models`
|
|
240
|
-
? Context7: ¿key? [input / dejar vacío y conectar después]
|
|
241
|
-
→ Plan (dry-run) → confirmación → backup → instalación → verificación
|
|
242
|
-
|
|
243
|
-
jorgex-stack install --agents claude,opencode --components all --dry-run --yes
|
|
244
|
-
jorgex-stack sync # re-aplica config (idempotente, tras editar el repo)
|
|
245
|
-
jorgex-stack models # re-escoger modelos por tier sin reinstalar
|
|
246
|
-
jorgex-stack update [--check] # stack + engram (solo binario, con confirmación) + skills upstream
|
|
247
|
-
jorgex-stack doctor # binarios, MCPs responden, hooks trusted (codex), engram serve vivo, versiones
|
|
248
|
-
jorgex-stack restore [--list] # restaurar backup
|
|
249
|
-
jorgex-stack uninstall [--agents ...] # quita solo lo nuestro (secciones marcadas + archivos propios)
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
Todo comando soporta `--dry-run` y no-interactivo (`--yes` + flags) para CI/scripts.
|
|
253
|
-
|
|
254
|
-
## 9. Mejoras al harness (no solo portar)
|
|
255
|
-
|
|
256
|
-
Detectadas en la auditoría de la config actual + ideas de gentle-ai:
|
|
257
|
-
|
|
258
|
-
1. **Orchestrator — handoff explícito**: documentar la secuencia ANALYZE → PLAN → IMPLEMENT (hoy el paso analyst → implementer queda implícito) y que el orchestrator DEBE procesar las líneas de delegación `→ [agente]: …` que devuelven los subagentes.
|
|
259
|
-
2. **Escape valve medible**: sustituir el "if the work turns out to be single scope" por criterios concretos (ej.: <3 archivos, 1 capa, sin cambio de contrato público → se permite saltar PRD).
|
|
260
|
-
3. **Deslindar solapamientos**: `code-reviewer` (bugs + guidelines) vs `code-simplifier` (claridad/estructura); renombrar o re-describir `test-analyzer` → deja claro que NUNCA escribe tests (eso es `tester`).
|
|
261
|
-
4. **Result contract en subagentes** (de gentle-ai): todo subagente termina con `status / summary / artifacts / delegations / risks` + `mem_save` con `topic_key` estable antes de reportar → las cadenas largas sobreviven a cortes de sesión.
|
|
262
|
-
5. **Engram visible cuando falla**: el plugin hoy falla en silencio si `engram serve` no corre. Mínimo: warning una vez por sesión.
|
|
263
|
-
6. **Protocolo de memoria sin duplicar** (ver §7.2).
|
|
264
|
-
7. **Tiers de modelo** en lugar de modelos hardcodeados (§6).
|
|
265
|
-
8. **Secretos fuera de la config** (§7.4).
|
|
266
|
-
9. **Backups con retención** en lugar de los `opencode.json.bak-*` manuales acumulados.
|
|
267
|
-
10. **Limpieza**: `rules/` y `prompts/` vacíos, backups sueltos y archivos de estado no se migran.
|
|
268
|
-
11. **Una sola casa por artefacto: `work/{nombre}/` para lo que revisa el humano, Engram para lo que consumen los agentes (D9 v2)**. Referencia gentle-ai: su default es memory-first puro (topic_keys `sdd/{cambio}/{artefacto}`), con un modo `openspec` de archivos para equipos y un `hybrid` que escribe en ambos (~2x tokens — descartado). El stack toma la partición sin duplicar: **(a)** `work/{nombre}/` (gitignorada — es andamiaje, no producto — y solo existe mientras el trabajo está EN CURSO) con `PRD.md` (lo escribe `to-prd`) y `plan.md` (objetivo, enfoque y tabla de tareas con título + descripción de una línea + estado/wave/deps); el estado vive SOLO en esa tabla y se actualiza con edits quirúrgicos, sin releer el plan tras cada tarea; **(b)** la spec completa de cada tarea atómica → Engram (`work/{nombre}/task/{NN}`): el subagente recibe topic_key + título — prompt fino y visible desde cualquier worktree (un worktree no ve archivos gitignorados del checkout principal); resultados de fase y decisiones → `work/{nombre}/{fase}`; cierre → `work/{nombre}/done`; **(c)** backlog del proyecto en la clave ÚNICA `work/backlog` (una lista upsertada, nunca una clave por idea) o issues (`to-issues`) si el proyecto usa tracker; **(d)** al cerrar, el PRD pasa a `docs/` solo si tiene valor duradero y `work/{nombre}/` se borra — sin `1-TODOs/` ni `3-finalized/`; el historial es memoria + git. La skill `work-lifecycle` es la fuente única del flujo (templates incluidos); `to-prd` escribe el PRD en `work/{nombre}/PRD.md`. Complemento opcional: **vista HTML de revisión bajo demanda** — al presentar PRD o plan, el orquestador la ofrece; si el humano acepta, se genera un render desechable en `work/{nombre}/` (`*.review.html`); los cambios pedidos se aplican SIEMPRE al markdown (única fuente, el HTML se regenera de él) y el HTML se borra al aprobar, antes de ejecutar. Los subagentes nunca lo leen.
|
|
269
|
-
12. **Loop autónomo del orquestador**: el humano decide hasta el plan (idea, PRD y plan se iteran con él); aprobado el plan, EXECUTE → VERIFY → SHIP corren sin intervención dentro de un **worktree** (rama = nombre canónico, el checkout principal no se toca) — **commit por tarea o grupo acotado** (el historial de la rama mapea al plan, nunca un commit gigante), verificación por secciones acotadas (por wave, no por micro-cambio), y al terminar: push + `gh pr create` automáticos. Regla general en AGENTS.md §Git: nunca push directo a ramas de producción; push de rama de trabajo/worktree y creación de PR no piden permiso. El hook post-PR lanza la review de los 7 subagentes; el orquestador procesa el informe por niveles: Critical → se aplican sí o sí, Important → a criterio, Suggestions → solo triviales; lo NO aplicado se documenta en `work/backlog` (una línea: qué + por qué se difiere) y lo aplicado entra como nuevas tasks (plan.md + Engram), se ejecuta y se re-verifica. CLOSE devuelve el control: informe al usuario + recomendación de test manual cuando aplica. **El merge del PR jamás es automático** — siempre orden explícita del usuario; tras el merge se cierra (done, borrar carpeta, retirar worktree). Loop interno blindado (loop engineering): VERIFY valida y marca los **Success criteria** del plan (tests verdes no bastan) y regla **anti-thrashing** — máx. 3 intentos por tarea/criterio fallido; al tercero se documenta el bloqueo bajo el topic_key del trabajo y se re-planifica con otro enfoque o se reporta el bloqueo (única interrupción legítima de la autonomía; reintentar a ciegas jamás).
|
|
270
|
-
|
|
271
|
-
## 10. Seguridad
|
|
272
|
-
|
|
273
|
-
- Descargas (Engram, skills) con checksum SHA256 fail-closed; instalación de paquetes siempre con pnpm y versiones pinneadas.
|
|
274
|
-
- El repo nunca contiene secretos (CI check simple con patrón regex en F5). Por D5, ninguna key de la config actual de Jorge entra en el proyecto en ningún formato.
|
|
275
|
-
- ⚠ **Recomendación aparte del proyecto**: las keys de Context7 y Hostinger llevan tiempo en claro en `opencode.json` — conviene rotarlas aunque aquí no se usen.
|
|
276
|
-
- `uninstall` y `restore` siempre disponibles; ningún paso destructivo sin backup previo.
|
|
277
|
-
|
|
278
|
-
## 11. Roadmap
|
|
279
|
-
|
|
280
|
-
| Fase | Contenido | Done cuando |
|
|
281
|
-
|---|---|---|
|
|
282
|
-
| **F0** | Scaffold del repo + este PRD + git init | PRD aprobado |
|
|
283
|
-
| **F1** | Fuente canónica: migrar y MEJORAR (§9) agentes, AGENTS.md, hooks, scripts, commands, manifiesto MCP; auditar skills propias vs terceros → `upstreams.json`; copiar plugins de jorgex-custom-tools | `stack/` completo, sin secretos, revisado por Jorge |
|
|
284
|
-
| **F2** | CLI core: detect, backup/restore, filemerge (md/JSON/TOML), pipeline + **adapter OpenCode** | install en OpenCode reproduce la config actual (paridad verificada) |
|
|
285
|
-
| **F3** | **Adapter Claude Code** (agentes md, skills espejo, hooks en settings.json, MCP user scope, CLAUDE.md) | install funcional en Claude Code real |
|
|
286
|
-
| **F4** | **Adapter Codex** (agentes TOML, skills en ~/.agents, hooks.json + aviso trust, MCP TOML, AGENTS.md) | install funcional en Codex real |
|
|
287
|
-
| **F5** | `update` (stack + engram + upstreams), `doctor`, `uninstall`, tests de paridad, publicación en el registry npm | `pnpm dlx jorgex-stack` funciona en máquina limpia |
|
|
288
|
-
| **F6** | **Migración final**: eliminar plugins de jorgex-custom-tools, desinstalar config legacy de `~/.config/opencode`, reinstalar TODO desde JorgeX Stack | El stack es la única fuente; los re-exports `file:///` han desaparecido |
|
|
289
|
-
|
|
290
|
-
Backlog v1.x: skill registry cacheado, scope proyecto, más runtimes (Cursor/Gemini), diff de 3 vías en updates de skills.
|
|
291
|
-
|
|
292
|
-
## 12. Riesgos
|
|
293
|
-
|
|
294
|
-
| Riesgo | Mitigación |
|
|
295
|
-
|---|---|
|
|
296
|
-
| Los formatos de los runtimes cambian (Codex evoluciona rápido) | Adapters aislados; tests de instalación; docs de cada formato enlazadas en el código |
|
|
297
|
-
| Hooks de Codex requieren trust manual | `doctor` lo verifica y da la instrucción exacta; documentado en README |
|
|
298
|
-
| Capacidades desiguales (OpenCode sin hooks nativos; captura pasiva de Engram solo en OpenCode) | Tabla de paridad documentada; el puente cubre lo crítico; lo no portable se declara, no se simula |
|
|
299
|
-
| Symlinks/permisos en Windows | No usamos symlinks: copias gestionadas por `sync` |
|
|
300
|
-
| Romper la config en uso durante el desarrollo | Backups automáticos + los originales de jorgex-custom-tools intactos hasta F6 |
|
|
301
|
-
|
|
302
|
-
## 13. Criterios de aceptación (v1)
|
|
303
|
-
|
|
304
|
-
1. En una máquina limpia con los 3 CLIs instalados: `pnpm dlx jorgex-stack install --agents claude,codex,opencode --yes` deja los 3 funcionando con agentes, skills, hooks, Engram y MCPs.
|
|
305
|
-
1b. En la máquina de Jorge: el install detecta su Engram existente, lo respeta (binario y DB intactos) y ninguna key personal aparece en el repo ni en los artefactos generados.
|
|
306
|
-
2. Re-ejecutar `sync` dos veces seguidas produce cero cambios (idempotencia byte a byte en lo gestionado).
|
|
307
|
-
3. Una edición manual del usuario fuera de las secciones marcadas sobrevive a `sync` y `update`.
|
|
308
|
-
4. `update --check` detecta una release nueva de Engram y una skill de terceros desactualizada.
|
|
309
|
-
5. `doctor` detecta: runtime ausente, hook sin trust en Codex, Engram caído, secreto faltante.
|
|
310
|
-
6. `uninstall` + `restore` devuelven cada runtime a su estado previo.
|