@damiangilgonzalez/harness 1.3.18 → 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.
package/README.md CHANGED
@@ -174,6 +174,11 @@ rsc nunca las instala, actualiza ni sobrescribe: su versión es el commit.
174
174
  Al cerrar, guarda rama, ficheros y commits; al abrir, te dice dónde lo dejaste.
175
175
  Nunca guarda prompts, respuestas ni secretos, y no sale de tu máquina. `rsc memory off` lo apaga.
176
176
 
177
+ Lo que el proyecto *sabe* va a `docs/wiki/`: dile «recuerda que…» y te dirá en qué página lo
178
+ guardó. Esa wiki, `docs/attachments/` y `01-TOOLS/` viajan solas a tu equipo por la rama
179
+ `rsc/knowledge` y llegan a `main` dentro de vuestras PRs. `rsc knowledge-sync off` lo apaga.
180
+ Cómo funciona todo junto: [la memoria del harness](docs/como-funciona/memoria.md).
181
+
177
182
  ![Una rama, una copia de trabajo](docs/img/readme/readme-18-worktrees.png)
178
183
 
179
184
  Cada rama en su worktree, cortada siempre desde `origin`.
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3.18",
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.18",
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": {
@@ -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();
@@ -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"
@@ -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
  /**