@ai-substrate/engineering-harness 0.2.0-canary.45
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/LICENSE +21 -0
- package/README.md +119 -0
- package/harness/cli/bin/harness.js +12 -0
- package/harness/cli/dist/acts/docs.d.ts +17 -0
- package/harness/cli/dist/acts/docs.js +73 -0
- package/harness/cli/dist/acts/docs.js.map +1 -0
- package/harness/cli/dist/acts/doctor.d.ts +14 -0
- package/harness/cli/dist/acts/doctor.js +43 -0
- package/harness/cli/dist/acts/doctor.js.map +1 -0
- package/harness/cli/dist/acts/help.d.ts +14 -0
- package/harness/cli/dist/acts/help.js +29 -0
- package/harness/cli/dist/acts/help.js.map +1 -0
- package/harness/cli/dist/acts/init.d.ts +22 -0
- package/harness/cli/dist/acts/init.js +61 -0
- package/harness/cli/dist/acts/init.js.map +1 -0
- package/harness/cli/dist/acts/instructions.d.ts +21 -0
- package/harness/cli/dist/acts/instructions.js +75 -0
- package/harness/cli/dist/acts/instructions.js.map +1 -0
- package/harness/cli/dist/acts/new.d.ts +19 -0
- package/harness/cli/dist/acts/new.js +66 -0
- package/harness/cli/dist/acts/new.js.map +1 -0
- package/harness/cli/dist/acts/observe.d.ts +23 -0
- package/harness/cli/dist/acts/observe.js +129 -0
- package/harness/cli/dist/acts/observe.js.map +1 -0
- package/harness/cli/dist/acts/record.d.ts +24 -0
- package/harness/cli/dist/acts/record.js +93 -0
- package/harness/cli/dist/acts/record.js.map +1 -0
- package/harness/cli/dist/acts/skills.d.ts +32 -0
- package/harness/cli/dist/acts/skills.js +256 -0
- package/harness/cli/dist/acts/skills.js.map +1 -0
- package/harness/cli/dist/acts/update.d.ts +23 -0
- package/harness/cli/dist/acts/update.js +297 -0
- package/harness/cli/dist/acts/update.js.map +1 -0
- package/harness/cli/dist/acts/verb.d.ts +27 -0
- package/harness/cli/dist/acts/verb.js +56 -0
- package/harness/cli/dist/acts/verb.js.map +1 -0
- package/harness/cli/dist/adapters/clock/clock-port.d.ts +10 -0
- package/harness/cli/dist/adapters/clock/clock-port.js +2 -0
- package/harness/cli/dist/adapters/clock/clock-port.js.map +1 -0
- package/harness/cli/dist/adapters/clock/fake-clock.d.ts +15 -0
- package/harness/cli/dist/adapters/clock/fake-clock.js +25 -0
- package/harness/cli/dist/adapters/clock/fake-clock.js.map +1 -0
- package/harness/cli/dist/adapters/clock/system-clock.d.ts +5 -0
- package/harness/cli/dist/adapters/clock/system-clock.js +7 -0
- package/harness/cli/dist/adapters/clock/system-clock.js.map +1 -0
- package/harness/cli/dist/adapters/env/env-port.d.ts +17 -0
- package/harness/cli/dist/adapters/env/env-port.js +2 -0
- package/harness/cli/dist/adapters/env/env-port.js.map +1 -0
- package/harness/cli/dist/adapters/env/fake-env.d.ts +15 -0
- package/harness/cli/dist/adapters/env/fake-env.js +24 -0
- package/harness/cli/dist/adapters/env/fake-env.js.map +1 -0
- package/harness/cli/dist/adapters/env/node-env.d.ts +6 -0
- package/harness/cli/dist/adapters/env/node-env.js +16 -0
- package/harness/cli/dist/adapters/env/node-env.js.map +1 -0
- package/harness/cli/dist/adapters/exec/exec-port.d.ts +22 -0
- package/harness/cli/dist/adapters/exec/exec-port.js +2 -0
- package/harness/cli/dist/adapters/exec/exec-port.js.map +1 -0
- package/harness/cli/dist/adapters/exec/fake-exec.d.ts +25 -0
- package/harness/cli/dist/adapters/exec/fake-exec.js +25 -0
- package/harness/cli/dist/adapters/exec/fake-exec.js.map +1 -0
- package/harness/cli/dist/adapters/exec/node-exec.d.ts +14 -0
- package/harness/cli/dist/adapters/exec/node-exec.js +38 -0
- package/harness/cli/dist/adapters/exec/node-exec.js.map +1 -0
- package/harness/cli/dist/adapters/fs/fake-fs.d.ts +22 -0
- package/harness/cli/dist/adapters/fs/fake-fs.js +63 -0
- package/harness/cli/dist/adapters/fs/fake-fs.js.map +1 -0
- package/harness/cli/dist/adapters/fs/fs-port.d.ts +20 -0
- package/harness/cli/dist/adapters/fs/fs-port.js +2 -0
- package/harness/cli/dist/adapters/fs/fs-port.js.map +1 -0
- package/harness/cli/dist/adapters/fs/node-fs.d.ts +9 -0
- package/harness/cli/dist/adapters/fs/node-fs.js +30 -0
- package/harness/cli/dist/adapters/fs/node-fs.js.map +1 -0
- package/harness/cli/dist/adapters/git/exec-git.d.ts +6 -0
- package/harness/cli/dist/adapters/git/exec-git.js +21 -0
- package/harness/cli/dist/adapters/git/exec-git.js.map +1 -0
- package/harness/cli/dist/adapters/git/fake-git.d.ts +15 -0
- package/harness/cli/dist/adapters/git/fake-git.js +20 -0
- package/harness/cli/dist/adapters/git/fake-git.js.map +1 -0
- package/harness/cli/dist/adapters/git/git-port.d.ts +12 -0
- package/harness/cli/dist/adapters/git/git-port.js +2 -0
- package/harness/cli/dist/adapters/git/git-port.js.map +1 -0
- package/harness/cli/dist/adapters/loader/fake-loader.d.ts +13 -0
- package/harness/cli/dist/adapters/loader/fake-loader.js +25 -0
- package/harness/cli/dist/adapters/loader/fake-loader.js.map +1 -0
- package/harness/cli/dist/adapters/loader/jiti-loader.d.ts +15 -0
- package/harness/cli/dist/adapters/loader/jiti-loader.js +29 -0
- package/harness/cli/dist/adapters/loader/jiti-loader.js.map +1 -0
- package/harness/cli/dist/adapters/loader/module-loader-port.d.ts +12 -0
- package/harness/cli/dist/adapters/loader/module-loader-port.js +2 -0
- package/harness/cli/dist/adapters/loader/module-loader-port.js.map +1 -0
- package/harness/cli/dist/adapters/process/fake-process.d.ts +13 -0
- package/harness/cli/dist/adapters/process/fake-process.js +21 -0
- package/harness/cli/dist/adapters/process/fake-process.js.map +1 -0
- package/harness/cli/dist/adapters/process/node-process.d.ts +6 -0
- package/harness/cli/dist/adapters/process/node-process.js +17 -0
- package/harness/cli/dist/adapters/process/node-process.js.map +1 -0
- package/harness/cli/dist/adapters/process/process-port.d.ts +13 -0
- package/harness/cli/dist/adapters/process/process-port.js +2 -0
- package/harness/cli/dist/adapters/process/process-port.js.map +1 -0
- package/harness/cli/dist/adapters/version-lookup/fake-version-lookup.d.ts +13 -0
- package/harness/cli/dist/adapters/version-lookup/fake-version-lookup.js +21 -0
- package/harness/cli/dist/adapters/version-lookup/fake-version-lookup.js.map +1 -0
- package/harness/cli/dist/adapters/version-lookup/node-version-lookup.d.ts +18 -0
- package/harness/cli/dist/adapters/version-lookup/node-version-lookup.js +51 -0
- package/harness/cli/dist/adapters/version-lookup/node-version-lookup.js.map +1 -0
- package/harness/cli/dist/adapters/version-lookup/version-lookup-port.d.ts +19 -0
- package/harness/cli/dist/adapters/version-lookup/version-lookup-port.js +2 -0
- package/harness/cli/dist/adapters/version-lookup/version-lookup-port.js.map +1 -0
- package/harness/cli/dist/app.d.ts +70 -0
- package/harness/cli/dist/app.js +221 -0
- package/harness/cli/dist/app.js.map +1 -0
- package/harness/cli/dist/index.d.ts +2 -0
- package/harness/cli/dist/index.js +26 -0
- package/harness/cli/dist/index.js.map +1 -0
- package/harness/cli/dist/output/envelope.d.ts +68 -0
- package/harness/cli/dist/output/envelope.js +56 -0
- package/harness/cli/dist/output/envelope.js.map +1 -0
- package/harness/cli/dist/output/error-codes.d.ts +57 -0
- package/harness/cli/dist/output/error-codes.js +57 -0
- package/harness/cli/dist/output/error-codes.js.map +1 -0
- package/harness/cli/dist/output/exit.d.ts +29 -0
- package/harness/cli/dist/output/exit.js +36 -0
- package/harness/cli/dist/output/exit.js.map +1 -0
- package/harness/cli/dist/output/output-port.d.ts +54 -0
- package/harness/cli/dist/output/output-port.js +55 -0
- package/harness/cli/dist/output/output-port.js.map +1 -0
- package/harness/cli/dist/output/style.d.ts +33 -0
- package/harness/cli/dist/output/style.js +68 -0
- package/harness/cli/dist/output/style.js.map +1 -0
- package/harness/cli/dist/services/config/load-config.d.ts +27 -0
- package/harness/cli/dist/services/config/load-config.js +114 -0
- package/harness/cli/dist/services/config/load-config.js.map +1 -0
- package/harness/cli/dist/services/docs/contract.d.ts +41 -0
- package/harness/cli/dist/services/docs/contract.js +14 -0
- package/harness/cli/dist/services/docs/contract.js.map +1 -0
- package/harness/cli/dist/services/docs/docs-content.d.ts +37 -0
- package/harness/cli/dist/services/docs/docs-content.js +48 -0
- package/harness/cli/dist/services/docs/docs-content.js.map +1 -0
- package/harness/cli/dist/services/docs/docs-service.d.ts +26 -0
- package/harness/cli/dist/services/docs/docs-service.js +25 -0
- package/harness/cli/dist/services/docs/docs-service.js.map +1 -0
- package/harness/cli/dist/services/doctor/doctor-service.d.ts +69 -0
- package/harness/cli/dist/services/doctor/doctor-service.js +237 -0
- package/harness/cli/dist/services/doctor/doctor-service.js.map +1 -0
- package/harness/cli/dist/services/extensions/contract.d.ts +138 -0
- package/harness/cli/dist/services/extensions/contract.js +17 -0
- package/harness/cli/dist/services/extensions/contract.js.map +1 -0
- package/harness/cli/dist/services/extensions/discovery.d.ts +53 -0
- package/harness/cli/dist/services/extensions/discovery.js +116 -0
- package/harness/cli/dist/services/extensions/discovery.js.map +1 -0
- package/harness/cli/dist/services/extensions/registry.d.ts +63 -0
- package/harness/cli/dist/services/extensions/registry.js +165 -0
- package/harness/cli/dist/services/extensions/registry.js.map +1 -0
- package/harness/cli/dist/services/extensions/verb-context.d.ts +44 -0
- package/harness/cli/dist/services/extensions/verb-context.js +97 -0
- package/harness/cli/dist/services/extensions/verb-context.js.map +1 -0
- package/harness/cli/dist/services/help/help-service.d.ts +42 -0
- package/harness/cli/dist/services/help/help-service.js +108 -0
- package/harness/cli/dist/services/help/help-service.js.map +1 -0
- package/harness/cli/dist/services/init/governance-template.d.ts +27 -0
- package/harness/cli/dist/services/init/governance-template.js +72 -0
- package/harness/cli/dist/services/init/governance-template.js.map +1 -0
- package/harness/cli/dist/services/init/init-service.d.ts +38 -0
- package/harness/cli/dist/services/init/init-service.js +44 -0
- package/harness/cli/dist/services/init/init-service.js.map +1 -0
- package/harness/cli/dist/services/instructions/core-instructions.d.ts +11 -0
- package/harness/cli/dist/services/instructions/core-instructions.js +80 -0
- package/harness/cli/dist/services/instructions/core-instructions.js.map +1 -0
- package/harness/cli/dist/services/instructions/instructions-service.d.ts +52 -0
- package/harness/cli/dist/services/instructions/instructions-service.js +53 -0
- package/harness/cli/dist/services/instructions/instructions-service.js.map +1 -0
- package/harness/cli/dist/services/observe/buffer-codec.d.ts +51 -0
- package/harness/cli/dist/services/observe/buffer-codec.js +139 -0
- package/harness/cli/dist/services/observe/buffer-codec.js.map +1 -0
- package/harness/cli/dist/services/observe/observe-service.d.ts +87 -0
- package/harness/cli/dist/services/observe/observe-service.js +221 -0
- package/harness/cli/dist/services/observe/observe-service.js.map +1 -0
- package/harness/cli/dist/services/record/contract.d.ts +32 -0
- package/harness/cli/dist/services/record/contract.js +17 -0
- package/harness/cli/dist/services/record/contract.js.map +1 -0
- package/harness/cli/dist/services/record/core-types/retro.d.ts +20 -0
- package/harness/cli/dist/services/record/core-types/retro.js +55 -0
- package/harness/cli/dist/services/record/core-types/retro.js.map +1 -0
- package/harness/cli/dist/services/record/record-service.d.ts +38 -0
- package/harness/cli/dist/services/record/record-service.js +144 -0
- package/harness/cli/dist/services/record/record-service.js.map +1 -0
- package/harness/cli/dist/services/record/registry.d.ts +46 -0
- package/harness/cli/dist/services/record/registry.js +71 -0
- package/harness/cli/dist/services/record/registry.js.map +1 -0
- package/harness/cli/dist/services/scaffold/scaffold-service.d.ts +29 -0
- package/harness/cli/dist/services/scaffold/scaffold-service.js +88 -0
- package/harness/cli/dist/services/scaffold/scaffold-service.js.map +1 -0
- package/harness/cli/dist/services/scaffold/templates.d.ts +42 -0
- package/harness/cli/dist/services/scaffold/templates.js +178 -0
- package/harness/cli/dist/services/scaffold/templates.js.map +1 -0
- package/harness/cli/dist/services/shared/posix-path.d.ts +54 -0
- package/harness/cli/dist/services/shared/posix-path.js +94 -0
- package/harness/cli/dist/services/shared/posix-path.js.map +1 -0
- package/harness/cli/dist/services/shared/temp.d.ts +24 -0
- package/harness/cli/dist/services/shared/temp.js +29 -0
- package/harness/cli/dist/services/shared/temp.js.map +1 -0
- package/harness/cli/dist/services/skills/contract.d.ts +52 -0
- package/harness/cli/dist/services/skills/contract.js +55 -0
- package/harness/cli/dist/services/skills/contract.js.map +1 -0
- package/harness/cli/dist/services/skills/skills-service.d.ts +73 -0
- package/harness/cli/dist/services/skills/skills-service.js +132 -0
- package/harness/cli/dist/services/skills/skills-service.js.map +1 -0
- package/harness/cli/dist/services/update/banner.d.ts +26 -0
- package/harness/cli/dist/services/update/banner.js +28 -0
- package/harness/cli/dist/services/update/banner.js.map +1 -0
- package/harness/cli/dist/services/update/cache.d.ts +21 -0
- package/harness/cli/dist/services/update/cache.js +61 -0
- package/harness/cli/dist/services/update/cache.js.map +1 -0
- package/harness/cli/dist/services/update/constants.d.ts +9 -0
- package/harness/cli/dist/services/update/constants.js +10 -0
- package/harness/cli/dist/services/update/constants.js.map +1 -0
- package/harness/cli/dist/services/update/install.d.ts +26 -0
- package/harness/cli/dist/services/update/install.js +78 -0
- package/harness/cli/dist/services/update/install.js.map +1 -0
- package/harness/cli/dist/services/update/semver.d.ts +16 -0
- package/harness/cli/dist/services/update/semver.js +108 -0
- package/harness/cli/dist/services/update/semver.js.map +1 -0
- package/harness/cli/dist/services/update/update-service.d.ts +46 -0
- package/harness/cli/dist/services/update/update-service.js +91 -0
- package/harness/cli/dist/services/update/update-service.js.map +1 -0
- package/harness/cli/dist/version.d.ts +8 -0
- package/harness/cli/dist/version.js +15 -0
- package/harness/cli/dist/version.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { formatDegraded, formatError, formatOk, formatUnconfigured, } from '../../output/envelope.js';
|
|
2
|
+
import { ErrorCodes } from '../../output/error-codes.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build the `VerbContext` an author's handler receives. Surfaces the injected
|
|
5
|
+
* ports (read-mostly) + envelope-helper closures so a verb never imports the
|
|
6
|
+
* kernel. `ctx.exec` adapts the author-facing signature (optional args + cwd) to
|
|
7
|
+
* `ExecPort.run`, defaulting cwd to the invocation cwd (WS-A Decision 3).
|
|
8
|
+
*/
|
|
9
|
+
export function buildVerbContext(deps, invocation) {
|
|
10
|
+
return {
|
|
11
|
+
cwd: invocation.cwd,
|
|
12
|
+
args: invocation.args,
|
|
13
|
+
options: invocation.options,
|
|
14
|
+
exec: (command, args = [], opts) => deps.exec.run(command, args, { cwd: opts?.cwd ?? invocation.cwd }),
|
|
15
|
+
fs: deps.fs,
|
|
16
|
+
env: deps.env,
|
|
17
|
+
git: deps.git,
|
|
18
|
+
clock: deps.clock,
|
|
19
|
+
ok: (data, opts) => ({
|
|
20
|
+
status: 'ok',
|
|
21
|
+
data,
|
|
22
|
+
...(opts?.evidence && { evidence: opts.evidence }),
|
|
23
|
+
...(opts?.next_action && { next_action: opts.next_action }),
|
|
24
|
+
}),
|
|
25
|
+
degraded: (data, next_action, opts) => ({
|
|
26
|
+
status: 'degraded',
|
|
27
|
+
data,
|
|
28
|
+
next_action,
|
|
29
|
+
...(opts?.evidence && { evidence: opts.evidence }),
|
|
30
|
+
}),
|
|
31
|
+
unconfigured: (next_action, opts) => ({
|
|
32
|
+
status: 'unconfigured',
|
|
33
|
+
next_action,
|
|
34
|
+
...(opts?.data !== undefined && { data: opts.data }),
|
|
35
|
+
}),
|
|
36
|
+
error: (code, message, opts) => ({
|
|
37
|
+
status: 'error',
|
|
38
|
+
error: { code, message, ...(opts?.details !== undefined && { details: opts.details }) },
|
|
39
|
+
next_action: opts?.next_action ?? message,
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** Treat empty/whitespace-only strings as absent so a blank value can't satisfy P5. */
|
|
44
|
+
function nonBlank(value) {
|
|
45
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Finalize what a handler RETURNED into a canonical Envelope: the kernel adds
|
|
49
|
+
* `command` (the verb name) + `timestamp` (from the clock) and maps status →
|
|
50
|
+
* the right constructor, guaranteeing a non-blank `next_action` on every non-`ok`
|
|
51
|
+
* status (P5) even if the author returned a raw object without one — a blank or
|
|
52
|
+
* whitespace-only `next_action` is treated as missing. A JS extension that returns
|
|
53
|
+
* an unknown `status` (outside the typed union) is mapped to an `E141` error
|
|
54
|
+
* Envelope rather than falling through to `undefined`.
|
|
55
|
+
*/
|
|
56
|
+
export function finalizeVerbResult(result, name, clock) {
|
|
57
|
+
switch (result.status) {
|
|
58
|
+
case 'ok':
|
|
59
|
+
return formatOk(name, result.data, clock, {
|
|
60
|
+
...(result.evidence && { evidence: result.evidence }),
|
|
61
|
+
...(nonBlank(result.next_action) && { next_action: result.next_action }),
|
|
62
|
+
});
|
|
63
|
+
case 'degraded':
|
|
64
|
+
return formatDegraded(name, result.data, nonBlank(result.next_action) ?? 'Review the degraded result above.', clock, result.evidence ? { evidence: result.evidence } : undefined);
|
|
65
|
+
case 'unconfigured':
|
|
66
|
+
return formatUnconfigured(name, nonBlank(result.next_action) ?? 'No behaviour is mapped for this verb yet.', clock, result.data !== undefined ? { data: result.data } : undefined);
|
|
67
|
+
case 'error': {
|
|
68
|
+
const message = nonBlank(result.error?.message) ?? 'The verb reported an error.';
|
|
69
|
+
return formatError(name, result.error?.code ?? ErrorCodes.UNKNOWN, message, clock, {
|
|
70
|
+
...(result.error?.details !== undefined && { details: result.error.details }),
|
|
71
|
+
next_action: nonBlank(result.next_action) ?? message,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
default:
|
|
75
|
+
return formatError(name, ErrorCodes.EXTENSION_RUNTIME_ERROR, `Verb '${name}' returned an invalid status '${String(result.status)}'.`, clock, {
|
|
76
|
+
next_action: `This is a bug in the extension; a verb must return one of ok/degraded/unconfigured/error. Fix or remove the extension providing '${name}'.`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Invoke a verb's handler with isolation: a returned `VerbResult` is finalized;
|
|
82
|
+
* a thrown error becomes an `E141` error Envelope surfacing the message (never a
|
|
83
|
+
* raw stack trace — honesty, AC-style). Awaits async handlers.
|
|
84
|
+
*/
|
|
85
|
+
export async function runVerb(verb, ctx, clock) {
|
|
86
|
+
try {
|
|
87
|
+
const result = await verb.run(ctx);
|
|
88
|
+
return finalizeVerbResult(result, verb.name, clock);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
92
|
+
return formatError(verb.name, ErrorCodes.EXTENSION_RUNTIME_ERROR, `Verb '${verb.name}' threw at runtime: ${message}`, clock, {
|
|
93
|
+
next_action: `This is a bug in the extension; fix or remove the extension providing '${verb.name}'.`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=verb-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-context.js","sourceRoot":"","sources":["../../../src/services/extensions/verb-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { FsPort } from '../../adapters/fs/fs-port.js';
|
|
2
|
+
import type { VerbRegistry } from '../extensions/registry.js';
|
|
3
|
+
/** Machine-readable per-verb summary surfaced by `help` (AC-1/AC-6). */
|
|
4
|
+
export interface VerbSummary {
|
|
5
|
+
name: string;
|
|
6
|
+
summary: string;
|
|
7
|
+
status: 'loaded';
|
|
8
|
+
/** True when the verb's extension folder carries an `instructions.md` briefing (plan 014 AC-4). */
|
|
9
|
+
has_instructions: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** The full help payload — human-rendered as text, JSON-rendered as an envelope `data`. */
|
|
12
|
+
export interface HelpContent {
|
|
13
|
+
purpose: string;
|
|
14
|
+
/** The agent's first hop: where to self-brief (plan 014 AC-4). */
|
|
15
|
+
agents_start_here: string;
|
|
16
|
+
output_modes: string[];
|
|
17
|
+
exit_codes: Record<string, string>;
|
|
18
|
+
safe_first_actions: string[];
|
|
19
|
+
verbs: VerbSummary[];
|
|
20
|
+
extensions: {
|
|
21
|
+
installed: number;
|
|
22
|
+
failed: number;
|
|
23
|
+
conflicts: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build the help payload from the assembled verb registry. The `FsPort` is used
|
|
28
|
+
* ONLY for per-verb `instructions.md` existence probes at help-build time (plan
|
|
29
|
+
* 014 D4) — no content is read here.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildHelp(registry: VerbRegistry, fs: FsPort): HelpContent;
|
|
32
|
+
/** The honest "no extensions installed yet" next_action, or undefined when verbs exist. */
|
|
33
|
+
export declare function helpEmptyHint(content: HelpContent): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Render the help payload as human-readable text (pure — returns a string).
|
|
36
|
+
* Core commands and contributed verbs print under SEPARATE headings (`Commands:`
|
|
37
|
+
* vs `Extensions:`) so the dynamic, extension-owned surface is visually distinct
|
|
38
|
+
* from the fixed core. `useColor` accents the headings (cyan core / green
|
|
39
|
+
* extensions) + dims descriptions; it defaults off so non-interactive callers
|
|
40
|
+
* (and tests) get plain text. The entrypoint resolves it from human + TTY state.
|
|
41
|
+
*/
|
|
42
|
+
export declare function renderHelpText(content: HelpContent, useColor?: boolean): string;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { helpPalette } from '../../output/style.js';
|
|
2
|
+
import { instructionsPathFor } from '../instructions/instructions-service.js';
|
|
3
|
+
const PURPOSE = "The agent-friendly front door to this repo's engineering harness. " +
|
|
4
|
+
'Verbs are owned by extensions: each is a little package at `./.harness/extensions/<name>/` ' +
|
|
5
|
+
'(entry `extension.ts`, briefing `instructions.md`) and becomes a `harness <verb>` command. ' +
|
|
6
|
+
'`help`, `doctor`, `new`, `docs`, `skills`, `record`, and `instructions` are always available.';
|
|
7
|
+
const AGENTS_START_HERE = 'npx harness instructions — the agent briefing (then `harness instructions <verb>` per verb)';
|
|
8
|
+
const OUTPUT_MODES = [
|
|
9
|
+
'--json forces JSON output',
|
|
10
|
+
'--no-json forces human output',
|
|
11
|
+
'HARNESS_JSON=1 forces JSON (useful in CI)',
|
|
12
|
+
'otherwise: piped output → JSON, an interactive TTY → human',
|
|
13
|
+
];
|
|
14
|
+
const EXIT_CODES = {
|
|
15
|
+
'0': 'ok or degraded (the command reported successfully)',
|
|
16
|
+
'1': 'error (something failed; see error.code + next_action)',
|
|
17
|
+
'2': 'unconfigured (no behaviour mapped to this verb yet)',
|
|
18
|
+
};
|
|
19
|
+
const EMPTY_HINT = 'No extensions installed yet. Run `harness new <name>` to scaffold one into ' +
|
|
20
|
+
'`./.harness/extensions/<name>/` (entry `extension.ts` + briefing `instructions.md`). See the authoring guide.';
|
|
21
|
+
/**
|
|
22
|
+
* Build the help payload from the assembled verb registry. The `FsPort` is used
|
|
23
|
+
* ONLY for per-verb `instructions.md` existence probes at help-build time (plan
|
|
24
|
+
* 014 D4) — no content is read here.
|
|
25
|
+
*/
|
|
26
|
+
export function buildHelp(registry, fs) {
|
|
27
|
+
const installed = registry.records.filter((r) => r.status === 'loaded').length;
|
|
28
|
+
const failed = registry.records.filter((r) => r.status === 'failed').length;
|
|
29
|
+
const conflicts = registry.records.filter((r) => r.status === 'conflict').length;
|
|
30
|
+
const safeFirstActions = [
|
|
31
|
+
'harness instructions — the agent briefing (AGENTS START HERE)',
|
|
32
|
+
'harness doctor — see which extensions loaded (and any that failed)',
|
|
33
|
+
'harness docs — list the bundled docs (then `harness docs <id>` to read one)',
|
|
34
|
+
'harness new <name> — scaffold a new extension (add --wrap "<cmd>" to wrap a real command)',
|
|
35
|
+
"harness skills install --target <cli> — install this harness's skills into your CLI",
|
|
36
|
+
'harness help --json — the machine-readable verb list',
|
|
37
|
+
];
|
|
38
|
+
if (registry.verbs.length > 0) {
|
|
39
|
+
safeFirstActions.push(`harness ${registry.verbs[0]?.name} --help — usage for a contributed verb`);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
purpose: PURPOSE,
|
|
43
|
+
agents_start_here: AGENTS_START_HERE,
|
|
44
|
+
output_modes: OUTPUT_MODES,
|
|
45
|
+
exit_codes: EXIT_CODES,
|
|
46
|
+
safe_first_actions: safeFirstActions,
|
|
47
|
+
verbs: registry.verbs.map((verb) => {
|
|
48
|
+
const briefingPath = instructionsPathFor(verb.name, registry);
|
|
49
|
+
return {
|
|
50
|
+
name: verb.name,
|
|
51
|
+
summary: verb.summary,
|
|
52
|
+
status: 'loaded',
|
|
53
|
+
has_instructions: briefingPath !== null && fs.exists(briefingPath),
|
|
54
|
+
};
|
|
55
|
+
}),
|
|
56
|
+
extensions: { installed, failed, conflicts },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** The honest "no extensions installed yet" next_action, or undefined when verbs exist. */
|
|
60
|
+
export function helpEmptyHint(content) {
|
|
61
|
+
return content.verbs.length === 0 ? EMPTY_HINT : undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Render the help payload as human-readable text (pure — returns a string).
|
|
65
|
+
* Core commands and contributed verbs print under SEPARATE headings (`Commands:`
|
|
66
|
+
* vs `Extensions:`) so the dynamic, extension-owned surface is visually distinct
|
|
67
|
+
* from the fixed core. `useColor` accents the headings (cyan core / green
|
|
68
|
+
* extensions) + dims descriptions; it defaults off so non-interactive callers
|
|
69
|
+
* (and tests) get plain text. The entrypoint resolves it from human + TTY state.
|
|
70
|
+
*/
|
|
71
|
+
export function renderHelpText(content, useColor = false) {
|
|
72
|
+
const c = helpPalette(useColor);
|
|
73
|
+
const lines = [];
|
|
74
|
+
lines.push('▶ AGENTS START HERE: npx harness instructions (the agent briefing)', '');
|
|
75
|
+
lines.push('harness — engineering harness front door', '', content.purpose, '');
|
|
76
|
+
lines.push(c.heading('Commands:'));
|
|
77
|
+
lines.push(` help ${c.dim('explain the harness (this output)')}`);
|
|
78
|
+
lines.push(` doctor ${c.dim('report what is configured + which extensions loaded')}`);
|
|
79
|
+
lines.push(` instructions [verb] ${c.dim("the agent briefing (core, or one verb's instructions.md)")}`);
|
|
80
|
+
lines.push(` new <name> ${c.dim('scaffold a new extension into ./.harness/extensions/<name>/')}`);
|
|
81
|
+
lines.push(` docs [id] ${c.dim('list the bundled docs, or print one by id')}`);
|
|
82
|
+
lines.push('', c.extHeading('Extensions:'));
|
|
83
|
+
if (content.verbs.length === 0) {
|
|
84
|
+
lines.push(' (no extensions installed yet)');
|
|
85
|
+
}
|
|
86
|
+
for (const verb of content.verbs) {
|
|
87
|
+
const briefing = verb.has_instructions ? ' 📖' : '';
|
|
88
|
+
lines.push(` ${verb.name.padEnd(18)}${c.dim(verb.summary)}${briefing}`);
|
|
89
|
+
}
|
|
90
|
+
const { failed, conflicts } = content.extensions;
|
|
91
|
+
if (failed > 0 || conflicts > 0) {
|
|
92
|
+
lines.push('', `⚠ ${failed} failed, ${conflicts} conflict(s) — run \`harness doctor\`.`);
|
|
93
|
+
}
|
|
94
|
+
lines.push('', c.heading('Output modes:'));
|
|
95
|
+
for (const mode of content.output_modes) {
|
|
96
|
+
lines.push(` - ${mode}`);
|
|
97
|
+
}
|
|
98
|
+
lines.push('', c.heading('Exit codes:'));
|
|
99
|
+
for (const [code, meaning] of Object.entries(content.exit_codes)) {
|
|
100
|
+
lines.push(` ${code} ${meaning}`);
|
|
101
|
+
}
|
|
102
|
+
lines.push('', c.heading('Safe first actions:'));
|
|
103
|
+
for (const action of content.safe_first_actions) {
|
|
104
|
+
lines.push(` - ${action}`);
|
|
105
|
+
}
|
|
106
|
+
return `${lines.join('\n')}\n`;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=help-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help-service.js","sourceRoot":"","sources":["../../../src/services/help/help-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The governance-doc skeleton `harness init` stamps. An inline TypeScript string
|
|
3
|
+
* constant (mirroring `services/record/core-types/retro.ts` → `RETRO_TEMPLATE`
|
|
4
|
+
* and `services/scaffold/templates.ts`): it compiles into `dist/` and ships via
|
|
5
|
+
* the existing `files: ["harness/cli/bin","harness/cli/dist","LICENSE"]` glob —
|
|
6
|
+
* no `package.json`/build/`gen:docs` change, no runtime file read.
|
|
7
|
+
*
|
|
8
|
+
* Scaffold-and-seed ONLY. The skeleton's section set + order mirror the canonical
|
|
9
|
+
* governance doc (`skills/eng-harness-loop/eng-harness-flow/references/governance-doc.md`
|
|
10
|
+
* and this repo's own `.harness/engineering-harness.md`) so its runtime readers —
|
|
11
|
+
* boot (maturity), `eng-harness-0-adopt` (the `## Injection map`), and the
|
|
12
|
+
* router's S3 rung — recognise it as a real governance doc. Every repo-specific
|
|
13
|
+
* field is a `TODO`/empty placeholder and maturity is seeded `L0`: a
|
|
14
|
+
* populated-but-false doc would make boot misreport and violates "the harness
|
|
15
|
+
* never fakes success". Emptiness here is correctness — the skills and the
|
|
16
|
+
* Improve beat fill the body.
|
|
17
|
+
*
|
|
18
|
+
* This module is intentionally I/O-free (zero imports): the act owns the fs
|
|
19
|
+
* side-effects via the injected `fs` port, never the builder (Constitution P2).
|
|
20
|
+
*/
|
|
21
|
+
export declare const GOVERNANCE_SKELETON = "# Engineering harness\n\n> **AGENTS START HERE \u2192 `npx --no-install harness instructions`** \u2014 the CLI's\n> baked agent briefing (envelope contract, role split, discovery loop). Then\n> `npx --no-install harness instructions <verb>` per verb.\n\n## Boot command\n<!-- TODO (eng-harness-0-adopt / `harness new boot --wrap \"<cmd>\"`):\n the exact command that boots the system to a healthy, observable state (<60s target). -->\n\n## Health check\n<!-- TODO: the command/endpoint that proves the system is up (read by boot Stage 1). -->\n\n## Interact method\n<!-- TODO: how an agent sends input to the running system (boot Stage 2). -->\n\n## Observe method\n<!-- TODO: how an agent captures evidence \u2014 logs, screenshots, traces (boot Stage 3). -->\n\n## Deterministic signal inventory\n<!-- TODO: sensors that prove behaviour without inference \u2014 runtime inspectability,\n smoke paths, architecture/static checks, security/dependency/schema checks. -->\n\n## Evidence paths\n<!-- TODO: where artifacts land (log/trace/screenshot/output locations). -->\n\n## Injection map\n<!-- Where the repo's extant dev/SDD flow calls /eng-harness-flow. One row per seam.\n Filled by eng-harness-0-adopt Step 3 (with the user's go-ahead). -->\n\n| Seam event | Fires from | What fires it |\n|---|---|---|\n| <!-- e.g. session-start --> | | |\n\n## Back-pressure gaps\n<!-- TODO: behaviours still relying on inference/human eyeballing \u2014 improvement\n candidates, named honestly. Never scores. -->\n\n## Current maturity snapshot\n**L0 \u2014 seeded at inception by `harness init`; nothing proven yet.**\n<!-- The single, current L0\u2013L4 level the harness is ACTUALLY at. Updated ONLY at\n the Improve beat (never by boot, which is read-only). See maturity-assessment.md. -->\n";
|
|
22
|
+
/**
|
|
23
|
+
* The governance-doc skeleton `harness init` writes. Pure — no fs/clock/process
|
|
24
|
+
* access — so it is unit-testable by snapshot and stays a deterministic "copy in
|
|
25
|
+
* a template". Repo-specific content is the skills' / Improve beat's job.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildGovernanceSkeleton(): string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The governance-doc skeleton `harness init` stamps. An inline TypeScript string
|
|
3
|
+
* constant (mirroring `services/record/core-types/retro.ts` → `RETRO_TEMPLATE`
|
|
4
|
+
* and `services/scaffold/templates.ts`): it compiles into `dist/` and ships via
|
|
5
|
+
* the existing `files: ["harness/cli/bin","harness/cli/dist","LICENSE"]` glob —
|
|
6
|
+
* no `package.json`/build/`gen:docs` change, no runtime file read.
|
|
7
|
+
*
|
|
8
|
+
* Scaffold-and-seed ONLY. The skeleton's section set + order mirror the canonical
|
|
9
|
+
* governance doc (`skills/eng-harness-loop/eng-harness-flow/references/governance-doc.md`
|
|
10
|
+
* and this repo's own `.harness/engineering-harness.md`) so its runtime readers —
|
|
11
|
+
* boot (maturity), `eng-harness-0-adopt` (the `## Injection map`), and the
|
|
12
|
+
* router's S3 rung — recognise it as a real governance doc. Every repo-specific
|
|
13
|
+
* field is a `TODO`/empty placeholder and maturity is seeded `L0`: a
|
|
14
|
+
* populated-but-false doc would make boot misreport and violates "the harness
|
|
15
|
+
* never fakes success". Emptiness here is correctness — the skills and the
|
|
16
|
+
* Improve beat fill the body.
|
|
17
|
+
*
|
|
18
|
+
* This module is intentionally I/O-free (zero imports): the act owns the fs
|
|
19
|
+
* side-effects via the injected `fs` port, never the builder (Constitution P2).
|
|
20
|
+
*/
|
|
21
|
+
export const GOVERNANCE_SKELETON = `# Engineering harness
|
|
22
|
+
|
|
23
|
+
> **AGENTS START HERE → \`npx --no-install harness instructions\`** — the CLI's
|
|
24
|
+
> baked agent briefing (envelope contract, role split, discovery loop). Then
|
|
25
|
+
> \`npx --no-install harness instructions <verb>\` per verb.
|
|
26
|
+
|
|
27
|
+
## Boot command
|
|
28
|
+
<!-- TODO (eng-harness-0-adopt / \`harness new boot --wrap "<cmd>"\`):
|
|
29
|
+
the exact command that boots the system to a healthy, observable state (<60s target). -->
|
|
30
|
+
|
|
31
|
+
## Health check
|
|
32
|
+
<!-- TODO: the command/endpoint that proves the system is up (read by boot Stage 1). -->
|
|
33
|
+
|
|
34
|
+
## Interact method
|
|
35
|
+
<!-- TODO: how an agent sends input to the running system (boot Stage 2). -->
|
|
36
|
+
|
|
37
|
+
## Observe method
|
|
38
|
+
<!-- TODO: how an agent captures evidence — logs, screenshots, traces (boot Stage 3). -->
|
|
39
|
+
|
|
40
|
+
## Deterministic signal inventory
|
|
41
|
+
<!-- TODO: sensors that prove behaviour without inference — runtime inspectability,
|
|
42
|
+
smoke paths, architecture/static checks, security/dependency/schema checks. -->
|
|
43
|
+
|
|
44
|
+
## Evidence paths
|
|
45
|
+
<!-- TODO: where artifacts land (log/trace/screenshot/output locations). -->
|
|
46
|
+
|
|
47
|
+
## Injection map
|
|
48
|
+
<!-- Where the repo's extant dev/SDD flow calls /eng-harness-flow. One row per seam.
|
|
49
|
+
Filled by eng-harness-0-adopt Step 3 (with the user's go-ahead). -->
|
|
50
|
+
|
|
51
|
+
| Seam event | Fires from | What fires it |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| <!-- e.g. session-start --> | | |
|
|
54
|
+
|
|
55
|
+
## Back-pressure gaps
|
|
56
|
+
<!-- TODO: behaviours still relying on inference/human eyeballing — improvement
|
|
57
|
+
candidates, named honestly. Never scores. -->
|
|
58
|
+
|
|
59
|
+
## Current maturity snapshot
|
|
60
|
+
**L0 — seeded at inception by \`harness init\`; nothing proven yet.**
|
|
61
|
+
<!-- The single, current L0–L4 level the harness is ACTUALLY at. Updated ONLY at
|
|
62
|
+
the Improve beat (never by boot, which is read-only). See maturity-assessment.md. -->
|
|
63
|
+
`;
|
|
64
|
+
/**
|
|
65
|
+
* The governance-doc skeleton `harness init` writes. Pure — no fs/clock/process
|
|
66
|
+
* access — so it is unit-testable by snapshot and stays a deterministic "copy in
|
|
67
|
+
* a template". Repo-specific content is the skills' / Improve beat's job.
|
|
68
|
+
*/
|
|
69
|
+
export function buildGovernanceSkeleton() {
|
|
70
|
+
return GOVERNANCE_SKELETON;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=governance-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"governance-template.js","sourceRoot":"","sources":["../../../src/services/init/governance-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0ClC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,mBAAmB,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { FsPort } from '../../adapters/fs/fs-port.js';
|
|
2
|
+
import type { ProcessPort } from '../../adapters/process/process-port.js';
|
|
3
|
+
/** The governance doc's fixed filename inside `.harness/` (singleton — no date, no slug). */
|
|
4
|
+
export declare const GOVERNANCE_DOC = "engineering-harness.md";
|
|
5
|
+
/**
|
|
6
|
+
* Ports the init service needs: `fs` for the side-effects, `proc` to resolve the
|
|
7
|
+
* repo root. No Clock — the skeleton is static and the path is fixed.
|
|
8
|
+
*/
|
|
9
|
+
export interface InitDeps {
|
|
10
|
+
fs: FsPort;
|
|
11
|
+
proc: ProcessPort;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Outcome of seeding the governance doc. `created` distinguishes a fresh stamp
|
|
15
|
+
* (`true`) from an idempotent no-op on an existing doc (`false`); the act maps
|
|
16
|
+
* this onto the Envelope + exit code (ok → 0, error → 1).
|
|
17
|
+
*/
|
|
18
|
+
export type InitOutcome = {
|
|
19
|
+
ok: true;
|
|
20
|
+
path: string;
|
|
21
|
+
created: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
ok: false;
|
|
24
|
+
code: string;
|
|
25
|
+
message: string;
|
|
26
|
+
next_action: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Seed `.harness/engineering-harness.md` with the governance-doc skeleton — the
|
|
30
|
+
* Inception write-condition (governance-doc.md G5). This is the INCEPTION writer,
|
|
31
|
+
* so it bootstraps `.harness/` itself (unlike `record`, which returns
|
|
32
|
+
* `unconfigured` when `.harness/` is absent). Idempotent + NEVER-CLOBBER: the
|
|
33
|
+
* exists-check runs FIRST, and an existing doc is left byte-identical and
|
|
34
|
+
* reported `created:false` (it holds the real maturity + injection map by then).
|
|
35
|
+
* Side-effects go through the injected `fs` port; the pure skeleton comes from
|
|
36
|
+
* the builder (Constitution P2 — no `node:fs` in services).
|
|
37
|
+
*/
|
|
38
|
+
export declare function initGovernance(deps: InitDeps): InitOutcome;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ErrorCodes } from '../../output/error-codes.js';
|
|
2
|
+
import { posixJoin, toPosix } from '../shared/posix-path.js';
|
|
3
|
+
import { HARNESS_DIR } from '../shared/temp.js';
|
|
4
|
+
import { buildGovernanceSkeleton } from './governance-template.js';
|
|
5
|
+
/** The governance doc's fixed filename inside `.harness/` (singleton — no date, no slug). */
|
|
6
|
+
export const GOVERNANCE_DOC = 'engineering-harness.md';
|
|
7
|
+
/**
|
|
8
|
+
* Seed `.harness/engineering-harness.md` with the governance-doc skeleton — the
|
|
9
|
+
* Inception write-condition (governance-doc.md G5). This is the INCEPTION writer,
|
|
10
|
+
* so it bootstraps `.harness/` itself (unlike `record`, which returns
|
|
11
|
+
* `unconfigured` when `.harness/` is absent). Idempotent + NEVER-CLOBBER: the
|
|
12
|
+
* exists-check runs FIRST, and an existing doc is left byte-identical and
|
|
13
|
+
* reported `created:false` (it holds the real maturity + injection map by then).
|
|
14
|
+
* Side-effects go through the injected `fs` port; the pure skeleton comes from
|
|
15
|
+
* the builder (Constitution P2 — no `node:fs` in services).
|
|
16
|
+
*/
|
|
17
|
+
export function initGovernance(deps) {
|
|
18
|
+
const { fs, proc } = deps;
|
|
19
|
+
// Logical paths are POSIX on every OS (plan 017) — convert once at the boundary.
|
|
20
|
+
const cwd = toPosix(proc.cwd());
|
|
21
|
+
const harnessDir = posixJoin(cwd, HARNESS_DIR);
|
|
22
|
+
const fileAbs = posixJoin(harnessDir, GOVERNANCE_DOC);
|
|
23
|
+
const relPath = posixJoin(HARNESS_DIR, GOVERNANCE_DOC);
|
|
24
|
+
// Never clobber: exists-check FIRST — skip mkdirp+write entirely on a hit.
|
|
25
|
+
if (fs.exists(fileAbs)) {
|
|
26
|
+
return { ok: true, path: relPath, created: false };
|
|
27
|
+
}
|
|
28
|
+
// Absent → bootstrap `.harness/` then stamp the skeleton, both under ONE guard
|
|
29
|
+
// so a permissions failure surfaces as E190 (not a generic E100).
|
|
30
|
+
try {
|
|
31
|
+
fs.mkdirp(harnessDir);
|
|
32
|
+
fs.writeText(fileAbs, buildGovernanceSkeleton());
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return {
|
|
36
|
+
ok: false,
|
|
37
|
+
code: ErrorCodes.INIT_WRITE_FAILED,
|
|
38
|
+
message: `Could not write ${relPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
39
|
+
next_action: `Could not write \`${relPath}\` (permissions?). Check the repo root is writable.`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return { ok: true, path: relPath, created: true };
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=init-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-service.js","sourceRoot":"","sources":["../../../src/services/init/init-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,6FAA6F;AAC7F,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAoBvD;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,IAAc;IAC3C,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAC1B,iFAAiF;IACjF,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAChC,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,SAAS,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,SAAS,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAEvD,2EAA2E;IAC3E,IAAI,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACrD,CAAC;IAED,+EAA+E;IAC/E,kEAAkE;IAClE,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACtB,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,UAAU,CAAC,iBAAiB;YAClC,OAAO,EAAE,mBAAmB,OAAO,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;YAC1F,WAAW,EAAE,qBAAqB,OAAO,qDAAqD;SAC/F,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The baked CORE agent briefing surfaced by bare `harness instructions` (plan
|
|
3
|
+
* 014 AC-1). A TS constant on purpose: it documents the CLI's own contract, so
|
|
4
|
+
* it should version with the CLI binary (plan § Accepted assumptions) — unlike
|
|
5
|
+
* per-extension briefings, which live beside their extension as
|
|
6
|
+
* `.harness/extensions/<name>/instructions.md` and are read from disk at every
|
|
7
|
+
* invocation.
|
|
8
|
+
*
|
|
9
|
+
* Audience: the CALLING agent (not minih workers, not humans skimming docs).
|
|
10
|
+
*/
|
|
11
|
+
export declare const CORE_INSTRUCTIONS = "# Harness CLI \u2014 agent briefing\n\nAGENTS START HERE. You are an agent operating this repository through its\nengineering harness. Division of labour: **the harness brings determinism,\nyou bring the inference.** Verbs compute repeatable facts (run suites, collect\nevidence, scaffold files); these instructions \u2014 and each verb's own briefing \u2014\ntell you the role you play around those facts and the judgment expected back.\n\nWhy this surface exists: the harness is the **focal point of the repo's\ndeterministic layer** \u2014 the one discoverable place where proof lives (build,\ntest, boot, sensors, evidence) instead of diffuse scripts and tribal\nknowledge. The deal that makes it compound: **encode, don't document** \u2014 if\nyou had to infer something twice, that is a missing command, not a missing\ndoc; propose it as a one-line observation (\u00A7 Friction capture, below).\nDiscoverability is the point: `--help`, `doctor`, and these briefings exist\nso the layer can be enumerated, not remembered.\n\n## The envelope contract\n\nEvery command emits ONE envelope. Pass `--json` to get it machine-readable:\n\n { \"command\", \"status\", \"data\", \"error?\", \"next_action?\", \"timestamp\" }\n\n- `status` is one of: `ok` | `degraded` | `unconfigured` | `error`.\n- Exit codes: 0 = ok/degraded \u00B7 2 = unconfigured \u00B7 1 = error.\n- `next_action` is REQUIRED on any non-ok status \u2014 it is the prescribed fix.\n Follow it before improvising.\n- The harness never fakes success: `unconfigured` means \"nothing is mapped\n here yet\", not \"it worked\".\n\n## Self-briefing loop (one hop each)\n\n1. `harness help --json` \u2014 the verb map; each verb carries\n `has_instructions` so you know which briefings exist.\n2. `harness doctor --json` \u2014 what loaded, what failed, and why (per-extension\n provenance; convention complaints; every complaint has a next_action).\n3. `harness instructions <verb>` \u2014 read a verb's briefing BEFORE using it.\n It states what the verb computes deterministically and what judgment it\n expects back from you.\n\n## Friction capture (observe as you work)\n\nNotice friction while you work? Capture it the moment it happens \u2014 one\ncommand, no path/ID/timestamp bookkeeping, and it survives context\ncompaction (the buffer lives on disk):\n\n harness observe \"<what happened, 10+ chars>\" --kind difficulty --severity degrading\n\n- Kinds: difficulty | magic-wand | gift | insight | coordination |\n improvement-suggestion | confusion. Severities: blocking | degrading |\n annoying. `--target`, `--workaround`, `--suggested-encoding` optional.\n- Identity is optional: `--agent <slug>` \u2192 `HARNESS_AGENT` env \u2192 a shared\n `agent` bucket. Capture never fails on identity.\n- Two storage classes: `.harness/records/` is COMMITTED team memory;\n `.harness/temp/` is TRANSIENT session scratch \u2014 gitignored, never\n committed (capture and `harness record` calls self-heal the protection;\n `harness doctor` checks it).\n- Drain at session end: `harness observe --list --json` sweeps all buckets \u2192\n save what matters via `harness record retro` (use the returned\n `data.path`) \u2192 `harness observe --clear`.\n\n## Where briefings live\n\nEach extension is a little package: `.harness/extensions/<name>/` with\n`extension.ts` (the verb code) and `instructions.md` (the briefing you are\nreading the equivalent of now). Briefings are loaded from disk at every\ninvocation \u2014 edits are live on the next call, no rebuild. Multi-verb\nextensions share their folder's single briefing.\n";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The baked CORE agent briefing surfaced by bare `harness instructions` (plan
|
|
3
|
+
* 014 AC-1). A TS constant on purpose: it documents the CLI's own contract, so
|
|
4
|
+
* it should version with the CLI binary (plan § Accepted assumptions) — unlike
|
|
5
|
+
* per-extension briefings, which live beside their extension as
|
|
6
|
+
* `.harness/extensions/<name>/instructions.md` and are read from disk at every
|
|
7
|
+
* invocation.
|
|
8
|
+
*
|
|
9
|
+
* Audience: the CALLING agent (not minih workers, not humans skimming docs).
|
|
10
|
+
*/
|
|
11
|
+
export const CORE_INSTRUCTIONS = `# Harness CLI — agent briefing
|
|
12
|
+
|
|
13
|
+
AGENTS START HERE. You are an agent operating this repository through its
|
|
14
|
+
engineering harness. Division of labour: **the harness brings determinism,
|
|
15
|
+
you bring the inference.** Verbs compute repeatable facts (run suites, collect
|
|
16
|
+
evidence, scaffold files); these instructions — and each verb's own briefing —
|
|
17
|
+
tell you the role you play around those facts and the judgment expected back.
|
|
18
|
+
|
|
19
|
+
Why this surface exists: the harness is the **focal point of the repo's
|
|
20
|
+
deterministic layer** — the one discoverable place where proof lives (build,
|
|
21
|
+
test, boot, sensors, evidence) instead of diffuse scripts and tribal
|
|
22
|
+
knowledge. The deal that makes it compound: **encode, don't document** — if
|
|
23
|
+
you had to infer something twice, that is a missing command, not a missing
|
|
24
|
+
doc; propose it as a one-line observation (§ Friction capture, below).
|
|
25
|
+
Discoverability is the point: \`--help\`, \`doctor\`, and these briefings exist
|
|
26
|
+
so the layer can be enumerated, not remembered.
|
|
27
|
+
|
|
28
|
+
## The envelope contract
|
|
29
|
+
|
|
30
|
+
Every command emits ONE envelope. Pass \`--json\` to get it machine-readable:
|
|
31
|
+
|
|
32
|
+
{ "command", "status", "data", "error?", "next_action?", "timestamp" }
|
|
33
|
+
|
|
34
|
+
- \`status\` is one of: \`ok\` | \`degraded\` | \`unconfigured\` | \`error\`.
|
|
35
|
+
- Exit codes: 0 = ok/degraded · 2 = unconfigured · 1 = error.
|
|
36
|
+
- \`next_action\` is REQUIRED on any non-ok status — it is the prescribed fix.
|
|
37
|
+
Follow it before improvising.
|
|
38
|
+
- The harness never fakes success: \`unconfigured\` means "nothing is mapped
|
|
39
|
+
here yet", not "it worked".
|
|
40
|
+
|
|
41
|
+
## Self-briefing loop (one hop each)
|
|
42
|
+
|
|
43
|
+
1. \`harness help --json\` — the verb map; each verb carries
|
|
44
|
+
\`has_instructions\` so you know which briefings exist.
|
|
45
|
+
2. \`harness doctor --json\` — what loaded, what failed, and why (per-extension
|
|
46
|
+
provenance; convention complaints; every complaint has a next_action).
|
|
47
|
+
3. \`harness instructions <verb>\` — read a verb's briefing BEFORE using it.
|
|
48
|
+
It states what the verb computes deterministically and what judgment it
|
|
49
|
+
expects back from you.
|
|
50
|
+
|
|
51
|
+
## Friction capture (observe as you work)
|
|
52
|
+
|
|
53
|
+
Notice friction while you work? Capture it the moment it happens — one
|
|
54
|
+
command, no path/ID/timestamp bookkeeping, and it survives context
|
|
55
|
+
compaction (the buffer lives on disk):
|
|
56
|
+
|
|
57
|
+
harness observe "<what happened, 10+ chars>" --kind difficulty --severity degrading
|
|
58
|
+
|
|
59
|
+
- Kinds: difficulty | magic-wand | gift | insight | coordination |
|
|
60
|
+
improvement-suggestion | confusion. Severities: blocking | degrading |
|
|
61
|
+
annoying. \`--target\`, \`--workaround\`, \`--suggested-encoding\` optional.
|
|
62
|
+
- Identity is optional: \`--agent <slug>\` → \`HARNESS_AGENT\` env → a shared
|
|
63
|
+
\`agent\` bucket. Capture never fails on identity.
|
|
64
|
+
- Two storage classes: \`.harness/records/\` is COMMITTED team memory;
|
|
65
|
+
\`.harness/temp/\` is TRANSIENT session scratch — gitignored, never
|
|
66
|
+
committed (capture and \`harness record\` calls self-heal the protection;
|
|
67
|
+
\`harness doctor\` checks it).
|
|
68
|
+
- Drain at session end: \`harness observe --list --json\` sweeps all buckets →
|
|
69
|
+
save what matters via \`harness record retro\` (use the returned
|
|
70
|
+
\`data.path\`) → \`harness observe --clear\`.
|
|
71
|
+
|
|
72
|
+
## Where briefings live
|
|
73
|
+
|
|
74
|
+
Each extension is a little package: \`.harness/extensions/<name>/\` with
|
|
75
|
+
\`extension.ts\` (the verb code) and \`instructions.md\` (the briefing you are
|
|
76
|
+
reading the equivalent of now). Briefings are loaded from disk at every
|
|
77
|
+
invocation — edits are live on the next call, no rebuild. Multi-verb
|
|
78
|
+
extensions share their folder's single briefing.
|
|
79
|
+
`;
|
|
80
|
+
//# sourceMappingURL=core-instructions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-instructions.js","sourceRoot":"","sources":["../../../src/services/instructions/core-instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoEhC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { FsPort } from '../../adapters/fs/fs-port.js';
|
|
2
|
+
import type { VerbRegistry } from '../extensions/registry.js';
|
|
3
|
+
/** The bare `harness instructions` payload (plan 014 AC-1). */
|
|
4
|
+
export interface CoreInstructionsPayload {
|
|
5
|
+
/** The baked core agent briefing (a TS constant — versions with the CLI). */
|
|
6
|
+
instructions: string;
|
|
7
|
+
/** Verbs whose owning extension folder carries an `instructions.md` right now. */
|
|
8
|
+
verbs_with_instructions: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* What `harness instructions <verb>` resolves to. The act maps these onto the
|
|
12
|
+
* envelope per D3: `ok` → ok/0, `unknown-verb`/`missing` → unconfigured/2 (gap
|
|
13
|
+
* semantics, no error code), `unreadable` → error E145/1.
|
|
14
|
+
*/
|
|
15
|
+
export type VerbInstructionsOutcome = {
|
|
16
|
+
kind: 'ok';
|
|
17
|
+
verb: string;
|
|
18
|
+
path: string;
|
|
19
|
+
instructions: string;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'unknown-verb';
|
|
22
|
+
verb: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: 'missing';
|
|
25
|
+
verb: string;
|
|
26
|
+
path: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'unreadable';
|
|
29
|
+
verb: string;
|
|
30
|
+
path: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Verb → briefing path (plan 014 D5): find the record whose accepted `verbs[]`
|
|
34
|
+
* contains the verb, take `dirname(entryPath)`, join `instructions.md`. The
|
|
35
|
+
* contract stays untouched — no `instructions` field anywhere — and multi-verb
|
|
36
|
+
* extensions share their folder's single file for free. `null` = unknown verb.
|
|
37
|
+
*/
|
|
38
|
+
export declare function instructionsPathFor(verbName: string, registry: VerbRegistry): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Build the bare-invocation payload. `verbs_with_instructions` is an `FsPort`
|
|
41
|
+
* existence probe per registered verb at build time (D4) — cheap, and honest
|
|
42
|
+
* about what is queryable right now.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildCoreInstructions(registry: VerbRegistry, fs: FsPort): CoreInstructionsPayload;
|
|
45
|
+
/**
|
|
46
|
+
* Load a verb's briefing from disk NOW (D4 — lazily at invocation, never cached
|
|
47
|
+
* across calls, so an edit is visible on the next invocation with no rebuild).
|
|
48
|
+
* FsPort semantics make the missing/unreadable split: `exists()` false → the
|
|
49
|
+
* file was never authored (a gap); `exists()` true but `readText()` null → it
|
|
50
|
+
* is there but unreadable (a fault).
|
|
51
|
+
*/
|
|
52
|
+
export declare function loadVerbInstructions(verbName: string, registry: VerbRegistry, fs: FsPort): VerbInstructionsOutcome;
|