harness-everything 0.32.0 → 0.34.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -1
- package/CHANGELOG.md +33 -0
- package/README.md +3 -2
- package/ci/fixtures/doc-lifecycle/prd-revisions/config.json +9 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/config.json +20 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/expected.json +12 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/invalid-fork/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/invalid-fork/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/invalid-fork/docs/prd/graph/r003.md +26 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/valid/docs/prd/graph/r001.md +20 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/packaged/valid/docs/prd/graph/r002.md +26 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r001.body.md +39 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-dropped.body.md +35 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-incomplete.body.md +37 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-one-replaced.body.md +39 -0
- package/ci/fixtures/doc-lifecycle/prd-revisions/ten-reqs/r002-overlap.body.md +39 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/docs/adr/GRPH-001.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/docs/adr/GRPH-002.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/docs/adr/GRPH-001.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/docs/adr/GRPH-002.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-active-mismatch/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/docs/adr/GRPH-010.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/docs/adr/legacy/GRPH-007.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/active-to-legacy/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/docs/adr/GRAPH-007.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/docs/adr/legacy/GRPH-007.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/alias-rename/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/docs/adr/legacy/v1/GRPH-007.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/docs/adr/legacy/v2/GRPH-007.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/duplicate-legacy/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-003.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-004.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-005.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-006.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/docs/adr/GRPH-007.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/invalid-refs/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/docs/adr/GRPH-020.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/docs/adr/GRPH-021.md +9 -0
- package/ci/fixtures/doc-lifecycle/scenarios/pending-edge/expected.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/config.json +1 -0
- package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/docs/adr/GRPH-003.md +8 -0
- package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/docs/prd/graph.md +10 -0
- package/ci/fixtures/doc-lifecycle/scenarios/recorded-conflict/expected.json +1 -0
- package/ci/mechanism-2aw-project-docs-config.test.js +759 -0
- package/ci/mechanism-2ax-doc-lifecycle.test.js +1061 -0
- package/ci/mechanism-2ay-prd-revisions.test.js +2891 -0
- package/ci/mechanism-2az-step-type-registry.test.js +86 -0
- package/ci/openai-plugin-package.test.js +1 -1
- package/ci/release-consistency-check.js +26 -9
- package/ci/release-consistency-check.test.js +30 -0
- package/ci/router-workflow-plan.test.js +36 -0
- package/code-review/SKILL.md +41 -0
- package/code-review/references/review-playbook.md +40 -0
- package/code-review/templates/finding.template.md +33 -0
- package/docs/document-lifecycle.md +206 -0
- package/docs/openai-plugin.md +1 -1
- package/docs/workflows/code-review.md +58 -0
- package/evals/code-review/eval.yaml +26 -0
- package/evals/code-review/tasks/negative-grill-me.yaml +16 -0
- package/evals/code-review/tasks/negative-security-review.yaml +16 -0
- package/evals/code-review/tasks/negative-tdd.yaml +16 -0
- package/evals/code-review/tasks/negative-verification-loop.yaml +16 -0
- package/evals/code-review/tasks/positive-pr-review.yaml +16 -0
- package/evals/code-review/tasks/positive-revised-head-rereview.yaml +16 -0
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/references/skill-registry.md +1 -0
- package/harness-everything/scripts/doc-lifecycle.js +2401 -0
- package/harness-everything/scripts/project-docs-resolver.js +764 -0
- package/harness-everything/scripts/routing-keywords.json +7 -0
- package/hooks/scripts/lib/binding-resolver.js +1 -0
- package/multi-agent-workspace/SKILL.md +1 -1
- package/multi-agent-workspace/scripts/project-docs-resolver.js +437 -30
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +2 -2
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/binding-resolver.js +1 -0
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/code-review/SKILL.md +41 -0
- package/plugins/harness-everything/skills/code-review/references/review-playbook.md +40 -0
- package/plugins/harness-everything/skills/code-review/templates/finding.template.md +33 -0
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/references/skill-registry.md +1 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +2401 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/project-docs-resolver.js +764 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/routing-keywords.json +7 -0
- package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +1 -1
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/project-docs-resolver.js +437 -30
- package/plugins/harness-everything/skills/to-spec/SKILL.md +1 -1
- package/plugins/harness-everything/skills/to-spec/scripts/check-project-docs.js +168 -54
- package/plugins/harness-everything/skills/to-spec/scripts/project-docs-resolver.js +749 -96
- package/sbom.cdx.json +7 -7
- package/to-spec/SKILL.md +1 -1
- package/to-spec/scripts/check-project-docs.js +168 -54
- package/to-spec/scripts/project-docs-resolver.js +749 -96
package/sbom.cdx.json
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
"$schema": "https://cyclonedx.org/schema/bom-1.7.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.7",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:a1e5f8bb-3e11-505d-a9e9-4d3796393d51",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
8
|
+
"timestamp": "2026-10-17T16:23:38.167Z",
|
|
9
9
|
"tools": [
|
|
10
10
|
{
|
|
11
11
|
"vendor": "Harness Everything",
|
|
12
12
|
"name": "generate-sbom.js",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.34.0"
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
16
|
"component": {
|
|
17
17
|
"type": "application",
|
|
18
|
-
"bom-ref": "pkg:npm/harness-everything@0.
|
|
18
|
+
"bom-ref": "pkg:npm/harness-everything@0.34.0",
|
|
19
19
|
"name": "harness-everything",
|
|
20
|
-
"version": "0.
|
|
21
|
-
"purl": "pkg:npm/harness-everything@0.
|
|
20
|
+
"version": "0.34.0",
|
|
21
|
+
"purl": "pkg:npm/harness-everything@0.34.0"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"components": [
|
|
@@ -1999,7 +1999,7 @@
|
|
|
1999
1999
|
]
|
|
2000
2000
|
},
|
|
2001
2001
|
{
|
|
2002
|
-
"ref": "pkg:npm/harness-everything@0.
|
|
2002
|
+
"ref": "pkg:npm/harness-everything@0.34.0",
|
|
2003
2003
|
"dependsOn": [
|
|
2004
2004
|
"pkg:npm/%40commitlint/cli@19.8.1",
|
|
2005
2005
|
"pkg:npm/%40commitlint/config-conventional@19.8.1",
|
package/to-spec/SKILL.md
CHANGED
|
@@ -4,23 +4,21 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Mechanized gate for `to-spec` (and `to-tickets`, once installed): has this
|
|
6
6
|
* repo already pinned down where reference docs live, how issues are
|
|
7
|
-
* tracked, and what counts as a valid issue?
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* surface, not two.
|
|
7
|
+
* tracked, and what counts as a valid issue? Read the validated project
|
|
8
|
+
* configuration first; its project-docs.json file wholly overrides host
|
|
9
|
+
* manifest values. Without it, host configuration is used only when selected
|
|
10
|
+
* unambiguously by the shared resolver.
|
|
12
11
|
*
|
|
13
12
|
* Deliberately repo-local only: unlike self-evolve's `generated` skills
|
|
14
13
|
* (intentionally reusable across projects), doc location / tracker / issue
|
|
15
14
|
* definition are PER-REPO facts. Reading or writing them through the global
|
|
16
15
|
* ~/.agents or ~/.claude manifest homes would leak one project's tracker
|
|
17
|
-
* into every other project sharing that global install, so
|
|
18
|
-
*
|
|
16
|
+
* into every other project sharing that global install, so the resolver reads
|
|
17
|
+
* only the workspace project file and workspace-relative platform manifests.
|
|
19
18
|
*
|
|
20
|
-
* Exit 0 ->
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* to-spec/SKILL.md for the flagged field(s), then persist with `init`.
|
|
19
|
+
* Exit 0 -> a complete effective config or allowed workspace inference exists.
|
|
20
|
+
* Exit 1 -> required fields are missing, or configuration is invalid/ambiguous.
|
|
21
|
+
* Run the Step 0 interview in to-spec/SKILL.md, then persist with `init`.
|
|
24
22
|
*
|
|
25
23
|
* manifest.json is read on every single tier-router.js invocation (every
|
|
26
24
|
* prompt, not just to-spec runs) to scan for self-evolve's generated
|
|
@@ -39,7 +37,7 @@ const fs = require('fs');
|
|
|
39
37
|
const path = require('path');
|
|
40
38
|
// Keep project-doc path decisions in the resolver carried by the installed
|
|
41
39
|
// multi-agent-workspace skill. This script consumes the same contract.
|
|
42
|
-
const { getWorkspaceRoot, getRepoManifestHomes, detectProjectDocsConventions } =
|
|
40
|
+
const { getWorkspaceRoot, getRepoManifestHomes, detectProjectDocsConventions, validateDocsConfig } =
|
|
43
41
|
require('./project-docs-resolver');
|
|
44
42
|
|
|
45
43
|
function loadManifestHelper() {
|
|
@@ -102,33 +100,37 @@ function detectWorkspaceConventions(workspaceRoot) {
|
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
function runCheck(workspaceRoot) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
console.log(`[Project Docs Check] OK: ${manifestPath} defines document location, issue tracker, and issue definition.`);
|
|
114
|
-
process.exit(0);
|
|
103
|
+
// Selection and validation happen in the shared resolver. Invalid or
|
|
104
|
+
// ambiguous sources cannot authorize a destination or fall through to a
|
|
105
|
+
// different host; only `none` permits workspace convention inference.
|
|
106
|
+
const effective = detectWorkspaceConventions(workspaceRoot);
|
|
107
|
+
if (effective.status === 'invalid' || effective.status === 'ambiguous') {
|
|
108
|
+
console.log(`[Project Docs Check] ${effective.status.toUpperCase()}: no usable projectDocs selection.`);
|
|
109
|
+
for (const diagnostic of effective.diagnostics || []) {
|
|
110
|
+
console.log(` - ${diagnostic.code}: ${diagnostic.message} (${diagnostic.path})`);
|
|
115
111
|
}
|
|
112
|
+
process.exit(1);
|
|
116
113
|
}
|
|
117
114
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
console.log(`
|
|
126
|
-
console.log(` -
|
|
115
|
+
// The resolver reports which required fields its selection left uncovered
|
|
116
|
+
// (configured-only for project/host selections, plus inference for none).
|
|
117
|
+
const missing = Array.isArray(effective.missing) ? effective.missing : missingFields(effective);
|
|
118
|
+
if (missing.length === 0) {
|
|
119
|
+
const source = effective.status === 'none'
|
|
120
|
+
? 'Inferred from workspace structure'
|
|
121
|
+
: `Selected ${effective.status} projectDocs configuration`;
|
|
122
|
+
console.log(`[Project Docs Check] OK (${source}):`);
|
|
123
|
+
console.log(` - docLocation: ${effective.docLocation}`);
|
|
124
|
+
console.log(` - tracker: ${effective.tracker}`);
|
|
125
|
+
console.log(` - issueDefinition: ${effective.issueDefinition}`);
|
|
127
126
|
process.exit(0);
|
|
128
127
|
}
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
const source = effective.status === 'none'
|
|
130
|
+
? 'Inferred from workspace structure'
|
|
131
|
+
: `Selected ${effective.status} projectDocs configuration`;
|
|
132
|
+
console.log(`[Project Docs Check] INCOMPLETE (${source}): missing fields: ${missing.join(', ')}`);
|
|
133
|
+
console.log('=> Run the Step 0 framework interview for missing fields, then persist with init; selected configs are not completed from workspace inference.');
|
|
132
134
|
process.exit(1);
|
|
133
135
|
}
|
|
134
136
|
|
|
@@ -144,13 +146,51 @@ function parseFlags(args) {
|
|
|
144
146
|
return values;
|
|
145
147
|
}
|
|
146
148
|
|
|
149
|
+
function isPlainObject(value) {
|
|
150
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// init never overwrites a configured value: absent/empty required fields are
|
|
154
|
+
// filled, matching flags are accepted as no-ops, and a differing flag is a
|
|
155
|
+
// field-specific conflict that aborts before any write.
|
|
156
|
+
function mergeProjectFields(existing, flags, fields) {
|
|
157
|
+
const filled = [];
|
|
158
|
+
const conflicts = [];
|
|
159
|
+
const next = { ...existing };
|
|
160
|
+
for (const [flag, field] of Object.entries(fields)) {
|
|
161
|
+
const value = flags[flag];
|
|
162
|
+
const current = existing[field];
|
|
163
|
+
if (typeof current === 'string' && current.trim()) {
|
|
164
|
+
if (current !== value) {
|
|
165
|
+
conflicts.push({ flag, field, existing: current });
|
|
166
|
+
}
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
next[field] = value;
|
|
170
|
+
filled.push(field);
|
|
171
|
+
}
|
|
172
|
+
return { next, filled, conflicts };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function printConflicts(conflicts) {
|
|
176
|
+
for (const conflict of conflicts) {
|
|
177
|
+
console.error(`[Project Docs Check] --${conflict.flag} conflicts with the configured ${conflict.field} (existing "${conflict.existing}").`);
|
|
178
|
+
console.error('=> init fills only absent/empty fields and never overwrites configured values; remove the flag or update the configuration explicitly.');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
147
182
|
function runInit(workspaceRoot, args) {
|
|
148
183
|
const flags = parseFlags(args);
|
|
149
|
-
|
|
184
|
+
// Required values must be non-empty after trimming, the same presence rule
|
|
185
|
+
// the resolver applies when reading the configuration; whitespace-only
|
|
186
|
+
// pointers are rejected before anything is read or written, and accepted
|
|
187
|
+
// values are stored trimmed.
|
|
188
|
+
const missingFlags = Object.keys(FLAG_TO_FIELD).filter(f => !flags[f] || !String(flags[f]).trim());
|
|
150
189
|
if (missingFlags.length > 0) {
|
|
151
|
-
console.error(`[Project Docs Check] init requires --${missingFlags.join(', --')}`);
|
|
190
|
+
console.error(`[Project Docs Check] init requires --${missingFlags.join(', --')} (values must not be empty or whitespace-only)`);
|
|
152
191
|
process.exit(1);
|
|
153
192
|
}
|
|
193
|
+
for (const f of Object.keys(FLAG_TO_FIELD)) flags[f] = String(flags[f]).trim();
|
|
154
194
|
|
|
155
195
|
const oversizedFlags = Object.keys(FLAG_TO_FIELD).filter(f => flags[f].length > MAX_FIELD_LENGTH);
|
|
156
196
|
if (oversizedFlags.length > 0) {
|
|
@@ -161,38 +201,112 @@ function runInit(workspaceRoot, args) {
|
|
|
161
201
|
process.exit(1);
|
|
162
202
|
}
|
|
163
203
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
204
|
+
// The project file wholly overrides host manifests, so an existing one is
|
|
205
|
+
// the authoritative repair target: fill only its absent/empty fields,
|
|
206
|
+
// preserve every other key, validate the candidate, and write only it.
|
|
207
|
+
const projectFile = path.join(workspaceRoot, '.agents', 'harness-everything', 'project-docs.json');
|
|
208
|
+
if (fs.existsSync(projectFile)) {
|
|
209
|
+
const rel = path.relative(workspaceRoot, projectFile).replace(/\\/g, '/');
|
|
210
|
+
let existing;
|
|
211
|
+
try {
|
|
212
|
+
existing = JSON.parse(fs.readFileSync(projectFile, 'utf8'));
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error(`[Project Docs Check] ${rel} exists but is not valid JSON (${error.message}); leaving it untouched.`);
|
|
215
|
+
console.error('=> Fix the JSON syntax (or remove the file after saving its content elsewhere), then rerun init.');
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
if (!isPlainObject(existing)) {
|
|
219
|
+
console.error(`[Project Docs Check] ${rel} must contain a JSON object; leaving it untouched.`);
|
|
220
|
+
console.error('=> Fix the structure, then rerun init.');
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
const validation = validateDocsConfig(existing);
|
|
224
|
+
if (!validation.valid) {
|
|
225
|
+
console.error(`[Project Docs Check] ${rel} failed schema validation; leaving it untouched.`);
|
|
226
|
+
for (const diagnostic of validation.diagnostics) {
|
|
227
|
+
console.error(` - ${diagnostic.code}: ${diagnostic.message}`);
|
|
228
|
+
}
|
|
229
|
+
console.error('=> Fix the reported issues, then rerun init.');
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
const { next, filled, conflicts } = mergeProjectFields(existing, flags, FLAG_TO_FIELD);
|
|
233
|
+
if (conflicts.length > 0) {
|
|
234
|
+
printConflicts(conflicts);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
if (filled.length === 0) {
|
|
238
|
+
console.log(`[Project Docs Check] ${rel} already covers all requested fields; nothing to fill, no write.`);
|
|
239
|
+
process.exit(0);
|
|
240
|
+
}
|
|
241
|
+
fs.writeFileSync(projectFile, `${JSON.stringify(next, null, 2)}\n`, 'utf8');
|
|
242
|
+
console.log(`[Project Docs Check] Updated ${rel}, filling: ${filled.join(', ')}; existing projectDocs keys preserved; host manifests untouched (the project file overrides them).`);
|
|
243
|
+
process.exit(0);
|
|
244
|
+
}
|
|
170
245
|
|
|
171
246
|
const homes = getRepoManifestHomes(workspaceRoot);
|
|
172
247
|
|
|
173
248
|
// Write to every platform home already bootstrapped with harness in THIS
|
|
174
249
|
// repo (their harness-everything/ subfolder already exists), so switching
|
|
175
|
-
// editors mid-project never loses this config.
|
|
176
|
-
|
|
250
|
+
// editors mid-project never loses this config. Existing projectDocs keys
|
|
251
|
+
// are preserved: only absent/empty required fields are filled here too.
|
|
252
|
+
const writableHomes = [];
|
|
177
253
|
for (const home of homes) {
|
|
178
254
|
const manifestPath = getManifestPath(home);
|
|
179
255
|
if (!fs.existsSync(path.dirname(manifestPath))) continue;
|
|
180
256
|
const data = readManifest(manifestPath);
|
|
181
|
-
data.projectDocs
|
|
182
|
-
|
|
183
|
-
console.log(`[Project Docs Check] Wrote projectDocs to ${manifestPath}`);
|
|
184
|
-
wrote++;
|
|
257
|
+
const existingProjectDocs = isPlainObject(data.projectDocs) ? data.projectDocs : null;
|
|
258
|
+
writableHomes.push({ manifestPath, data, existingProjectDocs });
|
|
185
259
|
}
|
|
186
260
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
261
|
+
const plans = [];
|
|
262
|
+
const conflicts = [];
|
|
263
|
+
for (const home of writableHomes) {
|
|
264
|
+
// A home without projectDocs keeps the previous fresh-object behavior;
|
|
265
|
+
// an existing projectDocs object only receives absent/empty required
|
|
266
|
+
// fields, preserving governance and unknown keys.
|
|
267
|
+
const baseProjectDocs = home.existingProjectDocs || {
|
|
268
|
+
docLocation: flags['doc-location'],
|
|
269
|
+
tracker: flags['tracker'],
|
|
270
|
+
issueDefinition: flags['issue-definition'],
|
|
271
|
+
updatedAt: new Date().toISOString(),
|
|
272
|
+
};
|
|
273
|
+
const { next, filled, conflicts: homeConflicts } = mergeProjectFields(baseProjectDocs, flags, FLAG_TO_FIELD);
|
|
274
|
+
if (homeConflicts.length) {
|
|
275
|
+
conflicts.push({ manifestPath: home.manifestPath, homeConflicts });
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
plans.push({ write: filled.length > 0 || !home.existingProjectDocs, manifestPath: home.manifestPath, data: home.data, projectDocs: next, filled });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Abort before the first write when ANY bootstrapped home conflicts, so a
|
|
282
|
+
// partial multi-editor update can never leave divergent manifests behind.
|
|
283
|
+
if (conflicts.length) {
|
|
284
|
+
for (const entry of conflicts) {
|
|
285
|
+
console.error(`[Project Docs Check] ${path.relative(workspaceRoot, entry.manifestPath).replace(/\\/g, '/')}:`);
|
|
286
|
+
printConflicts(entry.homeConflicts);
|
|
287
|
+
}
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (writableHomes.length === 0) {
|
|
191
292
|
const manifestPath = getManifestPath(homes[0]);
|
|
192
293
|
const data = readManifest(manifestPath);
|
|
193
|
-
data.projectDocs =
|
|
294
|
+
data.projectDocs = {
|
|
295
|
+
docLocation: flags['doc-location'],
|
|
296
|
+
tracker: flags['tracker'],
|
|
297
|
+
issueDefinition: flags['issue-definition'],
|
|
298
|
+
updatedAt: new Date().toISOString(),
|
|
299
|
+
};
|
|
194
300
|
writeManifest(manifestPath, data);
|
|
195
301
|
console.log(`[Project Docs Check] No platform bootstrapped yet in this repo - wrote projectDocs to ${manifestPath}`);
|
|
302
|
+
process.exit(0);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
for (const plan of plans) {
|
|
306
|
+
if (!plan.write) continue;
|
|
307
|
+
plan.data.projectDocs = plan.projectDocs;
|
|
308
|
+
writeManifest(plan.manifestPath, plan.data);
|
|
309
|
+
console.log(`[Project Docs Check] Wrote projectDocs to ${plan.manifestPath} (filled: ${plan.filled.join(', ')}; existing projectDocs keys preserved)`);
|
|
196
310
|
}
|
|
197
311
|
|
|
198
312
|
process.exit(0);
|
|
@@ -202,9 +316,9 @@ if (process.argv[2] === '--help' || process.argv[2] === '-h') {
|
|
|
202
316
|
console.log(`Gate: has this repo already pinned doc location / issue tracker / issue definition?
|
|
203
317
|
|
|
204
318
|
Usage:
|
|
205
|
-
node check-project-docs.js [check] Exit 0
|
|
319
|
+
node check-project-docs.js [check] Exit 0 for complete effective config or allowed inference; exit 1 for missing, invalid, or ambiguous config.
|
|
206
320
|
node check-project-docs.js init --doc-location "..." --tracker "..." --issue-definition "..."
|
|
207
|
-
|
|
321
|
+
Fill only absent/empty fields (each <= ${MAX_FIELD_LENGTH} chars - a pointer, not the content). With an existing .agents/harness-everything/project-docs.json, that file is validated and written; otherwise the fields go to every already-bootstrapped platform manifest.json in this repo, preserving existing projectDocs keys. Conflicting flags fail before any write.
|
|
208
322
|
|
|
209
323
|
Repo-local only: writes only to workspace-relative manifest homes (.claude/, .cursor/,
|
|
210
324
|
.github/, .codex/, .continue/), never to the user-home manifests self-evolve uses.`);
|