@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,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure string builders for the scaffolded extension starters (`harness new`,
|
|
3
|
+
* plan 006). Output is byte-exact to workshop 001 §4 so it can be asserted in
|
|
4
|
+
* tests and survives `npx`/bundling (no loose template files to resolve at
|
|
5
|
+
* runtime). Authors never see this module — only the file it writes.
|
|
6
|
+
*/
|
|
7
|
+
/** kebab/lower verb name → a valid camelCase JS identifier for the local const. */
|
|
8
|
+
export function toIdentifier(name) {
|
|
9
|
+
return name.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
10
|
+
}
|
|
11
|
+
function splitCommand(command) {
|
|
12
|
+
const parts = command.trim().split(/\s+/);
|
|
13
|
+
return { argv0: parts[0] ?? '', rest: parts.slice(1) };
|
|
14
|
+
}
|
|
15
|
+
function argsLiteral(rest) {
|
|
16
|
+
return `[${rest.map((a) => `'${a}'`).join(', ')}]`;
|
|
17
|
+
}
|
|
18
|
+
/** Minimal TypeScript starter — an honest `unconfigured` stub (workshop §4a). */
|
|
19
|
+
export function minimalTs(name) {
|
|
20
|
+
const id = toIdentifier(name);
|
|
21
|
+
return `import type { HarnessVerb } from '@ai-substrate/engineering-harness/contract';
|
|
22
|
+
|
|
23
|
+
const ${id}: HarnessVerb = {
|
|
24
|
+
name: '${name}',
|
|
25
|
+
summary: 'TODO: one-line summary of what \`harness ${name}\` does.',
|
|
26
|
+
// options: [{ flags: '--example <value>', description: 'an example flag' }],
|
|
27
|
+
run(ctx) {
|
|
28
|
+
// TODO: implement this verb. Until you do, it honestly reports "not built yet".
|
|
29
|
+
return ctx.unconfigured('Implement run() in .harness/extensions/${name}/extension.ts');
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default ${id};
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
/** Wrap-a-real-command TypeScript starter (workshop §4b). */
|
|
37
|
+
export function wrapTs(name, command) {
|
|
38
|
+
const id = toIdentifier(name);
|
|
39
|
+
const { argv0, rest } = splitCommand(command);
|
|
40
|
+
return `import type { HarnessVerb } from '@ai-substrate/engineering-harness/contract';
|
|
41
|
+
|
|
42
|
+
const ${id}: HarnessVerb = {
|
|
43
|
+
name: '${name}',
|
|
44
|
+
summary: 'Wraps \`${command}\`.',
|
|
45
|
+
async run(ctx) {
|
|
46
|
+
const started = Date.now();
|
|
47
|
+
const r = await ctx.exec('${argv0}', ${argsLiteral(rest)});
|
|
48
|
+
const durationMs = Date.now() - started;
|
|
49
|
+
const tail = r.stdout.trimEnd().split('\\n').slice(-20).join('\\n');
|
|
50
|
+
return r.ok
|
|
51
|
+
? ctx.ok({ command: '${command}', durationMs, stdout: tail })
|
|
52
|
+
: ctx.error('E1', \`${command} failed (exit \${r.code})\`, {
|
|
53
|
+
details: r.stderr,
|
|
54
|
+
next_action: 'Fix the failure above, then re-run \`harness ${name}\`.',
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default ${id};
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
/** Minimal plain-JS starter — JSDoc contract reference, no runtime import (workshop §4c). */
|
|
63
|
+
export function minimalJs(name) {
|
|
64
|
+
const id = toIdentifier(name);
|
|
65
|
+
return `/** @type {import('@ai-substrate/engineering-harness/contract').HarnessVerb} */
|
|
66
|
+
const ${id} = {
|
|
67
|
+
name: '${name}',
|
|
68
|
+
summary: 'TODO: one-line summary of what \`harness ${name}\` does.',
|
|
69
|
+
run(ctx) {
|
|
70
|
+
// TODO: implement this verb.
|
|
71
|
+
return ctx.unconfigured('Implement run() in .harness/extensions/${name}/extension.js');
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default ${id};
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
/** Wrap-a-real-command plain-JS starter (workshop §4d). */
|
|
79
|
+
export function wrapJs(name, command) {
|
|
80
|
+
const id = toIdentifier(name);
|
|
81
|
+
const { argv0, rest } = splitCommand(command);
|
|
82
|
+
return `/** @type {import('@ai-substrate/engineering-harness/contract').HarnessVerb} */
|
|
83
|
+
const ${id} = {
|
|
84
|
+
name: '${name}',
|
|
85
|
+
summary: 'Wraps \`${command}\`.',
|
|
86
|
+
async run(ctx) {
|
|
87
|
+
const started = Date.now();
|
|
88
|
+
const r = await ctx.exec('${argv0}', ${argsLiteral(rest)});
|
|
89
|
+
const durationMs = Date.now() - started;
|
|
90
|
+
const tail = r.stdout.trimEnd().split('\\n').slice(-20).join('\\n');
|
|
91
|
+
return r.ok
|
|
92
|
+
? ctx.ok({ command: '${command}', durationMs, stdout: tail })
|
|
93
|
+
: ctx.error('E1', \`${command} failed (exit \${r.code})\`, {
|
|
94
|
+
details: r.stderr,
|
|
95
|
+
next_action: 'Fix the failure above, then re-run \`harness ${name}\`.',
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default ${id};
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Record-type extension starter (`harness new <name> --record`). Exports a
|
|
105
|
+
* {@link HarnessRecordType} the loader discovers by its `kind:'record'`. The
|
|
106
|
+
* record's "schema" lives in the `template` body (frontmatter + comments); the
|
|
107
|
+
* CLI is schema-agnostic, so editing the template is all it takes to design a type.
|
|
108
|
+
*/
|
|
109
|
+
export function recordTs(name) {
|
|
110
|
+
const id = toIdentifier(name);
|
|
111
|
+
return `import type { HarnessRecordType } from '@ai-substrate/engineering-harness/contract';
|
|
112
|
+
|
|
113
|
+
const ${id}: HarnessRecordType = {
|
|
114
|
+
kind: 'record',
|
|
115
|
+
type: '${name}',
|
|
116
|
+
description: 'TODO: one-line description of the ${name} record type.',
|
|
117
|
+
// The record's schema lives HERE — frontmatter keys + commented guidance.
|
|
118
|
+
template: \`---
|
|
119
|
+
record_type: ${name}
|
|
120
|
+
captured_at: "<ISO8601Z>"
|
|
121
|
+
# TODO: add the fields this record captures.
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
# ${name} — <subject>
|
|
125
|
+
|
|
126
|
+
<!-- TODO: optional narrative; the frontmatter above is the durable signal. -->
|
|
127
|
+
\`,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export default ${id};
|
|
131
|
+
`;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Starter `instructions.md` for a scaffolded extension package (plan 014 AC-8).
|
|
135
|
+
* A guided TODO addressed to the CALLING agent: what the verb computes
|
|
136
|
+
* deterministically, and what judgment it expects back. Served verbatim by
|
|
137
|
+
* `harness instructions <name>` once authored.
|
|
138
|
+
*/
|
|
139
|
+
export function starterInstructions(name) {
|
|
140
|
+
return `# \`harness ${name}\` — agent briefing
|
|
141
|
+
|
|
142
|
+
<!-- TODO: author this briefing for the CALLING agent (not a human README).
|
|
143
|
+
\`harness instructions ${name}\` serves this file verbatim, freshly read
|
|
144
|
+
on every invocation — edit it any time, no rebuild. -->
|
|
145
|
+
|
|
146
|
+
## What this verb computes (the deterministic part)
|
|
147
|
+
|
|
148
|
+
TODO: state exactly what \`harness ${name}\` runs or collects, and what its
|
|
149
|
+
envelope \`data\` contains.
|
|
150
|
+
|
|
151
|
+
## Your role (the inference part)
|
|
152
|
+
|
|
153
|
+
TODO: state the judgment the calling agent is expected to apply to the output —
|
|
154
|
+
what to review, what to compare against, what verdict to reach.
|
|
155
|
+
|
|
156
|
+
## Watch out for
|
|
157
|
+
|
|
158
|
+
TODO: list failure modes or misleading outputs an agent should not take at
|
|
159
|
+
face value.
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
162
|
+
/** Pick the starter + extension from the `harness new` flags. */
|
|
163
|
+
export function renderStarter(opts) {
|
|
164
|
+
const { name, js, wrap, record } = opts;
|
|
165
|
+
if (record) {
|
|
166
|
+
// Record types are TS stubs (they import the record contract); --wrap/--js don't apply.
|
|
167
|
+
return { contents: recordTs(name), variant: 'record-ts', ext: 'ts' };
|
|
168
|
+
}
|
|
169
|
+
if (wrap !== undefined) {
|
|
170
|
+
return js
|
|
171
|
+
? { contents: wrapJs(name, wrap), variant: 'wrap-js', ext: 'js' }
|
|
172
|
+
: { contents: wrapTs(name, wrap), variant: 'wrap-ts', ext: 'ts' };
|
|
173
|
+
}
|
|
174
|
+
return js
|
|
175
|
+
? { contents: minimalJs(name), variant: 'minimal-js', ext: 'js' }
|
|
176
|
+
: { contents: minimalTs(name), variant: 'minimal-ts', ext: 'ts' };
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/services/scaffold/templates.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logical paths are POSIX on every OS (plan 017).
|
|
3
|
+
*
|
|
4
|
+
* Every path the CLI *surfaces or compares* — envelope `data.path`, record
|
|
5
|
+
* messages, extension `entryPath`/`folder`, doctor hints, dedupe keys — is a
|
|
6
|
+
* LOGICAL path: forward slashes only, `.harness/...` shapes literal, identical
|
|
7
|
+
* on Windows and POSIX hosts. Physical I/O (NodeFs) keeps native separators;
|
|
8
|
+
* conversion happens ONCE at the boundary (`toPosix(proc.cwd())`,
|
|
9
|
+
* `toPosix(entryPath)`), after which everything stays in POSIX space.
|
|
10
|
+
*
|
|
11
|
+
* Allowed surface (all lexical): `toPosix`, `posixNormalize`, `posixJoin`,
|
|
12
|
+
* `posixDirname`, `posixRelative`, `isWithin`, `dedupeKey`.
|
|
13
|
+
*
|
|
14
|
+
* FORBIDDEN: `posix.resolve` (and native `resolve`) on logical paths. A
|
|
15
|
+
* drive-letter path (`C:/repo`) does not start with `/`, so `posix.resolve`
|
|
16
|
+
* treats it as RELATIVE and prepends the host cwd — silently corrupting the
|
|
17
|
+
* path. Everything here is built on `posix.normalize`/`posix.join` instead.
|
|
18
|
+
*
|
|
19
|
+
* UNC guard: Node's `posix.normalize` collapses a leading `//`
|
|
20
|
+
* (`//server/share` → `/server/share`), so every normalize-based op reattaches
|
|
21
|
+
* the second slash when the input was UNC-rooted.
|
|
22
|
+
*/
|
|
23
|
+
/** Module-level platform constant — the DEFAULT for case-folding, never probed in tests (P3: pass the parameter explicitly instead of patching `process.platform`). */
|
|
24
|
+
export declare const IS_WIN32: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Convert a possibly-Windows-shaped path to its logical POSIX form:
|
|
27
|
+
* backslashes → `/`, drive letter upper-cased (`c:` → `C:`), UNC
|
|
28
|
+
* `\\server\share` → `//server/share`. POSIX inputs pass through unchanged.
|
|
29
|
+
*/
|
|
30
|
+
export declare function toPosix(path: string): string;
|
|
31
|
+
/** Lexical normalize in POSIX space (UNC-guarded). Accepts Windows-shaped input. */
|
|
32
|
+
export declare function posixNormalize(path: string): string;
|
|
33
|
+
/** Join in POSIX space (UNC-guarded). Each segment is converted via `toPosix` on the way in. */
|
|
34
|
+
export declare function posixJoin(...parts: string[]): string;
|
|
35
|
+
/** Dirname in POSIX space (UNC-guarded). Accepts Windows-shaped input. */
|
|
36
|
+
export declare function posixDirname(path: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Relative in POSIX space. Drive-letter inputs are safe: when both sides are
|
|
39
|
+
* resolved against the same host cwd the shared prefix cancels, so the result
|
|
40
|
+
* is the lexical relative between the two logical paths.
|
|
41
|
+
*/
|
|
42
|
+
export declare function posixRelative(from: string, to: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* True when `candidate` is `dir` or a descendant of it — the `../`-escape
|
|
45
|
+
* guard, computed entirely in POSIX space (literal `'../'`, never `sep`).
|
|
46
|
+
*/
|
|
47
|
+
export declare function isWithin(dir: string, candidate: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Stable identity key for dedupe maps: POSIX-normalized, optionally
|
|
50
|
+
* case-folded. Case sensitivity is an EXPLICIT parameter so tests exercise
|
|
51
|
+
* both branches deterministically on any host; the default follows the
|
|
52
|
+
* platform (Windows filesystems are case-insensitive).
|
|
53
|
+
*/
|
|
54
|
+
export declare function dedupeKey(path: string, caseInsensitive?: boolean): string;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { posix } from 'node:path';
|
|
2
|
+
/**
|
|
3
|
+
* Logical paths are POSIX on every OS (plan 017).
|
|
4
|
+
*
|
|
5
|
+
* Every path the CLI *surfaces or compares* — envelope `data.path`, record
|
|
6
|
+
* messages, extension `entryPath`/`folder`, doctor hints, dedupe keys — is a
|
|
7
|
+
* LOGICAL path: forward slashes only, `.harness/...` shapes literal, identical
|
|
8
|
+
* on Windows and POSIX hosts. Physical I/O (NodeFs) keeps native separators;
|
|
9
|
+
* conversion happens ONCE at the boundary (`toPosix(proc.cwd())`,
|
|
10
|
+
* `toPosix(entryPath)`), after which everything stays in POSIX space.
|
|
11
|
+
*
|
|
12
|
+
* Allowed surface (all lexical): `toPosix`, `posixNormalize`, `posixJoin`,
|
|
13
|
+
* `posixDirname`, `posixRelative`, `isWithin`, `dedupeKey`.
|
|
14
|
+
*
|
|
15
|
+
* FORBIDDEN: `posix.resolve` (and native `resolve`) on logical paths. A
|
|
16
|
+
* drive-letter path (`C:/repo`) does not start with `/`, so `posix.resolve`
|
|
17
|
+
* treats it as RELATIVE and prepends the host cwd — silently corrupting the
|
|
18
|
+
* path. Everything here is built on `posix.normalize`/`posix.join` instead.
|
|
19
|
+
*
|
|
20
|
+
* UNC guard: Node's `posix.normalize` collapses a leading `//`
|
|
21
|
+
* (`//server/share` → `/server/share`), so every normalize-based op reattaches
|
|
22
|
+
* the second slash when the input was UNC-rooted.
|
|
23
|
+
*/
|
|
24
|
+
/** Module-level platform constant — the DEFAULT for case-folding, never probed in tests (P3: pass the parameter explicitly instead of patching `process.platform`). */
|
|
25
|
+
export const IS_WIN32 = process.platform === 'win32';
|
|
26
|
+
/** True when the (already forward-slashed) path is UNC-rooted: exactly `//` then a name. */
|
|
27
|
+
const UNC_ROOT = /^\/\/[^/]/;
|
|
28
|
+
/** Reattach the UNC root a normalize-style op collapsed (`/server/…` → `//server/…`). */
|
|
29
|
+
function reattachUncRoot(original, result) {
|
|
30
|
+
if (UNC_ROOT.test(original) && !result.startsWith('//')) {
|
|
31
|
+
return `/${result}`;
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Convert a possibly-Windows-shaped path to its logical POSIX form:
|
|
37
|
+
* backslashes → `/`, drive letter upper-cased (`c:` → `C:`), UNC
|
|
38
|
+
* `\\server\share` → `//server/share`. POSIX inputs pass through unchanged.
|
|
39
|
+
*/
|
|
40
|
+
export function toPosix(path) {
|
|
41
|
+
const slashed = path.replace(/\\/g, '/');
|
|
42
|
+
return slashed.replace(/^([a-z]):/, (_, drive) => `${drive.toUpperCase()}:`);
|
|
43
|
+
}
|
|
44
|
+
/** Lexical normalize in POSIX space (UNC-guarded). Accepts Windows-shaped input. */
|
|
45
|
+
export function posixNormalize(path) {
|
|
46
|
+
const p = toPosix(path);
|
|
47
|
+
return reattachUncRoot(p, posix.normalize(p));
|
|
48
|
+
}
|
|
49
|
+
/** Join in POSIX space (UNC-guarded). Each segment is converted via `toPosix` on the way in. */
|
|
50
|
+
export function posixJoin(...parts) {
|
|
51
|
+
const posixParts = parts.map(toPosix);
|
|
52
|
+
const joined = posix.join(...posixParts);
|
|
53
|
+
return reattachUncRoot(posixParts[0] ?? '', joined);
|
|
54
|
+
}
|
|
55
|
+
/** Dirname in POSIX space (UNC-guarded). Accepts Windows-shaped input. */
|
|
56
|
+
export function posixDirname(path) {
|
|
57
|
+
const p = toPosix(path);
|
|
58
|
+
return reattachUncRoot(p, posix.dirname(p));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Relative in POSIX space. Drive-letter inputs are safe: when both sides are
|
|
62
|
+
* resolved against the same host cwd the shared prefix cancels, so the result
|
|
63
|
+
* is the lexical relative between the two logical paths.
|
|
64
|
+
*/
|
|
65
|
+
export function posixRelative(from, to) {
|
|
66
|
+
return posix.relative(toPosix(from), toPosix(to));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* True when `candidate` is `dir` or a descendant of it — the `../`-escape
|
|
70
|
+
* guard, computed entirely in POSIX space (literal `'../'`, never `sep`).
|
|
71
|
+
*/
|
|
72
|
+
export function isWithin(dir, candidate) {
|
|
73
|
+
const d = posixNormalize(dir);
|
|
74
|
+
const c = posixNormalize(candidate);
|
|
75
|
+
// Root kinds must match: a UNC tree (`//server/…`) never contains a
|
|
76
|
+
// single-slash path (or vice versa). Without this, Node's relative()
|
|
77
|
+
// collapses the doubled slash and conflates the two roots (companion F001).
|
|
78
|
+
if (UNC_ROOT.test(d) !== UNC_ROOT.test(c)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const rel = posixRelative(d, c);
|
|
82
|
+
return rel === '' || (!rel.startsWith('../') && rel !== '..' && !posix.isAbsolute(rel));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Stable identity key for dedupe maps: POSIX-normalized, optionally
|
|
86
|
+
* case-folded. Case sensitivity is an EXPLICIT parameter so tests exercise
|
|
87
|
+
* both branches deterministically on any host; the default follows the
|
|
88
|
+
* platform (Windows filesystems are case-insensitive).
|
|
89
|
+
*/
|
|
90
|
+
export function dedupeKey(path, caseInsensitive = IS_WIN32) {
|
|
91
|
+
const key = posixNormalize(path);
|
|
92
|
+
return caseInsensitive ? key.toLowerCase() : key;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=posix-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posix-path.js","sourceRoot":"","sources":["../../../src/services/shared/posix-path.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FsPort } from '../../adapters/fs/fs-port.js';
|
|
2
|
+
import type { ProcessPort } from '../../adapters/process/process-port.js';
|
|
3
|
+
/**
|
|
4
|
+
* Shared transient-storage mechanics for `.harness/temp/` — the gitignored,
|
|
5
|
+
* never-committed scratch class that sits beside the committed
|
|
6
|
+
* `.harness/records/`. Relocated from record-service (plan 015 D1) so the
|
|
7
|
+
* `record` and `observe` services share the guarantee without either importing
|
|
8
|
+
* the other's internals.
|
|
9
|
+
*/
|
|
10
|
+
export declare const HARNESS_DIR = ".harness";
|
|
11
|
+
export declare const TEMP_DIR = "temp";
|
|
12
|
+
export declare const TEMP_GITIGNORE = "# Crash-resilient agent scratch \u2014 never committed.\n*\n";
|
|
13
|
+
/** The ports the temp guarantee needs (a structural subset of RecordDeps/ObserveDeps). */
|
|
14
|
+
export interface TempDeps {
|
|
15
|
+
fs: FsPort;
|
|
16
|
+
proc: ProcessPort;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Ensure `.harness/temp/` exists and is self-gitignored on first use: the
|
|
20
|
+
* crash-resilient scratch buffer is never committed even in a consumer repo
|
|
21
|
+
* that hasn't added the root `.gitignore` rule. Idempotent — only writes what's
|
|
22
|
+
* missing. Returns the absolute temp dir.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ensureTemp(deps: TempDeps): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { posixJoin, toPosix } from './posix-path.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shared transient-storage mechanics for `.harness/temp/` — the gitignored,
|
|
4
|
+
* never-committed scratch class that sits beside the committed
|
|
5
|
+
* `.harness/records/`. Relocated from record-service (plan 015 D1) so the
|
|
6
|
+
* `record` and `observe` services share the guarantee without either importing
|
|
7
|
+
* the other's internals.
|
|
8
|
+
*/
|
|
9
|
+
export const HARNESS_DIR = '.harness';
|
|
10
|
+
export const TEMP_DIR = 'temp';
|
|
11
|
+
export const TEMP_GITIGNORE = '# Crash-resilient agent scratch — never committed.\n*\n';
|
|
12
|
+
/**
|
|
13
|
+
* Ensure `.harness/temp/` exists and is self-gitignored on first use: the
|
|
14
|
+
* crash-resilient scratch buffer is never committed even in a consumer repo
|
|
15
|
+
* that hasn't added the root `.gitignore` rule. Idempotent — only writes what's
|
|
16
|
+
* missing. Returns the absolute temp dir.
|
|
17
|
+
*/
|
|
18
|
+
export function ensureTemp(deps) {
|
|
19
|
+
const tempDir = posixJoin(toPosix(deps.proc.cwd()), HARNESS_DIR, TEMP_DIR);
|
|
20
|
+
if (!deps.fs.exists(tempDir)) {
|
|
21
|
+
deps.fs.mkdirp(tempDir);
|
|
22
|
+
}
|
|
23
|
+
const gitignore = posixJoin(tempDir, '.gitignore');
|
|
24
|
+
if (!deps.fs.exists(gitignore)) {
|
|
25
|
+
deps.fs.writeText(gitignore, TEMP_GITIGNORE);
|
|
26
|
+
}
|
|
27
|
+
return tempDir;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=temp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp.js","sourceRoot":"","sources":["../../../src/services/shared/temp.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;AACtC,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE/B,MAAM,CAAC,MAAM,cAAc,GAAG,yDAAyD,CAAC;AAQxF;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAc;IACvC,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC3E,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IACD,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACnD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract for `harness skills install` — the options the act collects and the
|
|
3
|
+
* pure service turns into an `npx skills` argv. Kept tiny + I/O-free so the argv
|
|
4
|
+
* construction is unit-testable (Principle 3: pure logic, no child spawn here).
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The default skills source: this repo's `skills/` subtree (the `owner/repo/subdir`
|
|
8
|
+
* form `npx skills add` accepts), so the command installs THIS harness's skills from
|
|
9
|
+
* anywhere it runs. Scoped to `/skills` ON PURPOSE: a bare `owner/repo` makes the
|
|
10
|
+
* installer fetch the whole repo and recursively sweep EVERY `SKILL.md` it finds —
|
|
11
|
+
* which would pull in any stray skill under `agents/`, fixtures, or future top-level
|
|
12
|
+
* dirs. Pinning the subdir keeps the install to the published skill set only.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_SKILLS_SOURCE = "AI-Substrate/harness-engineering/skills";
|
|
15
|
+
/** CLI targets the Vercel `skills` tool understands (the `-a <agent>` values). Surfaced for help/validation. */
|
|
16
|
+
export declare const KNOWN_SKILL_TARGETS: readonly ["claude-code", "codex", "cursor", "github-copilot", "opencode", "pi"];
|
|
17
|
+
/**
|
|
18
|
+
* Slugs this harness PUBLISHED in the past and has since RENAMED or REMOVED. The
|
|
19
|
+
* Vercel `npx skills` installer has no native prune — `add` and `update` are both
|
|
20
|
+
* additive (verified empirically), so a renamed skill's OLD copy lingers on disk
|
|
21
|
+
* (and keeps loading) forever. `harness skills update` removes these AFTER the
|
|
22
|
+
* refresh, so a rename never leaves a stale twin behind — the exact failure this
|
|
23
|
+
* fixes is `harnessability-assessment` AND its new name
|
|
24
|
+
* `eng-harness-0-harnessability-assessment` both installed at once.
|
|
25
|
+
*
|
|
26
|
+
* MAINTENANCE CONTRACT: when you RENAME or REMOVE a published skill (any dir under
|
|
27
|
+
* `skills/`), add its OLD slug here IN THE SAME CHANGE. `npx skills remove` no-ops
|
|
28
|
+
* (exit 0) on a slug that isn't installed, so over-listing is harmless; the only
|
|
29
|
+
* cost of forgetting is that the old copy lingers for consumers. Keep every entry
|
|
30
|
+
* HARNESS-NAMESPACED (`harness-*` / `eng-harness-*` / `engineering-harness-*` /
|
|
31
|
+
* `harnessability-*`) so a third-party skill that happens to share a generic name
|
|
32
|
+
* is never pruned out from under a user.
|
|
33
|
+
*/
|
|
34
|
+
export declare const LEGACY_SKILL_SLUGS: readonly ["harness-0-setup", "harness-1-boot", "harness-2-backpressure", "harness-3-observe", "harness-4-retro", "eng-harness-0-setup", "eng-harness-3-observe", "engineering-harness-orient", "engineering-harness-setup", "harnessability-assessment"];
|
|
35
|
+
export interface SkillsInstallOptions {
|
|
36
|
+
/** `owner/repo` (or `owner/repo/subdir`, or a local path) passed to `npx skills add`. */
|
|
37
|
+
source: string;
|
|
38
|
+
/** One or more CLI targets → fanned out as repeated `-a <target>`. Must be non-empty. */
|
|
39
|
+
targets: string[];
|
|
40
|
+
/** Global install (`-g`) vs project-local (omit `-g`). */
|
|
41
|
+
global: boolean;
|
|
42
|
+
/** Optional single-skill filters → repeated `-s <slug>`. */
|
|
43
|
+
skills?: string[];
|
|
44
|
+
}
|
|
45
|
+
export interface SkillsRemoveOptions {
|
|
46
|
+
/** Skill slugs to remove → POSITIONAL args to `npx skills remove`. Must be non-empty. */
|
|
47
|
+
slugs: string[];
|
|
48
|
+
/** One or more CLI targets → fanned out as repeated `-a <target>`. Must be non-empty. */
|
|
49
|
+
targets: string[];
|
|
50
|
+
/** Remove from the global install (`-g`) vs project-local (omit `-g`). */
|
|
51
|
+
global: boolean;
|
|
52
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract for `harness skills install` — the options the act collects and the
|
|
3
|
+
* pure service turns into an `npx skills` argv. Kept tiny + I/O-free so the argv
|
|
4
|
+
* construction is unit-testable (Principle 3: pure logic, no child spawn here).
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The default skills source: this repo's `skills/` subtree (the `owner/repo/subdir`
|
|
8
|
+
* form `npx skills add` accepts), so the command installs THIS harness's skills from
|
|
9
|
+
* anywhere it runs. Scoped to `/skills` ON PURPOSE: a bare `owner/repo` makes the
|
|
10
|
+
* installer fetch the whole repo and recursively sweep EVERY `SKILL.md` it finds —
|
|
11
|
+
* which would pull in any stray skill under `agents/`, fixtures, or future top-level
|
|
12
|
+
* dirs. Pinning the subdir keeps the install to the published skill set only.
|
|
13
|
+
*/
|
|
14
|
+
export const DEFAULT_SKILLS_SOURCE = 'AI-Substrate/harness-engineering/skills';
|
|
15
|
+
/** CLI targets the Vercel `skills` tool understands (the `-a <agent>` values). Surfaced for help/validation. */
|
|
16
|
+
export const KNOWN_SKILL_TARGETS = [
|
|
17
|
+
'claude-code',
|
|
18
|
+
'codex',
|
|
19
|
+
'cursor',
|
|
20
|
+
'github-copilot',
|
|
21
|
+
'opencode',
|
|
22
|
+
'pi',
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* Slugs this harness PUBLISHED in the past and has since RENAMED or REMOVED. The
|
|
26
|
+
* Vercel `npx skills` installer has no native prune — `add` and `update` are both
|
|
27
|
+
* additive (verified empirically), so a renamed skill's OLD copy lingers on disk
|
|
28
|
+
* (and keeps loading) forever. `harness skills update` removes these AFTER the
|
|
29
|
+
* refresh, so a rename never leaves a stale twin behind — the exact failure this
|
|
30
|
+
* fixes is `harnessability-assessment` AND its new name
|
|
31
|
+
* `eng-harness-0-harnessability-assessment` both installed at once.
|
|
32
|
+
*
|
|
33
|
+
* MAINTENANCE CONTRACT: when you RENAME or REMOVE a published skill (any dir under
|
|
34
|
+
* `skills/`), add its OLD slug here IN THE SAME CHANGE. `npx skills remove` no-ops
|
|
35
|
+
* (exit 0) on a slug that isn't installed, so over-listing is harmless; the only
|
|
36
|
+
* cost of forgetting is that the old copy lingers for consumers. Keep every entry
|
|
37
|
+
* HARNESS-NAMESPACED (`harness-*` / `eng-harness-*` / `engineering-harness-*` /
|
|
38
|
+
* `harnessability-*`) so a third-party skill that happens to share a generic name
|
|
39
|
+
* is never pruned out from under a user.
|
|
40
|
+
*/
|
|
41
|
+
export const LEGACY_SKILL_SLUGS = [
|
|
42
|
+
// Pre-`eng-` loop family → renamed to eng-harness-1-boot … eng-harness-4-retro.
|
|
43
|
+
'harness-0-setup',
|
|
44
|
+
'harness-1-boot',
|
|
45
|
+
'harness-2-backpressure',
|
|
46
|
+
'harness-3-observe',
|
|
47
|
+
'harness-4-retro',
|
|
48
|
+
// Setup-group slugs since renamed/removed.
|
|
49
|
+
'eng-harness-0-setup', // → eng-harness-0-adopt
|
|
50
|
+
'eng-harness-3-observe', // observe folded into eng-harness-4-retro
|
|
51
|
+
'engineering-harness-orient', // → harnessability-assessment → eng-harness-0-harnessability-assessment
|
|
52
|
+
'engineering-harness-setup', // → eng-harness-0-setup → eng-harness-0-adopt
|
|
53
|
+
'harnessability-assessment', // → eng-harness-0-harnessability-assessment
|
|
54
|
+
];
|
|
55
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../src/services/skills/contract.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,yCAAyC,CAAC;AAE/E,gHAAgH;AAChH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,aAAa;IACb,OAAO;IACP,QAAQ;IACR,gBAAgB;IAChB,UAAU;IACV,IAAI;CACI,CAAC;AAEX;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,gFAAgF;IAChF,iBAAiB;IACjB,gBAAgB;IAChB,wBAAwB;IACxB,mBAAmB;IACnB,iBAAiB;IACjB,2CAA2C;IAC3C,qBAAqB,EAAE,wBAAwB;IAC/C,uBAAuB,EAAE,0CAA0C;IACnE,4BAA4B,EAAE,wEAAwE;IACtG,2BAA2B,EAAE,8CAA8C;IAC3E,2BAA2B,EAAE,4CAA4C;CACjE,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { SkillsInstallOptions, SkillsRemoveOptions } from './contract.js';
|
|
2
|
+
/**
|
|
3
|
+
* Outcome of resolving a `--source` (+ optional branch) into a specifier `npx
|
|
4
|
+
* skills add` actually understands. Discriminated so the act can map a bad
|
|
5
|
+
* combination onto an `E108` envelope without the pure layer doing any I/O.
|
|
6
|
+
*/
|
|
7
|
+
export type SkillsSourceResolution = {
|
|
8
|
+
ok: true;
|
|
9
|
+
source: string;
|
|
10
|
+
branch?: string;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
reason: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Translate a friendly `--source` (+ optional `--branch`, or a `#ref` suffix on
|
|
17
|
+
* the source) into the specifier the Vercel installer truly accepts.
|
|
18
|
+
*
|
|
19
|
+
* Why this exists: `npx skills add` has NO `--branch`/`--ref` flag and NO
|
|
20
|
+
* `owner/repo#ref` shorthand (vercel-labs/skills#42 is an open request for it).
|
|
21
|
+
* Its ONLY documented branch mechanism is a GitHub *tree URL*:
|
|
22
|
+
* `https://github.com/<owner>/<repo>/tree/<ref>[/<subdir>]`. So when the user
|
|
23
|
+
* names a branch, we rewrite the GitHub shorthand into that URL form.
|
|
24
|
+
*
|
|
25
|
+
* Invariants:
|
|
26
|
+
* - **No ref → verbatim pass-through.** The source is returned unchanged (the
|
|
27
|
+
* default `owner/repo` shorthand installs the default branch, exactly as
|
|
28
|
+
* before this flag existed) — so every existing call site is untouched.
|
|
29
|
+
* - **Ref precedence**: an explicit `branch` arg wins over a `#ref` suffix.
|
|
30
|
+
* - **Slashed refs are rejected** (`ok:false`). The installer reads the first
|
|
31
|
+
* path segment after `/tree/` as the *entire* ref, so `feat/x` would silently
|
|
32
|
+
* mis-resolve to branch `feat` + subdir `x`. Failing fast (deterministic
|
|
33
|
+
* backpressure) beats handing the installer a URL it will mis-parse.
|
|
34
|
+
* - **Branch only applies to GitHub sources** (`owner/repo[/subdir]` or
|
|
35
|
+
* `https://github.com/owner/repo`). A branch against a local path / GitLab /
|
|
36
|
+
* generic git URL is rejected with guidance rather than ignored.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveSkillsSource(rawSource: string, branch?: string): SkillsSourceResolution;
|
|
39
|
+
/**
|
|
40
|
+
* Pure `npx skills add …` argv builder — the one place the Vercel `skills` flag
|
|
41
|
+
* surface is encoded (Principle 8: wrap, don't rebuild). No I/O, no child spawn:
|
|
42
|
+
* the act feeds the returned argv to the injected `ExecPort`, and tests assert
|
|
43
|
+
* the exact array (Principle 3). The returned array is the args AFTER `npx`, i.e.
|
|
44
|
+
* `['skills@latest', 'add', <source>, '-a', <t>, …, '-g'?, '-s', <slug>, …, '-y']`.
|
|
45
|
+
*
|
|
46
|
+
* Invariants (AC3/AC4):
|
|
47
|
+
* - always pins `skills@latest` and always appends `-y` (UNCONDITIONALLY — the
|
|
48
|
+
* builder makes it impossible to construct a blocking invocation, so the
|
|
49
|
+
* interactive picker never appears regardless of caller input);
|
|
50
|
+
* - each target fans out as a repeated `-a <target>`;
|
|
51
|
+
* - `-g` is present iff `global` is true.
|
|
52
|
+
*
|
|
53
|
+
* Precondition: `targets` is non-empty — enforced by the act (missing `--target`
|
|
54
|
+
* → `E108`) before this is ever called.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildInstallArgv(opts: SkillsInstallOptions): string[];
|
|
57
|
+
/**
|
|
58
|
+
* Pure `npx skills remove …` argv builder — the PRUNE half of `harness skills
|
|
59
|
+
* update` (Principle 8: wrap, don't rebuild). No I/O. Renamed/removed skills are
|
|
60
|
+
* passed as POSITIONAL slugs; `npx skills remove` no-ops (exit 0) on any slug that
|
|
61
|
+
* isn't installed, so it is safe to call with the full legacy list every time.
|
|
62
|
+
* Like the install builder it fans out `-a <target>`, adds `-g` iff global, and
|
|
63
|
+
* always appends `-y` (never blocks). Returns the args AFTER `npx`:
|
|
64
|
+
* `['skills@latest', 'remove', <slug>, …, '-a', <t>, …, '-g'?, '-y']`.
|
|
65
|
+
*
|
|
66
|
+
* Precondition: `slugs` and `targets` are both non-empty — enforced by the act.
|
|
67
|
+
*/
|
|
68
|
+
export declare function buildRemoveArgv(opts: SkillsRemoveOptions): string[];
|
|
69
|
+
/**
|
|
70
|
+
* The human-readable command line we announce before running (and echo in the JSON
|
|
71
|
+
* envelope). Generic over any `npx skills …` argv — used for both `add` and `remove`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function formatInstallCommand(argv: string[]): string;
|