@rune-kit/rune 2.28.0 → 2.29.1
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/.codex-plugin/plugin.json +14 -0
- package/README.md +114 -41
- package/compiler/__tests__/adapter-model-mapping.test.js +22 -24
- package/compiler/__tests__/adapters.test.js +4 -1
- package/compiler/__tests__/doctor-mesh.test.js +55 -1
- package/compiler/__tests__/governance-collector.test.js +1 -0
- package/compiler/__tests__/hooks-codex.test.js +85 -0
- package/compiler/__tests__/hooks-doctor-tier.test.js +3 -5
- package/compiler/__tests__/hooks-drift.test.js +2 -2
- package/compiler/__tests__/hooks-install.test.js +2 -2
- package/compiler/__tests__/hooks-tiers.test.js +3 -4
- package/compiler/__tests__/setup.test.js +59 -0
- package/compiler/__tests__/status.test.js +7 -1
- package/compiler/__tests__/tier-override.test.js +42 -1
- package/compiler/__tests__/transforms.test.js +15 -0
- package/compiler/__tests__/update.test.js +416 -0
- package/compiler/adapters/codex.js +88 -11
- package/compiler/adapters/hooks/codex.js +178 -0
- package/compiler/adapters/hooks/index.js +10 -0
- package/compiler/adapters/openclaw.js +2 -2
- package/compiler/bin/rune.js +24 -4
- package/compiler/commands/hooks/install.js +2 -2
- package/compiler/commands/hooks/status.js +1 -1
- package/compiler/commands/setup.js +101 -28
- package/compiler/commands/update.js +354 -0
- package/compiler/doctor.js +12 -6
- package/compiler/emitter.js +46 -3
- package/compiler/governance-collector.js +3 -2
- package/compiler/status.js +4 -7
- package/compiler/transforms/branding.js +2 -2
- package/compiler/transforms/subagents.js +3 -3
- package/hooks/codex-hooks.json +96 -0
- package/hooks/context-watch/index.cjs +8 -5
- package/hooks/intent-router/index.cjs +3 -0
- package/hooks/lib/hook-output.cjs +11 -3
- package/hooks/post-session-reflect/index.cjs +65 -24
- package/hooks/pre-compact/index.cjs +10 -4
- package/hooks/pre-tool-guard/index.cjs +38 -36
- package/hooks/run-hook +1 -1
- package/hooks/run-hook.cmd +1 -1
- package/hooks/secrets-scan/index.cjs +18 -2
- package/package.json +3 -2
- package/skills/browser-pilot/SKILL.md +1 -0
- package/skills/completion-gate/SKILL.md +5 -5
- package/skills/doc-processor/SKILL.md +2 -2
- package/skills/hallucination-guard/SKILL.md +1 -0
- package/skills/journal/SKILL.md +1 -0
- package/skills/retro/SKILL.md +2 -2
- package/skills/session-bridge/SKILL.md +1 -0
- package/skills/session-bridge/scripts/load-invariants.js +1 -1
- package/skills/video-creator/SKILL.md +1 -1
|
@@ -21,18 +21,21 @@
|
|
|
21
21
|
* Codex GPT-5.6 tiers (verified against codex-cli 0.144.1 + developers.openai.com/codex/models):
|
|
22
22
|
* sol = flagship/complex → opus
|
|
23
23
|
* terra = balanced everyday → sonnet
|
|
24
|
-
*
|
|
24
|
+
* terra at low effort = fast/affordable → haiku
|
|
25
25
|
* Codex also exposes `model_reasoning_effort = minimal|low|medium|high|xhigh`
|
|
26
26
|
* (a config.toml key, not per-skill frontmatter) — Rune surfaces the tier→effort
|
|
27
27
|
* suggestion in AGENTS.md rather than emitting a speculative per-skill field.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
+
import { existsSync } from 'node:fs';
|
|
31
|
+
import { readFile } from 'node:fs/promises';
|
|
32
|
+
import path from 'node:path';
|
|
30
33
|
import { BRANDING_FOOTER } from '../transforms/branding.js';
|
|
31
34
|
|
|
32
35
|
const MODEL_MAP = {
|
|
33
36
|
opus: 'gpt-5.6-sol',
|
|
34
37
|
sonnet: 'gpt-5.6-terra',
|
|
35
|
-
haiku: 'gpt-5.6-
|
|
38
|
+
haiku: 'gpt-5.6-terra',
|
|
36
39
|
};
|
|
37
40
|
|
|
38
41
|
// Suggested config.toml `model_reasoning_effort` per tier (global key, documented in AGENTS.md).
|
|
@@ -78,8 +81,6 @@ export default {
|
|
|
78
81
|
generateHeader(skill) {
|
|
79
82
|
const desc = (skill.description || '').replace(/"/g, '\\"');
|
|
80
83
|
const lines = ['---', `name: rune-${skill.name}`, `description: "${desc}"`];
|
|
81
|
-
const translatedModel = skill.model ? MODEL_MAP[skill.model] || skill.model : null;
|
|
82
|
-
if (translatedModel) lines.push(`model: ${translatedModel}`);
|
|
83
84
|
lines.push('---', '', '');
|
|
84
85
|
return lines.join('\n');
|
|
85
86
|
},
|
|
@@ -102,25 +103,27 @@ export default {
|
|
|
102
103
|
|
|
103
104
|
// Codex (and OpenAI's AGENTS.md convention generally) reads AGENTS.md at the
|
|
104
105
|
// project root for high-level context. Migrated from emitter.js v2.18.
|
|
105
|
-
generateExtraFiles({ stats }) {
|
|
106
|
-
const
|
|
106
|
+
async generateExtraFiles({ stats, outputRoot }) {
|
|
107
|
+
const runeBlock = [
|
|
108
|
+
'<!-- rune:codex:start -->',
|
|
107
109
|
'# Rune — Project Configuration',
|
|
108
110
|
'',
|
|
109
111
|
'## Overview',
|
|
110
112
|
'',
|
|
111
113
|
'Rune is an interconnected skill ecosystem for AI coding assistants.',
|
|
112
|
-
`${stats.skillCount} core skills | 5-layer mesh architecture | ${stats.crossRefsResolved} connections | Multi-platform.`,
|
|
114
|
+
`${stats.coreSkillCount ?? stats.skillCount} core skills${stats.tierSkillCount ? ` + ${stats.tierSkillCount} tier skills` : ''} | 5-layer mesh architecture | ${stats.crossRefsResolved} connections | Multi-platform.`,
|
|
113
115
|
'Philosophy: "Less skills. Deeper connections."',
|
|
114
116
|
'',
|
|
115
117
|
'Platform: codex',
|
|
116
118
|
'',
|
|
117
119
|
'## Skills',
|
|
118
120
|
'',
|
|
119
|
-
`**${stats.skillCount} core skills
|
|
121
|
+
`**${stats.coreSkillCount ?? stats.skillCount} core skills**${stats.tierSkillCount ? ` + **${stats.tierSkillCount} tier skills**` : ''} + **${stats.packCount} extension packs**`,
|
|
120
122
|
'',
|
|
121
123
|
'## Usage',
|
|
122
124
|
'',
|
|
123
|
-
'
|
|
125
|
+
'Mention a skill as `$rune-<name>` or let Codex activate it from the task description.',
|
|
126
|
+
'When an applicable Rune skill requests delegation, Codex may run bounded subagents in parallel.',
|
|
124
127
|
'',
|
|
125
128
|
'## Skills Directory',
|
|
126
129
|
'',
|
|
@@ -134,12 +137,86 @@ export default {
|
|
|
134
137
|
`- sonnet → \`${MODEL_MAP.sonnet}\` (\`model_reasoning_effort = "${REASONING_EFFORT_MAP.sonnet}"\`) — code / edit / review`,
|
|
135
138
|
`- haiku → \`${MODEL_MAP.haiku}\` (\`model_reasoning_effort = "${REASONING_EFFORT_MAP.haiku}"\`) — scan / read-only`,
|
|
136
139
|
'',
|
|
137
|
-
'
|
|
140
|
+
'Generated project-scoped roles live in `.codex/agents/` so spawned agents can apply these settings natively.',
|
|
138
141
|
'',
|
|
139
142
|
'---',
|
|
140
143
|
'> Rune Skill Mesh — https://github.com/rune-kit/rune',
|
|
144
|
+
'<!-- rune:codex:end -->',
|
|
141
145
|
'',
|
|
142
146
|
].join('\n');
|
|
143
|
-
|
|
147
|
+
|
|
148
|
+
const agentsMd = await mergeAgentsMd(outputRoot, runeBlock);
|
|
149
|
+
return [
|
|
150
|
+
{ path: 'AGENTS.md', content: agentsMd },
|
|
151
|
+
{
|
|
152
|
+
path: '.codex/agents/rune-heavy.toml',
|
|
153
|
+
content: renderAgentRole({
|
|
154
|
+
name: 'rune_heavy',
|
|
155
|
+
description: 'Rune worker for architecture, security, planning, and other high-complexity tasks.',
|
|
156
|
+
model: MODEL_MAP.opus,
|
|
157
|
+
effort: REASONING_EFFORT_MAP.opus,
|
|
158
|
+
instructions:
|
|
159
|
+
'Follow the applicable Rune skill exactly. Prioritize correctness, explicit assumptions, impact analysis, and complete verification.',
|
|
160
|
+
}),
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
path: '.codex/agents/rune-standard.toml',
|
|
164
|
+
content: renderAgentRole({
|
|
165
|
+
name: 'rune_standard',
|
|
166
|
+
description: 'Rune worker for implementation, debugging, review, and everyday engineering tasks.',
|
|
167
|
+
model: MODEL_MAP.sonnet,
|
|
168
|
+
effort: REASONING_EFFORT_MAP.sonnet,
|
|
169
|
+
instructions:
|
|
170
|
+
'Follow the applicable Rune skill exactly. Keep changes scoped, preserve user work, and verify behavior before reporting completion.',
|
|
171
|
+
}),
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
path: '.codex/agents/rune-fast.toml',
|
|
175
|
+
content: renderAgentRole({
|
|
176
|
+
name: 'rune_fast',
|
|
177
|
+
description: 'Read-heavy Rune worker for scouting, dependency checks, and focused evidence gathering.',
|
|
178
|
+
model: MODEL_MAP.haiku,
|
|
179
|
+
effort: REASONING_EFFORT_MAP.haiku,
|
|
180
|
+
instructions:
|
|
181
|
+
'Stay read-only unless the parent explicitly requests edits. Return concise evidence with file and line references.',
|
|
182
|
+
}),
|
|
183
|
+
},
|
|
184
|
+
];
|
|
144
185
|
},
|
|
145
186
|
};
|
|
187
|
+
|
|
188
|
+
const RUNE_AGENTS_START = '<!-- rune:codex:start -->';
|
|
189
|
+
const RUNE_AGENTS_END = '<!-- rune:codex:end -->';
|
|
190
|
+
|
|
191
|
+
async function mergeAgentsMd(outputRoot, runeBlock) {
|
|
192
|
+
if (!outputRoot) return runeBlock;
|
|
193
|
+
|
|
194
|
+
const agentsPath = path.join(outputRoot, 'AGENTS.md');
|
|
195
|
+
if (!existsSync(agentsPath)) return runeBlock;
|
|
196
|
+
|
|
197
|
+
const existing = await readFile(agentsPath, 'utf-8');
|
|
198
|
+
const start = existing.indexOf(RUNE_AGENTS_START);
|
|
199
|
+
const end = existing.indexOf(RUNE_AGENTS_END);
|
|
200
|
+
if (start !== -1 && end > start) {
|
|
201
|
+
const after = end + RUNE_AGENTS_END.length;
|
|
202
|
+
return `${existing.slice(0, start)}${runeBlock}${existing.slice(after)}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Migrate files produced by Rune versions before managed markers existed.
|
|
206
|
+
if (existing.trimStart().startsWith('# Rune — Project Configuration')) return runeBlock;
|
|
207
|
+
|
|
208
|
+
return `${existing.trimEnd()}\n\n${runeBlock}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function renderAgentRole({ name, description, model, effort, instructions }) {
|
|
212
|
+
return [
|
|
213
|
+
`name = "${name}"`,
|
|
214
|
+
`description = "${description}"`,
|
|
215
|
+
`model = "${model}"`,
|
|
216
|
+
`model_reasoning_effort = "${effort}"`,
|
|
217
|
+
'developer_instructions = """',
|
|
218
|
+
instructions,
|
|
219
|
+
'"""',
|
|
220
|
+
'',
|
|
221
|
+
].join('\n');
|
|
222
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Codex hooks adapter.
|
|
3
|
+
*
|
|
4
|
+
* Target: `.codex/hooks.json` — Codex-native lifecycle hooks. Codex currently
|
|
5
|
+
* skips handlers marked `async`, so Rune emits every command synchronously and
|
|
6
|
+
* relies on the preset's `--gentle` flag for warn-only behavior.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { readFile } from 'node:fs/promises';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import {
|
|
13
|
+
appendHookBlock,
|
|
14
|
+
detectPreset,
|
|
15
|
+
stripHooksBySkill,
|
|
16
|
+
stripRuneHooks,
|
|
17
|
+
summarizeRuneHooks,
|
|
18
|
+
} from '../../commands/hooks/merge.js';
|
|
19
|
+
import { buildPreset, WIRED_SKILLS } from '../../commands/hooks/presets.js';
|
|
20
|
+
|
|
21
|
+
export const id = 'codex';
|
|
22
|
+
export const HOOKS_REL_PATH = '.codex/hooks.json';
|
|
23
|
+
|
|
24
|
+
const RUNE_DESCRIPTION = 'Rune lifecycle hooks for OpenAI Codex.';
|
|
25
|
+
|
|
26
|
+
export function detect(projectRoot) {
|
|
27
|
+
return existsSync(path.join(projectRoot, '.codex'));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function emit({ preset, projectRoot, tierManifests = [] }) {
|
|
31
|
+
if (preset === 'off' && tierManifests.length === 0) return uninstall({ projectRoot });
|
|
32
|
+
if (preset !== 'off' && preset !== 'strict' && preset !== 'gentle') {
|
|
33
|
+
throw new Error(`codex adapter: invalid preset '${preset}'`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const hooksPath = path.join(projectRoot, HOOKS_REL_PATH);
|
|
37
|
+
const existing = await readJson(hooksPath);
|
|
38
|
+
let merged = stripRuneHooks(existing);
|
|
39
|
+
const notes = [
|
|
40
|
+
'Codex requires review/trust for new or changed project hooks. Open `/hooks` after installation.',
|
|
41
|
+
'Codex does not execute async command hooks; Rune emitted synchronous handlers with preset-controlled behavior.',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
if (!merged.description) merged.description = RUNE_DESCRIPTION;
|
|
45
|
+
if (preset !== 'off') {
|
|
46
|
+
merged = appendHookBlock(merged, normalizeBlock(buildPreset(preset)));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const manifest of tierManifests) {
|
|
50
|
+
const overrideKeys = Object.keys(manifest.overrides || {});
|
|
51
|
+
if (overrideKeys.length > 0) merged = stripHooksBySkill(merged, overrideKeys);
|
|
52
|
+
|
|
53
|
+
const tierBlock = buildCodexTierBlock(manifest);
|
|
54
|
+
if (Object.keys(tierBlock.hooks).length > 0) {
|
|
55
|
+
merged = appendHookBlock(merged, tierBlock);
|
|
56
|
+
}
|
|
57
|
+
if (tierBlock.skipped.length > 0) {
|
|
58
|
+
notes.push(`${manifest.tier}: skipped Codex-unsupported entries (${tierBlock.skipped.join(', ')}).`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
files: [{ path: hooksPath, content: `${JSON.stringify(merged, null, 2)}\n` }],
|
|
64
|
+
notes,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function uninstall({ projectRoot }) {
|
|
69
|
+
const hooksPath = path.join(projectRoot, HOOKS_REL_PATH);
|
|
70
|
+
if (!existsSync(hooksPath)) {
|
|
71
|
+
return { files: [], notes: ['no .codex/hooks.json — nothing to uninstall'] };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const existing = await readJson(hooksPath);
|
|
75
|
+
const stripped = stripRuneHooks(existing);
|
|
76
|
+
const keys = Object.keys(stripped);
|
|
77
|
+
const isRuneOnlyShell = keys.length === 1 && keys[0] === 'description' && stripped.description === RUNE_DESCRIPTION;
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
files: [{ path: hooksPath, content: isRuneOnlyShell ? null : `${JSON.stringify(stripped, null, 2)}\n` }],
|
|
81
|
+
notes: [],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function status(projectRoot) {
|
|
86
|
+
const hooksPath = path.join(projectRoot, HOOKS_REL_PATH);
|
|
87
|
+
if (!existsSync(hooksPath)) {
|
|
88
|
+
return {
|
|
89
|
+
installed: false,
|
|
90
|
+
preset: null,
|
|
91
|
+
wired: [],
|
|
92
|
+
missing: [...WIRED_SKILLS],
|
|
93
|
+
notes: ['no .codex/hooks.json'],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const settings = await readJson(hooksPath);
|
|
98
|
+
const summary = summarizeRuneHooks(settings);
|
|
99
|
+
const preset = detectPreset(settings);
|
|
100
|
+
const wired = Array.from(new Set(Object.values(summary.events).flat()));
|
|
101
|
+
return {
|
|
102
|
+
installed: summary.total > 0,
|
|
103
|
+
preset: preset === 'none' ? null : preset,
|
|
104
|
+
wired,
|
|
105
|
+
missing: WIRED_SKILLS.filter((skill) => !wired.includes(skill)),
|
|
106
|
+
events: summary.events,
|
|
107
|
+
notes: ['Use `/hooks` in Codex to inspect the current trust state.'],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function normalizeBlock(block) {
|
|
112
|
+
const hooks = {};
|
|
113
|
+
for (const [event, groups] of Object.entries(block.hooks || {})) {
|
|
114
|
+
hooks[event] = groups.map((group) => {
|
|
115
|
+
const normalized = {
|
|
116
|
+
...group,
|
|
117
|
+
hooks: group.hooks.map(normalizeCommand),
|
|
118
|
+
};
|
|
119
|
+
if (event === 'UserPromptSubmit' || event === 'Stop') delete normalized.matcher;
|
|
120
|
+
return normalized;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return { hooks };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeCommand(entry) {
|
|
127
|
+
const { async: _unsupported, ...normalized } = entry;
|
|
128
|
+
const commandWindows = toWindowsCommand(normalized.command);
|
|
129
|
+
if (commandWindows !== normalized.command) normalized.commandWindows = commandWindows;
|
|
130
|
+
return normalized;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function buildCodexTierBlock(manifest) {
|
|
134
|
+
const hooks = {};
|
|
135
|
+
const skipped = [];
|
|
136
|
+
|
|
137
|
+
for (const entry of manifest.entries) {
|
|
138
|
+
if (entry.event === 'statusLine' || entry.claudeOnly || entry.platforms?.codex === 'unsupported') {
|
|
139
|
+
skipped.push(entry.id || entry.skill);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const event = entry.event;
|
|
144
|
+
const matcher = entry.matcher || '.*';
|
|
145
|
+
if (!hooks[event]) hooks[event] = [];
|
|
146
|
+
let group = hooks[event].find((candidate) => candidate.matcher === matcher);
|
|
147
|
+
if (!group) {
|
|
148
|
+
group = { matcher, hooks: [] };
|
|
149
|
+
hooks[event].push(group);
|
|
150
|
+
}
|
|
151
|
+
group.hooks.push(
|
|
152
|
+
normalizeCommand({
|
|
153
|
+
type: 'command',
|
|
154
|
+
command: entry.command,
|
|
155
|
+
}),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { ...normalizeBlock({ hooks }), skipped };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function toWindowsCommand(command) {
|
|
163
|
+
if (typeof command !== 'string') return command;
|
|
164
|
+
return command.replace(/\$\{([A-Z][A-Z0-9_]*)\}/g, '%$1%');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function readJson(hooksPath) {
|
|
168
|
+
if (!existsSync(hooksPath)) return {};
|
|
169
|
+
const raw = await readFile(hooksPath, 'utf-8');
|
|
170
|
+
if (!raw.trim()) return {};
|
|
171
|
+
try {
|
|
172
|
+
return JSON.parse(raw);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`${hooksPath} is not valid JSON — fix it manually or delete the file and re-run \`rune hooks install --platform codex\`. (${err.message})`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -15,11 +15,13 @@
|
|
|
15
15
|
|
|
16
16
|
import * as antigravity from './antigravity.js';
|
|
17
17
|
import * as claude from './claude.js';
|
|
18
|
+
import * as codex from './codex.js';
|
|
18
19
|
import * as cursor from './cursor.js';
|
|
19
20
|
import * as windsurf from './windsurf.js';
|
|
20
21
|
|
|
21
22
|
export const ADAPTERS = Object.freeze({
|
|
22
23
|
claude,
|
|
24
|
+
codex,
|
|
23
25
|
cursor,
|
|
24
26
|
windsurf,
|
|
25
27
|
antigravity,
|
|
@@ -55,6 +57,14 @@ export const CAPABILITIES = Object.freeze({
|
|
|
55
57
|
stop: true,
|
|
56
58
|
notes: 'Full hook parity — PreToolUse/PostToolUse/Stop all supported.',
|
|
57
59
|
},
|
|
60
|
+
codex: {
|
|
61
|
+
maturity: 'stable',
|
|
62
|
+
preToolEdit: true,
|
|
63
|
+
preToolBash: true,
|
|
64
|
+
postToolEdit: true,
|
|
65
|
+
stop: true,
|
|
66
|
+
notes: 'Native hooks via `.codex/hooks.json`; operator trust is required before first execution.',
|
|
67
|
+
},
|
|
58
68
|
cursor: {
|
|
59
69
|
maturity: 'beta',
|
|
60
70
|
preToolEdit: 'rule-injection',
|
|
@@ -105,7 +105,7 @@ export default {
|
|
|
105
105
|
id: 'rune',
|
|
106
106
|
name: 'Rune',
|
|
107
107
|
kind: 'skills',
|
|
108
|
-
description: `${skills.length}-skill mesh for AI coding assistants. Routes all code tasks through specialized skills.
|
|
108
|
+
description: `${skills.length}-skill mesh for AI coding assistants. Routes all code tasks through specialized skills. 248 sync connections + 45 async signals, 14 extension packs.`,
|
|
109
109
|
version: pluginJson.version || '0.0.0',
|
|
110
110
|
skills: ['./skills'],
|
|
111
111
|
artifactConvention: {
|
|
@@ -170,7 +170,7 @@ export default {
|
|
|
170
170
|
|
|
171
171
|
> Less skills. Deeper connections.
|
|
172
172
|
|
|
173
|
-
**${skills.length}-skill mesh** for AI coding assistants — 5-layer architecture,
|
|
173
|
+
**${skills.length}-skill mesh** for AI coding assistants — 5-layer architecture, 248 connections + 45 signals, 14 extension packs.
|
|
174
174
|
|
|
175
175
|
## Install
|
|
176
176
|
|
package/compiler/bin/rune.js
CHANGED
|
@@ -25,6 +25,7 @@ import { installHooks } from '../commands/hooks/install.js';
|
|
|
25
25
|
import { hookStatus } from '../commands/hooks/status.js';
|
|
26
26
|
import { uninstallHooks } from '../commands/hooks/uninstall.js';
|
|
27
27
|
import { formatSetupResult, runSetup } from '../commands/setup.js';
|
|
28
|
+
import { formatUpdateResult, runUpdate } from '../commands/update.js';
|
|
28
29
|
import { generateComprehensionHTML } from '../comprehension.js';
|
|
29
30
|
import { generateDashboardHTML } from '../dashboard.js';
|
|
30
31
|
import { checkMeshIntegrity, formatDoctorResults, formatMeshResults, runDoctor } from '../doctor.js';
|
|
@@ -63,9 +64,10 @@ function detectPlatform(projectRoot) {
|
|
|
63
64
|
if (existsSync(path.join(projectRoot, '.claude-plugin'))) return 'claude';
|
|
64
65
|
if (existsSync(path.join(projectRoot, '.cursor'))) return 'cursor';
|
|
65
66
|
if (existsSync(path.join(projectRoot, '.windsurf'))) return 'windsurf';
|
|
66
|
-
if (existsSync(path.join(projectRoot, '.agents'))) return 'antigravity';
|
|
67
|
-
if (existsSync(path.join(projectRoot, '.openclaw'))) return 'openclaw';
|
|
68
67
|
if (existsSync(path.join(projectRoot, '.codex'))) return 'codex';
|
|
68
|
+
if (existsSync(path.join(projectRoot, '.agents', 'skills'))) return 'codex';
|
|
69
|
+
if (existsSync(path.join(projectRoot, '.antigravity'))) return 'antigravity';
|
|
70
|
+
if (existsSync(path.join(projectRoot, '.openclaw'))) return 'openclaw';
|
|
69
71
|
if (existsSync(path.join(projectRoot, '.opencode'))) return 'opencode';
|
|
70
72
|
return null;
|
|
71
73
|
}
|
|
@@ -337,6 +339,18 @@ async function cmdSetup(projectRoot, args) {
|
|
|
337
339
|
}
|
|
338
340
|
}
|
|
339
341
|
|
|
342
|
+
async function cmdUpdate(projectRoot, args) {
|
|
343
|
+
try {
|
|
344
|
+
const result = await runUpdate({ projectRoot, runeRoot: RUNE_ROOT, args });
|
|
345
|
+
log(formatUpdateResult(result));
|
|
346
|
+
if (!result.ok) process.exit(1);
|
|
347
|
+
} catch (err) {
|
|
348
|
+
log('');
|
|
349
|
+
log(` ✗ Update failed: ${err.message}`);
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
340
354
|
async function readVersion() {
|
|
341
355
|
try {
|
|
342
356
|
const pkg = JSON.parse(await readFile(path.join(RUNE_ROOT, 'package.json'), 'utf-8'));
|
|
@@ -655,7 +669,7 @@ async function cmdHooks(projectRoot, args, subcommand) {
|
|
|
655
669
|
' status [--platform <name>|all] [--tier pro|business] Show active preset, wired skills, tier coverage',
|
|
656
670
|
);
|
|
657
671
|
log('');
|
|
658
|
-
log(' Platforms: claude, cursor, windsurf, antigravity (auto-detected if omitted)');
|
|
672
|
+
log(' Platforms: claude, codex, cursor, windsurf, antigravity (auto-detected if omitted)');
|
|
659
673
|
log(' Tiers: pro, business — requires $RUNE_PRO_ROOT / $RUNE_BUSINESS_ROOT env var or monorepo sibling.');
|
|
660
674
|
log(' Options:');
|
|
661
675
|
log(' --dry Preview changes without writing');
|
|
@@ -851,6 +865,9 @@ async function main() {
|
|
|
851
865
|
case 'setup':
|
|
852
866
|
await cmdSetup(projectRoot, args);
|
|
853
867
|
break;
|
|
868
|
+
case 'update':
|
|
869
|
+
await cmdUpdate(projectRoot, args);
|
|
870
|
+
break;
|
|
854
871
|
case 'status':
|
|
855
872
|
await cmdStatus(projectRoot, args);
|
|
856
873
|
break;
|
|
@@ -886,6 +903,9 @@ async function main() {
|
|
|
886
903
|
' setup Interactive wizard — auto-detect tiers, pick scope, install hooks (recommended for first-time)',
|
|
887
904
|
);
|
|
888
905
|
log(' [--here|--global] [--tier pro,business] [--preset gentle|strict] [--dry]');
|
|
906
|
+
log(' update Update an existing install — git-pull detected tier repos, re-run the managed');
|
|
907
|
+
log(' setup rewrite in place (reuses installed platforms/preset/tiers), verify with doctor');
|
|
908
|
+
log(' [--no-pull] [--preset gentle|strict] [--tier pro,business] [--dry]');
|
|
889
909
|
log(' init Interactive setup for build pipeline (auto-detects platform)');
|
|
890
910
|
log(' build Compile skills for configured platform');
|
|
891
911
|
log(' doctor Validate compiled output + mesh integrity');
|
|
@@ -899,7 +919,7 @@ async function main() {
|
|
|
899
919
|
log(' [--days <n>] Lookback window for gate counts (default: 30)');
|
|
900
920
|
log(' hooks Install/uninstall/status for multi-platform auto-discipline');
|
|
901
921
|
log(
|
|
902
|
-
' hooks install [--preset gentle|strict|off] [--platform claude|cursor|windsurf|antigravity|all] [--global]',
|
|
922
|
+
' hooks install [--preset gentle|strict|off] [--platform claude|codex|cursor|windsurf|antigravity|all] [--global]',
|
|
903
923
|
);
|
|
904
924
|
log(' hooks uninstall [--platform <name>|all]');
|
|
905
925
|
log(' hooks status [--platform <name>|all]');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `rune hooks install [--preset strict|gentle|off] [--platform claude|cursor|windsurf|antigravity|all]`
|
|
2
|
+
* `rune hooks install [--preset strict|gentle|off] [--platform claude|codex|cursor|windsurf|antigravity|all]`
|
|
3
3
|
*
|
|
4
4
|
* Writes Rune-managed hook/rule/workflow entries for one or more platforms.
|
|
5
5
|
* Idempotent: re-running replaces existing Rune entries, preserves user entries.
|
|
@@ -41,7 +41,7 @@ export async function installHooks(projectRoot, args = {}) {
|
|
|
41
41
|
results: [],
|
|
42
42
|
written: false,
|
|
43
43
|
notes: [
|
|
44
|
-
'No target platform detected. Create `.claude/`, `.cursor/`, `.windsurf/`, or `.antigravity/` first, or pass `--platform <name>`.',
|
|
44
|
+
'No target platform detected. Create `.claude/`, `.codex/`, `.cursor/`, `.windsurf/`, or `.antigravity/` first, or pass `--platform <name>`.',
|
|
45
45
|
...tierNotes,
|
|
46
46
|
],
|
|
47
47
|
};
|
|
@@ -34,7 +34,7 @@ export async function hookStatus(projectRoot, runeRoot, args = {}) {
|
|
|
34
34
|
tiers,
|
|
35
35
|
missingInRepo,
|
|
36
36
|
notes: [
|
|
37
|
-
'No target platform detected. Create `.claude/`, `.cursor/`, `.windsurf/`, or `.antigravity/` first, or pass `--platform <name>`.',
|
|
37
|
+
'No target platform detected. Create `.claude/`, `.codex/`, `.cursor/`, `.windsurf/`, or `.antigravity/` first, or pass `--platform <name>`.',
|
|
38
38
|
],
|
|
39
39
|
};
|
|
40
40
|
}
|