@wkronmiller/lisa 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +407 -0
- package/bin/lisa-runtime.js +8797 -0
- package/bin/lisa.js +21 -0
- package/completion.ts +58 -0
- package/install.ps1 +51 -0
- package/install.sh +93 -0
- package/lisa.ts +6 -0
- package/package.json +66 -0
- package/skills/README.md +28 -0
- package/skills/claude-code/CLAUDE.md +151 -0
- package/skills/codex/AGENTS.md +151 -0
- package/skills/gemini/GEMINI.md +151 -0
- package/skills/opencode/AGENTS.md +152 -0
- package/src/cli.ts +85 -0
- package/src/harness/base-adapter.ts +47 -0
- package/src/harness/claude-code.ts +106 -0
- package/src/harness/codex.ts +80 -0
- package/src/harness/command.ts +173 -0
- package/src/harness/gemini.ts +74 -0
- package/src/harness/opencode.ts +84 -0
- package/src/harness/registry.ts +29 -0
- package/src/harness/runner.ts +19 -0
- package/src/harness/types.ts +73 -0
- package/src/output-mode.ts +32 -0
- package/src/skill/artifacts.ts +174 -0
- package/src/skill/cli.ts +29 -0
- package/src/skill/install.ts +317 -0
- package/src/spec/agent-guidance.ts +466 -0
- package/src/spec/cli.ts +151 -0
- package/src/spec/commands/check.ts +1 -0
- package/src/spec/commands/config.ts +146 -0
- package/src/spec/commands/diff.ts +1 -0
- package/src/spec/commands/generate.ts +1 -0
- package/src/spec/commands/guide.ts +1 -0
- package/src/spec/commands/harness-list.ts +36 -0
- package/src/spec/commands/implement.ts +1 -0
- package/src/spec/commands/import.ts +1 -0
- package/src/spec/commands/init.ts +1 -0
- package/src/spec/commands/status.ts +87 -0
- package/src/spec/config.ts +63 -0
- package/src/spec/diff.ts +791 -0
- package/src/spec/extensions/benchmark.ts +347 -0
- package/src/spec/extensions/registry.ts +59 -0
- package/src/spec/extensions/types.ts +56 -0
- package/src/spec/grammar/index.ts +14 -0
- package/src/spec/grammar/parser.ts +443 -0
- package/src/spec/grammar/types.ts +70 -0
- package/src/spec/grammar/validator.ts +104 -0
- package/src/spec/loader.ts +174 -0
- package/src/spec/local-config.ts +59 -0
- package/src/spec/parser.ts +226 -0
- package/src/spec/path-utils.ts +73 -0
- package/src/spec/planner.ts +299 -0
- package/src/spec/prompt-renderer.ts +318 -0
- package/src/spec/skill-content.ts +119 -0
- package/src/spec/types.ts +239 -0
- package/src/spec/validator.ts +443 -0
- package/src/spec/workflows/check.ts +1534 -0
- package/src/spec/workflows/diff.ts +209 -0
- package/src/spec/workflows/generate.ts +1270 -0
- package/src/spec/workflows/guide.ts +190 -0
- package/src/spec/workflows/implement.ts +797 -0
- package/src/spec/workflows/import.ts +986 -0
- package/src/spec/workflows/init.ts +548 -0
- package/src/spec/workflows/status.ts +22 -0
- package/src/spec/workspace.ts +541 -0
- package/uninstall.ps1 +21 -0
- package/uninstall.sh +22 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Lisa for Gemini CLI
|
|
2
|
+
|
|
3
|
+
Use this skill when working on Lisa from a Gemini CLI session.
|
|
4
|
+
|
|
5
|
+
## What Lisa is
|
|
6
|
+
|
|
7
|
+
Lisa is a spec-driven agent orchestration CLI. Treat `.specs/` as the source of truth for intended behavior.
|
|
8
|
+
|
|
9
|
+
Core workflow:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
specs -> git diff -> structured deltas -> plan -> single writer harness -> tests/benchmarks -> parallel read-only verification -> reports
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Working in this repo
|
|
16
|
+
|
|
17
|
+
- Start with `lisa spec status` to inspect the workspace and harness availability.
|
|
18
|
+
- Review active specs in `.specs/backend/` and `.specs/frontend/` before changing behavior.
|
|
19
|
+
- Prefer repo-local Lisa commands over ad hoc workflows that bypass the spec-first model.
|
|
20
|
+
- When behavior changes, update the relevant spec, code, tests, and checked-in docs or skills together.
|
|
21
|
+
|
|
22
|
+
If the local `lisa` command is not installed yet, run the same workflows from the checkout with `bun run lisa.ts spec ...`.
|
|
23
|
+
|
|
24
|
+
## Current CLI surface
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
lisa skill install --global|--local
|
|
28
|
+
lisa spec init
|
|
29
|
+
lisa spec config [options]
|
|
30
|
+
lisa spec status
|
|
31
|
+
lisa spec harness list
|
|
32
|
+
lisa spec guide [backend|frontend] [name] [options]
|
|
33
|
+
lisa spec generate [backend|frontend] [name] [options]
|
|
34
|
+
lisa spec implement [options]
|
|
35
|
+
lisa spec check --changed|--all [options]
|
|
36
|
+
lisa spec import <path...> [options]
|
|
37
|
+
lisa spec diff [options]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Common repo-local commands:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
lisa skill install --local
|
|
44
|
+
lisa skill install --global
|
|
45
|
+
lisa spec config --harness gemini
|
|
46
|
+
lisa spec status
|
|
47
|
+
lisa spec harness list
|
|
48
|
+
lisa spec guide backend checkout-flow
|
|
49
|
+
lisa spec generate backend checkout-flow
|
|
50
|
+
lisa spec diff
|
|
51
|
+
lisa spec implement
|
|
52
|
+
lisa spec check --changed
|
|
53
|
+
lisa spec check --all
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Installing Lisa guidance
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
lisa skill install --local
|
|
60
|
+
lisa skill install --global
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Local install target: `GEMINI.md`
|
|
64
|
+
- Global install target: `~/.gemini/GEMINI.md`
|
|
65
|
+
|
|
66
|
+
## Seed guide workflow
|
|
67
|
+
|
|
68
|
+
- Run `lisa spec guide backend checkout-flow` when you want Lisa to create or reuse a draft seed spec and point you at the right harness-specific skill artifact.
|
|
69
|
+
- Keep editing the seed spec in `.specs/backend/` or `.specs/frontend/` until the placeholders are replaced with real Summary, Use Cases, Invariants, Failure Modes, Acceptance Criteria, and Out of Scope content.
|
|
70
|
+
- After the spec is ready, return to `lisa spec diff`, `lisa spec implement`, and `lisa spec check --changed`.
|
|
71
|
+
|
|
72
|
+
## Spec layout
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
.specs/
|
|
76
|
+
backend/
|
|
77
|
+
<spec-name>.md
|
|
78
|
+
<spec-name>.bench.<environment>.md
|
|
79
|
+
frontend/
|
|
80
|
+
<spec-name>.md
|
|
81
|
+
<spec-name>.bench.<environment>.md
|
|
82
|
+
environments/
|
|
83
|
+
<environment>.yaml
|
|
84
|
+
config.yaml
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Create backend specs in `.specs/backend/` and frontend specs in `.specs/frontend/`.
|
|
88
|
+
Keep environment definitions in `.specs/environments/` and workspace harness settings in `.specs/config.yaml`.
|
|
89
|
+
|
|
90
|
+
## Spec editing rules
|
|
91
|
+
|
|
92
|
+
Preserve YAML frontmatter and required sections when creating or updating an active base spec.
|
|
93
|
+
|
|
94
|
+
```md
|
|
95
|
+
---
|
|
96
|
+
id: backend.example-spec
|
|
97
|
+
status: active
|
|
98
|
+
code_paths:
|
|
99
|
+
- src/example/**
|
|
100
|
+
test_paths:
|
|
101
|
+
- tests/example/**
|
|
102
|
+
test_commands:
|
|
103
|
+
- bun test ./tests
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
# Summary
|
|
107
|
+
|
|
108
|
+
## Use Cases
|
|
109
|
+
|
|
110
|
+
## Invariants
|
|
111
|
+
|
|
112
|
+
## Failure Modes
|
|
113
|
+
|
|
114
|
+
## Acceptance Criteria
|
|
115
|
+
|
|
116
|
+
## Out of Scope
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Notes:
|
|
120
|
+
|
|
121
|
+
- `id`, `status`, and `code_paths` are required in frontmatter.
|
|
122
|
+
- Include at least one of `test_paths` or `test_commands`.
|
|
123
|
+
- `# Summary`, `## Use Cases`, `## Invariants`, `## Acceptance Criteria`, and `## Out of Scope` are required for active base specs.
|
|
124
|
+
- `lisa spec generate` also emits `## Failure Modes`, and planning uses it, so keep that section intact when present.
|
|
125
|
+
- Benchmark sidecars belong next to the base spec as `<spec-name>.bench.<environment>.md`.
|
|
126
|
+
|
|
127
|
+
## Guardrails
|
|
128
|
+
|
|
129
|
+
- Do not treat spec deletion as permission to remove code silently.
|
|
130
|
+
- Keep edits inside the mapped repo paths unless a small supporting change is required.
|
|
131
|
+
- Prefer the checked-in workflow in this repository over global harness-specific shortcuts.
|
|
132
|
+
|
|
133
|
+
## Aborting Operations
|
|
134
|
+
|
|
135
|
+
If you detect that you cannot complete a task due to missing tools or capabilities (e.g., no web browser for UI changes, no file system access for file modifications), you should abort gracefully rather than looping on a task you cannot complete.
|
|
136
|
+
|
|
137
|
+
To abort, output one of these formats as your final response:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
LISA_ABORT: <reason>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Or for multi-line reasons:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
LISA_ABORT_START
|
|
147
|
+
<reason>
|
|
148
|
+
LISA_ABORT_END
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The workflow will detect the abort signal, stop execution, and report the reason to the user. This prevents wasted time and provides clear feedback about why the operation could not be completed.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Lisa for OpenCode
|
|
2
|
+
|
|
3
|
+
Use this skill when working on Lisa from an OpenCode session.
|
|
4
|
+
|
|
5
|
+
## What Lisa is
|
|
6
|
+
|
|
7
|
+
Lisa is a spec-driven agent orchestration CLI. Treat `.specs/` as the source of truth for intended behavior.
|
|
8
|
+
|
|
9
|
+
Core workflow:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
specs -> git diff -> structured deltas -> plan -> single writer harness -> tests/benchmarks -> parallel read-only verification -> reports
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Working in this repo
|
|
16
|
+
|
|
17
|
+
- Start with `lisa spec status` to inspect the workspace and harness availability.
|
|
18
|
+
- Review active specs in `.specs/backend/` and `.specs/frontend/` before changing behavior.
|
|
19
|
+
- Prefer repo-local Lisa commands over ad hoc workflows that bypass the spec-first model.
|
|
20
|
+
- When behavior changes, update the relevant spec, code, tests, and checked-in docs or skills together.
|
|
21
|
+
|
|
22
|
+
If the local `lisa` command is not installed yet, run the same workflows from the checkout with `bun run lisa.ts spec ...`.
|
|
23
|
+
|
|
24
|
+
## Current CLI surface
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
lisa skill install --global|--local
|
|
28
|
+
lisa spec init
|
|
29
|
+
lisa spec config [options]
|
|
30
|
+
lisa spec status
|
|
31
|
+
lisa spec harness list
|
|
32
|
+
lisa spec guide [backend|frontend] [name] [options]
|
|
33
|
+
lisa spec generate [backend|frontend] [name] [options]
|
|
34
|
+
lisa spec implement [options]
|
|
35
|
+
lisa spec check --changed|--all [options]
|
|
36
|
+
lisa spec import <path...> [options]
|
|
37
|
+
lisa spec diff [options]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Common repo-local commands:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
lisa skill install --local
|
|
44
|
+
lisa skill install --global
|
|
45
|
+
lisa spec config --harness opencode
|
|
46
|
+
lisa spec status
|
|
47
|
+
lisa spec harness list
|
|
48
|
+
lisa spec guide backend checkout-flow
|
|
49
|
+
lisa spec generate backend checkout-flow
|
|
50
|
+
lisa spec diff
|
|
51
|
+
lisa spec implement
|
|
52
|
+
lisa spec check --changed
|
|
53
|
+
lisa spec check --all
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Installing Lisa guidance
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
lisa skill install --local
|
|
60
|
+
lisa skill install --global
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Local install target: `.opencode/skills/lisa/SKILL.md`
|
|
64
|
+
- Global install target: `${XDG_CONFIG_HOME:-~/.config}/opencode/skills/lisa/SKILL.md`
|
|
65
|
+
- The installed `SKILL.md` wraps the checked-in Lisa guidance with the small OpenCode skill frontmatter.
|
|
66
|
+
|
|
67
|
+
## Seed guide workflow
|
|
68
|
+
|
|
69
|
+
- Run `lisa spec guide backend checkout-flow` when you want Lisa to create or reuse a draft seed spec and point you at the right harness-specific skill artifact.
|
|
70
|
+
- Keep editing the seed spec in `.specs/backend/` or `.specs/frontend/` until the placeholders are replaced with real Summary, Use Cases, Invariants, Failure Modes, Acceptance Criteria, and Out of Scope content.
|
|
71
|
+
- After the spec is ready, return to `lisa spec diff`, `lisa spec implement`, and `lisa spec check --changed`.
|
|
72
|
+
|
|
73
|
+
## Spec layout
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
.specs/
|
|
77
|
+
backend/
|
|
78
|
+
<spec-name>.md
|
|
79
|
+
<spec-name>.bench.<environment>.md
|
|
80
|
+
frontend/
|
|
81
|
+
<spec-name>.md
|
|
82
|
+
<spec-name>.bench.<environment>.md
|
|
83
|
+
environments/
|
|
84
|
+
<environment>.yaml
|
|
85
|
+
config.yaml
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Create backend specs in `.specs/backend/` and frontend specs in `.specs/frontend/`.
|
|
89
|
+
Keep environment definitions in `.specs/environments/` and workspace harness settings in `.specs/config.yaml`.
|
|
90
|
+
|
|
91
|
+
## Spec editing rules
|
|
92
|
+
|
|
93
|
+
Preserve YAML frontmatter and required sections when creating or updating an active base spec.
|
|
94
|
+
|
|
95
|
+
```md
|
|
96
|
+
---
|
|
97
|
+
id: backend.example-spec
|
|
98
|
+
status: active
|
|
99
|
+
code_paths:
|
|
100
|
+
- src/example/**
|
|
101
|
+
test_paths:
|
|
102
|
+
- tests/example/**
|
|
103
|
+
test_commands:
|
|
104
|
+
- bun test ./tests
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
# Summary
|
|
108
|
+
|
|
109
|
+
## Use Cases
|
|
110
|
+
|
|
111
|
+
## Invariants
|
|
112
|
+
|
|
113
|
+
## Failure Modes
|
|
114
|
+
|
|
115
|
+
## Acceptance Criteria
|
|
116
|
+
|
|
117
|
+
## Out of Scope
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Notes:
|
|
121
|
+
|
|
122
|
+
- `id`, `status`, and `code_paths` are required in frontmatter.
|
|
123
|
+
- Include at least one of `test_paths` or `test_commands`.
|
|
124
|
+
- `# Summary`, `## Use Cases`, `## Invariants`, `## Acceptance Criteria`, and `## Out of Scope` are required for active base specs.
|
|
125
|
+
- `lisa spec generate` also emits `## Failure Modes`, and planning uses it, so keep that section intact when present.
|
|
126
|
+
- Benchmark sidecars belong next to the base spec as `<spec-name>.bench.<environment>.md`.
|
|
127
|
+
|
|
128
|
+
## Guardrails
|
|
129
|
+
|
|
130
|
+
- Do not treat spec deletion as permission to remove code silently.
|
|
131
|
+
- Keep edits inside the mapped repo paths unless a small supporting change is required.
|
|
132
|
+
- Prefer the checked-in workflow in this repository over global harness-specific shortcuts.
|
|
133
|
+
|
|
134
|
+
## Aborting Operations
|
|
135
|
+
|
|
136
|
+
If you detect that you cannot complete a task due to missing tools or capabilities (e.g., no web browser for UI changes, no file system access for file modifications), you should abort gracefully rather than looping on a task you cannot complete.
|
|
137
|
+
|
|
138
|
+
To abort, output one of these formats as your final response:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
LISA_ABORT: <reason>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Or for multi-line reasons:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
LISA_ABORT_START
|
|
148
|
+
<reason>
|
|
149
|
+
LISA_ABORT_END
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The workflow will detect the abort signal, stop execution, and report the reason to the user. This prevents wasted time and provides clear feedback about why the operation could not be completed.
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { resolveOutputMode } from "./output-mode";
|
|
2
|
+
import { runSkillCli } from "./skill/cli";
|
|
3
|
+
import { runSpecCli } from "./spec/cli";
|
|
4
|
+
|
|
5
|
+
const VERSION = "1.2.2";
|
|
6
|
+
|
|
7
|
+
function printLisaHelp(): void {
|
|
8
|
+
const outputMode = resolveOutputMode();
|
|
9
|
+
const interactive = outputMode === "interactive";
|
|
10
|
+
|
|
11
|
+
const header = interactive
|
|
12
|
+
? `Lisa turns markdown specs into implementation work, then verifies code, tests, and benchmark budgets against those specs.
|
|
13
|
+
|
|
14
|
+
Workflow:
|
|
15
|
+
1. init Scaffold a .specs workspace for the repo
|
|
16
|
+
2. generate or import Create or draft specs for the behavior you want
|
|
17
|
+
3. implement Apply code and test changes from changed specs
|
|
18
|
+
4. check Verify specs against code, tests, and benchmarks
|
|
19
|
+
|
|
20
|
+
Override output mode with LISA_OUTPUT_MODE=interactive or LISA_OUTPUT_MODE=concise.
|
|
21
|
+
|
|
22
|
+
`
|
|
23
|
+
: "Lisa - spec-driven agent orchestration\n\n";
|
|
24
|
+
|
|
25
|
+
console.log(`${header}Usage:
|
|
26
|
+
lisa <command>
|
|
27
|
+
|
|
28
|
+
Commands:
|
|
29
|
+
skill Install Lisa guidance for supported harnesses
|
|
30
|
+
spec Run Lisa spec workflows
|
|
31
|
+
|
|
32
|
+
Top-level options:
|
|
33
|
+
--help, -h Show Lisa help
|
|
34
|
+
--version, -v Show Lisa version
|
|
35
|
+
|
|
36
|
+
Environment:
|
|
37
|
+
LISA_OUTPUT_MODE=interactive|concise Override help/install copy mode
|
|
38
|
+
|
|
39
|
+
${interactive ? `Typical flow:
|
|
40
|
+
1. lisa spec init
|
|
41
|
+
Create the .specs workspace for this repo.
|
|
42
|
+
2. lisa skill install --local
|
|
43
|
+
Optionally install repo-local Lisa guidance for supported harnesses.
|
|
44
|
+
3. lisa spec generate backend <name>
|
|
45
|
+
Write a new spec for planned work.
|
|
46
|
+
or lisa spec import <path...>
|
|
47
|
+
Draft specs from existing code.
|
|
48
|
+
4. lisa spec implement
|
|
49
|
+
Turn changed specs into code and test updates.
|
|
50
|
+
5. lisa spec check --changed
|
|
51
|
+
Verify mapped tests, benchmarks, and spec compliance.
|
|
52
|
+
` : ""}
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
lisa skill install --local
|
|
56
|
+
lisa skill install --global
|
|
57
|
+
lisa spec status
|
|
58
|
+
lisa spec harness list
|
|
59
|
+
lisa spec guide backend checkout-flow
|
|
60
|
+
`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function runCli(args: string[], cwd = process.cwd()): Promise<number> {
|
|
64
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
65
|
+
console.log(`lisa ${VERSION}`);
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h" || args[0] === "help") {
|
|
70
|
+
printLisaHelp();
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (args[0] === "spec") {
|
|
75
|
+
return runSpecCli(args.slice(1), cwd);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (args[0] === "skill") {
|
|
79
|
+
return runSkillCli(args.slice(1), cwd);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.error(`Unknown command: ${args[0]}`);
|
|
83
|
+
printLisaHelp();
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
|
|
4
|
+
import { installSkillArtifact, resolveLocalSkillWorkspace, resolveSkillArtifactPath } from "../skill/artifacts";
|
|
5
|
+
import { detectHarnessCommand } from "./command";
|
|
6
|
+
import type { HarnessAvailability, HarnessSkillInstallDefinition, SkillInstallScope } from "./types";
|
|
7
|
+
|
|
8
|
+
export interface BaseAdapterConfig {
|
|
9
|
+
harnessId: string;
|
|
10
|
+
commandName: string;
|
|
11
|
+
envVars: string[];
|
|
12
|
+
artifactName: string;
|
|
13
|
+
resolveGlobalRoot: () => string;
|
|
14
|
+
resolveGlobalDestination?: () => string;
|
|
15
|
+
resolveLocalDestination?: (cwd: string) => string;
|
|
16
|
+
renderContent?: (content: string) => string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createBaseAdapterHelpers(config: BaseAdapterConfig, cwd = process.cwd()) {
|
|
20
|
+
function getSkillInstallDefinition(scope: SkillInstallScope): HarnessSkillInstallDefinition {
|
|
21
|
+
return {
|
|
22
|
+
scope,
|
|
23
|
+
sourcePath: resolveSkillArtifactPath(config.harnessId, config.artifactName),
|
|
24
|
+
destinationPath: scope === "global"
|
|
25
|
+
? config.resolveGlobalDestination ? config.resolveGlobalDestination() : join(config.resolveGlobalRoot(), config.artifactName)
|
|
26
|
+
: config.resolveLocalDestination ? config.resolveLocalDestination(cwd) : join(resolveLocalSkillWorkspace(cwd), config.artifactName),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
async detect(): Promise<HarnessAvailability> {
|
|
32
|
+
return detectHarnessCommand(config.commandName, config.envVars);
|
|
33
|
+
},
|
|
34
|
+
async getSkillInstallDefinition(scope: SkillInstallScope): Promise<HarnessSkillInstallDefinition> {
|
|
35
|
+
return getSkillInstallDefinition(scope);
|
|
36
|
+
},
|
|
37
|
+
async installSkill(scope: SkillInstallScope) {
|
|
38
|
+
const definition = getSkillInstallDefinition(scope);
|
|
39
|
+
const rawContent = readFileSync(definition.sourcePath, "utf8");
|
|
40
|
+
const content = config.renderContent ? config.renderContent(rawContent) : undefined;
|
|
41
|
+
return installSkillArtifact(definition, content, {
|
|
42
|
+
rootPath: scope === "local" ? resolveLocalSkillWorkspace(cwd) : config.resolveGlobalRoot(),
|
|
43
|
+
scope,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { resolveHomeDirectory } from "../skill/artifacts";
|
|
3
|
+
import { createBaseAdapterHelpers } from "./base-adapter";
|
|
4
|
+
import { resolveHarnessCommand, runHarnessCommand } from "./command";
|
|
5
|
+
import type {
|
|
6
|
+
HarnessAdapter,
|
|
7
|
+
HarnessCapabilities,
|
|
8
|
+
HarnessRequest,
|
|
9
|
+
HarnessResult,
|
|
10
|
+
Stage,
|
|
11
|
+
} from "./types";
|
|
12
|
+
|
|
13
|
+
const SUPPORTED_STAGES: Stage[] = ["generate", "implement", "check", "import"];
|
|
14
|
+
const CLAUDE_ENV_VARS = ["LISA_CLAUDE_BINARY", "RALPH_CLAUDE_BINARY"];
|
|
15
|
+
|
|
16
|
+
function getClaudeCodeCapabilities(): HarnessCapabilities {
|
|
17
|
+
return {
|
|
18
|
+
canEditFiles: true,
|
|
19
|
+
canResumeSessions: false,
|
|
20
|
+
supportsStructuredOutput: true,
|
|
21
|
+
supportsStreaming: false,
|
|
22
|
+
supportsModelSelection: true,
|
|
23
|
+
supportsReadOnlyMode: true,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildClaudeCodeArgs(request: HarnessRequest): string[] {
|
|
28
|
+
const args = ["-p", request.prompt];
|
|
29
|
+
|
|
30
|
+
if (request.model) {
|
|
31
|
+
args.push("--model", request.model);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (request.outputSchema) {
|
|
35
|
+
args.push("--output-format", "json", "--json-schema", JSON.stringify(request.outputSchema));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (request.allowEdits) {
|
|
39
|
+
args.push("--permission-mode", "bypassPermissions", "--dangerously-skip-permissions");
|
|
40
|
+
} else {
|
|
41
|
+
args.push("--permission-mode", "plan");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (request.extraArgs?.length) {
|
|
45
|
+
args.push(...request.extraArgs);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return args;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createClaudeCodeHarnessAdapter(cwd = process.cwd()): HarnessAdapter {
|
|
52
|
+
const baseHelpers = createBaseAdapterHelpers(
|
|
53
|
+
{
|
|
54
|
+
harnessId: "claude-code",
|
|
55
|
+
commandName: "claude",
|
|
56
|
+
envVars: CLAUDE_ENV_VARS,
|
|
57
|
+
artifactName: "CLAUDE.md",
|
|
58
|
+
resolveGlobalRoot: () => join(resolveHomeDirectory(), ".claude"),
|
|
59
|
+
},
|
|
60
|
+
cwd
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
id: "claude-code",
|
|
65
|
+
displayName: "Claude Code",
|
|
66
|
+
description: "Direct adapter for the Claude Code CLI.",
|
|
67
|
+
supportedStages: SUPPORTED_STAGES,
|
|
68
|
+
...baseHelpers,
|
|
69
|
+
async capabilities(): Promise<HarnessCapabilities> {
|
|
70
|
+
return getClaudeCodeCapabilities();
|
|
71
|
+
},
|
|
72
|
+
async run(request: HarnessRequest): Promise<HarnessResult> {
|
|
73
|
+
const command = request.env?.LISA_HARNESS_COMMAND ?? resolveHarnessCommand("claude", CLAUDE_ENV_VARS);
|
|
74
|
+
if (!command) {
|
|
75
|
+
return {
|
|
76
|
+
status: "failed",
|
|
77
|
+
finalText: "Claude Code CLI not found.",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result = runHarnessCommand(command, buildClaudeCodeArgs(request), request);
|
|
82
|
+
if (!request.outputSchema || result.status !== "success" || !result.finalText?.trim()) {
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const stdout = typeof result.rawEvents?.[0] === "object" && result.rawEvents[0] !== null && "stdout" in result.rawEvents[0]
|
|
88
|
+
? (result.rawEvents[0] as { stdout?: unknown }).stdout
|
|
89
|
+
: undefined;
|
|
90
|
+
const rawJson = typeof stdout === "string" ? stdout.trim() : result.finalText;
|
|
91
|
+
return {
|
|
92
|
+
...result,
|
|
93
|
+
structuredOutput: JSON.parse(rawJson),
|
|
94
|
+
};
|
|
95
|
+
} catch (error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
const details = `Failed to parse structured output: ${message}`;
|
|
98
|
+
return {
|
|
99
|
+
...result,
|
|
100
|
+
status: "failed",
|
|
101
|
+
finalText: [result.finalText?.trim(), details].filter((entry): entry is string => Boolean(entry)).join("\n\n"),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { resolveCodexHome } from "../skill/artifacts";
|
|
2
|
+
import { createBaseAdapterHelpers } from "./base-adapter";
|
|
3
|
+
import { resolveHarnessCommand, runHarnessCommand } from "./command";
|
|
4
|
+
import type {
|
|
5
|
+
HarnessAdapter,
|
|
6
|
+
HarnessCapabilities,
|
|
7
|
+
HarnessRequest,
|
|
8
|
+
HarnessResult,
|
|
9
|
+
Stage,
|
|
10
|
+
} from "./types";
|
|
11
|
+
|
|
12
|
+
const SUPPORTED_STAGES: Stage[] = ["generate", "implement", "check", "import"];
|
|
13
|
+
const CODEX_ENV_VARS = ["LISA_CODEX_BINARY", "RALPH_CODEX_BINARY"];
|
|
14
|
+
|
|
15
|
+
function getCodexCapabilities(): HarnessCapabilities {
|
|
16
|
+
return {
|
|
17
|
+
canEditFiles: true,
|
|
18
|
+
canResumeSessions: false,
|
|
19
|
+
supportsStructuredOutput: false,
|
|
20
|
+
supportsStreaming: false,
|
|
21
|
+
supportsModelSelection: true,
|
|
22
|
+
supportsReadOnlyMode: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildCodexArgs(request: HarnessRequest): string[] {
|
|
27
|
+
const args = ["exec"];
|
|
28
|
+
|
|
29
|
+
if (request.model) {
|
|
30
|
+
args.push("--model", request.model);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (request.allowEdits) {
|
|
34
|
+
args.push("--approval-mode", "full-auto", "--sandbox", "workspace-write");
|
|
35
|
+
} else {
|
|
36
|
+
args.push("--sandbox", "read-only");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (request.extraArgs?.length) {
|
|
40
|
+
args.push(...request.extraArgs);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
args.push(request.prompt);
|
|
44
|
+
return args;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createCodexHarnessAdapter(cwd = process.cwd()): HarnessAdapter {
|
|
48
|
+
const baseHelpers = createBaseAdapterHelpers(
|
|
49
|
+
{
|
|
50
|
+
harnessId: "codex",
|
|
51
|
+
commandName: "codex",
|
|
52
|
+
envVars: CODEX_ENV_VARS,
|
|
53
|
+
artifactName: "AGENTS.md",
|
|
54
|
+
resolveGlobalRoot: () => resolveCodexHome(),
|
|
55
|
+
},
|
|
56
|
+
cwd
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
id: "codex",
|
|
61
|
+
displayName: "Codex",
|
|
62
|
+
description: "Direct adapter for the OpenAI Codex CLI.",
|
|
63
|
+
supportedStages: SUPPORTED_STAGES,
|
|
64
|
+
...baseHelpers,
|
|
65
|
+
async capabilities(): Promise<HarnessCapabilities> {
|
|
66
|
+
return getCodexCapabilities();
|
|
67
|
+
},
|
|
68
|
+
async run(request: HarnessRequest): Promise<HarnessResult> {
|
|
69
|
+
const command = request.env?.LISA_HARNESS_COMMAND ?? resolveHarnessCommand("codex", CODEX_ENV_VARS);
|
|
70
|
+
if (!command) {
|
|
71
|
+
return {
|
|
72
|
+
status: "failed",
|
|
73
|
+
finalText: "Codex CLI not found.",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return runHarnessCommand(command, buildCodexArgs(request), request);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|