@damiangilgonzalez/harness 1.3.17 → 1.3.19

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.
Files changed (37) hide show
  1. package/README.md +117 -657
  2. package/manifest.json +1 -1
  3. package/package.json +1 -1
  4. package/scripts/doctor.js +1 -1
  5. package/scripts/install-apply.js +9 -0
  6. package/scripts/rsc.js +90 -2
  7. package/skills/building-agents/SKILL.md +1 -1
  8. package/skills/grill-with-docs/SKILL.md +0 -1
  9. package/skills/harness/references/daily-curation-automation.md +2 -1
  10. package/skills/harness/references/wiki-protocol.md +3 -1
  11. package/skills/implement/SKILL.md +0 -1
  12. package/skills/implement-spec/SKILL.md +1 -2
  13. package/skills/init/SKILL.md +7 -5
  14. package/skills/postgresdb/SKILL.md +1 -1
  15. package/skills/suggest/SKILL.md +17 -13
  16. package/skills/suggest/evals/cases.yaml +6 -0
  17. package/skills/to-spec/SKILL.md +0 -1
  18. package/skills/to-tickets/SKILL.md +0 -1
  19. package/skills/wayfinder/SKILL.md +0 -1
  20. package/targets/_md-block.js +1 -1
  21. package/targets/claude.js +2 -1
  22. package/targets/clone-bootstrap.mjs +1 -1
  23. package/targets/danger-guard.mjs +114 -52
  24. package/targets/hook-once.mjs +44 -13
  25. package/targets/knowledge-sync.mjs +759 -0
  26. package/targets/knowledge-wiring.js +196 -0
  27. package/targets/memory.js +7 -1
  28. package/targets/opencode-memory-plugin.js +67 -22
  29. package/targets/opt-outs.js +4 -0
  30. package/targets/session-memory-adapter.mjs +51 -6
  31. package/targets/session-memory-core.mjs +284 -37
  32. package/targets/session-start.mjs +2 -1
  33. package/targets/ship-guard.mjs +26 -9
  34. package/targets/trunk-policy.mjs +86 -0
  35. package/targets/userprompt-gate.mjs +7 -2
  36. package/targets/worklog-checkpoint.mjs +16 -1
  37. package/targets/worktree-reaper.mjs +144 -4
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3.17",
2
+ "version": "1.3.19",
3
3
  "counts": {
4
4
  "skills": 294,
5
5
  "agents": 34,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damiangilgonzalez/harness",
3
- "version": "1.3.17",
3
+ "version": "1.3.19",
4
4
  "description": "A guided harness builder that turns your goal into a minimal, verified AI-agent setup using strict best practices.",
5
5
  "type": "module",
6
6
  "bin": {
package/scripts/doctor.js CHANGED
@@ -261,7 +261,7 @@ export function doctor({ target, home, cwd }) {
261
261
  // #277 — the three "missing" lists share one shape, { id, path, action }, so whoever builds on top
262
262
  // of doctor reads them the same way; and `healthy` is what the exit code says.
263
263
  for (const [id, e] of Object.entries(state.skills)) {
264
- for (const f of e.files) if (!existsSync(f)) report.missing.push({ id, path: f, action: 'Run `npx @ericrisco/rsc sync` to restore this managed skill.' });
264
+ for (const f of e.files) if (!existsSync(f)) report.missing.push({ id, path: f, action: 'Run `npx @damiangilgonzalez/harness sync` to restore this managed skill.' });
265
265
  }
266
266
  report.healthy = report.hookWired && !report.missing.length && !missingAgents.length && !missingCommands.length;
267
267
  return report;
@@ -21,6 +21,7 @@ import {
21
21
  import {
22
22
  wireMemory, unwireMemory, memoryManagedPaths, memoryModeFor, memoryEnabledForProject, memoryArtifactsPresent,
23
23
  } from '../targets/memory.js';
24
+ import { wireKnowledge, unwireKnowledge, knowledgeManagedPaths, knowledgeArtifactsPresent } from '../targets/knowledge-wiring.js';
24
25
 
25
26
  const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
26
27
  const CLI_VERSION = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8')).version;
@@ -94,6 +95,7 @@ export function managedPathsForInstall({ skillIds, agentIds = [], target, home,
94
95
  const out = [paths.stateFile, versionFile(cwd), baseVersionsFile(cwd)];
95
96
  if (policy?.context7 === false) out.push(join(cwd, '.rsc', '.no-context7'));
96
97
  if (policy?.memory !== false) out.push(...memoryManagedPaths(target, cwd));
98
+ out.push(...knowledgeManagedPaths(target, cwd));
97
99
  if (targetHasAgents(target)) {
98
100
  const state = readState(paths.stateFile);
99
101
  const explicit = [...new Set([...(state.explicitAgents || readManifest(cwd)?.agents || []), ...agentIds])];
@@ -273,6 +275,10 @@ export async function applyInstall({ skillIds = [], agentIds = [], target, home,
273
275
  ? { mode: 'disabled', reason: 'onboarding-policy', paths: unwireMemory(target, cwd) }
274
276
  : wireMemory(target, cwd);
275
277
  state.memory = { mode: memoryResult.mode, reason: memoryResult.reason, paths: memoryResult.paths };
278
+ // Knowledge sync is its own decision (`.no-knowledge-sync`, checked at runtime) and never part of
279
+ // the memory, so the memory's policy does not decide it: wired wherever the assistant can run it.
280
+ const knowledgeResult = wireKnowledge(target, cwd);
281
+ state.knowledge = { mode: knowledgeResult.mode, reason: knowledgeResult.reason || null, paths: knowledgeResult.paths };
276
282
  const context7OptOut = join(cwd, '.rsc', '.no-context7');
277
283
  if (policy?.context7 === false) {
278
284
  mkdirSync(dirname(context7OptOut), { recursive: true });
@@ -587,6 +593,7 @@ export function removeTargetInstall({ target, home, cwd = process.cwd() }) {
587
593
  }
588
594
  unwireHook(target, paths);
589
595
  unwireMemory(target, cwd);
596
+ unwireKnowledge(target, cwd);
590
597
  rmSync(paths.stateFile, { force: true });
591
598
  const gitignore = join(cwd, '.gitignore');
592
599
  if (existsSync(gitignore)) {
@@ -731,8 +738,10 @@ export async function purge({ home, cwd = process.cwd(), withDocs = false, dryRu
731
738
  if (!dryRun) {
732
739
  removed.push(...unwireHook(target, paths));
733
740
  removed.push(...unwireMemory(target, cwd));
741
+ removed.push(...unwireKnowledge(target, cwd));
734
742
  } else {
735
743
  removed.push(...memoryArtifactsPresent(target, cwd));
744
+ removed.push(...knowledgeArtifactsPresent(target, cwd));
736
745
  }
737
746
  // A lost state file also loses proof of ownership. Leave same-named user files
738
747
  // behind rather than guessing from a catalog id and deleting their work.
package/scripts/rsc.js CHANGED
@@ -41,7 +41,7 @@ if (['--version', '-v', 'version'].includes(rawArgv[0])) {
41
41
  const GLOBAL_VALUE_FLAGS = new Set([
42
42
  '--target', '--technical-level', '--accompaniment', '--project-kind', '--goal', '--goal-base64', '--software-scope', '--accept-plan',
43
43
  ]);
44
- const COMMANDS = new Set(['onboard', 'reassess', 'add', 'install', 'consult', 'catalog', 'audit', 'list', 'doctor', 'memory', 'sync', 'backups', 'restore', 'upgrade', 'registry', 'worktrees', 'capabilities', 'review', 'sello', 'repair', 'uninstall', 'purge']);
44
+ const COMMANDS = new Set(['onboard', 'reassess', 'add', 'install', 'consult', 'catalog', 'audit', 'list', 'doctor', 'memory', 'knowledge-sync', 'sync', 'backups', 'restore', 'upgrade', 'registry', 'worktrees', 'capabilities', 'review', 'sello', 'repair', 'uninstall', 'purge']);
45
45
  function positionalTokens(input) {
46
46
  const out = [];
47
47
  for (let i = 0; i < input.length; i++) {
@@ -756,6 +756,32 @@ async function main() {
756
756
  printContextBudget(report.contextBudget);
757
757
  return void say(JSON.stringify({ ...report, contextBudget: undefined }, null, 2));
758
758
  }
759
+ case 'knowledge-sync': {
760
+ // On by default; the switch is a PROJECT decision (`.no-knowledge-sync`, recorded in `.rsc.json`
761
+ // optOuts by the sync below), because one person off means the team stops seeing their work.
762
+ const sub = argv[1] || 'status';
763
+ const root = process.cwd();
764
+ const { mkdirSync, writeFileSync, rmSync } = await import('node:fs');
765
+ const { join } = await import('node:path');
766
+ const marker = join(root, '.rsc', '.no-knowledge-sync');
767
+ if (sub === 'on' || sub === 'off') {
768
+ mkdirSync(join(root, '.rsc'), { recursive: true });
769
+ if (sub === 'off') writeFileSync(marker, '');
770
+ else rmSync(marker, { force: true });
771
+ for (const t of targets) await syncInstalled({ target: t, cwd: root });
772
+ say(sub === 'off'
773
+ ? 'rsc knowledge-sync off: 01-TOOLS/ and docs/ no longer sync on their own in this project. Commit .rsc.json so the team gets the same decision.'
774
+ : 'rsc knowledge-sync on: 01-TOOLS/ and docs/ go up when a turn ends and come down before each message. Commit .rsc.json so the team gets the same decision.');
775
+ return;
776
+ }
777
+ if (sub === 'status') {
778
+ const { knowledgeStatus } = await import('../targets/knowledge-sync.mjs');
779
+ return void say(JSON.stringify(knowledgeStatus(root), null, 2));
780
+ }
781
+ say('Use: npx @damiangilgonzalez/harness@latest knowledge-sync on|off|status');
782
+ process.exitCode = 2;
783
+ return;
784
+ }
759
785
  case 'memory': {
760
786
  const sub = argv[1] || 'status';
761
787
  const root = process.cwd();
@@ -885,11 +911,72 @@ async function main() {
885
911
  const root = W.resolveMainRoot(process.cwd());
886
912
  const sub = argv[1];
887
913
 
914
+ if (sub === 'new') {
915
+ // Creating is not cleaning, so the cleanup opt-out does not apply here. Two things a worktree
916
+ // was measured lacking when it is born: a current base (local main was 400 commits behind
917
+ // origin) and what git does not track (.env, dependencies). The base is fixed here; the rest
918
+ // is the project's own hook, because only the project knows what makes it runnable.
919
+ const branchAt = argv.indexOf('--branch');
920
+ const branch = branchAt >= 0 ? argv[branchAt + 1] : undefined;
921
+ if (branchAt >= 0 && (!branch || branch.startsWith('--'))) { say('rsc: --branch needs a branch name.'); process.exitCode = 1; return; }
922
+ const name = argv.slice(2).find((a, i, all) => !a.startsWith('--') && all[i - 1] !== '--branch');
923
+ if (!name) { say('Use: npx @damiangilgonzalez/harness worktrees new <nombre> [--branch <rama>]'); process.exitCode = 1; return; }
924
+ const out = W.createWorktree(root, name, { branch });
925
+ if (!out.created) { say(`rsc: ${out.reason}`); process.exitCode = 1; return; }
926
+ if (!out.fetched) say(`⚠️ git fetch origin failed (${out.fetchError || 'no detail'}); starting from the last known ${out.base}, which may be stale.`);
927
+ say(`✅ worktree ${out.path}`);
928
+ say(` branch ${out.branch}, from ${out.base}`);
929
+ const hook = W.findPrepareHook(out.path, root);
930
+ if (hook) {
931
+ say(` preparing it: ${hook}`);
932
+ const ran = W.runPrepareHook(hook, out.path, root);
933
+ if (!ran.ok) { say(`⚠️ the preparation hook failed (${ran.reason}). The worktree exists; finish preparing it by hand.`); process.exitCode = 1; }
934
+ } else {
935
+ say(` No preparation hook (${W.PREPARE_HOOKS.join(' or ')}): .env files and dependencies are NOT copied — create the hook to do it on every new worktree.`);
936
+ }
937
+ say(`Next: cd "${out.path}"`);
938
+ return;
939
+ }
940
+
888
941
  if (!W.isCleanupEnabled(root)) {
889
942
  say(`worktree cleanup is off for this project (.rsc/${W.OPT_OUT}). Delete that file to turn it back on.`);
890
943
  return;
891
944
  }
892
945
 
946
+ if (sub === 'reap' && argv.includes('--merged')) {
947
+ // The bulk answer to "delete the worktrees" — asked four times in one project, with up to
948
+ // fourteen open. Merged is judged against origin's trunk AFTER a fetch, so a branch landed in
949
+ // the forge an hour ago counts. Without --confirm this only lists: one look, then one yes.
950
+ const fetched = W.fetchOrigin(root);
951
+ if (!fetched.ok) say(`⚠️ git fetch origin failed (${fetched.err || 'no detail'}); judging against the last known trunk.`);
952
+ const all = W.classifyWorktrees(root);
953
+ const merged = W.mergedCandidates(all);
954
+ const held = all.filter((c) => c.verdict === 'ask' && !merged.includes(c));
955
+ if (!merged.length) say('No merged worktree that can be removed in bulk.');
956
+ if (!argv.includes('--confirm')) {
957
+ if (merged.length) {
958
+ say(`${merged.length} merged worktree(s), clean, removable in one go:`);
959
+ for (const c of merged) say(` ${c.path}${c.branch ? ` (${c.branch})` : ''}`);
960
+ }
961
+ for (const c of held) say(W.describe(c));
962
+ if (merged.length) say('\nRemove them all: npx @damiangilgonzalez/harness worktrees reap --merged --confirm');
963
+ return;
964
+ }
965
+ for (const c of merged) {
966
+ const out = W.reapWorktree(root, c.path, { confirmed: true });
967
+ if (out.removed) {
968
+ say(`✅ removed ${out.path}`);
969
+ if (out.branchKept) say(` branch ${out.branch} kept — git will not delete it safely, and while it exists the commits are recoverable.`);
970
+ } else {
971
+ say(`⏸ kept ${c.path} — ${out.reason}`);
972
+ process.exitCode = 1;
973
+ }
974
+ }
975
+ // What a bulk yes never covers, said out loud so nobody thinks it was forgotten.
976
+ for (const c of held) say(`⏸ kept ${c.path} — ${W.refusal(c)}`);
977
+ return;
978
+ }
979
+
893
980
  const candidates = W.classifyWorktrees(root).filter((c) => c.verdict !== 'skip');
894
981
 
895
982
  if (sub === 'reap') {
@@ -920,6 +1007,7 @@ async function main() {
920
1007
  say('');
921
1008
  say('Remove the safe ones: npx @damiangilgonzalez/harness worktrees reap');
922
1009
  say('Remove one by name: npx @damiangilgonzalez/harness worktrees reap <path> [--confirm]');
1010
+ say('Remove every merged: npx @damiangilgonzalez/harness worktrees reap --merged [--confirm]');
923
1011
  return;
924
1012
  }
925
1013
  case 'capabilities': {
@@ -1195,7 +1283,7 @@ async function main() {
1195
1283
  return void (await runPurge(argv.includes('--dry-run'), argv.includes('--with-docs')));
1196
1284
  default:
1197
1285
  say(`rsc: unknown command '${cmd}'.`);
1198
- say('Use: npx @damiangilgonzalez/harness onboard | reassess | add <id...> | install --profile <p> | consult "<text>" | list | capabilities [--full|gap-log] | audit | registry refresh | doctor | sync | memory <on|off|status|save|resume|learn|metrics> | sello <on|off|status|…> | worktrees [reap [path] [--confirm]] | backups | restore <id|latest> | upgrade | repair | uninstall <id> | purge');
1286
+ say('Use: npx @damiangilgonzalez/harness onboard | reassess | add <id...> | install --profile <p> | consult "<text>" | list | capabilities [--full|gap-log] | audit | registry refresh | doctor | sync | memory <on|off|status|save|resume|learn|metrics> | sello <on|off|status|…> | worktrees [new <nombre> [--branch <rama>] | reap [path|--merged] [--confirm]] | backups | restore <id|latest> | upgrade | repair | uninstall <id> | purge');
1199
1287
  say('Any command takes --target <claude|codex|cursor|copilot|gemini|…> (comma-separate for several)');
1200
1288
  say(' → without it, rsc uses the assistant already installed here; if two are, it asks instead of guessing.');
1201
1289
  process.exitCode = 1;
@@ -14,7 +14,7 @@ A thin provider adapter, a disciplined agent loop, schema-validated tools, provi
14
14
 
15
15
  > Program against a **capability interface**, never a vendor SDK. Vendor specifics (model id, tool-schema shape, JSON mode, caching, token limits) live behind one adapter resolved from config. Model names and prices rot — if one appears in business logic it's a bug, and re-verify the dated tables before quoting a number.
16
16
 
17
- **Hand off instead when:** a new non-trivial feature has no approved spec + plan under `docs/wiki/sdd/` → stop and write one first (the `to-spec` skill, or `superpowers:writing-plans`), which routes back here once the plan is approved; one-line/low-risk changes go straight through. Anthropic-SDK internals (caching, thinking, batch) in a file that *only* imports `anthropic` → `claude-api` if your environment has it, since this skill stays multi-provider. Workspace scaffolding → [`harness`](../harness/SKILL.md). Choosing *which* coding agent to use → agent-eval territory. Pure prompt-wording tuning with no architecture change → prompt engineering, not this. A one-shot throwaway prompt, or no retrieval/tools/loop/evals at all → you don't need an agent; call the SDK directly and say so.
17
+ **Hand off instead when:** a new non-trivial feature has no approved spec + plan under `docs/wiki/sdd/` → stop and write one first (the `to-spec` skill, then `to-tickets`), which routes back here once the plan is approved; one-line/low-risk changes go straight through. Anthropic-SDK internals (caching, thinking, batch) in a file that *only* imports `anthropic` → `claude-api` if your environment has it, since this skill stays multi-provider. Workspace scaffolding → [`harness`](../harness/SKILL.md). Choosing *which* coding agent to use → agent-eval territory. Pure prompt-wording tuning with no architecture change → prompt engineering, not this. A one-shot throwaway prompt, or no retrieval/tools/loop/evals at all → you don't need an agent; call the SDK directly and say so.
18
18
 
19
19
  ## Decision rules (read before writing code)
20
20
 
@@ -2,7 +2,6 @@
2
2
  name: grill-with-docs
3
3
  description: "A relentless interrogation to sharpen a plan or design, which also builds documentation (ADRs and a glossary) along the way."
4
4
  tags: ["planning", "questioning", "documentation", "adr"]
5
- disable-model-invocation: true
6
5
  profiles: [core, ui, full]
7
6
  origin: damiangil
8
7
  ---
@@ -42,7 +42,8 @@ process inbox/, then scan the workspace (minus .rscignore) for un-ingested docum
42
42
  applying Clean-as-you-go (move loose root-level + inbox files into raw/ after a
43
43
  verified ingest; copy files nested in user-maintained folders and propose
44
44
  consolidation), record them in wiki/.ingested.json, and list ambiguous folders as
45
- proposals (do not grab them). Then compile pending raw/worklog/ material into wiki/
45
+ proposals (do not grab them). Then compile pending raw/worklog/ material (skip this
46
+ step if raw/worklog/ does not exist; do not list a missing folder) into wiki/
46
47
  pages (update existing before creating new), keep frontmatter + relative markdown
47
48
  links OKF-consistent, append significant decisions to wiki/harness/decisions.md,
48
49
  refresh the .base-backed navigation, recompute scores.json + the score: property,
@@ -417,7 +417,9 @@ safety rule the curation pass honors.
417
417
  ### Steps
418
418
 
419
419
  1. **Capture.** Write the worklog to `raw/worklog/YYYY-MM-DD-<slug>.md`
420
- (`status: unprocessed`). Infer `topic` from content.
420
+ (`status: unprocessed`). Infer `topic` from content. If `raw/worklog/` does
421
+ not exist yet, create it (`mkdir -p`) — never list it blindly: the checkpoint
422
+ hook already says whether it exists and how many worklogs it holds.
421
423
  2. **Compile.** Run the normal Compile (Fetch is a no-op — the worklog is already
422
424
  `raw`): distill durable points into `wiki/<topic>/` articles (update existing
423
425
  before creating new), add markdown-link cross-refs + `## Related`, cascade.
@@ -2,7 +2,6 @@
2
2
  name: implement
3
3
  description: "Implements a piece of work from a spec or a set of tickets."
4
4
  tags: ["implementation", "spec", "tickets"]
5
- disable-model-invocation: true
6
5
  profiles: [core, ui, full]
7
6
  origin: damiangil
8
7
  ---
@@ -2,12 +2,11 @@
2
2
  name: implement-spec
3
3
  description: "Implement the result of /to-spec and /to-tickets in code, orchestrating parallel implementer subagents over the ticket graph."
4
4
  tags: ["implementation", "spec", "tickets", "subagents", "orchestration"]
5
- disable-model-invocation: true
6
5
  profiles: [core, ui, full]
7
6
  origin: damiangil
8
7
  ---
9
8
 
10
- Te han dado una spec. Esa spec debería tener tickets asociados que describen cómo implementarla.
9
+ Te han dado una spec. Esa spec debería tener tickets asociados que describen cómo implementarla. Si no los tiene, **para**: lanza `to-tickets` sobre la spec y vuelve aquí con el grafo ya publicado. Sin tickets no hay frontera que repartir, y esto se convierte en una implementación a ciegas.
11
10
 
12
11
  El issue tracker debería estar descrito en `docs/agents/issue-tracker.md`. Si falta, pregunta al usuario dónde viven los tickets y cómo se cierra el trabajo (PR, issue, fichero).
13
12
 
@@ -165,11 +165,13 @@ means the user starts clean.
165
165
  machine and flags overlap or skills with no footprint, so the project starts with the right set
166
166
  rather than a pile. Summarize at their level.
167
167
  4. **Tell them about the danger guard.** A `technical_level` of `non-technical` or `mixed` (and the
168
- state before any profile exists) turns on a `PreToolUse` guard that blocks irreversible commands —
169
- `rm -rf`, `git push --force`, `git reset --hard`, `DROP`/`TRUNCATE`, `DELETE`/`UPDATE` with no
170
- `WHERE`, `dd` to a device, `curl | bash` — and asks for a safer alternative. A fully `technical`
171
- user is never guarded. It turns off only if the user explicitly asks: `.rsc/.no-danger-guard`.
172
- Mention it when you set a non-technical level, so a later block is not a surprise.
168
+ state before any profile exists) turns on a `PreToolUse` guard for irreversible commands —
169
+ `rm -rf`, `git push --force`, `git reset --hard`, `git branch -D`, `DROP`/`TRUNCATE`,
170
+ `DELETE`/`UPDATE` with no `WHERE`, `dd` to a device, `curl | bash`. For `non-technical` (or no
171
+ profile) Claude Code asks the user to confirm each one, saying what the command destroys; their
172
+ yes is the order that runs it. For `mixed` it only warns. A fully `technical` user is never
173
+ guarded. It turns off only if the user explicitly asks: `.rsc/.no-danger-guard`.
174
+ Mention it when you set a non-technical level, so a later confirmation is not a surprise.
173
175
 
174
176
  ### Phase 5 — HANDOFF
175
177
 
@@ -13,7 +13,7 @@ fix slow SQL, run zero-downtime migrations, and operate/secure the database. Too
13
13
  example is runnable.
14
14
 
15
15
  New, non-trivial feature with no approved spec + plan under `docs/wiki/sdd/`? Hand off to
16
- the `to-spec` skill before writing feature code (or `superpowers:writing-plans`); build
16
+ the `to-spec` skill before writing feature code (then `to-tickets`); build
17
17
  straight from here only for a genuinely one-line, low-risk change.
18
18
 
19
19
  Deep dives: [schema-and-indexing](references/schema-and-indexing.md) (types, constraints, every index
@@ -34,8 +34,8 @@ The shape of the chain:
34
34
  when the effort will not fit one sitting.
35
35
  - Once the shape is known, capture it as a spec, and give any call that would be costly to undo
36
36
  its own ADR.
37
- - Build from a written plan. Tickets are the exception: only work that grew out of the
38
- wayfinding step is broken into them first. A screen also routes through `flujo-interfaz`.
37
+ - Small work builds straight from the clarified ask; big work is split into tickets first and
38
+ built from them. A screen also routes through `flujo-interfaz`.
39
39
  - Review the finished change against the repo's standards and against the spec before merging.
40
40
 
41
41
  Two exceptions, and say out loud when you take one:
@@ -54,9 +54,9 @@ whole run — advance through the phases without re-asking.
54
54
  If a step's skill is not installed, offer to add it (§2) before routing. `implement` always
55
55
  pairs with test-driven development and a verification pass before anything is called done.
56
56
 
57
- Exact skill names and order: `grill-with-docs` or `wayfinder` -> `superpowers:brainstorming` or
58
- `to-spec` (and `write-adr` for decisions) -> `superpowers:writing-plans` ->
59
- `superpowers:executing-plans` (big spec: `to-tickets` -> `implement-spec`) -> `code-review` -> `pr`.
57
+ Exact skill names, by size. Small: `superpowers:brainstorming` or `grill-with-docs` ->
58
+ `implement`. Big: `grill-with-docs` or `wayfinder` -> `to-spec` -> `to-tickets` ->
59
+ `implement-spec`. Both: `write-adr` for decisions, then `code-review` -> `pr`.
60
60
  On Claude Code this rule also arrives as a per-turn hook; the brevity here is deduplication,
61
61
  not relaxation.
62
62
 
@@ -71,13 +71,12 @@ name it and offer it. This runs mid-conversation, not only at project start.
71
71
  1. `npx @damiangilgonzalez/harness catalog --available` lists every not-installed skill as
72
72
  `id available short description`.
73
73
  2. Pick the single best fit **by meaning**, the way you would match a request to a teammate's
74
- expertise — "mandar emails de bienvenida" → an email/outreach skill, though not one keyword
75
- overlaps. If nothing genuinely fits, say so
76
- and move on: a tangential suggestion is worse than none.
74
+ expertise, even when no keyword overlaps. If nothing genuinely fits, say so and move on: a
75
+ tangential suggestion is worse than none.
77
76
  3. Ask once, plainly: "Para esto instalaría `<id>`, que aún no tienes. ¿La instalo? (sí/no)".
78
77
  4. On yes, run `npx @damiangilgonzalez/harness add <id>`, then continue the original task.
79
78
 
80
- Installing changes the user's environment, so it is always their call. One suggestion at a time,
79
+ One suggestion at a time,
81
80
  and never to interrupt a flow with a nice-to-have. Never recommend something already installed
82
81
  (`npx @damiangilgonzalez/harness list`).
83
82
 
@@ -95,9 +94,8 @@ Not covered → one line, skill or agent. Rules: `skill-scout`.
95
94
 
96
95
  ## 3. A harness that is broken fixes itself
97
96
 
98
- You are injected into every session, so you are the only thing that can notice a broken
99
- harness before its owner does — and its owner usually cannot, because the symptoms name
100
- nothing they recognise. When any of these is true, act on it **once** in the session:
97
+ Its owner rarely notices: the symptoms name nothing they know. When any of these
98
+ is true, act on it **once** in the session:
101
99
 
102
100
  - `.rsc.json` exists and what it declares is not what is installed;
103
101
  - the same hook seems to run several times;
@@ -117,7 +115,7 @@ a cause. Then:
117
115
  teammate added, disarming a gate — **ask first**. A `git pull` never rewrites someone's
118
116
  machine.
119
117
 
120
- Offer once per session. Never mention any of it when the harness is healthy.
118
+ Offer once per session; say nothing when the harness is healthy.
121
119
 
122
120
  ## 4. First contact
123
121
 
@@ -130,6 +128,12 @@ Before handling the first request of a session, check the workspace:
130
128
  `.rsc/.no-harness`, confirm in one line, and never auto-start `init` in this repo again.
131
129
  - Once the profile exists, this gate is inert. Never re-onboard.
132
130
 
131
+ ## 5. «Recuerda esto»
132
+
133
+ A project or business fact goes to its topic page in `docs/wiki/` (link a new page from
134
+ `wiki/index.md`); say the path. Your own memory sits outside the project, unseen by person and
135
+ team: keep there only how this person likes you to work (or all, without `docs/`).
136
+
133
137
  ## Explain without assuming
134
138
 
135
139
  Define a term at first use; never give a command, flag or path without saying what it does.
@@ -76,3 +76,9 @@ capability:
76
76
  - "Names that one missing automation/connector skill"
77
77
  - "Asks one short confirmation before installing and does not auto-install"
78
78
  - "On confirmation, runs `npx @damiangilgonzalez/harness add <id>` for that one missing skill, then resumes the automation task"
79
+
80
+ - scenario: "In a workspace with `docs/wiki/`, the user says 'recuerda que toda la actividad, también la de Andorra, va por una sola sociedad, Nadsan 2006 S.L.'. Show where rsc-suggest puts it."
81
+ must_include:
82
+ - "Writes the fact into the `docs/wiki/` page of its topic (creating it and linking it from `wiki/index.md` if none exists), not only into the assistant's own memory under `~/.claude/projects/`"
83
+ - "Tells the person the path where it was saved"
84
+ - "Does not start the feature chain or ask a lane question for it"
@@ -2,7 +2,6 @@
2
2
  name: to-spec
3
3
  description: "Turns the current conversation into a spec (PRD) and publishes it to the project's issue tracker — no interview, just a synthesis of what has already been discussed."
4
4
  tags: ["spec", "prd", "issue-tracker"]
5
- disable-model-invocation: true
6
5
  profiles: [core, ui, full]
7
6
  origin: damiangil
8
7
  ---
@@ -2,7 +2,6 @@
2
2
  name: to-tickets
3
3
  description: "Breaks down a plan, a spec, or the current conversation into a set of \\"tracer bullet\\" tickets (vertical slices), each declaring which tickets block it, and publishes them to the configured tracker — as local files (one per ticket) or as issues with native blocking links in a real tracker."
4
4
  tags: ["tickets", "planning", "issue-tracker"]
5
- disable-model-invocation: true
6
5
  profiles: [core, ui, full]
7
6
  origin: damiangil
8
7
  ---
@@ -2,7 +2,6 @@
2
2
  name: wayfinder
3
3
  description: "Plans a huge chunk of work — more than fits in a single agent session — as a shared map of decision tickets in the repo's issue tracker, and resolves them one at a time until the path to the destination is clear."
4
4
  tags: ["planning", "issue-tracker", "large-scale-work"]
5
- disable-model-invocation: true
6
5
  profiles: [core, ui, full]
7
6
  origin: damiangil
8
7
  ---
@@ -21,7 +21,7 @@ export function writeSkill(id, fromDir, toPath) {
21
21
  export function wireHook(paths, sourceMd, policy = {}) {
22
22
  const full = stripFrontmatter(readFileSync(sourceMd, 'utf8'));
23
23
  const body = policy.codeHooks === false
24
- ? `# rsc-suggest — always-on operations layer\n\nRead \`docs/wiki/harness/user-profile.md\` before acting. If the user wants to build, add or change something, route it through \`grill-with-docs\` or \`wayfinder\`, then write the spec with \`superpowers:brainstorming\` or \`to-spec\` (decisions go to \`write-adr\`) before any code, then build it with \`superpowers:writing-plans\` (or \`to-tickets\` then \`implement-spec\` for a big spec or one that came out of \`wayfinder\`), and review it with \`code-review\` before merging, writing the PR body with \`pr\` (si toca pantalla, \`flujo-interfaz\` dice que skill de diseno manda) — unless it is a one-line change, typo, or config bump, which skips the chain. Use \`orient\` to keep the user situated and \`suggest\` to offer a missing skill only when the current task needs it. Close with the configured orientation block.\n`
24
+ ? `# rsc-suggest — always-on operations layer\n\nRead \`docs/wiki/harness/user-profile.md\` before acting. If the user wants to build, add or change something, pick the chain by size and chain each step yourself. Small (one sitting, one ticket): clarify with \`superpowers:brainstorming\` (idea still vague) or \`grill-with-docs\` (a plan to harden), then build with \`implement\`. Big: \`grill-with-docs\` or \`wayfinder\` -> \`to-spec\` -> \`to-tickets\` -> \`implement-spec\`. Hard-to-reverse decisions go to \`write-adr\`; review with \`code-review\` and write the PR with \`pr\` before merging (si toca pantalla, \`flujo-interfaz\` dice que skill de diseno manda) — unless it is a one-line change, typo, or config bump, which skips the chain. Use \`orient\` to keep the user situated and \`suggest\` to offer a missing skill only when the current task needs it. Close with the configured orientation block.\n`
25
25
  : full;
26
26
  const block = `${MARK_START}\n${body}\n${MARK_END}`;
27
27
  let doc = existsSync(paths.hookTarget) ? readFileSync(paths.hookTarget, 'utf8') : '';
package/targets/claude.js CHANGED
@@ -175,7 +175,8 @@ export function wireHook(paths, sourceMd, policy = {}) {
175
175
 
176
176
  if (policy.codeHooks !== false) {
177
177
  // Ship guard: a PreToolUse(Bash) hook that DENIES switching to / merging the trunk
178
- // while the current feature branch has uncommitted or unpushed work — forcing the
178
+ // while the current feature branch has unpushed commits (uncommitted changes only get a
179
+ // worktree hint: they may be the user's own) — forcing the
179
180
  // commit → push → PR close (the `ship` skill). Materialized + node-run (Windows-safe),
180
181
  // registered idempotently, fail-open, opt-out via .rsc/.no-ship-guard. Other
181
182
  // (non-rsc) PreToolUse hooks are preserved.
@@ -43,7 +43,7 @@ const SEMVER = /^\d+\.\d+\.\d+(?:-[0-9a-z.-]+)?$/i;
43
43
  * nothing checks is just drift with a head start.
44
44
  */
45
45
  export const PROJECT_OPT_OUTS = [
46
- 'audit', 'claudemd-check', 'danger-guard', 'feature-gate', 'gitmoji', 'ship-guard', 'worktree-cleanup',
46
+ 'audit', 'claudemd-check', 'danger-guard', 'feature-gate', 'gitmoji', 'knowledge-sync', 'ship-guard', 'trunk-guard', 'trunk-open', 'worktree-cleanup', 'worktree-isolation',
47
47
  ];
48
48
 
49
49
  /**