@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,190 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
+ import { dirname, join } from "path";
3
+
4
+ import { getHarnessAdapterById } from "../../harness/registry";
5
+ import { assertSafeRepoWritePath, ensureGuidanceSkillArtifact, renderSeedSpec, resolveGuidanceHarnessId } from "../agent-guidance";
6
+ import { loadSpecWorkspace } from "../loader";
7
+ import type { SpecArea } from "../types";
8
+ import { SPEC_AREAS } from "../types";
9
+ import { assertSafeLisaStorageWritePath, resolveSpecWriteRoot, resolveWorkspaceLayout, toLogicalPath, type SpecWriteScope } from "../workspace";
10
+
11
+ interface GuideCommandOptions {
12
+ area?: SpecArea;
13
+ name?: string;
14
+ harness?: string;
15
+ scope?: SpecWriteScope;
16
+ help: boolean;
17
+ }
18
+
19
+ export interface GuideCommandIO {
20
+ print(message: string): void;
21
+ error(message: string): void;
22
+ }
23
+
24
+ function createConsoleIO(): GuideCommandIO {
25
+ return {
26
+ print(message: string): void {
27
+ console.log(message);
28
+ },
29
+ error(message: string): void {
30
+ console.error(message);
31
+ },
32
+ };
33
+ }
34
+
35
+ function printGuideHelp(io: GuideCommandIO): void {
36
+ io.print(`Lisa spec guide
37
+
38
+ Usage:
39
+ lisa spec guide [backend|frontend] [name] [options]
40
+
41
+ Options:
42
+ --harness <id> Select the coding harness whose Lisa skill you want to use
43
+ --scope <name> Write to the shared repo layer or worktree override layer
44
+ --help, -h Show this help
45
+ `);
46
+ }
47
+
48
+ function normalizeSpecName(inputName: string): string {
49
+ return inputName
50
+ .trim()
51
+ .replace(/\.md$/i, "")
52
+ .toLowerCase()
53
+ .replace(/[^a-z0-9]+/g, "-")
54
+ .replace(/^-+|-+$/g, "");
55
+ }
56
+
57
+ function parseGuideArgs(args: string[]): GuideCommandOptions {
58
+ const positionals: string[] = [];
59
+ const options: GuideCommandOptions = { help: false };
60
+
61
+ for (let index = 0; index < args.length; index += 1) {
62
+ const arg = args[index];
63
+ if (!arg) {
64
+ continue;
65
+ }
66
+
67
+ if (arg === "--help" || arg === "-h") {
68
+ options.help = true;
69
+ continue;
70
+ }
71
+
72
+ if (arg === "--harness") {
73
+ const value = args[index + 1];
74
+ if (!value) {
75
+ throw new Error("--harness requires a value.");
76
+ }
77
+ options.harness = value;
78
+ index += 1;
79
+ continue;
80
+ }
81
+
82
+ if (arg === "--scope") {
83
+ const value = args[index + 1];
84
+ if (value !== "repo" && value !== "worktree") {
85
+ throw new Error("--scope requires `repo` or `worktree`.");
86
+ }
87
+ options.scope = value;
88
+ index += 1;
89
+ continue;
90
+ }
91
+
92
+ if (arg.startsWith("--")) {
93
+ throw new Error(`Unknown lisa spec guide option: ${arg}`);
94
+ }
95
+
96
+ positionals.push(arg);
97
+ }
98
+
99
+ if (positionals[0] && SPEC_AREAS.includes(positionals[0] as SpecArea)) {
100
+ options.area = positionals[0] as SpecArea;
101
+ } else if (positionals[0]) {
102
+ throw new Error(`Expected first positional argument to be one of: ${SPEC_AREAS.join(", ")}.`);
103
+ }
104
+
105
+ if (positionals[1]) {
106
+ options.name = positionals[1];
107
+ }
108
+
109
+ if (!options.help && (!options.area || !options.name)) {
110
+ throw new Error("lisa spec guide requires [backend|frontend] and [name].");
111
+ }
112
+
113
+ if (positionals.length > 2) {
114
+ throw new Error("lisa spec guide accepts at most two positional arguments: [backend|frontend] [name].");
115
+ }
116
+
117
+ return options;
118
+ }
119
+
120
+ export async function runSpecGuideCommand(
121
+ args: string[],
122
+ cwd = process.cwd(),
123
+ io: GuideCommandIO = createConsoleIO(),
124
+ ): Promise<number> {
125
+ try {
126
+ const options = parseGuideArgs(args);
127
+ if (options.help) {
128
+ printGuideHelp(io);
129
+ return 0;
130
+ }
131
+
132
+ const layout = resolveWorkspaceLayout(cwd);
133
+ const workspaceRoot = layout.workspacePath;
134
+ const workspace = loadSpecWorkspace(workspaceRoot);
135
+ const area = options.area as SpecArea;
136
+ const name = normalizeSpecName(options.name as string);
137
+ if (!name) {
138
+ throw new Error("Spec name must contain at least one alphanumeric character.");
139
+ }
140
+
141
+ const harnessId = resolveGuidanceHarnessId(workspace.config, options.harness);
142
+ const harness = getHarnessAdapterById(harnessId, workspaceRoot);
143
+ if (!harness) {
144
+ throw new Error(`No harness adapter is registered for \`${harnessId}\`.`);
145
+ }
146
+
147
+ const skillArtifact = ensureGuidanceSkillArtifact(workspaceRoot, harnessId);
148
+
149
+ const targetPath = join(resolveSpecWriteRoot(workspaceRoot, options.scope ?? "repo"), area, `${name}.md`);
150
+ const relativeTargetPath = toLogicalPath(layout, targetPath);
151
+ const existingDocument = workspace.documents.find((document) => toLogicalPath(layout, document.path) === relativeTargetPath);
152
+ const existed = existsSync(targetPath);
153
+ let status = existed ? "reused existing spec" : "created draft seed spec";
154
+ if (!existed) {
155
+ if (layout.storageMode === "repo") {
156
+ assertSafeRepoWritePath(workspaceRoot, targetPath);
157
+ } else {
158
+ assertSafeLisaStorageWritePath(resolveSpecWriteRoot(workspaceRoot, options.scope ?? "repo"), targetPath, "Lisa spec guide");
159
+ }
160
+ mkdirSync(dirname(targetPath), { recursive: true });
161
+ if (layout.storageMode === "external" && options.scope === "worktree" && existingDocument) {
162
+ writeFileSync(targetPath, readFileSync(existingDocument.path, "utf8"));
163
+ status = "created worktree override from existing spec";
164
+ } else {
165
+ writeFileSync(targetPath, renderSeedSpec(area, name));
166
+ }
167
+ }
168
+
169
+ io.print("Lisa spec guide");
170
+ io.print("");
171
+ io.print(`Target spec: ${relativeTargetPath}`);
172
+ io.print(`Status: ${status}`);
173
+ io.print(`Harness skill: ${skillArtifact}`);
174
+ if (layout.storageMode === "external") {
175
+ io.print(`Storage layer: ${options.scope === "worktree" ? "worktree" : "repo"}`);
176
+ }
177
+ io.print("");
178
+ io.print("Next steps");
179
+ io.print(`- Open ${relativeTargetPath} in your ${harness.displayName} session with the guidance from ${skillArtifact}.`);
180
+ io.print("- Replace the placeholder code_paths, test_paths or test_commands, Summary, Use Cases, Invariants, Failure Modes, Acceptance Criteria, and Out of Scope content before implementation.");
181
+ if (!workspace.config) {
182
+ io.print("- Run `lisa spec init` before diff, implement, or check so the repository has a `.specs/config.yaml` stage profile setup.");
183
+ }
184
+ io.print("- When the spec is ready, run `lisa spec diff`, `lisa spec implement`, and `lisa spec check --changed` from this repository.");
185
+ return 0;
186
+ } catch (error) {
187
+ io.error(error instanceof Error ? error.message : String(error));
188
+ return 1;
189
+ }
190
+ }