@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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +407 -0
  3. package/bin/lisa-runtime.js +8797 -0
  4. package/bin/lisa.js +21 -0
  5. package/completion.ts +58 -0
  6. package/install.ps1 +51 -0
  7. package/install.sh +93 -0
  8. package/lisa.ts +6 -0
  9. package/package.json +66 -0
  10. package/skills/README.md +28 -0
  11. package/skills/claude-code/CLAUDE.md +151 -0
  12. package/skills/codex/AGENTS.md +151 -0
  13. package/skills/gemini/GEMINI.md +151 -0
  14. package/skills/opencode/AGENTS.md +152 -0
  15. package/src/cli.ts +85 -0
  16. package/src/harness/base-adapter.ts +47 -0
  17. package/src/harness/claude-code.ts +106 -0
  18. package/src/harness/codex.ts +80 -0
  19. package/src/harness/command.ts +173 -0
  20. package/src/harness/gemini.ts +74 -0
  21. package/src/harness/opencode.ts +84 -0
  22. package/src/harness/registry.ts +29 -0
  23. package/src/harness/runner.ts +19 -0
  24. package/src/harness/types.ts +73 -0
  25. package/src/output-mode.ts +32 -0
  26. package/src/skill/artifacts.ts +174 -0
  27. package/src/skill/cli.ts +29 -0
  28. package/src/skill/install.ts +317 -0
  29. package/src/spec/agent-guidance.ts +466 -0
  30. package/src/spec/cli.ts +151 -0
  31. package/src/spec/commands/check.ts +1 -0
  32. package/src/spec/commands/config.ts +146 -0
  33. package/src/spec/commands/diff.ts +1 -0
  34. package/src/spec/commands/generate.ts +1 -0
  35. package/src/spec/commands/guide.ts +1 -0
  36. package/src/spec/commands/harness-list.ts +36 -0
  37. package/src/spec/commands/implement.ts +1 -0
  38. package/src/spec/commands/import.ts +1 -0
  39. package/src/spec/commands/init.ts +1 -0
  40. package/src/spec/commands/status.ts +87 -0
  41. package/src/spec/config.ts +63 -0
  42. package/src/spec/diff.ts +791 -0
  43. package/src/spec/extensions/benchmark.ts +347 -0
  44. package/src/spec/extensions/registry.ts +59 -0
  45. package/src/spec/extensions/types.ts +56 -0
  46. package/src/spec/grammar/index.ts +14 -0
  47. package/src/spec/grammar/parser.ts +443 -0
  48. package/src/spec/grammar/types.ts +70 -0
  49. package/src/spec/grammar/validator.ts +104 -0
  50. package/src/spec/loader.ts +174 -0
  51. package/src/spec/local-config.ts +59 -0
  52. package/src/spec/parser.ts +226 -0
  53. package/src/spec/path-utils.ts +73 -0
  54. package/src/spec/planner.ts +299 -0
  55. package/src/spec/prompt-renderer.ts +318 -0
  56. package/src/spec/skill-content.ts +119 -0
  57. package/src/spec/types.ts +239 -0
  58. package/src/spec/validator.ts +443 -0
  59. package/src/spec/workflows/check.ts +1534 -0
  60. package/src/spec/workflows/diff.ts +209 -0
  61. package/src/spec/workflows/generate.ts +1270 -0
  62. package/src/spec/workflows/guide.ts +190 -0
  63. package/src/spec/workflows/implement.ts +797 -0
  64. package/src/spec/workflows/import.ts +986 -0
  65. package/src/spec/workflows/init.ts +548 -0
  66. package/src/spec/workflows/status.ts +22 -0
  67. package/src/spec/workspace.ts +541 -0
  68. package/uninstall.ps1 +21 -0
  69. package/uninstall.sh +22 -0
@@ -0,0 +1,146 @@
1
+ import { join } from "path";
2
+
3
+ import { loadLocalConfig, localConfigPath, resetLocalConfig, saveLocalConfig } from "../local-config";
4
+ import { resolveWorkspaceLayout } from "../workspace";
5
+
6
+ interface ConfigCommandOptions {
7
+ harness?: string;
8
+ reset: boolean;
9
+ help: boolean;
10
+ }
11
+
12
+ export interface ConfigCommandIO {
13
+ print(message: string): void;
14
+ error(message: string): void;
15
+ }
16
+
17
+ function createConsoleIO(): ConfigCommandIO {
18
+ return {
19
+ print(message: string): void {
20
+ console.log(message);
21
+ },
22
+ error(message: string): void {
23
+ console.error(message);
24
+ },
25
+ };
26
+ }
27
+
28
+ function printConfigHelp(io: ConfigCommandIO): void {
29
+ io.print(`Lisa spec config
30
+
31
+ Usage:
32
+ lisa spec config [options]
33
+
34
+ Options:
35
+ --harness <id> Set the local harness override
36
+ --reset Remove all local config overrides
37
+ --help, -h Show this help
38
+
39
+ With no options, prints the current local configuration.
40
+
41
+ Local config is saved to .specs/config.local.yaml (gitignored) and
42
+ overrides the repo config for all stages. CLI flags and the LISA_HARNESS
43
+ environment variable take precedence over local config. External
44
+ workspaces store the local override in the Lisa-managed runtime root
45
+ instead of the repository.
46
+ `);
47
+ }
48
+
49
+ function parseConfigArgs(args: string[]): ConfigCommandOptions {
50
+ const options: ConfigCommandOptions = {
51
+ reset: false,
52
+ help: false,
53
+ };
54
+
55
+ for (let index = 0; index < args.length; index += 1) {
56
+ const arg = args[index];
57
+ if (!arg) {
58
+ continue;
59
+ }
60
+
61
+ if (arg === "--help" || arg === "-h") {
62
+ options.help = true;
63
+ continue;
64
+ }
65
+
66
+ if (arg === "--reset") {
67
+ options.reset = true;
68
+ continue;
69
+ }
70
+
71
+ if (arg === "--harness") {
72
+ const value = args[index + 1];
73
+ if (!value) {
74
+ throw new Error("--harness requires a value.");
75
+ }
76
+ index += 1;
77
+ options.harness = value;
78
+ continue;
79
+ }
80
+
81
+ throw new Error(`Unknown lisa spec config option: ${arg}`);
82
+ }
83
+
84
+ return options;
85
+ }
86
+
87
+ export async function runSpecConfigCommand(
88
+ args: string[],
89
+ cwd = process.cwd(),
90
+ io: ConfigCommandIO = createConsoleIO(),
91
+ ): Promise<number> {
92
+ try {
93
+ const options = parseConfigArgs(args);
94
+ if (options.help) {
95
+ printConfigHelp(io);
96
+ return 0;
97
+ }
98
+
99
+ const layout = resolveWorkspaceLayout(cwd);
100
+ const configRoot = layout.localConfigRoot;
101
+ const configFile = localConfigPath(configRoot);
102
+
103
+ if (options.reset) {
104
+ const removed = resetLocalConfig(configRoot);
105
+ io.print(removed ? `Removed ${configFile}` : "No local config to remove.");
106
+ return 0;
107
+ }
108
+
109
+ if (options.harness !== undefined) {
110
+ saveLocalConfig(configRoot, { harness: options.harness });
111
+
112
+ io.print("Lisa spec config");
113
+ io.print("");
114
+ io.print(`Wrote ${configFile}`);
115
+ io.print(` harness: ${options.harness}`);
116
+ return 0;
117
+ }
118
+
119
+ // No flags — show current local config
120
+ const current = loadLocalConfig(configRoot);
121
+
122
+ io.print("Lisa spec config");
123
+ io.print("");
124
+ if (!current.harness) {
125
+ io.print("No local config overrides set.");
126
+ io.print("");
127
+ io.print("Set overrides with:");
128
+ io.print(" lisa spec config --harness claude-code");
129
+ } else {
130
+ io.print(`Local config: ${configFile}`);
131
+ io.print(` harness: ${current.harness}`);
132
+ }
133
+
134
+ const envHarness = process.env.LISA_HARNESS;
135
+ if (envHarness) {
136
+ io.print("");
137
+ io.print("Environment override (takes precedence over local config):");
138
+ io.print(` LISA_HARNESS=${envHarness}`);
139
+ }
140
+
141
+ return 0;
142
+ } catch (error) {
143
+ io.error(error instanceof Error ? error.message : String(error));
144
+ return 1;
145
+ }
146
+ }
@@ -0,0 +1 @@
1
+ export { runSpecDiffCommand } from "../workflows/diff";
@@ -0,0 +1 @@
1
+ export { runSpecGenerateCommand } from "../workflows/generate";
@@ -0,0 +1 @@
1
+ export { runSpecGuideCommand } from "../workflows/guide";
@@ -0,0 +1,36 @@
1
+ import { inspectHarnesses } from "../../harness/registry";
2
+ import type { HarnessCapabilities } from "../../harness/types";
3
+
4
+ function formatCapabilities(capabilities: HarnessCapabilities): string {
5
+ const labels: string[] = [];
6
+
7
+ if (capabilities.canEditFiles) labels.push("edit-files");
8
+ if (capabilities.canResumeSessions) labels.push("resume-sessions");
9
+ if (capabilities.supportsStructuredOutput) labels.push("structured-output");
10
+ if (capabilities.supportsStreaming) labels.push("streaming");
11
+ if (capabilities.supportsModelSelection) labels.push("model-selection");
12
+ if (capabilities.supportsReadOnlyMode) labels.push("read-only");
13
+
14
+ return labels.length > 0 ? labels.join(", ") : "none";
15
+ }
16
+
17
+ export async function runSpecHarnessListCommand(cwd = process.cwd()): Promise<number> {
18
+ const harnesses = await inspectHarnesses(cwd);
19
+
20
+ console.log("Lisa Spec Harnesses");
21
+ console.log("");
22
+
23
+ for (const inspection of harnesses) {
24
+ const { adapter, availability, capabilities } = inspection;
25
+ console.log(`${adapter.id} - ${adapter.displayName}`);
26
+ console.log(` status: ${availability.available ? "available" : "unavailable"}`);
27
+ console.log(` reason: ${availability.reason}`);
28
+ console.log(` command: ${availability.command ?? "n/a"}`);
29
+ console.log(` stages: ${adapter.supportedStages.join(", ")}`);
30
+ console.log(` capabilities: ${formatCapabilities(capabilities)}`);
31
+ console.log(` description: ${adapter.description}`);
32
+ console.log("");
33
+ }
34
+
35
+ return 0;
36
+ }
@@ -0,0 +1 @@
1
+ export { runSpecImplementCommand } from "../workflows/implement";
@@ -0,0 +1 @@
1
+ export { runSpecImportCommand } from "../workflows/import";
@@ -0,0 +1 @@
1
+ export { runSpecInitCommand } from "../workflows/init";
@@ -0,0 +1,87 @@
1
+ import { runSpecStatusWorkflow } from "../workflows/status";
2
+
3
+ function printStatusHelp(): void {
4
+ console.log(`Lisa spec status
5
+
6
+ Usage:
7
+ lisa spec status
8
+ `);
9
+ }
10
+
11
+ function parseStatusArgs(args: string[]): { help: boolean } {
12
+ const options = { help: false };
13
+
14
+ for (const arg of args) {
15
+ if (!arg) {
16
+ continue;
17
+ }
18
+
19
+ if (arg === "--help" || arg === "-h") {
20
+ options.help = true;
21
+ continue;
22
+ }
23
+
24
+ throw new Error(`Unknown lisa spec status option: ${arg}`);
25
+ }
26
+
27
+ return options;
28
+ }
29
+
30
+ export async function runSpecStatusCommand(args: string[] = [], cwd = process.cwd()): Promise<number> {
31
+ try {
32
+ const options = parseStatusArgs(args);
33
+ if (options.help) {
34
+ printStatusHelp();
35
+ return 0;
36
+ }
37
+
38
+ const { workspace, harnesses, availableHarnesses } = await runSpecStatusWorkflow(cwd);
39
+
40
+ console.log("Lisa Spec Status");
41
+ console.log("");
42
+ console.log(`Workspace: ${workspace.workspacePath}`);
43
+ console.log(`Storage mode: ${workspace.storageMode}`);
44
+ console.log(`Spec root: ${workspace.exists ? `present (${workspace.rootPath})` : `missing (${workspace.rootPath})`}`);
45
+ console.log(`Spec config: ${workspace.hasConfig ? `present (${workspace.configPath})` : `missing (${workspace.configPath})`}`);
46
+ console.log(`Base specs: ${workspace.backendSpecCount + workspace.frontendSpecCount} (${workspace.backendSpecCount} backend, ${workspace.frontendSpecCount} frontend)`);
47
+ console.log(`Benchmark sidecars: ${workspace.benchmarkSpecCount}`);
48
+ console.log(`Environment configs: ${workspace.environmentCount}`);
49
+ console.log("Resolved roots:");
50
+ console.log(`- repo spec root: ${workspace.repoSpecRoot}`);
51
+ if (workspace.worktreeSpecRoot) {
52
+ console.log(`- worktree spec root: ${workspace.worktreeSpecRoot}`);
53
+ }
54
+ if (workspace.globalPackRoots.length > 0) {
55
+ console.log(`- global pack roots: ${workspace.globalPackRoots.join(", ")}`);
56
+ } else {
57
+ console.log("- global pack roots: none");
58
+ }
59
+ console.log(`- runtime root: ${workspace.runtimeRoot}`);
60
+ console.log(`- snapshot root: ${workspace.snapshotRoot}`);
61
+ console.log("");
62
+
63
+ if (!workspace.exists) {
64
+ console.log(workspace.storageMode === "external"
65
+ ? "Run `lisa spec init --external` to scaffold the external Lisa workspace roots for this repository."
66
+ : "Run `lisa spec init` to scaffold `.specs/config.yaml` and starter directories.");
67
+ console.log("");
68
+ }
69
+
70
+ console.log(`Harness adapters: ${availableHarnesses.length}/${harnesses.length} available`);
71
+
72
+ for (const inspection of harnesses) {
73
+ const status = inspection.availability.available ? "available" : "unavailable";
74
+ console.log(`- ${inspection.adapter.id}: ${status} - ${inspection.availability.reason}`);
75
+ }
76
+
77
+ if (harnesses.length > 0) {
78
+ console.log("");
79
+ console.log("Run `lisa spec harness list` for capability details.");
80
+ }
81
+
82
+ return 0;
83
+ } catch (error) {
84
+ console.error(error instanceof Error ? error.message : String(error));
85
+ return 1;
86
+ }
87
+ }
@@ -0,0 +1,63 @@
1
+ import type { Stage } from "../harness/types";
2
+ import type { ParsedSpecConfig, ResolvedStageProfile, StageProfileOverrides } from "./types";
3
+
4
+ const LEGACY_HARNESS_ALIASES: Record<string, string> = {
5
+ ralph: "opencode",
6
+ };
7
+
8
+ export function resolveConfiguredHarnessId(config: ParsedSpecConfig, harness: string): string {
9
+ if (config.harnesses[harness]) {
10
+ return harness;
11
+ }
12
+
13
+ const alias = LEGACY_HARNESS_ALIASES[harness];
14
+ return alias && config.harnesses[alias] ? alias : harness;
15
+ }
16
+
17
+ function hasValidHarnessArgs(args: unknown): boolean {
18
+ return args === undefined || (Array.isArray(args) && args.every((entry) => typeof entry === "string"));
19
+ }
20
+
21
+ export interface LocalOverrides {
22
+ harness?: string;
23
+ }
24
+
25
+ export function resolveStageProfile(
26
+ config: ParsedSpecConfig,
27
+ stage: Stage,
28
+ overrides: StageProfileOverrides = {},
29
+ localOverrides: LocalOverrides = {},
30
+ ): ResolvedStageProfile | undefined {
31
+ const profileName = overrides.profile ?? config.defaultStageProfiles[stage];
32
+ if (!profileName) {
33
+ return undefined;
34
+ }
35
+
36
+ const profile = config.profiles[profileName];
37
+ if (!profile?.harness) {
38
+ return undefined;
39
+ }
40
+
41
+ const selectedHarness = overrides.harness ?? localOverrides.harness ?? profile.harness;
42
+ if (!selectedHarness) {
43
+ return undefined;
44
+ }
45
+
46
+ const harness = resolveConfiguredHarnessId(config, selectedHarness);
47
+
48
+ const harnessConfig = config.harnesses[harness];
49
+ const hasValidArgs = harnessConfig && hasValidHarnessArgs(harnessConfig.raw.args);
50
+ if (!harnessConfig?.command || !hasValidArgs) {
51
+ return undefined;
52
+ }
53
+
54
+ return {
55
+ stage,
56
+ profileName,
57
+ harness,
58
+ model: overrides.model ?? profile.model,
59
+ allowEdits: profile.allowEdits ?? false,
60
+ command: harnessConfig.command,
61
+ args: harnessConfig.args,
62
+ };
63
+ }