0xray 4.0.19 → 4.0.21
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/.grok-plugin/plugin.json +1 -1
- package/.opencode/skills/orchestrator/SKILL.md +1 -1
- package/.xray/features.json +1 -1
- package/AGENTS-consumer.md +4 -4
- package/AGENTS.md +4 -4
- package/README.md +14 -14
- package/SKILLS.md +3 -3
- package/dist/AGENTS.md +4 -4
- package/dist/CHANGELOG.md +27 -6
- package/dist/README.md +14 -14
- package/dist/SKILLS.md +3 -3
- package/dist/cli/commands/validate.d.ts +1 -1
- package/dist/cli/commands/validate.js +7 -1
- package/dist/integrations/cursor/README.md +6 -5
- package/dist/integrations/cursor/hooks/after-file-edit.js +6 -3
- package/dist/integrations/cursor/hooks/after-file-edit.sh +3 -0
- package/dist/integrations/cursor/hooks/before-read-file.sh +3 -0
- package/dist/integrations/cursor/hooks/before-shell-execution.sh +3 -0
- package/dist/integrations/cursor/hooks/cursor-hook-utils.js +121 -20
- package/dist/integrations/cursor/hooks/cursor-usage-receipt.js +3 -0
- package/dist/integrations/cursor/hooks/hooks.json +13 -3
- package/dist/integrations/cursor/hooks/pre-compact.js +39 -22
- package/dist/integrations/cursor/hooks/pre-compact.sh +3 -0
- package/dist/integrations/cursor/hooks/pre-tool-use.js +20 -15
- package/dist/integrations/cursor/hooks/pre-tool-use.sh +3 -0
- package/dist/integrations/cursor/hooks/xray-cloud-hook.sh +63 -0
- package/dist/integrations/grok/hooks/grok-hook-utils.js +6 -3
- package/dist/integrations/hermes-agent/bridge.mjs +14 -0
- package/dist/integrations/hooks/station-hook-runtime.cjs +416 -37
- package/dist/integrations/hooks/station-hook-runtime.mjs +15 -0
- package/dist/integrations/hooks/station-memory-ingest.mjs +152 -4
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/nucleus/delegation-gate.js +10 -11
- package/dist/nucleus/suit-temperament.d.ts +0 -5
- package/dist/nucleus/suit-temperament.js +18 -2
- package/dist/skills/orchestrator/SKILL.md +1 -1
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/scripts/foundry/README.md +2 -2
- package/scripts/foundry/assert-packed-dist-cli.mjs +4 -0
- package/scripts/foundry/plant/skills/mill/SKILL.md +1 -1
- package/scripts/foundry/validate-release-docs.mjs +44 -0
- package/scripts/node/install-bridges.cjs +256 -10
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/skills/orchestrator/SKILL.md +1 -1
- package/xray/features.json +1 -1
|
@@ -1,12 +1,160 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Station organ write — compact ingest + wake grow.
|
|
4
|
+
* Grow uses the worn RepertoireService. Groover field stays off.
|
|
4
5
|
*/
|
|
5
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
7
|
import { join } from 'node:path';
|
|
8
|
+
import { pathToFileURL } from 'node:url';
|
|
7
9
|
|
|
8
10
|
const root = process.argv[2];
|
|
9
|
-
const
|
|
11
|
+
const mode = process.argv[3] || 'station';
|
|
12
|
+
|
|
13
|
+
function readRouting(projectRoot) {
|
|
14
|
+
let routing = { enabled: false, provider: 'null' };
|
|
15
|
+
const featuresPath = join(projectRoot, '.xray', 'features.json');
|
|
16
|
+
if (existsSync(featuresPath)) {
|
|
17
|
+
try {
|
|
18
|
+
routing = JSON.parse(readFileSync(featuresPath, 'utf8')).memory_routing || routing;
|
|
19
|
+
} catch {
|
|
20
|
+
/* leftover */
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return routing;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isExplicitOptOut(routing) {
|
|
27
|
+
return routing && routing.enabled === false && routing.provider === 'repertoire';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function organServiceCandidates(projectRoot, routing) {
|
|
31
|
+
const configured = routing.module_path
|
|
32
|
+
? routing.module_path.startsWith('/') || /^[A-Za-z]:[\\/]/.test(routing.module_path)
|
|
33
|
+
? routing.module_path
|
|
34
|
+
: join(projectRoot, routing.module_path)
|
|
35
|
+
: null;
|
|
36
|
+
const fromProvider = configured
|
|
37
|
+
? configured.replace(/provider\/memory-routing-provider\.js$/, 'RepertoireService.js')
|
|
38
|
+
: null;
|
|
39
|
+
const raw = [
|
|
40
|
+
fromProvider,
|
|
41
|
+
join(projectRoot, 'node_modules', '@0xray', 'repertoire', 'dist', 'RepertoireService.js'),
|
|
42
|
+
join(projectRoot, 'vendor', '@0xray', 'repertoire', 'dist', 'RepertoireService.js'),
|
|
43
|
+
join(projectRoot, '..', 'repertoire', 'dist', 'RepertoireService.js'),
|
|
44
|
+
].filter(Boolean);
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
const out = [];
|
|
47
|
+
for (const dest of raw) {
|
|
48
|
+
if (seen.has(dest) || !existsSync(dest)) continue;
|
|
49
|
+
seen.add(dest);
|
|
50
|
+
out.push(dest);
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function ensureDestShape(destSignals) {
|
|
56
|
+
if (!existsSync(destSignals)) return;
|
|
57
|
+
try {
|
|
58
|
+
const data = JSON.parse(readFileSync(destSignals, 'utf8'));
|
|
59
|
+
if (!Array.isArray(data.signals)) return;
|
|
60
|
+
let dirty = false;
|
|
61
|
+
for (const signal of data.signals) {
|
|
62
|
+
if (!signal) continue;
|
|
63
|
+
if (!Array.isArray(signal.tags)) {
|
|
64
|
+
signal.tags = [];
|
|
65
|
+
dirty = true;
|
|
66
|
+
}
|
|
67
|
+
if (typeof signal.definition !== 'string') {
|
|
68
|
+
signal.definition = String(signal.name || '');
|
|
69
|
+
dirty = true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (dirty) writeFileSync(destSignals, `${JSON.stringify(data, null, 2)}\n`);
|
|
73
|
+
} catch {
|
|
74
|
+
/* leftover */
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function readCleanupDiary(projectRoot) {
|
|
79
|
+
const files = [
|
|
80
|
+
join(projectRoot, '.xray', 'state', 'NOTES.md'),
|
|
81
|
+
join(projectRoot, '.xray', 'state', 'CLEANSE-LIST.md'),
|
|
82
|
+
join(projectRoot, 'docs', 'archive', 'README.md'),
|
|
83
|
+
];
|
|
84
|
+
const parts = [];
|
|
85
|
+
const sources = [];
|
|
86
|
+
for (const dest of files) {
|
|
87
|
+
if (!existsSync(dest)) continue;
|
|
88
|
+
try {
|
|
89
|
+
parts.push(readFileSync(dest, 'utf8'));
|
|
90
|
+
sources.push(dest);
|
|
91
|
+
} catch {
|
|
92
|
+
/* leftover */
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { text: parts.join('\n'), sources };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function growDest(projectRoot) {
|
|
99
|
+
const routing = readRouting(projectRoot);
|
|
100
|
+
if (isExplicitOptOut(routing)) {
|
|
101
|
+
process.stdout.write(`${JSON.stringify({ skipped: 'opt-out' })}\n`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const dest of organServiceCandidates(projectRoot, routing)) {
|
|
105
|
+
try {
|
|
106
|
+
const mod = await import(pathToFileURL(dest).href);
|
|
107
|
+
const RepertoireService = mod.RepertoireService;
|
|
108
|
+
if (typeof RepertoireService !== 'function') continue;
|
|
109
|
+
const pathsMod = await import(pathToFileURL(dest.replace(/RepertoireService\.js$/, 'paths.js')).href);
|
|
110
|
+
const destSignals = join(projectRoot, '.xray', 'state', 'repertoire', 'curated_signals.json');
|
|
111
|
+
ensureDestShape(destSignals);
|
|
112
|
+
const service = new RepertoireService({
|
|
113
|
+
projectRoot,
|
|
114
|
+
signalsPath: destSignals,
|
|
115
|
+
syncXray: false,
|
|
116
|
+
syncField: false,
|
|
117
|
+
});
|
|
118
|
+
ensureDestShape(destSignals);
|
|
119
|
+
const before = service.signalsManager.load().signals.length;
|
|
120
|
+
const sessions = service.syncXrayMemory();
|
|
121
|
+
const kernel =
|
|
122
|
+
typeof pathsMod.collectKernelDiaryText === 'function'
|
|
123
|
+
? pathsMod.collectKernelDiaryText(projectRoot)
|
|
124
|
+
: { text: '', sources: [] };
|
|
125
|
+
const cleanup = readCleanupDiary(projectRoot);
|
|
126
|
+
const diary = service.heatKernelDiary({
|
|
127
|
+
text: [kernel.text, cleanup.text].filter(Boolean).join('\n'),
|
|
128
|
+
sources: [...(kernel.sources || []), ...cleanup.sources],
|
|
129
|
+
});
|
|
130
|
+
const after = service.signalsManager.load().signals.length;
|
|
131
|
+
process.stdout.write(
|
|
132
|
+
`${JSON.stringify({
|
|
133
|
+
before,
|
|
134
|
+
after,
|
|
135
|
+
imported: sessions.imported,
|
|
136
|
+
observed: 0,
|
|
137
|
+
heated: diary.heated,
|
|
138
|
+
})}\n`,
|
|
139
|
+
);
|
|
140
|
+
return;
|
|
141
|
+
} catch {
|
|
142
|
+
/* try next organ */
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
process.stdout.write(`${JSON.stringify({ skipped: 'no-organ' })}\n`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!root) {
|
|
149
|
+
process.exit(0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (mode === '--grow') {
|
|
153
|
+
await growDest(root);
|
|
154
|
+
process.exit(0);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const sessionId = mode;
|
|
10
158
|
const hookEvent = process.argv[4] || 'post_compact';
|
|
11
159
|
let signals = [];
|
|
12
160
|
try {
|
|
@@ -14,7 +162,7 @@ try {
|
|
|
14
162
|
} catch {
|
|
15
163
|
signals = [];
|
|
16
164
|
}
|
|
17
|
-
if (!
|
|
165
|
+
if (!Array.isArray(signals) || signals.length === 0) {
|
|
18
166
|
process.exit(0);
|
|
19
167
|
}
|
|
20
168
|
|
|
@@ -462,17 +462,16 @@ export function evaluateConstitutionGate(toolName, toolInput, ctx) {
|
|
|
462
462
|
return { allow: true };
|
|
463
463
|
}
|
|
464
464
|
export function evaluatePreToolGate(toolName, toolInput, ctx) {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
465
|
+
const host = ctx.host || 'generic';
|
|
466
|
+
try {
|
|
467
|
+
maybeHeatHostStation(ctx.projectRoot, host, {
|
|
468
|
+
source: `0xray/${host}-pre-tool`,
|
|
469
|
+
sessionId: ctx.sessionId || host,
|
|
470
|
+
hookEvent: ctx.hookEvent || 'pre_tool_use',
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
/* station heat is best-effort — constitution still runs */
|
|
476
475
|
}
|
|
477
476
|
const constitution = evaluateConstitutionGate(toolName, toolInput, ctx);
|
|
478
477
|
if (!constitution.allow)
|
|
@@ -27,11 +27,6 @@ export declare function resolveSuitProfile(raw: SuitTemperamentConfig | undefine
|
|
|
27
27
|
* A Hermes/OpenCode host ignores a leftover Grok boot (and vice versa).
|
|
28
28
|
*/
|
|
29
29
|
export declare function resolveRuntimeSuitProfile(projectRoot: string, host?: SuitHost): SuitProfile;
|
|
30
|
-
/**
|
|
31
|
-
* Write station heat when this host/session has no live card yet.
|
|
32
|
-
* OpenClaw has no SessionStart — PreToolUse is the session boundary.
|
|
33
|
-
* Install/init cards without a session id are not a live session.
|
|
34
|
-
*/
|
|
35
30
|
export declare function maybeHeatHostStation(projectRoot: string, host: SuitHost, extra?: Record<string, unknown>): string | null;
|
|
36
31
|
/** Merge temperament fields into session-boot.json for the host that just started. */
|
|
37
32
|
export declare function writeSuitSessionBoot(projectRoot: string, host: SuitHost, extra?: Record<string, unknown>): string;
|
|
@@ -95,7 +95,13 @@ function concreteSessionId(value) {
|
|
|
95
95
|
* OpenClaw has no SessionStart — PreToolUse is the session boundary.
|
|
96
96
|
* Install/init cards without a session id are not a live session.
|
|
97
97
|
*/
|
|
98
|
+
function isCursorWorkspaceWrapper(projectRoot) {
|
|
99
|
+
return (fs.existsSync(path.join(projectRoot, 'repos', 'xray')) ||
|
|
100
|
+
fs.existsSync(path.join(projectRoot, 'repos', 'repertoire')));
|
|
101
|
+
}
|
|
98
102
|
export function maybeHeatHostStation(projectRoot, host, extra = {}) {
|
|
103
|
+
if (isCursorWorkspaceWrapper(projectRoot))
|
|
104
|
+
return null;
|
|
99
105
|
const bootPath = path.join(projectRoot, '.xray', 'state', 'session-boot.json');
|
|
100
106
|
const cardPath = path.join(projectRoot, '.xray', 'state', 'STATION.md');
|
|
101
107
|
let existing = {};
|
|
@@ -111,7 +117,11 @@ export function maybeHeatHostStation(projectRoot, host, extra = {}) {
|
|
|
111
117
|
const existingSessionId = concreteSessionId(existing.sessionId);
|
|
112
118
|
const sameHost = existing.host === host;
|
|
113
119
|
const sameSession = existingSessionId !== null && sessionId !== null && existingSessionId === sessionId;
|
|
114
|
-
if (sameHost &&
|
|
120
|
+
if (sameHost &&
|
|
121
|
+
sameSession &&
|
|
122
|
+
fs.existsSync(cardPath) &&
|
|
123
|
+
!stationHeat.stationBootNeedsRefresh(existing, projectRoot, host)) {
|
|
124
|
+
stationHeat.heatLiveMemory(projectRoot);
|
|
115
125
|
return bootPath;
|
|
116
126
|
}
|
|
117
127
|
return writeSuitSessionBoot(projectRoot, host, extra);
|
|
@@ -120,8 +130,14 @@ export function maybeHeatHostStation(projectRoot, host, extra = {}) {
|
|
|
120
130
|
export function writeSuitSessionBoot(projectRoot, host, extra = {}) {
|
|
121
131
|
const profile = resolveSuitProfile(loadSuitTemperamentRaw(projectRoot), host);
|
|
122
132
|
const stateDir = path.join(projectRoot, '.xray', 'state');
|
|
123
|
-
fs.mkdirSync(stateDir, { recursive: true });
|
|
124
133
|
const bootPath = path.join(stateDir, 'session-boot.json');
|
|
134
|
+
try {
|
|
135
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
/* Cloud workspace wrapper is often unwritable — fail open */
|
|
139
|
+
return bootPath;
|
|
140
|
+
}
|
|
125
141
|
let existing = {};
|
|
126
142
|
if (fs.existsSync(bootPath)) {
|
|
127
143
|
try {
|
|
@@ -76,5 +76,5 @@ The seven rules are the engine. The cadence is the time signature. Disk SSOT: `g
|
|
|
76
76
|
- **Close:** loop until Station is done. Compact: Station survives the cut. Overlay dest names are OP-PROC — reload `repertoire-working.json` `opProcNames`, not Station.
|
|
77
77
|
- **Board:** critic Strict comments; forge implements; lead stays main thread.
|
|
78
78
|
- **Codex 69:** rewire this skill and existing ops. Do not add a new `SKILL.md` to hold the cadence.
|
|
79
|
-
- **Ship matrix:** `LEAD-CADENCE.md` Decision matrix — A PR+CI · B pack · C `release:docs-check` · D `foundry release --i-mean-it`. Green CI is not ship. Spend / credentials stay Ask-first.
|
|
79
|
+
- **Ship matrix:** `LEAD-CADENCE.md` Decision matrix — A PR+CI · B pack · C `release:docs-check` (`reconcile --check`; no patch stamps on guides) · D `foundry release --i-mean-it`. Green CI is not ship. Spend / credentials stay Ask-first. Patch number lives in `package.json` + CHANGELOG.
|
|
80
80
|
- **Metamorphosis:** LLM → OP-PROC is in-context worn seats (same critic, same dummy, resume only). Not more bots.
|
package/llms.txt
CHANGED
|
@@ -35,7 +35,7 @@ Registered in `.mcp.json` as `npx -y 0xray mcp <cmd>`:
|
|
|
35
35
|
| `xray-code-review` | Proposal quality |
|
|
36
36
|
| `xray-architect-tools` | System design |
|
|
37
37
|
|
|
38
|
-
Not an 8th `xray-*` server. Repertoire is
|
|
38
|
+
Not an 8th `xray-*` server. **Repertoire is preferred** — vendored 0.2.5 ships on (`npx @0xray/repertoire mcp`). Dest is named laws, not hangar `repo-*` or git slugs. Survive compact is **Station** (Read `.xray/state/STATION.md`). Station is the ticket, not a substitute for Repertoire. Do not pin 0.1.8.
|
|
39
39
|
|
|
40
40
|
## Codex 69 (always on)
|
|
41
41
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "0xray",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.21",
|
|
4
4
|
"description": "Multi-agent orchestration and Codex governance for OpenCode, Hermes, Grok Build, and OpenClaw",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"license": "MIT",
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
"consumer:smoke": "node scripts/node/consumer-install-smoke.mjs",
|
|
125
125
|
"pack:tmp-proof": "node scripts/node/pack-tmp-suit-proof.mjs",
|
|
126
126
|
"release:gate": "node scripts/foundry/release-gate.mjs",
|
|
127
|
-
"release:docs-check": "node scripts/foundry/validate-release-docs.mjs",
|
|
127
|
+
"release:docs-check": "node scripts/foundry/reconcile-version.mjs --check && node scripts/foundry/validate-release-docs.mjs",
|
|
128
128
|
"pre-publish-guard": "node scripts/node/pre-publish-guard.js",
|
|
129
129
|
"safe-publish": "npm run prepare-consumer && npm run build",
|
|
130
130
|
"release:npm": "node scripts/foundry/release.mjs --publish-only",
|
|
@@ -60,7 +60,7 @@ After mill plant is fastened, overlay their plant (defaults; remap in `foundry.j
|
|
|
60
60
|
|
|
61
61
|
**Mill-fill is law:** their keys/files win; mill names they did not plant stay unless they overlay the same name. JSON facets always merge. Skill overlay is project dirs on every TUI floor (OpenCode, Grok, Hermes, OpenClaw). YML agents stay OpenCode. CLI re-wear calls mint **once** after mill copies. Isolated HOME is passwd home, not `os.homedir()`. CI report is `.xray/foundry-ci-report.json` (`.opencode/logs` shim).
|
|
62
62
|
|
|
63
|
-
**Default mill plant:** `mill` + `inspect` (inspect AI work). Fasten is **foundry-plant/0**: builtin plant is mill+inspect only (files from this package `plant/`). Fastens a suit, not an empty one. Not 45/42 costume. `foundry.json` `"costume": true` copies that dump.
|
|
63
|
+
**Default mill plant:** `mill` + `inspect` (inspect AI work). Fasten is **foundry-plant/0**: builtin plant is mill+inspect only (files from this package `plant/`). Fastens a suit, not an empty one. Not 45/42 costume. `foundry.json` `"costume": true` copies that dump. **Repertoire is preferred** (vendored, dest = laws). PPE stays worn.
|
|
64
64
|
|
|
65
65
|
**foundry-plant/0:** `foundry.json` `"plant": "@0xray/blip"` (aliases `"plant": "blip"` / `"plant": "sound"`) fastens whatever that mill package declares and ships in its own `plant/`. Organs (looker, vibe, mixer) live in the mill. Adding an organ is a mill publish, not a foundry catalog PR. CLI shims `foundry blip|sound` still exist on foundry 0.1.11+; fasten is the new cut. Foundry **0.1.12**.
|
|
66
66
|
|
|
@@ -76,7 +76,7 @@ A friend would hear: Blips should look like Rippel living motions with sound, no
|
|
|
76
76
|
|
|
77
77
|
**Shop plant:** `shop-extract`, `shop-witness`, `shop-pin` are first-class with mill plant. Inspect does not treat those worn names as a costume dump. `foundry.json` `"shopPlant"` names more shops (array), a plant dir (string), or `{ "skills": [...], "dir": "..." }`. This is not `"costume": true` and does not copy 45/42.
|
|
78
78
|
|
|
79
|
-
**
|
|
79
|
+
**Mill target (0xray postinstall, not this mill package):** `install-bridges.cjs` does not mill npm global prefix or npx `_npx` as a consumer. Isolated HOME skips machine `~/.grok`. Shared HOME Grok last-mile is project `.grok/plugins/0xray` (two seats do not clobber machine plugin). Nested mill ships in the 0xray tarball (foundry-plant/0; blip/sound drawers in `@0xray/blip`; inspect dest matches `resolveGrokPluginDests`).
|
|
80
80
|
|
|
81
81
|
## Publish this mill
|
|
82
82
|
|
|
@@ -24,6 +24,10 @@ export const REQUIRED_PACK_PATHS = [
|
|
|
24
24
|
"dist/integrations/cursor/hooks/after-file-edit.js",
|
|
25
25
|
"dist/integrations/cursor/hooks/cursor-hook-utils.js",
|
|
26
26
|
"dist/integrations/cursor/hooks/cursor-usage-receipt.js",
|
|
27
|
+
"dist/integrations/cursor/hooks/xray-cloud-hook.sh",
|
|
28
|
+
"dist/integrations/cursor/hooks/pre-tool-use.sh",
|
|
29
|
+
"dist/integrations/cursor/hooks/pre-compact.sh",
|
|
30
|
+
"dist/integrations/cursor/hooks/after-file-edit.sh",
|
|
27
31
|
];
|
|
28
32
|
|
|
29
33
|
export function packedFilePaths(files) {
|
|
@@ -11,7 +11,7 @@ The mill **fastens** a suit. Default mill plant is **inspect** (inspecting AI wo
|
|
|
11
11
|
|
|
12
12
|
**Mill-fill:** their keys/files win. Mill plant names they did not overlay stay. JSON always merges.
|
|
13
13
|
|
|
14
|
-
**Factory:** Repertoire
|
|
14
|
+
**Factory:** **Repertoire is preferred** (vendored 0.2.5 ships on; dest = named laws). Seven generated MCP. Constitution (PPE) stays worn. Not a fifth MCP. Not 5.0.
|
|
15
15
|
|
|
16
16
|
**Costume:** `foundry.json` `"costume": true` copies the 45/42 mill costume.
|
|
17
17
|
|
|
@@ -29,6 +29,40 @@ function kernelLineHasPatchStamp(content) {
|
|
|
29
29
|
return /^\*\*v?\d+\.\d+\.\d+\*\*/m.test(content);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Present-tense cut pins. Keep: package.json, CHANGELOG, stamped JSON,
|
|
34
|
+
* features-since headings. Rip from guides, Station, OP-PROC.
|
|
35
|
+
*/
|
|
36
|
+
const PRESENT_TENSE_PATCH_RE =
|
|
37
|
+
/This cut is|Do not republish \d+\.\d+\.\d+|0xray@\d+\.\d+\.\d+|npm is \*\*\d+\.\d+\.\d+\*\*|Product \*\*\d+\.\d+\.\d+\*\* is on npm/;
|
|
38
|
+
|
|
39
|
+
const NO_PRESENT_TENSE_PATCH = [
|
|
40
|
+
'README.md',
|
|
41
|
+
'AGENTS.md',
|
|
42
|
+
'AGENTS-consumer.md',
|
|
43
|
+
'SKILLS.md',
|
|
44
|
+
'llms.txt',
|
|
45
|
+
'docs-site/docs/architecture/v4-now.md',
|
|
46
|
+
'docs-site/docs/architecture/v4-vision.md',
|
|
47
|
+
'docs-site/docs/guides/memory-wake.md',
|
|
48
|
+
'docs-site/docs/guides/memory-routing.md',
|
|
49
|
+
'docs-site/docs/guides/repertoire.md',
|
|
50
|
+
'docs-site/docs/guides/getting-started.md',
|
|
51
|
+
'docs-site/docs/guides/integrations.md',
|
|
52
|
+
'docs-site/docs/guides/consumer-migration.md',
|
|
53
|
+
'docs-site/docs/mcp/README.md',
|
|
54
|
+
'grok-bot/ops/LEAD-CADENCE.md',
|
|
55
|
+
'grok-bot/AGENTS.md',
|
|
56
|
+
'src/skills/orchestrator/SKILL.md',
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
function presentTensePatchPinErrors(content) {
|
|
60
|
+
if (PRESENT_TENSE_PATCH_RE.test(content)) {
|
|
61
|
+
return ['present-tense patch pin — keep versions in package.json + CHANGELOG + stamped JSON only'];
|
|
62
|
+
}
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
|
|
32
66
|
/** Guides that must exist and reference the current release (body or header). */
|
|
33
67
|
const REQUIRED_GUIDES = [
|
|
34
68
|
'docs-site/docs/guides/features-since-3.1.md',
|
|
@@ -310,7 +344,17 @@ export function validateReleaseDocs(rootDir = resolveMillRoot()) {
|
|
|
310
344
|
if (content.trim().length < 200) {
|
|
311
345
|
errors.push(`${rel}: guide too short — likely stub`);
|
|
312
346
|
}
|
|
347
|
+
}
|
|
313
348
|
|
|
349
|
+
for (const rel of NO_PRESENT_TENSE_PATCH) {
|
|
350
|
+
const content = readFile(rootDir, rel);
|
|
351
|
+
if (!content) {
|
|
352
|
+
errors.push(`${rel}: file missing`);
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
for (const e of presentTensePatchPinErrors(content)) {
|
|
356
|
+
errors.push(`${rel}: ${e}`);
|
|
357
|
+
}
|
|
314
358
|
}
|
|
315
359
|
|
|
316
360
|
const docusaurus = readFile(rootDir, 'docs-site/docusaurus.config.ts');
|