@hanzlaa/rcode 4.10.5 ā 4.11.0
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/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/CONTRIBUTING.md +3 -0
- package/cli/install.js +272 -1
- package/dist/rcode.js +259 -236
- package/package.json +1 -1
- package/rcode/agents/rcode-project-researcher.md +1 -1
- package/rcode/agents/rcode-ux-designer.md +1 -0
- package/rcode/agents/rcode-verifier.md +2 -2
- package/rcode/agents/rules/planner/common-patterns.md +2 -1
- package/rcode/agents/rules/planner/goal-backward-thinking.md +33 -0
- package/rcode/agents/rules/planner/sprint-verification.md +1 -0
- package/rcode/agents/rules/planner/task-templates.md +20 -1
- package/rcode/agents/rules/project-researcher/detailed-guide.md +10 -0
- package/rcode/agents/rules/sprint-checker/dimensions.md +74 -0
- package/rcode/agents/rules/sprint-checker/process.md +12 -0
- package/rcode/agents/rules/verifier/behavioral-spot-checks.md +5 -1
- package/rcode/agents/rules/verifier/gap-output.md +5 -0
- package/rcode/agents/rules/verifier/reachability-check.md +69 -0
- package/rcode/bin/rcode-tools.cjs +39 -3
- package/rcode/commands/ui-phase.md +1 -1
- package/rcode/references/design-library/LICENSE +21 -0
- package/rcode/references/design-library/README.md +37 -0
- package/rcode/references/design-library/charts.csv +26 -0
- package/rcode/references/design-library/colors.csv +97 -0
- package/rcode/references/design-library/icons.csv +101 -0
- package/rcode/references/design-library/styles.csv +68 -0
- package/rcode/references/design-library/typography.csv +58 -0
- package/rcode/references/design-library/ui-reasoning.csv +101 -0
- package/rcode/references/design-library/ux-guidelines.csv +100 -0
- package/rcode/references/design-library/web-interface.csv +31 -0
- package/rcode/references/domain-probes.md +8 -0
- package/rcode/references/project-types.yaml +29 -0
- package/rcode/references/questioning.md +3 -0
- package/rcode/references/roadmapper-playbook.md +90 -0
- package/rcode/references/sprint-checker-playbook.md +14 -0
- package/rcode/references/verifier-playbook.md +20 -7
- package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/SKILL.md +10 -1
- package/rcode/skills/actions/4-implementation/rcode-herdr-orchestration/templates/wave-prompt.md +2 -0
- package/rcode/workflows/autonomous.md +26 -3
- package/rcode/workflows/complete-milestone.md +1 -1
- package/rcode/workflows/council.md +51 -6
- package/rcode/workflows/discuss-phase.md +9 -2
- package/rcode/workflows/execute-sprint.md +35 -7
- package/rcode/workflows/execute-waves.md +2 -2
- package/rcode/workflows/execute.md +21 -6
- package/rcode/workflows/help.md +1 -1
- package/rcode/workflows/plan-research-validation.md +1 -1
- package/rcode/workflows/plan.md +31 -0
- package/rcode/workflows/ship.md +22 -0
- package/rcode/workflows/ui-phase.md +104 -22
- package/rcode/workflows/ui-review.md +9 -1
- package/rcode/workflows/verify-work.md +1 -1
- package/server/dashboard.js +18 -1
- package/server/lib/scanner.js +25 -4
package/AGENTS.md
CHANGED
|
@@ -24,7 +24,7 @@ If a user says "just keep going" or "don't stop until done", that authorization
|
|
|
24
24
|
|
|
25
25
|
- Follow [Conventional Commits](https://www.conventionalcommits.org/) format: `type(scope): subject`
|
|
26
26
|
- Types allowed: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `perf`, `revert`
|
|
27
|
-
- Scopes allowed: `agents`, `skills`, `workflows`, `templates`, `dashboard`, `docs`, `config`, `github`, `github-sync`, `commands`, `memory`, `brand`, `cli`, `ci`, `release`, `meta`, `tasks`, `migrations`, `refs`, `state`, `hooks`, `init`, `install`, `parity`, `triggers`, `dogfood`, `namespace`, `planning`, `insights`, `help`, `roadmap`, `session`, `audits`, `execute`, `executor`, `plan`, `planner`, `readme`, `rcode`, `review`, `sync`, `sprint`, `agent-exp`, `extensibility`, `lens-audit`, `tiers`, `build`, `council`, `doctor`, `postinstall`, `progress`, `security`, `tools`, `uninstall`, `update`, `test`, `changelog`, `scopes`, `phases`, `references`, `kanban`, `orchestrator`, `orchpanel`, `status`, `bin`, `brain`, `dogfeed`, `new-project`, `package`, `rcode-tools`, `rihal-tools`, `team`, `usp`, `v4`, `observability`, `audit`, `agent-rules`, `cursor`, `i18n`, `phase`, `scaffold`, `campaign`, `ship`, `getting-started`, `do-router`, `milestone-health`, `modules`, `project-types`, `roadmapper`, `token`, `benchmarks`, `eval`, `scan`, plus numeric phase/sprint scopes (e.g. `docs(15)`, `feat(8.3)`)
|
|
27
|
+
- Scopes allowed: `agents`, `skills`, `workflows`, `templates`, `dashboard`, `docs`, `config`, `github`, `github-sync`, `commands`, `memory`, `brand`, `cli`, `ci`, `release`, `meta`, `tasks`, `migrations`, `refs`, `state`, `hooks`, `init`, `install`, `parity`, `triggers`, `dogfood`, `namespace`, `planning`, `insights`, `help`, `roadmap`, `session`, `audits`, `execute`, `executor`, `plan`, `planner`, `readme`, `rcode`, `review`, `sync`, `sprint`, `agent-exp`, `extensibility`, `lens-audit`, `tiers`, `build`, `council`, `doctor`, `postinstall`, `progress`, `security`, `tools`, `uninstall`, `update`, `test`, `changelog`, `scopes`, `phases`, `references`, `kanban`, `orchestrator`, `orchpanel`, `status`, `bin`, `brain`, `dogfeed`, `new-project`, `package`, `rcode-tools`, `rihal-tools`, `team`, `usp`, `v4`, `observability`, `audit`, `agent-rules`, `cursor`, `i18n`, `phase`, `scaffold`, `campaign`, `ship`, `getting-started`, `do-router`, `milestone-health`, `modules`, `project-types`, `roadmapper`, `token`, `benchmarks`, `eval`, `scan`, `verifier`, `discuss-phase`, `ui-phase`, plus numeric phase/sprint scopes (e.g. `docs(15)`, `feat(8.3)`)
|
|
28
28
|
- Subject: lowercase first letter, imperative mood, no trailing period, under 72 chars
|
|
29
29
|
- **NEVER add Claude/AI attribution to commit messages.** No "Generated with Claude Code", no "Co-Authored-By: Claude", no "š¤ Generated". The user does not want this.
|
|
30
30
|
- **NEVER use `--no-verify`** to bypass hooks. If hooks fail, fix the underlying issue.
|
package/CLAUDE.md
CHANGED
|
@@ -24,7 +24,7 @@ If a user says "just keep going" or "don't stop until done", that authorization
|
|
|
24
24
|
|
|
25
25
|
- Follow [Conventional Commits](https://www.conventionalcommits.org/) format: `type(scope): subject`
|
|
26
26
|
- Types allowed: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `perf`, `revert`
|
|
27
|
-
- Scopes allowed: `agents`, `skills`, `workflows`, `templates`, `dashboard`, `docs`, `config`, `github`, `github-sync`, `commands`, `memory`, `brand`, `cli`, `ci`, `release`, `meta`, `tasks`, `migrations`, `refs`, `state`, `hooks`, `install`, `parity`, `triggers`, `dogfood`, `namespace`, `planning`, `insights`, `help`, `roadmap`, `session`, `audits`, `execute`, `executor`, `plan`, `planner`, `readme`, `rcode`, `review`, `sync`, `sprint`, `agent-exp`, `extensibility`, `lens-audit`, `tiers`, `build`, `council`, `doctor`, `postinstall`, `progress`, `security`, `tools`, `uninstall`, `update`, `test`, `changelog`, `scopes`, `phases`, `references`, `kanban`, `orchestrator`, `orchpanel`, `status`, `bin`, `brain`, `dogfeed`, `new-project`, `package`, `rcode-tools`, `rihal-tools`, `team`, `usp`, `v4`, `observability`, `audit`, `init`, `agent-rules`, `cursor`, `i18n`, `phase`, `scaffold`, `campaign`, `ship`, `getting-started`, `do-router`, `milestone-health`, `modules`, `project-types`, `roadmapper`, `token`, plus numeric phase/sprint scopes (e.g. `docs(15)`, `feat(8.3)`)
|
|
27
|
+
- Scopes allowed: `agents`, `skills`, `workflows`, `templates`, `dashboard`, `docs`, `config`, `github`, `github-sync`, `commands`, `memory`, `brand`, `cli`, `ci`, `release`, `meta`, `tasks`, `migrations`, `refs`, `state`, `hooks`, `install`, `parity`, `triggers`, `dogfood`, `namespace`, `planning`, `insights`, `help`, `roadmap`, `session`, `audits`, `execute`, `executor`, `plan`, `planner`, `readme`, `rcode`, `review`, `sync`, `sprint`, `agent-exp`, `extensibility`, `lens-audit`, `tiers`, `build`, `council`, `doctor`, `postinstall`, `progress`, `security`, `tools`, `uninstall`, `update`, `test`, `changelog`, `scopes`, `phases`, `references`, `kanban`, `orchestrator`, `orchpanel`, `status`, `bin`, `brain`, `dogfeed`, `new-project`, `package`, `rcode-tools`, `rihal-tools`, `team`, `usp`, `v4`, `observability`, `audit`, `init`, `agent-rules`, `cursor`, `i18n`, `phase`, `scaffold`, `campaign`, `ship`, `getting-started`, `do-router`, `milestone-health`, `modules`, `project-types`, `roadmapper`, `token`, `verifier`, `discuss-phase`, `ui-phase`, plus numeric phase/sprint scopes (e.g. `docs(15)`, `feat(8.3)`)
|
|
28
28
|
- Subject: lowercase first letter, imperative mood, no trailing period, under 72 chars
|
|
29
29
|
- **NEVER add Claude/AI attribution to commit messages.** No "Generated with Claude Code", no "Co-Authored-By: Claude", no "š¤ Generated". The user does not want this.
|
|
30
30
|
- **NEVER use `--no-verify`** to bypass hooks. If hooks fail, fix the underlying issue.
|
package/CONTRIBUTING.md
CHANGED
|
@@ -366,6 +366,9 @@ We use [Conventional Commits](https://www.conventionalcommits.org/) format. The
|
|
|
366
366
|
- `benchmarks` ā `benchmarks/` scripts and benchmark reports
|
|
367
367
|
- `eval` ā `test/eval/` behavioral drift baselines and eval harness
|
|
368
368
|
- `scan` ā `/rcode-scan` workflow and existing-codebase analysis
|
|
369
|
+
- `verifier` ā `rcode-verifier` agent, verification playbook/rules, VERIFICATION.md flow
|
|
370
|
+
- `discuss-phase` ā `/rcode-discuss-phase` workflow and its gray-area/standing checks
|
|
371
|
+
- `ui-phase` ā `/rcode-ui-phase` workflow, UI-SPEC.md/WIREFRAMES.md generation, design-library
|
|
369
372
|
- `<phase-id>` ā numeric phase scope when committing inside a phase (e.g. `docs(15)`, `feat(8.3)`)
|
|
370
373
|
- `<sprint-id>` ā numeric sprint scope inside a phase (e.g. `feat(15.1)`)
|
|
371
374
|
|
package/cli/install.js
CHANGED
|
@@ -159,7 +159,9 @@ const ConfigSchema = z.object({
|
|
|
159
159
|
branching_strategy: z.string().optional(),
|
|
160
160
|
}).optional(),
|
|
161
161
|
// Declared for validation only ā default ('every') lives in the hook (rcode-hooks.cjs prompt-router).
|
|
162
|
-
// Install does NOT write this key; the
|
|
162
|
+
// Install does NOT write this key; the nudge behavior itself is controlled by
|
|
163
|
+
// whether hooks were enabled at install time (resolveEnableHooks / --no-hooks)
|
|
164
|
+
// or later via /rcode-enable-hooks.
|
|
163
165
|
prompt_nudge: z.enum(['every', 'once-per-intent', 'when-stale', 'off']).optional(),
|
|
164
166
|
}).passthrough();
|
|
165
167
|
|
|
@@ -199,6 +201,10 @@ function parseArgs(argv) {
|
|
|
199
201
|
// #199 ā git pre-commit hook. null = install if .git/ present (default).
|
|
200
202
|
// Set false by --no-git-hooks, true by --git-hooks.
|
|
201
203
|
gitHooks: null,
|
|
204
|
+
// Claude Code guardrail hooks (.claude/settings.json). null = resolve via
|
|
205
|
+
// resolveEnableHooks() (interactive prompt, or default-on for --yes/non-TTY).
|
|
206
|
+
// Set false by --no-hooks, true by --enable-hooks.
|
|
207
|
+
enableHooks: null,
|
|
202
208
|
// global install mode ā targets ~/.claude/, skips per-project artifacts
|
|
203
209
|
global: false,
|
|
204
210
|
// silent ā suppress non-error output (used by postinstall auto-run)
|
|
@@ -242,6 +248,8 @@ function parseArgs(argv) {
|
|
|
242
248
|
else if (arg === '--no-backup') opts.noBackup = true; // #381
|
|
243
249
|
else if (arg === '--no-git-hooks') opts.gitHooks = false; // #199
|
|
244
250
|
else if (arg === '--git-hooks') opts.gitHooks = true; // #199
|
|
251
|
+
else if (arg === '--no-hooks') opts.enableHooks = false;
|
|
252
|
+
else if (arg === '--enable-hooks') opts.enableHooks = true;
|
|
245
253
|
else if (arg === '--global') opts.global = true;
|
|
246
254
|
else if (arg === '--local-only') opts.localOnly = true; // #938 ā force self-contained install (don't defer to global skills)
|
|
247
255
|
else if (arg === '--silent') opts.silent = true;
|
|
@@ -526,6 +534,91 @@ async function resolveCommitPlanning(opts) {
|
|
|
526
534
|
return choice === 'commit';
|
|
527
535
|
}
|
|
528
536
|
|
|
537
|
+
/**
|
|
538
|
+
* Resolve whether to merge rcode's guardrail hooks (pre-edit, bash-guard,
|
|
539
|
+
* prompt-router, etc.) into .claude/settings.json at install time. Default
|
|
540
|
+
* is ON ā flag wins, else interactive confirm (Y default) on TTY installs,
|
|
541
|
+
* else default-on for --yes/--no-prompt/non-TTY runs so hooks work out of
|
|
542
|
+
* the box without requiring a separate /rcode-enable-hooks step.
|
|
543
|
+
*/
|
|
544
|
+
async function resolveEnableHooks(opts) {
|
|
545
|
+
if (opts.enableHooks !== null) return opts.enableHooks;
|
|
546
|
+
if (opts.global) return false; // global install has no project-local .claude/settings.json target
|
|
547
|
+
if (!opts.ides.includes('claude')) return false; // hooks are Claude Code specific
|
|
548
|
+
if (opts.noPrompt || opts.yes || !process.stdin.isTTY) return true;
|
|
549
|
+
|
|
550
|
+
const enable = await clack.confirm({
|
|
551
|
+
message: 'š”ļø Enable rcode guardrail hooks in .claude/settings.json? (pre-edit checks, bash-guard, prompt-router, etc.)',
|
|
552
|
+
initialValue: true,
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
if (clack.isCancel(enable)) {
|
|
556
|
+
clack.cancel('Install cancelled.');
|
|
557
|
+
process.exit(0);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return enable;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Merge rcode's opt-in guardrail hooks (rcode/templates/settings-hooks.json)
|
|
565
|
+
* into .claude/settings.json. Idempotent ā skips matcher+command pairs that
|
|
566
|
+
* already exist. Mirrors the /rcode-enable-hooks workflow so a fresh install
|
|
567
|
+
* doesn't require running that command separately.
|
|
568
|
+
*
|
|
569
|
+
* Returns: { action: 'merged' | 'skipped-flag' | 'skipped-template-missing' | 'skipped-error' }
|
|
570
|
+
*/
|
|
571
|
+
function ensureRcodeSettingsHooks(target, options = {}) {
|
|
572
|
+
if (options.enableHooks !== true) return { action: 'skipped-flag' };
|
|
573
|
+
|
|
574
|
+
const templatePath = path.join(PACKAGE_ROOT, 'rcode', 'templates', 'settings-hooks.json');
|
|
575
|
+
if (!fs.existsSync(templatePath)) return { action: 'skipped-template-missing' };
|
|
576
|
+
|
|
577
|
+
let template;
|
|
578
|
+
try {
|
|
579
|
+
template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
|
|
580
|
+
} catch {
|
|
581
|
+
return { action: 'skipped-error' };
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const settingsDir = path.join(target, '.claude');
|
|
585
|
+
const settingsPath = path.join(settingsDir, 'settings.json');
|
|
586
|
+
|
|
587
|
+
let settings = {};
|
|
588
|
+
if (fs.existsSync(settingsPath)) {
|
|
589
|
+
try {
|
|
590
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
591
|
+
} catch {
|
|
592
|
+
return { action: 'skipped-error' };
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
settings.hooks = settings.hooks || {};
|
|
597
|
+
|
|
598
|
+
for (const [hookType, matchers] of Object.entries(template.hooks || {})) {
|
|
599
|
+
settings.hooks[hookType] = settings.hooks[hookType] || [];
|
|
600
|
+
for (const incoming of matchers) {
|
|
601
|
+
let existingMatcher = settings.hooks[hookType].find((m) => m.matcher === incoming.matcher);
|
|
602
|
+
if (!existingMatcher) {
|
|
603
|
+
existingMatcher = { matcher: incoming.matcher, hooks: [] };
|
|
604
|
+
settings.hooks[hookType].push(existingMatcher);
|
|
605
|
+
}
|
|
606
|
+
for (const hook of incoming.hooks) {
|
|
607
|
+
const dup = existingMatcher.hooks.some((h) => h.command === hook.command && h.type === hook.type);
|
|
608
|
+
if (!dup) existingMatcher.hooks.push(hook);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
try {
|
|
614
|
+
fs.mkdirSync(settingsDir, { recursive: true });
|
|
615
|
+
writeFileAtomic(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
616
|
+
return { action: 'merged' };
|
|
617
|
+
} catch {
|
|
618
|
+
return { action: 'skipped-error' };
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
529
622
|
function printHelp() {
|
|
530
623
|
console.log(`
|
|
531
624
|
rcode installer
|
|
@@ -542,6 +635,8 @@ Options:
|
|
|
542
635
|
--language <lang> set communication_language (default: English)
|
|
543
636
|
--mode <guided|yolo> default mode (default: guided)
|
|
544
637
|
--ide <name> target IDE (claude, cursor, gemini; default: claude)
|
|
638
|
+
--enable-hooks merge rcode guardrail hooks into .claude/settings.json (default: on)
|
|
639
|
+
--no-hooks skip guardrail hooks; enable later via /rcode-enable-hooks
|
|
545
640
|
--dry-run preview what would be written; exit without writing any files
|
|
546
641
|
--list-files alias for --dry-run
|
|
547
642
|
--help this text
|
|
@@ -969,6 +1064,147 @@ function ensureRcodeGitignore(target, options = {}) {
|
|
|
969
1064
|
}
|
|
970
1065
|
}
|
|
971
1066
|
|
|
1067
|
+
/**
|
|
1068
|
+
* Splice a BEGIN/END-delimited block into a text file, creating the file if
|
|
1069
|
+
* missing, replacing the block in place if a prior version exists, or
|
|
1070
|
+
* appending if the file exists without the block yet. Shared by any
|
|
1071
|
+
* "own one marked section of a user-owned file" writer (gitignore, rule
|
|
1072
|
+
* files, etc.) so the splice logic (and its edge cases ā missing END,
|
|
1073
|
+
* trailing newlines) lives in one place.
|
|
1074
|
+
*
|
|
1075
|
+
* Returns: { action: 'created' | 'appended' | 'updated' | 'already-present' | 'skipped-error', error? }
|
|
1076
|
+
*/
|
|
1077
|
+
function spliceMarkedBlockIntoFile(filePath, begin, end, block) {
|
|
1078
|
+
try {
|
|
1079
|
+
if (!fs.existsSync(filePath)) {
|
|
1080
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
1081
|
+
writeFileAtomic(filePath, block);
|
|
1082
|
+
return { action: 'created' };
|
|
1083
|
+
}
|
|
1084
|
+
const existing = fs.readFileSync(filePath, 'utf8');
|
|
1085
|
+
|
|
1086
|
+
function splice(text, newBlock) {
|
|
1087
|
+
const start = text.indexOf(begin);
|
|
1088
|
+
if (start < 0) return null;
|
|
1089
|
+
const endIdx = text.indexOf(end, start);
|
|
1090
|
+
if (endIdx < 0) {
|
|
1091
|
+
let sliceStart = start;
|
|
1092
|
+
if (sliceStart > 0 && text[sliceStart - 1] === '\n') sliceStart -= 1;
|
|
1093
|
+
return text.slice(0, sliceStart) + newBlock;
|
|
1094
|
+
}
|
|
1095
|
+
let sliceStart = start;
|
|
1096
|
+
if (sliceStart > 0 && text[sliceStart - 1] === '\n') sliceStart -= 1;
|
|
1097
|
+
let sliceEnd = endIdx + end.length;
|
|
1098
|
+
if (text[sliceEnd] === '\n') sliceEnd += 1;
|
|
1099
|
+
return text.slice(0, sliceStart) + newBlock + text.slice(sliceEnd);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
if (existing.includes(begin)) {
|
|
1103
|
+
const rewritten = splice(existing, block);
|
|
1104
|
+
if (rewritten !== null && rewritten !== existing) {
|
|
1105
|
+
writeFileAtomic(filePath, rewritten);
|
|
1106
|
+
return { action: 'updated' };
|
|
1107
|
+
}
|
|
1108
|
+
return { action: 'already-present' };
|
|
1109
|
+
}
|
|
1110
|
+
const sep = existing.endsWith('\n') ? '\n' : '\n\n';
|
|
1111
|
+
writeFileAtomic(filePath, existing + sep + block);
|
|
1112
|
+
return { action: 'appended' };
|
|
1113
|
+
} catch (err) {
|
|
1114
|
+
return { action: 'skipped-error', error: err.message };
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Ensure every installed IDE's rule file carries a short, rcode-owned block
|
|
1120
|
+
* pointing agents at `/rcode-do` as the preferred entry point for non-trivial
|
|
1121
|
+
* work. This is NOT a full CLAUDE.md/AGENTS.md rewrite ā it only owns its own
|
|
1122
|
+
* marked section (or, for cursor/windsurf, a dedicated rcode-* rule file) so
|
|
1123
|
+
* a project's existing rule content is never touched or reordered.
|
|
1124
|
+
*
|
|
1125
|
+
* - claude / vscode ā root CLAUDE.md (splice, file created if missing)
|
|
1126
|
+
* - codex ā root AGENTS.md (splice, file created if missing)
|
|
1127
|
+
* - cursor ā .cursor/rules/rcode-prefer-do.mdc (dedicated, always rewritten)
|
|
1128
|
+
* - windsurf ā .windsurf/rules/rcode-prefer-do.mdc (dedicated, always rewritten)
|
|
1129
|
+
* - gemini / antigravity / grok ā skipped (no single canonical project rule file yet)
|
|
1130
|
+
*
|
|
1131
|
+
* Returns: { [ide]: { action, error? } } per IDE actually written.
|
|
1132
|
+
*/
|
|
1133
|
+
function ensureRcodePreferredCommandRule(target, ides) {
|
|
1134
|
+
const results = {};
|
|
1135
|
+
const idSet = new Set(ides || []);
|
|
1136
|
+
|
|
1137
|
+
const md = (heading) => [
|
|
1138
|
+
`## ${heading}`,
|
|
1139
|
+
'',
|
|
1140
|
+
'This project has [rcode](https://www.npmjs.com/package/@hanzlaa/rcode) installed ā',
|
|
1141
|
+
'persistent project memory, specialist agents, and structured workflows under `.rcode/`.',
|
|
1142
|
+
'',
|
|
1143
|
+
'For any non-trivial task (new feature, bug fix that needs investigation, multi-file',
|
|
1144
|
+
'change, planning) prefer routing through **`/rcode-do <task description>`** ā rcode\'s',
|
|
1145
|
+
'command picker ā instead of working ad hoc. It picks the right rcode command (plan,',
|
|
1146
|
+
'execute, review, debug, etc.) for the task and keeps `.rcode/state.json` and the',
|
|
1147
|
+
'Memory Bank in sync. Skip it only for trivial single-line/single-file edits that',
|
|
1148
|
+
'don\'t need planning or memory.',
|
|
1149
|
+
].join('\n');
|
|
1150
|
+
|
|
1151
|
+
if (idSet.has('claude') || idSet.has('vscode')) {
|
|
1152
|
+
const BEGIN = '<!-- ===== rcode-managed rule block (npx @hanzlaa/rcode install) ===== -->';
|
|
1153
|
+
const END = '<!-- ===== end rcode-managed rule block ===== -->';
|
|
1154
|
+
const block = `${BEGIN}\n\n${md('Working with rcode')}\n\n${END}\n`;
|
|
1155
|
+
results.claude = spliceMarkedBlockIntoFile(path.join(target, 'CLAUDE.md'), BEGIN, END, block);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
if (idSet.has('codex')) {
|
|
1159
|
+
const BEGIN = '<!-- ===== rcode-managed rule block (npx @hanzlaa/rcode install) ===== -->';
|
|
1160
|
+
const END = '<!-- ===== end rcode-managed rule block ===== -->';
|
|
1161
|
+
const block = `${BEGIN}\n\n${md('Working with rcode')}\n\n${END}\n`;
|
|
1162
|
+
results.codex = spliceMarkedBlockIntoFile(path.join(target, 'AGENTS.md'), BEGIN, END, block);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (idSet.has('cursor')) {
|
|
1166
|
+
const content = [
|
|
1167
|
+
'---',
|
|
1168
|
+
'description: Prefer rcode\'s /rcode-do command for non-trivial work',
|
|
1169
|
+
'alwaysApply: true',
|
|
1170
|
+
'---',
|
|
1171
|
+
'',
|
|
1172
|
+
md('Working with rcode'),
|
|
1173
|
+
'',
|
|
1174
|
+
].join('\n');
|
|
1175
|
+
try {
|
|
1176
|
+
const p = path.join(target, '.cursor', 'rules', 'rcode-prefer-do.mdc');
|
|
1177
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
1178
|
+
writeFileAtomic(p, content);
|
|
1179
|
+
results.cursor = { action: 'written' };
|
|
1180
|
+
} catch (err) {
|
|
1181
|
+
results.cursor = { action: 'skipped-error', error: err.message };
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
if (idSet.has('windsurf')) {
|
|
1186
|
+
const content = [
|
|
1187
|
+
'---',
|
|
1188
|
+
'description: Prefer rcode\'s /rcode-do command for non-trivial work',
|
|
1189
|
+
'trigger: always_on',
|
|
1190
|
+
'---',
|
|
1191
|
+
'',
|
|
1192
|
+
md('Working with rcode'),
|
|
1193
|
+
'',
|
|
1194
|
+
].join('\n');
|
|
1195
|
+
try {
|
|
1196
|
+
const p = path.join(target, '.windsurf', 'rules', 'rcode-prefer-do.mdc');
|
|
1197
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
1198
|
+
writeFileAtomic(p, content);
|
|
1199
|
+
results.windsurf = { action: 'written' };
|
|
1200
|
+
} catch (err) {
|
|
1201
|
+
results.windsurf = { action: 'skipped-error', error: err.message };
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
return results;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
972
1208
|
/**
|
|
973
1209
|
* Ensure .git/hooks/pre-commit includes the rcode-managed block that auto-syncs
|
|
974
1210
|
* state.json when .planning/ or .rcode/brain/sources.yaml files change.
|
|
@@ -2071,6 +2307,9 @@ async function installInner(opts) {
|
|
|
2071
2307
|
// Resolve commit-planning preference (interactive prompt or flag) ā #189.
|
|
2072
2308
|
opts.commitPlanning = await resolveCommitPlanning(opts);
|
|
2073
2309
|
|
|
2310
|
+
// Resolve guardrail-hooks preference (interactive prompt or flag). Default on.
|
|
2311
|
+
opts.enableHooks = await resolveEnableHooks(opts);
|
|
2312
|
+
|
|
2074
2313
|
console.log(`\nš ${bold('rcode')} ${pc.cyan('v' + pkgVersion)} ${dim('ā')} ${opts.target}`);
|
|
2075
2314
|
|
|
2076
2315
|
// Detect an existing install and surface it (#195).
|
|
@@ -2771,6 +3010,14 @@ async function installInner(opts) {
|
|
|
2771
3010
|
// Respects --no-git-hooks flag; skips silently when .git/ is absent.
|
|
2772
3011
|
const hookReport = ensureRcodePreCommitHook(opts.target, { gitHooks: opts.gitHooks });
|
|
2773
3012
|
|
|
3013
|
+
// Merge rcode guardrail hooks into .claude/settings.json (pre-edit, bash-guard,
|
|
3014
|
+
// prompt-router, etc). Default-on; resolved above via resolveEnableHooks().
|
|
3015
|
+
const settingsHooksReport = ensureRcodeSettingsHooks(opts.target, { enableHooks: opts.enableHooks });
|
|
3016
|
+
|
|
3017
|
+
// Point each installed IDE's rule file at /rcode-do as the preferred entry point
|
|
3018
|
+
// for non-trivial work ā an rcode-owned marked block/file, not a full rewrite.
|
|
3019
|
+
const preferredCommandReports = ensureRcodePreferredCommandRule(opts.target, opts.ides);
|
|
3020
|
+
|
|
2774
3021
|
// Pull rcode brain content (v2.0 ā issue #158).
|
|
2775
3022
|
// Runs rcode-tools brain pull as a detached background process. Placeholder
|
|
2776
3023
|
// URLs are skipped gracefully so this does not fail a fresh install.
|
|
@@ -2835,6 +3082,30 @@ async function installInner(opts) {
|
|
|
2835
3082
|
}[hookReport.action] || 'pre-commit hook unchanged';
|
|
2836
3083
|
console.log(' ' + dim(hookMsg));
|
|
2837
3084
|
}
|
|
3085
|
+
if (settingsHooksReport) {
|
|
3086
|
+
const settingsHooksMsg = {
|
|
3087
|
+
'merged': 'guardrail hooks enabled (.claude/settings.json)',
|
|
3088
|
+
'skipped-flag': 'guardrail hooks skipped (--no-hooks or declined)',
|
|
3089
|
+
'skipped-template-missing': 'guardrail hooks skipped (settings-hooks.json template missing)',
|
|
3090
|
+
'skipped-error': 'guardrail hooks skipped (error merging .claude/settings.json)',
|
|
3091
|
+
}[settingsHooksReport.action] || 'guardrail hooks unchanged';
|
|
3092
|
+
console.log(' ' + dim(settingsHooksMsg));
|
|
3093
|
+
}
|
|
3094
|
+
if (preferredCommandReports && Object.keys(preferredCommandReports).length > 0) {
|
|
3095
|
+
const RULE_FILE = { claude: 'CLAUDE.md', codex: 'AGENTS.md', cursor: '.cursor/rules/rcode-prefer-do.mdc', windsurf: '.windsurf/rules/rcode-prefer-do.mdc' };
|
|
3096
|
+
for (const [ide, report] of Object.entries(preferredCommandReports)) {
|
|
3097
|
+
const file = RULE_FILE[ide] || ide;
|
|
3098
|
+
const msg = {
|
|
3099
|
+
'created': `${file}: /rcode-do rule added`,
|
|
3100
|
+
'appended': `${file}: /rcode-do rule appended`,
|
|
3101
|
+
'updated': `${file}: /rcode-do rule refreshed`,
|
|
3102
|
+
'already-present': `${file}: /rcode-do rule already present`,
|
|
3103
|
+
'written': `${file}: /rcode-do rule written`,
|
|
3104
|
+
'skipped-error': `${file}: /rcode-do rule skipped (${report.error})`,
|
|
3105
|
+
}[report.action] || `${file}: /rcode-do rule unchanged`;
|
|
3106
|
+
console.log(' ' + dim(msg));
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
2838
3109
|
if (skipped > 0) console.log(' ' + dim(`${skipped} files skipped (unchanged)`));
|
|
2839
3110
|
|
|
2840
3111
|
// Diff display for preserved files (#251)
|