harness-everything 0.47.0 → 0.47.2
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 +1 -1
- package/.github/workflows/ci.yml +12 -0
- package/CHANGELOG.md +18 -0
- package/benchmarks/issue290-pilot/README.md +199 -0
- package/benchmarks/issue290-pilot/analyze.py +299 -0
- package/benchmarks/issue290-pilot/analyze.test.py +146 -0
- package/benchmarks/issue290-pilot/check.py +19 -0
- package/benchmarks/issue290-pilot/evidence/archive.json +7 -0
- package/benchmarks/issue290-pilot/evidence/group-summary.json +684 -0
- package/benchmarks/issue290-pilot/evidence/inventory.json +3728 -0
- package/benchmarks/issue290-pilot/evidence/ledger.json +4644 -0
- package/benchmarks/issue290-pilot/evidence/replay/pairs.json +482 -0
- package/benchmarks/issue290-pilot/evidence/replay/provenance.json +24 -0
- package/benchmarks/issue290-pilot/evidence/replay/tables.md +37 -0
- package/benchmarks/issue290-pilot/evidence.tar.gz +0 -0
- package/benchmarks/issue290-pilot/export.py +110 -0
- package/benchmarks/issue290-pilot/export.test.py +65 -0
- package/benchmarks/issue290-pilot/fable-supplement.py +55 -0
- package/benchmarks/issue290-pilot/fixture.test.py +28 -0
- package/benchmarks/issue290-pilot/fixtures/polyglot/instructions.md +19 -0
- package/benchmarks/issue290-pilot/fixtures/polyglot/package.json +29 -0
- package/benchmarks/issue290-pilot/fixtures/polyglot/simple-linked-list.js +44 -0
- package/benchmarks/issue290-pilot/fixtures/polyglot/simple-linked-list.spec.js +131 -0
- package/benchmarks/issue290-pilot/fixtures/quixbugs/LICENSE +7 -0
- package/benchmarks/issue290-pilot/fixtures/quixbugs/cases.json +7 -0
- package/benchmarks/issue290-pilot/fixtures/quixbugs/find_in_sorted.py +31 -0
- package/benchmarks/issue290-pilot/fixtures/quixbugs/test_find_in_sorted.py +15 -0
- package/benchmarks/issue290-pilot/frozen-documents.json +41 -0
- package/benchmarks/issue290-pilot/grader.cjs +43 -0
- package/benchmarks/issue290-pilot/grader.test.cjs +10 -0
- package/benchmarks/issue290-pilot/hook.test.cjs +27 -0
- package/benchmarks/issue290-pilot/host-hook.cjs +35 -0
- package/benchmarks/issue290-pilot/metrics.test.py +131 -0
- package/benchmarks/issue290-pilot/preflight/amendment.md +32 -0
- package/benchmarks/issue290-pilot/preflight/environment.json +15 -0
- package/benchmarks/issue290-pilot/preflight/fable-amendment.md +20 -0
- package/benchmarks/issue290-pilot/preflight/fable-frozen-files.json +4 -0
- package/benchmarks/issue290-pilot/preflight/fable-infrastructure-amendment.md +27 -0
- package/benchmarks/issue290-pilot/preflight/fable-retry-frozen-files.json +4 -0
- package/benchmarks/issue290-pilot/preflight/fable-supplement-v1.py +47 -0
- package/benchmarks/issue290-pilot/preflight/frozen-files.json +18 -0
- package/benchmarks/issue290-pilot/preflight/graders.json +67 -0
- package/benchmarks/issue290-pilot/pricing-assumptions.json +29 -0
- package/benchmarks/issue290-pilot/protocol.json +32 -0
- package/benchmarks/issue290-pilot/read_metrics.py +166 -0
- package/benchmarks/issue290-pilot/replay.py +167 -0
- package/benchmarks/issue290-pilot/reporting.py +82 -0
- package/benchmarks/issue290-pilot/run.py +262 -0
- package/benchmarks/issue290-pilot/sources.json +10 -0
- package/benchmarks/issue290-pilot/verification/analyzer-tests.log +5 -0
- package/benchmarks/issue290-pilot/verification/final-gates.json +58 -0
- package/benchmarks/issue290-pilot/verification/fixture-tests.log +5 -0
- package/benchmarks/issue290-pilot/verification/gates.json +58 -0
- package/benchmarks/issue290-pilot/verification/npm-audit.json +84 -0
- package/benchmarks/issue290-pilot/verification/offline-correction.json +59 -0
- package/benchmarks/issue290-pilot/verification/post-report/helper-tests.log +22 -0
- package/benchmarks/issue290-pilot/verification/post-report/test-consistency.log +626 -0
- package/benchmarks/issue290-pilot/verification/post-report/test-references.log +6 -0
- package/benchmarks/issue290-pilot/verification/post-report/waza-tokens.log +710 -0
- package/benchmarks/issue290-pilot/verification/test-collision.log +28 -0
- package/benchmarks/issue290-pilot/verification/test-consistency.log +28 -0
- package/benchmarks/issue290-pilot/verification/test-docs-capabilities.log +131 -0
- package/benchmarks/issue290-pilot/verification/test-mechanism.log +1829 -0
- package/benchmarks/issue290-pilot/verification/test-mutations.log +16 -0
- package/benchmarks/issue290-pilot/verification/test-platform-compatibility.log +9 -0
- package/benchmarks/issue290-pilot/verification/test-plugin-openai.log +6 -0
- package/benchmarks/issue290-pilot/verification/test-plugin-submission.log +5 -0
- package/benchmarks/issue290-pilot/verification/test-references.log +6 -0
- package/benchmarks/issue290-pilot/verification/test-release.log +59 -0
- package/benchmarks/issue290-pilot/verification/test-repo-contract.log +5 -0
- package/benchmarks/issue290-pilot/verification/test-routing-invariants.log +785 -0
- package/benchmarks/issue290-pilot/verification/test-routing-skills.log +28 -0
- package/benchmarks/issue290-pilot/verification/test.log +3373 -0
- package/benchmarks/issue290-pilot/verification/verification-report.md +76 -0
- package/benchmarks/issue290-pilot/verification/waza-tokens.log +710 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/gates.json +26 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-collision.log +8 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-consistency.log +626 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-mechanism.log +1693 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-release.log +38 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test-routing-skills.log +44 -0
- package/benchmarks/issue290-pilot/verification/with-dependencies/test.log +3253 -0
- package/ci/mechanism-379-self-evolve-workspace.test.js +151 -0
- package/ci/mechanism-383-doc-glob-line-terminators.test.js +131 -0
- package/docs/document-lifecycle.md +1 -1
- package/docs/platform-capabilities.md +4 -0
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/doc-check.js +1 -1
- package/harness-everything/scripts/doc-lifecycle.js +2 -2
- package/harness-everything/scripts/doc-obligations.js +2 -2
- package/hooks/scripts/lib/doc-reconciliation-core.js +2 -2
- package/hooks/scripts/lib/doc-reconciliation-provider.js +1 -1
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-core.js +2 -2
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-provider.js +1 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-check.js +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +2 -2
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-obligations.js +2 -2
- package/plugins/harness-everything/skills/self-evolve/SKILL.md +1 -1
- package/plugins/harness-everything/skills/self-evolve/references/memory-resolution.md +11 -0
- package/plugins/harness-everything/skills/self-evolve/scripts/lesson-candidate.js +9 -12
- package/plugins/harness-everything/skills/self-evolve/scripts/persist-memory.js +1 -14
- package/plugins/harness-everything/skills/self-evolve/scripts/register-dynamic-skill.js +5 -9
- package/plugins/harness-everything/skills/self-evolve/scripts/workspace-helper.js +98 -0
- package/sbom.cdx.json +7 -7
- package/self-evolve/SKILL.md +1 -1
- package/self-evolve/references/memory-resolution.md +11 -0
- package/self-evolve/scripts/lesson-candidate.js +9 -12
- package/self-evolve/scripts/persist-memory.js +1 -14
- package/self-evolve/scripts/register-dynamic-skill.js +5 -9
- package/self-evolve/scripts/workspace-helper.js +98 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function workspaceError(code, message, cause) {
|
|
7
|
+
const error = new Error(`${code}: ${message}${cause ? ` (${cause.code || cause.name}: ${cause.message})` : ''}`, { cause });
|
|
8
|
+
error.code = code;
|
|
9
|
+
return error;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function readMarker(file) {
|
|
13
|
+
try { return JSON.parse(fs.readFileSync(file, 'utf8')); }
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (error.code === 'ENOENT') return null;
|
|
16
|
+
throw workspaceError('WORKSPACE_HELPER_UNAVAILABLE', `cannot read installation marker ${file}`, error);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let helper;
|
|
21
|
+
function loadHelper() {
|
|
22
|
+
if (helper) return helper;
|
|
23
|
+
const sourceRoot = path.resolve(__dirname, '../..');
|
|
24
|
+
const sourceMarker = path.join(sourceRoot, 'package.json');
|
|
25
|
+
const source = readMarker(sourceMarker);
|
|
26
|
+
let installRoot;
|
|
27
|
+
// A source checkout may itself be named "skills". Its own package marker
|
|
28
|
+
// takes precedence, including when its helper is absent or broken.
|
|
29
|
+
if (source) {
|
|
30
|
+
if (source.name !== 'harness-everything') {
|
|
31
|
+
throw workspaceError('WORKSPACE_HELPER_UNAVAILABLE', `unrecognized source installation marker ${sourceMarker}`);
|
|
32
|
+
}
|
|
33
|
+
installRoot = sourceRoot;
|
|
34
|
+
} else {
|
|
35
|
+
const pluginRoot = path.dirname(sourceRoot);
|
|
36
|
+
const marker = path.join(pluginRoot, '.codex-plugin/plugin.json');
|
|
37
|
+
const plugin = path.basename(sourceRoot) === 'skills' ? readMarker(marker) : null;
|
|
38
|
+
if (!plugin || plugin.name !== 'harness-everything' || plugin.skills !== './skills/') {
|
|
39
|
+
throw workspaceError('WORKSPACE_HELPER_UNAVAILABLE', `unrecognized layout; expected ${sourceMarker} or packaged skills under ${marker}`);
|
|
40
|
+
}
|
|
41
|
+
installRoot = pluginRoot;
|
|
42
|
+
}
|
|
43
|
+
const candidate = path.join(installRoot, 'scripts/lib/workspace.js');
|
|
44
|
+
try {
|
|
45
|
+
const loaded = require(candidate);
|
|
46
|
+
for (const name of ['getWorkspaceRoot', 'getWorkspaceStateDir', 'getWorkspaceKey']) {
|
|
47
|
+
if (typeof loaded[name] !== 'function') throw new Error(`missing ${name} export`);
|
|
48
|
+
}
|
|
49
|
+
helper = loaded;
|
|
50
|
+
return helper;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throw workspaceError('WORKSPACE_HELPER_UNAVAILABLE', `cannot load selected installation helper ${candidate}`, error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getWorkspaceRoot(context) {
|
|
57
|
+
const loaded = loadHelper();
|
|
58
|
+
let root;
|
|
59
|
+
try { root = loaded.getWorkspaceRoot(context); }
|
|
60
|
+
catch (error) { throw workspaceError('WORKSPACE_ROOT_UNRESOLVED', 'canonical workspace resolution failed', error); }
|
|
61
|
+
if (typeof root !== 'string' || !root.trim()) {
|
|
62
|
+
throw workspaceError('WORKSPACE_ROOT_UNRESOLVED', 'canonical resolver returned no workspace identity');
|
|
63
|
+
}
|
|
64
|
+
return root;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getWorkspaceStateDir(root) {
|
|
68
|
+
const loaded = loadHelper();
|
|
69
|
+
if (typeof root !== 'string' || !root.trim()) {
|
|
70
|
+
throw workspaceError('WORKSPACE_ROOT_UNRESOLVED', 'workspace identity is required for state access');
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const state = loaded.getWorkspaceStateDir(root);
|
|
74
|
+
if (typeof state !== 'string' || !state.trim()) throw new Error('canonical state path must be nonempty');
|
|
75
|
+
// Missing state is valid for a new workspace. Existing non-directory or
|
|
76
|
+
// inaccessible ancestors are not an empty candidate list.
|
|
77
|
+
let ancestor = state;
|
|
78
|
+
while (true) {
|
|
79
|
+
try {
|
|
80
|
+
if (!fs.statSync(ancestor).isDirectory()) throw new Error(`${ancestor} is not a directory`);
|
|
81
|
+
fs.accessSync(ancestor, fs.constants.R_OK);
|
|
82
|
+
break;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error.code !== 'ENOENT') throw error;
|
|
85
|
+
const parent = path.dirname(ancestor);
|
|
86
|
+
if (parent === ancestor) throw error;
|
|
87
|
+
ancestor = parent;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return state;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw workspaceError('WORKSPACE_STATE_UNAVAILABLE', `cannot access canonical state for ${root}`, error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getWorkspaceKey(root) { return loadHelper().getWorkspaceKey(root); }
|
|
97
|
+
|
|
98
|
+
module.exports = { getWorkspaceRoot, getWorkspaceStateDir, getWorkspaceKey, workspaceError };
|
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:26736d02-183f-5f29-8a3c-f808613252e6",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
8
|
+
"timestamp": "2026-10-31T15:40:16.374Z",
|
|
9
9
|
"tools": [
|
|
10
10
|
{
|
|
11
11
|
"vendor": "Harness Everything",
|
|
12
12
|
"name": "generate-sbom.js",
|
|
13
|
-
"version": "0.47.
|
|
13
|
+
"version": "0.47.2"
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
16
|
"component": {
|
|
17
17
|
"type": "application",
|
|
18
|
-
"bom-ref": "pkg:npm/harness-everything@0.47.
|
|
18
|
+
"bom-ref": "pkg:npm/harness-everything@0.47.2",
|
|
19
19
|
"name": "harness-everything",
|
|
20
|
-
"version": "0.47.
|
|
21
|
-
"purl": "pkg:npm/harness-everything@0.47.
|
|
20
|
+
"version": "0.47.2",
|
|
21
|
+
"purl": "pkg:npm/harness-everything@0.47.2"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"components": [
|
|
@@ -2033,7 +2033,7 @@
|
|
|
2033
2033
|
]
|
|
2034
2034
|
},
|
|
2035
2035
|
{
|
|
2036
|
-
"ref": "pkg:npm/harness-everything@0.47.
|
|
2036
|
+
"ref": "pkg:npm/harness-everything@0.47.2",
|
|
2037
2037
|
"dependsOn": [
|
|
2038
2038
|
"pkg:npm/%40commitlint/cli@19.8.1",
|
|
2039
2039
|
"pkg:npm/%40commitlint/config-conventional@19.8.1",
|
package/self-evolve/SKILL.md
CHANGED
|
@@ -35,6 +35,17 @@ For a simple rule, inspect/list the session candidate, evaluate it with a genera
|
|
|
35
35
|
|
|
36
36
|
For a reusable multi-step procedure, load `skill-creator/SKILL.md`, create the draft skill, and use `register-dynamic-skill.js` to update each platform's `manifest.json` `generated[]` registry. The host agent remains responsible for deciding which evidence is relevant; these scripts do not discover session history.
|
|
37
37
|
|
|
38
|
+
## Workspace helper and diagnostics
|
|
39
|
+
|
|
40
|
+
| Contract | Behavior |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| Trusted imports | Select one layout using its installation marker: source `package.json` takes precedence; packaged `skills/` requires `.codex-plugin/plugin.json`. Load only that root’s `scripts/lib/workspace.js`; never fall through to ancestor/project/cwd code. |
|
|
43
|
+
| Identity and state | Use the canonical resolver and `HARNESS_STATE_HOME` precedence; source and plugin read the same workspace/session candidates. No alternate state root. |
|
|
44
|
+
| Missing helper | `WORKSPACE_HELPER_UNAVAILABLE` identifies the selected helper path or invalid layout marker and cause; absent or broken helpers fail without trying another layout. |
|
|
45
|
+
| Unresolved identity | `WORKSPACE_ROOT_UNRESOLVED` exits nonzero; do not substitute cwd when canonical resolution fails. |
|
|
46
|
+
| Unusable state | `WORKSPACE_STATE_UNAVAILABLE` exits nonzero for invalid state paths or failed reads; only a genuinely absent candidate directory returns `[]`. |
|
|
47
|
+
| Writes | Existing screening and workflow authorization still apply. Resolver failures never authorize persistence or state repair. |
|
|
48
|
+
|
|
38
49
|
## Candidate Lifecycle
|
|
39
50
|
|
|
40
51
|
```text
|
|
@@ -18,14 +18,7 @@ for (const relative of [
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
let getWorkspaceStateDir;
|
|
23
|
-
try {
|
|
24
|
-
({ getWorkspaceRoot, getWorkspaceStateDir } = require('../../scripts/lib/workspace'));
|
|
25
|
-
} catch (_) {
|
|
26
|
-
getWorkspaceRoot = () => null;
|
|
27
|
-
getWorkspaceStateDir = () => null;
|
|
28
|
-
}
|
|
21
|
+
const { getWorkspaceRoot, getWorkspaceStateDir, workspaceError } = require('./workspace-helper');
|
|
29
22
|
|
|
30
23
|
const SCHEMA_VERSION = 1;
|
|
31
24
|
const TERMINAL = new Set(['rejected', 'validated', 'regressed', 'superseded']);
|
|
@@ -48,10 +41,10 @@ function readJson(filePath) {
|
|
|
48
41
|
}
|
|
49
42
|
|
|
50
43
|
function candidatePath(workspace, sessionId, candidateId) {
|
|
51
|
-
|
|
44
|
+
const stateDir = getWorkspaceStateDir(workspace);
|
|
52
45
|
if (!/^lesson-[a-f0-9]{24}$/.test(String(candidateId || ''))) throw new Error('invalid candidate id');
|
|
53
46
|
return path.join(
|
|
54
|
-
|
|
47
|
+
stateDir,
|
|
55
48
|
'state',
|
|
56
49
|
'sessions',
|
|
57
50
|
safeSessionName(sessionId),
|
|
@@ -292,7 +285,10 @@ function observeCandidate(input) {
|
|
|
292
285
|
function listCandidates({ workspace, sessionId }) {
|
|
293
286
|
const base = path.join(getWorkspaceStateDir(workspace), 'state', 'sessions', safeSessionName(sessionId), 'learning-candidates');
|
|
294
287
|
let files = [];
|
|
295
|
-
try { files = fs.readdirSync(base).filter(name => /^lesson-[a-f0-9]{24}\.json$/.test(name)); } catch (
|
|
288
|
+
try { files = fs.readdirSync(base).filter(name => /^lesson-[a-f0-9]{24}\.json$/.test(name)); } catch (error) {
|
|
289
|
+
if (error.code === 'ENOENT') return [];
|
|
290
|
+
throw workspaceError('WORKSPACE_STATE_UNAVAILABLE', `cannot read candidate directory ${base}`, error);
|
|
291
|
+
}
|
|
296
292
|
return files.map(name => readJson(path.join(base, name))).filter(Boolean);
|
|
297
293
|
}
|
|
298
294
|
|
|
@@ -303,7 +299,8 @@ function option(args, name) {
|
|
|
303
299
|
|
|
304
300
|
function parseCli(argv) {
|
|
305
301
|
const command = argv[0];
|
|
306
|
-
const
|
|
302
|
+
const explicit = option(argv, '--workspace');
|
|
303
|
+
const workspace = getWorkspaceRoot(explicit ? { workspace_root: path.resolve(explicit) } : undefined);
|
|
307
304
|
const base = {
|
|
308
305
|
command,
|
|
309
306
|
workspace,
|
|
@@ -4,20 +4,7 @@
|
|
|
4
4
|
const crypto = require('crypto');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
-
|
|
8
|
-
let getWorkspaceStateDir;
|
|
9
|
-
let getWorkspaceKey;
|
|
10
|
-
try {
|
|
11
|
-
({ getWorkspaceRoot, getWorkspaceStateDir, getWorkspaceKey } = require('../../scripts/lib/workspace'));
|
|
12
|
-
} catch (err) {
|
|
13
|
-
getWorkspaceRoot = () => null;
|
|
14
|
-
getWorkspaceStateDir = () => null;
|
|
15
|
-
getWorkspaceKey = () => null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// NOTE: this script ships standalone - it is copied whole into every install
|
|
19
|
-
// target. When the canonical workspace resolver is not present, persistence
|
|
20
|
-
// fails closed rather than guessing a project root (issue #42).
|
|
7
|
+
const { getWorkspaceRoot, getWorkspaceStateDir, getWorkspaceKey } = require('./workspace-helper');
|
|
21
8
|
|
|
22
9
|
const SECRET_PATTERNS = [
|
|
23
10
|
['github-token', /\bgh[pousr]_[A-Za-z0-9]{20,}\b/],
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
|
|
5
|
-
try {
|
|
6
|
-
({ getWorkspaceRoot } = require('../../scripts/lib/workspace'));
|
|
7
|
-
} catch (err) {
|
|
8
|
-
getWorkspaceRoot = () => null;
|
|
9
|
-
}
|
|
4
|
+
const { getWorkspaceRoot } = require('./workspace-helper');
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
let workspaceRoot;
|
|
7
|
+
try { workspaceRoot = getWorkspaceRoot(); }
|
|
8
|
+
catch (error) {
|
|
9
|
+
console.error(`[self-evolve] ${error.message}`);
|
|
14
10
|
process.exit(1);
|
|
15
11
|
}
|
|
16
12
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function workspaceError(code, message, cause) {
|
|
7
|
+
const error = new Error(`${code}: ${message}${cause ? ` (${cause.code || cause.name}: ${cause.message})` : ''}`, { cause });
|
|
8
|
+
error.code = code;
|
|
9
|
+
return error;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function readMarker(file) {
|
|
13
|
+
try { return JSON.parse(fs.readFileSync(file, 'utf8')); }
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (error.code === 'ENOENT') return null;
|
|
16
|
+
throw workspaceError('WORKSPACE_HELPER_UNAVAILABLE', `cannot read installation marker ${file}`, error);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let helper;
|
|
21
|
+
function loadHelper() {
|
|
22
|
+
if (helper) return helper;
|
|
23
|
+
const sourceRoot = path.resolve(__dirname, '../..');
|
|
24
|
+
const sourceMarker = path.join(sourceRoot, 'package.json');
|
|
25
|
+
const source = readMarker(sourceMarker);
|
|
26
|
+
let installRoot;
|
|
27
|
+
// A source checkout may itself be named "skills". Its own package marker
|
|
28
|
+
// takes precedence, including when its helper is absent or broken.
|
|
29
|
+
if (source) {
|
|
30
|
+
if (source.name !== 'harness-everything') {
|
|
31
|
+
throw workspaceError('WORKSPACE_HELPER_UNAVAILABLE', `unrecognized source installation marker ${sourceMarker}`);
|
|
32
|
+
}
|
|
33
|
+
installRoot = sourceRoot;
|
|
34
|
+
} else {
|
|
35
|
+
const pluginRoot = path.dirname(sourceRoot);
|
|
36
|
+
const marker = path.join(pluginRoot, '.codex-plugin/plugin.json');
|
|
37
|
+
const plugin = path.basename(sourceRoot) === 'skills' ? readMarker(marker) : null;
|
|
38
|
+
if (!plugin || plugin.name !== 'harness-everything' || plugin.skills !== './skills/') {
|
|
39
|
+
throw workspaceError('WORKSPACE_HELPER_UNAVAILABLE', `unrecognized layout; expected ${sourceMarker} or packaged skills under ${marker}`);
|
|
40
|
+
}
|
|
41
|
+
installRoot = pluginRoot;
|
|
42
|
+
}
|
|
43
|
+
const candidate = path.join(installRoot, 'scripts/lib/workspace.js');
|
|
44
|
+
try {
|
|
45
|
+
const loaded = require(candidate);
|
|
46
|
+
for (const name of ['getWorkspaceRoot', 'getWorkspaceStateDir', 'getWorkspaceKey']) {
|
|
47
|
+
if (typeof loaded[name] !== 'function') throw new Error(`missing ${name} export`);
|
|
48
|
+
}
|
|
49
|
+
helper = loaded;
|
|
50
|
+
return helper;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throw workspaceError('WORKSPACE_HELPER_UNAVAILABLE', `cannot load selected installation helper ${candidate}`, error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getWorkspaceRoot(context) {
|
|
57
|
+
const loaded = loadHelper();
|
|
58
|
+
let root;
|
|
59
|
+
try { root = loaded.getWorkspaceRoot(context); }
|
|
60
|
+
catch (error) { throw workspaceError('WORKSPACE_ROOT_UNRESOLVED', 'canonical workspace resolution failed', error); }
|
|
61
|
+
if (typeof root !== 'string' || !root.trim()) {
|
|
62
|
+
throw workspaceError('WORKSPACE_ROOT_UNRESOLVED', 'canonical resolver returned no workspace identity');
|
|
63
|
+
}
|
|
64
|
+
return root;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getWorkspaceStateDir(root) {
|
|
68
|
+
const loaded = loadHelper();
|
|
69
|
+
if (typeof root !== 'string' || !root.trim()) {
|
|
70
|
+
throw workspaceError('WORKSPACE_ROOT_UNRESOLVED', 'workspace identity is required for state access');
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const state = loaded.getWorkspaceStateDir(root);
|
|
74
|
+
if (typeof state !== 'string' || !state.trim()) throw new Error('canonical state path must be nonempty');
|
|
75
|
+
// Missing state is valid for a new workspace. Existing non-directory or
|
|
76
|
+
// inaccessible ancestors are not an empty candidate list.
|
|
77
|
+
let ancestor = state;
|
|
78
|
+
while (true) {
|
|
79
|
+
try {
|
|
80
|
+
if (!fs.statSync(ancestor).isDirectory()) throw new Error(`${ancestor} is not a directory`);
|
|
81
|
+
fs.accessSync(ancestor, fs.constants.R_OK);
|
|
82
|
+
break;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error.code !== 'ENOENT') throw error;
|
|
85
|
+
const parent = path.dirname(ancestor);
|
|
86
|
+
if (parent === ancestor) throw error;
|
|
87
|
+
ancestor = parent;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return state;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw workspaceError('WORKSPACE_STATE_UNAVAILABLE', `cannot access canonical state for ${root}`, error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getWorkspaceKey(root) { return loadHelper().getWorkspaceKey(root); }
|
|
97
|
+
|
|
98
|
+
module.exports = { getWorkspaceRoot, getWorkspaceStateDir, getWorkspaceKey, workspaceError };
|