@spences10/pi-harness 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/enforcement/policy.d.ts +13 -0
- package/dist/enforcement/policy.js +85 -0
- package/dist/enforcement/policy.js.map +1 -0
- package/dist/extension.d.ts +3 -0
- package/dist/extension.js +180 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.js +4 -0
- package/dist/prompt.js.map +1 -0
- package/dist/runtime/files.d.ts +22 -0
- package/dist/runtime/files.js +46 -0
- package/dist/runtime/files.js.map +1 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +154 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/outcome.d.ts +5 -0
- package/dist/runtime/outcome.js +138 -0
- package/dist/runtime/outcome.js.map +1 -0
- package/dist/runtime/renderers.d.ts +7 -0
- package/dist/runtime/renderers.js +236 -0
- package/dist/runtime/renderers.js.map +1 -0
- package/dist/schema.d.ts +105 -0
- package/dist/schema.js +62 -0
- package/dist/schema.js.map +1 -0
- package/package.json +71 -0
- package/skills/create-harness/SKILL.md +54 -0
- package/skills/execute-harness/SKILL.md +55 -0
- package/skills/review-harness/SKILL.md +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Scott Spence
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @spences10/pi-harness
|
|
2
|
+
|
|
3
|
+
[](https://viteplus.dev)
|
|
4
|
+
[](https://vitest.dev)
|
|
5
|
+
[](https://www.npmjs.com/package/@spences10/pi-harness)
|
|
6
|
+
[](https://www.npmjs.com/package/@spences10/pi-harness)
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
Build and run ephemeral task harnesses with my-pi primitives. A
|
|
11
|
+
harness is a `/tmp` runtime containing a machine-readable contract,
|
|
12
|
+
executor prompt, task brief, validation script, review script, logs,
|
|
13
|
+
status, and runtime enforcement.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pi install npm:@spences10/pi-harness
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Local development from this monorepo:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm --filter @spences10/pi-harness run build
|
|
25
|
+
pi install ./packages/pi-harness
|
|
26
|
+
# or for one run only
|
|
27
|
+
pi -e ./packages/pi-harness
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## What it does
|
|
31
|
+
|
|
32
|
+
`pi-harness` adds:
|
|
33
|
+
|
|
34
|
+
- `/harness` command for create, run, review, status, use, and clear
|
|
35
|
+
- `harness_create` tool to create `/tmp/my-pi-harness-*` runtimes
|
|
36
|
+
- `harness_update` tool for phase/status/evidence logging
|
|
37
|
+
- generated `OUTCOME.md` and `outcome.json` review artifacts
|
|
38
|
+
- dirty-baseline tracking so pre-existing repo changes are reported
|
|
39
|
+
but not treated as task drift
|
|
40
|
+
- worktree-aware validate/review scripts for team-mode executors
|
|
41
|
+
- `harness_read` tool for summaries
|
|
42
|
+
- `before_agent_start` context injection for the active harness
|
|
43
|
+
- `tool_call` enforcement for edit/write paths and forbidden commands
|
|
44
|
+
- TUI status/widget for the active harness
|
|
45
|
+
- bundled `create-harness`, `execute-harness`, and `review-harness`
|
|
46
|
+
skills
|
|
47
|
+
|
|
48
|
+
## Runtime layout
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
/tmp/my-pi-harness-<id>/
|
|
52
|
+
harness.json # execution contract
|
|
53
|
+
SYSTEM.md # executor system prompt
|
|
54
|
+
TASK.md # task brief and required loop
|
|
55
|
+
status.json # phase/status/evidence log
|
|
56
|
+
OUTCOME.md # reviewable outcome summary
|
|
57
|
+
outcome.json # machine-readable outcome summary
|
|
58
|
+
outcome.mjs # outcome artifact refresher
|
|
59
|
+
validate.sh # validation runner
|
|
60
|
+
review.sh # drift/review helper
|
|
61
|
+
logs/events.jsonl # append-only event log
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Commands
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
/harness create <task>
|
|
68
|
+
/harness run <dir>
|
|
69
|
+
/harness review <dir>
|
|
70
|
+
/harness status [dir]
|
|
71
|
+
/harness use <dir>
|
|
72
|
+
/harness clear
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The create/run/review commands prompt the model to use the bundled
|
|
76
|
+
skills. The tools provide the actual runtime and enforcement layer.
|
|
77
|
+
|
|
78
|
+
Harnesses snapshot dirty files at creation time. Review and outcome
|
|
79
|
+
artifacts focus on changes after that baseline, while still recording
|
|
80
|
+
baseline files in `outcome.json`. If a team-mode executor runs in a
|
|
81
|
+
linked git worktree, run `review.sh` from that worktree or set
|
|
82
|
+
`HARNESS_CWD` so validation, guard checks, and outcome collection use
|
|
83
|
+
the executor workspace.
|
|
84
|
+
|
|
85
|
+
## Harness loop
|
|
86
|
+
|
|
87
|
+
1. Context recovery
|
|
88
|
+
2. Source-of-truth capture
|
|
89
|
+
3. Assumption challenge
|
|
90
|
+
4. Alignment checkpoint
|
|
91
|
+
5. Surgical execution
|
|
92
|
+
6. Validation evidence
|
|
93
|
+
7. Drift review
|
|
94
|
+
8. OUTCOME.md/outcome.json review
|
|
95
|
+
9. Delta/risk report
|
|
96
|
+
|
|
97
|
+
## Using from a custom harness
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import harness from '@spences10/pi-harness';
|
|
101
|
+
|
|
102
|
+
// pass `harness` as an ExtensionFactory to your Pi runtime
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`my-pi` imports this package directly and enables it as the built-in
|
|
106
|
+
harness workflow.
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pnpm --filter @spences10/pi-harness run check
|
|
112
|
+
pnpm --filter @spences10/pi-harness run test
|
|
113
|
+
pnpm --filter @spences10/pi-harness run build
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HarnessContract } from '../schema.js';
|
|
2
|
+
export declare function check_path_allowed(contract: HarnessContract, input_path: string): {
|
|
3
|
+
ok: true;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
reason: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function check_command_allowed(contract: HarnessContract, command: string): {
|
|
9
|
+
ok: true;
|
|
10
|
+
} | {
|
|
11
|
+
ok: false;
|
|
12
|
+
reason: string;
|
|
13
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
2
|
+
function resolve_project_path(cwd, input_path) {
|
|
3
|
+
return resolve(cwd, input_path);
|
|
4
|
+
}
|
|
5
|
+
function pattern_to_relative(cwd, pattern) {
|
|
6
|
+
const normalized_pattern = pattern.replaceAll('\\', '/');
|
|
7
|
+
if (!isAbsolute(pattern))
|
|
8
|
+
return normalized_pattern;
|
|
9
|
+
return relative(cwd, resolve(pattern)).replaceAll('\\', '/');
|
|
10
|
+
}
|
|
11
|
+
function pattern_matches(cwd, pattern, absolute_path) {
|
|
12
|
+
const normalized_pattern = pattern_to_relative(cwd, pattern);
|
|
13
|
+
const relative_path = relative(cwd, absolute_path).replaceAll('\\', '/');
|
|
14
|
+
if (normalized_pattern === '.')
|
|
15
|
+
return true;
|
|
16
|
+
if (normalized_pattern.endsWith('/**')) {
|
|
17
|
+
const prefix = normalized_pattern.slice(0, -3);
|
|
18
|
+
return (relative_path === prefix ||
|
|
19
|
+
relative_path.startsWith(`${prefix}/`));
|
|
20
|
+
}
|
|
21
|
+
if (normalized_pattern.includes('*')) {
|
|
22
|
+
const regex = new RegExp(`^${normalized_pattern
|
|
23
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
24
|
+
.replaceAll('**', '.*')
|
|
25
|
+
.replaceAll('*', '[^/]*')}$`);
|
|
26
|
+
return regex.test(relative_path);
|
|
27
|
+
}
|
|
28
|
+
const target = isAbsolute(pattern)
|
|
29
|
+
? resolve(pattern)
|
|
30
|
+
: resolve_project_path(cwd, pattern);
|
|
31
|
+
return (absolute_path === target || absolute_path.startsWith(`${target}/`));
|
|
32
|
+
}
|
|
33
|
+
function is_test_path(absolute_path) {
|
|
34
|
+
const path = absolute_path.replaceAll('\\', '/');
|
|
35
|
+
return (/(^|\/)(__tests__|tests?)\//.test(path) ||
|
|
36
|
+
/\.(test|spec)\.[cm]?[jt]sx?$/.test(path));
|
|
37
|
+
}
|
|
38
|
+
export function check_path_allowed(contract, input_path) {
|
|
39
|
+
const cwd = resolve(contract.cwd);
|
|
40
|
+
const absolute_path = resolve_project_path(cwd, input_path);
|
|
41
|
+
const inside_cwd = absolute_path === cwd || absolute_path.startsWith(`${cwd}/`);
|
|
42
|
+
if (!inside_cwd) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
reason: `Harness ${contract.id}: path is outside cwd: ${input_path}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (contract.forbidden_paths.some((pattern) => pattern_matches(cwd, pattern, absolute_path))) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
reason: `Harness ${contract.id}: path is forbidden: ${input_path}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (!contract.allow_test_changes && is_test_path(absolute_path)) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
reason: `Harness ${contract.id}: test changes are not allowed by harness.json`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (!contract.allowed_paths.some((pattern) => pattern_matches(cwd, pattern, absolute_path))) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
reason: `Harness ${contract.id}: path is outside allowed_paths: ${input_path}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return { ok: true };
|
|
67
|
+
}
|
|
68
|
+
export function check_command_allowed(contract, command) {
|
|
69
|
+
const blocked = contract.forbidden_commands.find((needle) => command.includes(needle));
|
|
70
|
+
if (blocked) {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
reason: `Harness ${contract.id}: command contains forbidden pattern: ${blocked}`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (!contract.allow_test_changes &&
|
|
77
|
+
/\brm\b.*((^|\/)(__tests__|tests?)\/|\.(test|spec)\.)/.test(command)) {
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
reason: `Harness ${contract.id}: deleting tests is not allowed`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return { ok: true };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/enforcement/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { BeforeAgentStartEvent, ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
export declare function should_inject_harness_prompt(event: Pick<BeforeAgentStartEvent, 'systemPromptOptions'>): boolean;
|
|
3
|
+
export default function harness(pi: ExtensionAPI): Promise<void>;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { check_command_allowed, check_path_allowed, } from './enforcement/policy.js';
|
|
3
|
+
import { harness_paths, read_contract } from './runtime/files.js';
|
|
4
|
+
import { HARNESS_SYSTEM_PROMPT } from './prompt.js';
|
|
5
|
+
import { active_harness_context, create_harness_runtime, format_harness_summary, update_harness_runtime, } from './runtime/index.js';
|
|
6
|
+
import { HARNESS_CUSTOM_TYPE, harness_create_params_schema, harness_read_params_schema, harness_update_params_schema, } from './schema.js';
|
|
7
|
+
function persist_active_harness(pi, harness_dir) {
|
|
8
|
+
pi.appendEntry(HARNESS_CUSTOM_TYPE, {
|
|
9
|
+
active_harness_dir: harness_dir,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function restore_active_harness(ctx) {
|
|
13
|
+
const entries = ctx.sessionManager.getEntries();
|
|
14
|
+
return entries
|
|
15
|
+
.filter((entry) => entry.type === 'custom' &&
|
|
16
|
+
entry.customType === HARNESS_CUSTOM_TYPE)
|
|
17
|
+
.pop()?.data?.active_harness_dir;
|
|
18
|
+
}
|
|
19
|
+
function update_harness_ui(ctx, harness_dir) {
|
|
20
|
+
if (!harness_dir ||
|
|
21
|
+
!existsSync(harness_paths(harness_dir).contract)) {
|
|
22
|
+
ctx.ui.setStatus('harness', undefined);
|
|
23
|
+
ctx.ui.setWidget('harness', undefined);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const summary = format_harness_summary(harness_dir).split('\n');
|
|
27
|
+
ctx.ui.setStatus('harness', `🧪 ${summary[2]?.replace('Status: ', '')}`);
|
|
28
|
+
ctx.ui.setWidget('harness', summary.slice(0, 4));
|
|
29
|
+
}
|
|
30
|
+
function parse_command(args) {
|
|
31
|
+
const [command = 'status', ...rest] = args.trim().split(/\s+/);
|
|
32
|
+
return { command, rest: rest.join(' ').trim() };
|
|
33
|
+
}
|
|
34
|
+
function tool_text(text) {
|
|
35
|
+
return { content: [{ type: 'text', text }], details: {} };
|
|
36
|
+
}
|
|
37
|
+
export function should_inject_harness_prompt(event) {
|
|
38
|
+
const selected_tools = event.systemPromptOptions?.selectedTools;
|
|
39
|
+
return !selected_tools || selected_tools.includes('bash');
|
|
40
|
+
}
|
|
41
|
+
export default async function harness(pi) {
|
|
42
|
+
let active_harness_dir;
|
|
43
|
+
function set_active_harness(harness_dir, ctx) {
|
|
44
|
+
active_harness_dir = harness_dir;
|
|
45
|
+
persist_active_harness(pi, harness_dir);
|
|
46
|
+
update_harness_ui(ctx, harness_dir);
|
|
47
|
+
}
|
|
48
|
+
pi.registerCommand('harness', {
|
|
49
|
+
description: 'Create, use, run, review, and inspect ephemeral /tmp task harnesses',
|
|
50
|
+
handler: async (args, ctx) => {
|
|
51
|
+
const { command, rest } = parse_command(args);
|
|
52
|
+
if (command === 'clear') {
|
|
53
|
+
set_active_harness(undefined, ctx);
|
|
54
|
+
ctx.ui.notify('Cleared active harness', 'info');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (command === 'use') {
|
|
58
|
+
if (!rest) {
|
|
59
|
+
ctx.ui.notify('Usage: /harness use <dir>', 'warning');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
set_active_harness(rest, ctx);
|
|
63
|
+
ctx.ui.notify(`Active harness: ${rest}`, 'info');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (command === 'status') {
|
|
67
|
+
const harness_dir = rest || active_harness_dir;
|
|
68
|
+
if (!harness_dir) {
|
|
69
|
+
ctx.ui.notify('No active harness', 'warning');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
ctx.ui.notify(format_harness_summary(harness_dir), 'info');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (command === 'create') {
|
|
76
|
+
pi.sendUserMessage(`Use the create-harness skill to build a complete /tmp my-pi harness for this task. Use harness_create after context gathering. If the team tool is available, also create or reuse a team and add a team task whose description includes the harness directory.\n\n${rest}`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (command === 'run') {
|
|
80
|
+
const harness_dir = rest || active_harness_dir;
|
|
81
|
+
if (!harness_dir) {
|
|
82
|
+
ctx.ui.notify('Usage: /harness run <dir>', 'warning');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
set_active_harness(harness_dir, ctx);
|
|
86
|
+
pi.sendUserMessage(`Use the execute-harness skill to run this harness until validation completes: ${harness_dir}. If the team tool is available, create or reuse a team, create a task for this harness, spawn one worktree mutating teammate with member_spawn, and have that teammate execute the harness. Then inspect team status/results before reporting.`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (command === 'review') {
|
|
90
|
+
const harness_dir = rest || active_harness_dir;
|
|
91
|
+
if (!harness_dir) {
|
|
92
|
+
ctx.ui.notify('Usage: /harness review <dir>', 'warning');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
set_active_harness(harness_dir, ctx);
|
|
96
|
+
pi.sendUserMessage(`Use the review-harness skill to review drift, shortcuts, validation evidence, and remaining risks for: ${harness_dir}`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
ctx.ui.notify('Usage: /harness create|run|review|status|use|clear', 'info');
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
pi.registerTool({
|
|
103
|
+
name: 'harness_create',
|
|
104
|
+
label: 'Create Harness',
|
|
105
|
+
description: 'Create an ephemeral /tmp my-pi task harness with machine-readable contract, prompts, scripts, logs, and status.',
|
|
106
|
+
promptSnippet: 'Create /tmp task harnesses for constrained agent execution',
|
|
107
|
+
promptGuidelines: [
|
|
108
|
+
'Use harness_create after context gathering for ambiguous or high-risk coding tasks.',
|
|
109
|
+
'Treat harness.json as the execution contract and record progress with harness_update.',
|
|
110
|
+
],
|
|
111
|
+
parameters: harness_create_params_schema,
|
|
112
|
+
async execute(_tool_call_id, params, _signal, _on_update, ctx) {
|
|
113
|
+
const { harness_dir, contract } = create_harness_runtime(params, ctx.cwd);
|
|
114
|
+
set_active_harness(harness_dir, ctx);
|
|
115
|
+
return tool_text(`Created harness ${contract.id}\n${format_harness_summary(harness_dir)}`);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
pi.registerTool({
|
|
119
|
+
name: 'harness_update',
|
|
120
|
+
label: 'Update Harness',
|
|
121
|
+
description: 'Update harness status, phase, notes, and validation evidence.',
|
|
122
|
+
parameters: harness_update_params_schema,
|
|
123
|
+
async execute(_tool_call_id, params, _signal, _on_update, ctx) {
|
|
124
|
+
const status_file = update_harness_runtime(params);
|
|
125
|
+
set_active_harness(params.harness_dir, ctx);
|
|
126
|
+
return tool_text(`Updated harness ${status_file.id}: ${status_file.status}`);
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
pi.registerTool({
|
|
130
|
+
name: 'harness_read',
|
|
131
|
+
label: 'Read Harness',
|
|
132
|
+
description: 'Read a harness contract, status, and runtime file locations.',
|
|
133
|
+
parameters: harness_read_params_schema,
|
|
134
|
+
async execute(_tool_call_id, params) {
|
|
135
|
+
return tool_text(format_harness_summary(params.harness_dir));
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
pi.on('session_start', async (_event, ctx) => {
|
|
139
|
+
active_harness_dir = restore_active_harness(ctx);
|
|
140
|
+
update_harness_ui(ctx, active_harness_dir);
|
|
141
|
+
});
|
|
142
|
+
pi.on('before_agent_start', async (event) => {
|
|
143
|
+
let system_prompt = event.systemPrompt;
|
|
144
|
+
if (should_inject_harness_prompt(event)) {
|
|
145
|
+
system_prompt = `${system_prompt}\n\n${HARNESS_SYSTEM_PROMPT}`;
|
|
146
|
+
}
|
|
147
|
+
if (active_harness_dir &&
|
|
148
|
+
existsSync(harness_paths(active_harness_dir).contract)) {
|
|
149
|
+
system_prompt = `${system_prompt}\n\n${active_harness_context(active_harness_dir)}`;
|
|
150
|
+
}
|
|
151
|
+
return { systemPrompt: system_prompt };
|
|
152
|
+
});
|
|
153
|
+
pi.on('tool_call', async (event) => {
|
|
154
|
+
if (!active_harness_dir ||
|
|
155
|
+
!existsSync(harness_paths(active_harness_dir).contract)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const contract = read_contract(active_harness_dir);
|
|
159
|
+
if (event.toolName === 'bash') {
|
|
160
|
+
const input = event.input;
|
|
161
|
+
const command = input.command ?? '';
|
|
162
|
+
const result = check_command_allowed(contract, command);
|
|
163
|
+
if (!result.ok)
|
|
164
|
+
return { block: true, reason: result.reason };
|
|
165
|
+
}
|
|
166
|
+
if (event.toolName === 'edit' || event.toolName === 'write') {
|
|
167
|
+
const input = event.input;
|
|
168
|
+
if (!input.path)
|
|
169
|
+
return;
|
|
170
|
+
const result = check_path_allowed(contract, input.path);
|
|
171
|
+
if (!result.ok)
|
|
172
|
+
return { block: true, reason: result.reason };
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
pi.on('session_shutdown', async (_event, ctx) => {
|
|
176
|
+
ctx.ui.setStatus('harness', undefined);
|
|
177
|
+
ctx.ui.setWidget('harness', undefined);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACN,qBAAqB,EACrB,kBAAkB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACN,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,mBAAmB,EACnB,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,GAE5B,MAAM,aAAa,CAAC;AAErB,SAAS,sBAAsB,CAC9B,EAAgB,EAChB,WAA+B;IAE/B,EAAE,CAAC,WAAW,CAAC,mBAAmB,EAAE;QACnC,kBAAkB,EAAE,WAAW;KAC/B,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAC9B,GAAqB;IAErB,MAAM,OAAO,GAAG,GAAG,CAAC,cAAc,CAAC,UAAU,EAI3C,CAAC;IACH,OAAO,OAAO;SACZ,MAAM,CACN,CAAC,KAAK,EAAE,EAAE,CACT,KAAK,CAAC,IAAI,KAAK,QAAQ;QACvB,KAAK,CAAC,UAAU,KAAK,mBAAmB,CACzC;SACA,GAAG,EAAE,EAAE,IAAI,EAAE,kBAAkB,CAAC;AACnC,CAAC;AAED,SAAS,iBAAiB,CACzB,GAAqB,EACrB,WAA+B;IAE/B,IACC,CAAC,WAAW;QACZ,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,EAC/C,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACvC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACvC,OAAO;IACR,CAAC;IACD,MAAM,OAAO,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChE,GAAG,CAAC,EAAE,CAAC,SAAS,CACf,SAAS,EACT,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,CAC3C,CAAC;IACF,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IAIlC,MAAM,CAAC,OAAO,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;AACjD,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC9B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC3C,KAAyD;IAEzD,MAAM,cAAc,GAAG,KAAK,CAAC,mBAAmB,EAAE,aAAa,CAAC;IAChE,OAAO,CAAC,cAAc,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,OAAO,CAAC,EAAgB;IACrD,IAAI,kBAAsC,CAAC;IAE3C,SAAS,kBAAkB,CAC1B,WAA+B,EAC/B,GAAqB;QAErB,kBAAkB,GAAG,WAAW,CAAC;QACjC,sBAAsB,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;QACxC,iBAAiB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,EAAE,CAAC,eAAe,CAAC,SAAS,EAAE;QAC7B,WAAW,EACV,qEAAqE;QACtE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;YAC5B,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;gBACzB,kBAAkB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;gBACnC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;gBAChD,OAAO;YACR,CAAC;YACD,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,EAAE,CAAC;oBACX,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,2BAA2B,EAAE,SAAS,CAAC,CAAC;oBACtD,OAAO;gBACR,CAAC;gBACD,kBAAkB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC9B,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,mBAAmB,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;gBACjD,OAAO;YACR,CAAC;YACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAC1B,MAAM,WAAW,GAAG,IAAI,IAAI,kBAAkB,CAAC;gBAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;oBAClB,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;oBAC9C,OAAO;gBACR,CAAC;gBACD,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC;gBAC3D,OAAO;YACR,CAAC;YACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAC1B,EAAE,CAAC,eAAe,CACjB,sQAAsQ,IAAI,EAAE,CAC5Q,CAAC;gBACF,OAAO;YACR,CAAC;YACD,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;gBACvB,MAAM,WAAW,GAAG,IAAI,IAAI,kBAAkB,CAAC;gBAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;oBAClB,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,2BAA2B,EAAE,SAAS,CAAC,CAAC;oBACtD,OAAO;gBACR,CAAC;gBACD,kBAAkB,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;gBACrC,EAAE,CAAC,eAAe,CACjB,iFAAiF,WAAW,iPAAiP,CAC7U,CAAC;gBACF,OAAO;YACR,CAAC;YACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAC1B,MAAM,WAAW,GAAG,IAAI,IAAI,kBAAkB,CAAC;gBAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;oBAClB,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,8BAA8B,EAAE,SAAS,CAAC,CAAC;oBACzD,OAAO;gBACR,CAAC;gBACD,kBAAkB,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;gBACrC,EAAE,CAAC,eAAe,CACjB,0GAA0G,WAAW,EAAE,CACvH,CAAC;gBACF,OAAO;YACR,CAAC;YACD,GAAG,CAAC,EAAE,CAAC,MAAM,CACZ,oDAAoD,EACpD,MAAM,CACN,CAAC;QACH,CAAC;KACD,CAAC,CAAC;IAEH,EAAE,CAAC,YAAY,CAAC;QACf,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,gBAAgB;QACvB,WAAW,EACV,iHAAiH;QAClH,aAAa,EACZ,4DAA4D;QAC7D,gBAAgB,EAAE;YACjB,qFAAqF;YACrF,uFAAuF;SACvF;QACD,UAAU,EAAE,4BAA4B;QACxC,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG;YAC5D,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,sBAAsB,CACvD,MAAM,EACN,GAAG,CAAC,GAAG,CACP,CAAC;YACF,kBAAkB,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YACrC,OAAO,SAAS,CACf,mBAAmB,QAAQ,CAAC,EAAE,KAAK,sBAAsB,CAAC,WAAW,CAAC,EAAE,CACxE,CAAC;QACH,CAAC;KACD,CAAC,CAAC;IAEH,EAAE,CAAC,YAAY,CAAC;QACf,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,gBAAgB;QACvB,WAAW,EACV,+DAA+D;QAChE,UAAU,EAAE,4BAA4B;QACxC,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG;YAC5D,MAAM,WAAW,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;YACnD,kBAAkB,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YAC5C,OAAO,SAAS,CACf,mBAAmB,WAAW,CAAC,EAAE,KAAK,WAAW,CAAC,MAAM,EAAE,CAC1D,CAAC;QACH,CAAC;KACD,CAAC,CAAC;IAEH,EAAE,CAAC,YAAY,CAAC;QACf,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,cAAc;QACrB,WAAW,EACV,8DAA8D;QAC/D,UAAU,EAAE,0BAA0B;QACtC,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,MAAyB;YACrD,OAAO,SAAS,CAAC,sBAAsB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QAC9D,CAAC;KACD,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;QAC5C,kBAAkB,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;QACjD,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,oBAAoB,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC3C,IAAI,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC;QACvC,IAAI,4BAA4B,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,aAAa,GAAG,GAAG,aAAa,OAAO,qBAAqB,EAAE,CAAC;QAChE,CAAC;QACD,IACC,kBAAkB;YAClB,UAAU,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAC,EACrD,CAAC;YACF,aAAa,GAAG,GAAG,aAAa,OAAO,sBAAsB,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACrF,CAAC;QACD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAClC,IACC,CAAC,kBAAkB;YACnB,CAAC,UAAU,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAC,EACtD,CAAC;YACF,OAAO;QACR,CAAC;QACD,MAAM,QAAQ,GAAG,aAAa,CAAC,kBAAkB,CAAC,CAAC;QACnD,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,KAA6B,CAAC;YAClD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,qBAAqB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACxD,IAAI,CAAC,MAAM,CAAC,EAAE;gBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QAC/D,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAC7D,MAAM,KAAK,GAAG,KAAK,CAAC,KAA0B,CAAC;YAC/C,IAAI,CAAC,KAAK,CAAC,IAAI;gBAAE,OAAO;YACxB,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxD,IAAI,CAAC,MAAM,CAAC,EAAE;gBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QAC/D,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;QAC/C,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACvC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export { default } from './extension.js';
|
|
2
|
+
export { should_inject_harness_prompt } from './extension.js';
|
|
3
|
+
export { check_command_allowed, check_path_allowed, } from './enforcement/policy.js';
|
|
4
|
+
export { HARNESS_SYSTEM_PROMPT } from './prompt.js';
|
|
5
|
+
export { active_harness_context, create_harness_runtime, format_harness_summary, update_harness_runtime, } from './runtime/index.js';
|
|
6
|
+
export { collect_harness_outcome, render_outcome_markdown, write_outcome_artifacts, } from './runtime/outcome.js';
|
|
7
|
+
export type { HarnessContract, HarnessCreateParams, HarnessLogEntry, HarnessOutcome, HarnessReadParams, HarnessStatus, HarnessStatusFile, HarnessThinking, HarnessUpdateParams, } from './schema.js';
|
|
8
|
+
export { DEFAULT_FORBIDDEN_COMMANDS, HARNESS_CUSTOM_TYPE, HARNESS_VERSION, harness_create_params_schema, harness_read_params_schema, harness_update_params_schema, thinking_levels_schema, } from './schema.js';
|
|
9
|
+
export declare const testing: {
|
|
10
|
+
harness_create_params_schema: import("typebox").TObject<{
|
|
11
|
+
task: import("typebox").TString;
|
|
12
|
+
slug: import("typebox").TOptional<import("typebox").TString>;
|
|
13
|
+
cwd: import("typebox").TOptional<import("typebox").TString>;
|
|
14
|
+
allowed_paths: import("typebox").TOptional<import("typebox").TArray<import("typebox").TString>>;
|
|
15
|
+
forbidden_paths: import("typebox").TOptional<import("typebox").TArray<import("typebox").TString>>;
|
|
16
|
+
validation_commands: import("typebox").TOptional<import("typebox").TArray<import("typebox").TString>>;
|
|
17
|
+
forbidden_commands: import("typebox").TOptional<import("typebox").TArray<import("typebox").TString>>;
|
|
18
|
+
allow_test_changes: import("typebox").TOptional<import("typebox").TBoolean>;
|
|
19
|
+
planner_model: import("typebox").TOptional<import("typebox").TString>;
|
|
20
|
+
planner_thinking: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"off">, import("typebox").TLiteral<"minimal">, import("typebox").TLiteral<"low">, import("typebox").TLiteral<"medium">, import("typebox").TLiteral<"high">, import("typebox").TLiteral<"xhigh">]>>;
|
|
21
|
+
executor_model: import("typebox").TOptional<import("typebox").TString>;
|
|
22
|
+
executor_thinking: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"off">, import("typebox").TLiteral<"minimal">, import("typebox").TLiteral<"low">, import("typebox").TLiteral<"medium">, import("typebox").TLiteral<"high">, import("typebox").TLiteral<"xhigh">]>>;
|
|
23
|
+
reviewer_model: import("typebox").TOptional<import("typebox").TString>;
|
|
24
|
+
reviewer_thinking: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"off">, import("typebox").TLiteral<"minimal">, import("typebox").TLiteral<"low">, import("typebox").TLiteral<"medium">, import("typebox").TLiteral<"high">, import("typebox").TLiteral<"xhigh">]>>;
|
|
25
|
+
}>;
|
|
26
|
+
harness_read_params_schema: import("typebox").TObject<{
|
|
27
|
+
harness_dir: import("typebox").TString;
|
|
28
|
+
}>;
|
|
29
|
+
harness_update_params_schema: import("typebox").TObject<{
|
|
30
|
+
harness_dir: import("typebox").TString;
|
|
31
|
+
status: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"created">, import("typebox").TLiteral<"running">, import("typebox").TLiteral<"reviewing">, import("typebox").TLiteral<"completed">, import("typebox").TLiteral<"failed">]>>;
|
|
32
|
+
phase: import("typebox").TOptional<import("typebox").TString>;
|
|
33
|
+
note: import("typebox").TOptional<import("typebox").TString>;
|
|
34
|
+
evidence: import("typebox").TOptional<import("typebox").TString>;
|
|
35
|
+
team_status: import("typebox").TOptional<import("typebox").TString>;
|
|
36
|
+
remaining_risks: import("typebox").TOptional<import("typebox").TArray<import("typebox").TString>>;
|
|
37
|
+
changed_files: import("typebox").TOptional<import("typebox").TArray<import("typebox").TString>>;
|
|
38
|
+
}>;
|
|
39
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { harness_create_params_schema, harness_read_params_schema, harness_update_params_schema, } from './schema.js';
|
|
2
|
+
export { default } from './extension.js';
|
|
3
|
+
export { should_inject_harness_prompt } from './extension.js';
|
|
4
|
+
export { check_command_allowed, check_path_allowed, } from './enforcement/policy.js';
|
|
5
|
+
export { HARNESS_SYSTEM_PROMPT } from './prompt.js';
|
|
6
|
+
export { active_harness_context, create_harness_runtime, format_harness_summary, update_harness_runtime, } from './runtime/index.js';
|
|
7
|
+
export { collect_harness_outcome, render_outcome_markdown, write_outcome_artifacts, } from './runtime/outcome.js';
|
|
8
|
+
export { DEFAULT_FORBIDDEN_COMMANDS, HARNESS_CUSTOM_TYPE, HARNESS_VERSION, harness_create_params_schema, harness_read_params_schema, harness_update_params_schema, thinking_levels_schema, } from './schema.js';
|
|
9
|
+
export const testing = {
|
|
10
|
+
harness_create_params_schema,
|
|
11
|
+
harness_read_params_schema,
|
|
12
|
+
harness_update_params_schema,
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,GAC5B,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EACN,qBAAqB,EACrB,kBAAkB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACN,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACvB,MAAM,sBAAsB,CAAC;AAY9B,OAAO,EACN,0BAA0B,EAC1B,mBAAmB,EACnB,eAAe,EACf,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,EAC5B,sBAAsB,GACtB,MAAM,aAAa,CAAC;AAErB,MAAM,CAAC,MAAM,OAAO,GAAG;IACtB,4BAA4B;IAC5B,0BAA0B;IAC1B,4BAA4B;CAC5B,CAAC"}
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const HARNESS_SYSTEM_PROMPT = "## my-pi harness workflow\n\nFor ambiguous, risky, or multi-step coding tasks, prefer a real harness instead of a prose-only plan. A harness is an ephemeral /tmp runtime with harness.json, SYSTEM.md, TASK.md, status.json, validation scripts, logs, and enforcement rules. Use the bundled create-harness skill plus the harness_create tool to build it, then execute inside the active harness until validation and review are complete.";
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export const HARNESS_SYSTEM_PROMPT = `## my-pi harness workflow
|
|
2
|
+
|
|
3
|
+
For ambiguous, risky, or multi-step coding tasks, prefer a real harness instead of a prose-only plan. A harness is an ephemeral /tmp runtime with harness.json, SYSTEM.md, TASK.md, status.json, validation scripts, logs, and enforcement rules. Use the bundled create-harness skill plus the harness_create tool to build it, then execute inside the active harness until validation and review are complete.`;
|
|
4
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,qBAAqB,GAAG;;kZAE6W,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { HarnessContract, HarnessLogEntry, HarnessStatusFile } from '../schema.js';
|
|
2
|
+
export declare function json_read<T>(path: string): T;
|
|
3
|
+
export declare function json_write(path: string, value: unknown): void;
|
|
4
|
+
export declare function shell_quote(value: string): string;
|
|
5
|
+
export declare function harness_paths(harness_dir: string): {
|
|
6
|
+
contract: string;
|
|
7
|
+
system: string;
|
|
8
|
+
task: string;
|
|
9
|
+
status: string;
|
|
10
|
+
outcome: string;
|
|
11
|
+
outcome_markdown: string;
|
|
12
|
+
outcome_script: string;
|
|
13
|
+
validate: string;
|
|
14
|
+
review: string;
|
|
15
|
+
guard: string;
|
|
16
|
+
logs: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function read_contract(harness_dir: string): HarnessContract;
|
|
19
|
+
export declare function read_status(harness_dir: string): HarnessStatusFile;
|
|
20
|
+
export declare function write_status(harness_dir: string, status_file: HarnessStatusFile): void;
|
|
21
|
+
export declare function write_contract(harness_dir: string, contract: HarnessContract): void;
|
|
22
|
+
export declare function append_event(harness_dir: string, entry: HarnessLogEntry): void;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export function json_read(path) {
|
|
4
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
5
|
+
}
|
|
6
|
+
export function json_write(path, value) {
|
|
7
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
8
|
+
}
|
|
9
|
+
export function shell_quote(value) {
|
|
10
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
11
|
+
}
|
|
12
|
+
export function harness_paths(harness_dir) {
|
|
13
|
+
return {
|
|
14
|
+
contract: join(harness_dir, 'harness.json'),
|
|
15
|
+
system: join(harness_dir, 'SYSTEM.md'),
|
|
16
|
+
task: join(harness_dir, 'TASK.md'),
|
|
17
|
+
status: join(harness_dir, 'status.json'),
|
|
18
|
+
outcome: join(harness_dir, 'outcome.json'),
|
|
19
|
+
outcome_markdown: join(harness_dir, 'OUTCOME.md'),
|
|
20
|
+
outcome_script: join(harness_dir, 'outcome.mjs'),
|
|
21
|
+
validate: join(harness_dir, 'validate.sh'),
|
|
22
|
+
review: join(harness_dir, 'review.sh'),
|
|
23
|
+
guard: join(harness_dir, 'guard.mjs'),
|
|
24
|
+
logs: join(harness_dir, 'logs'),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function read_contract(harness_dir) {
|
|
28
|
+
return json_read(harness_paths(harness_dir).contract);
|
|
29
|
+
}
|
|
30
|
+
export function read_status(harness_dir) {
|
|
31
|
+
return json_read(harness_paths(harness_dir).status);
|
|
32
|
+
}
|
|
33
|
+
export function write_status(harness_dir, status_file) {
|
|
34
|
+
json_write(harness_paths(harness_dir).status, status_file);
|
|
35
|
+
}
|
|
36
|
+
export function write_contract(harness_dir, contract) {
|
|
37
|
+
json_write(harness_paths(harness_dir).contract, contract);
|
|
38
|
+
}
|
|
39
|
+
export function append_event(harness_dir, entry) {
|
|
40
|
+
const paths = harness_paths(harness_dir);
|
|
41
|
+
mkdirSync(paths.logs, { recursive: true });
|
|
42
|
+
writeFileSync(join(paths.logs, 'events.jsonl'), `${JSON.stringify(entry)}\n`, {
|
|
43
|
+
flag: 'a',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/runtime/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAOjC,MAAM,UAAU,SAAS,CAAI,IAAY;IACxC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAc;IACtD,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,WAAmB;IAChD,OAAO;QACN,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC;QAC3C,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC;QACtC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC;QAClC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC;QACxC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC;QAC1C,gBAAgB,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;QACjD,cAAc,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC;QAChD,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC;QAC1C,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC;QACtC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC;QACrC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC;KAC/B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,WAAmB;IAChD,OAAO,SAAS,CACf,aAAa,CAAC,WAAW,CAAC,CAAC,QAAQ,CACnC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,WAAmB;IAC9C,OAAO,SAAS,CACf,aAAa,CAAC,WAAW,CAAC,CAAC,MAAM,CACjC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAC3B,WAAmB,EACnB,WAA8B;IAE9B,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,cAAc,CAC7B,WAAmB,EACnB,QAAyB;IAEzB,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,YAAY,CAC3B,WAAmB,EACnB,KAAsB;IAEtB,MAAM,KAAK,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IACzC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CACZ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,cAAc,CAAC,EAChC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAC5B;QACC,IAAI,EAAE,GAAG;KACT,CACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type HarnessContract, type HarnessCreateParams, type HarnessStatusFile, type HarnessUpdateParams } from '../schema.js';
|
|
2
|
+
export declare function create_harness_runtime(params: HarnessCreateParams, default_cwd: string): {
|
|
3
|
+
harness_dir: string;
|
|
4
|
+
contract: HarnessContract;
|
|
5
|
+
};
|
|
6
|
+
export declare function update_harness_runtime(params: HarnessUpdateParams): HarnessStatusFile;
|
|
7
|
+
export declare function format_harness_summary(harness_dir: string): string;
|
|
8
|
+
export declare function active_harness_context(harness_dir: string): string;
|