@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 +5 -0
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/install-apply.js +9 -0
- package/scripts/rsc.js +27 -1
- package/skills/suggest/SKILL.md +12 -8
- package/skills/suggest/evals/cases.yaml +6 -0
- package/targets/clone-bootstrap.mjs +1 -1
- package/targets/knowledge-sync.mjs +759 -0
- package/targets/knowledge-wiring.js +196 -0
- package/targets/memory.js +1 -0
- package/targets/opencode-memory-plugin.js +67 -22
- package/targets/opt-outs.js +4 -0
- package/targets/session-memory-adapter.mjs +47 -5
- package/targets/session-memory-core.mjs +244 -29
- package/targets/trunk-policy.mjs +86 -0
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
|

|
|
178
183
|
|
|
179
184
|
Cada rama en su worktree, cortada siempre desde `origin`.
|
package/manifest.json
CHANGED
package/package.json
CHANGED
package/scripts/install-apply.js
CHANGED
|
@@ -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();
|
package/skills/suggest/SKILL.md
CHANGED
|
@@ -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
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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
|
|
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
|
/**
|