solveos-cli 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 +194 -0
- package/agents/solveos-build-validator.md +183 -0
- package/agents/solveos-debugger.md +226 -0
- package/agents/solveos-executor.md +187 -0
- package/agents/solveos-plan-validator.md +200 -0
- package/agents/solveos-planner.md +190 -0
- package/agents/solveos-researcher.md +152 -0
- package/agents/solveos-reviewer.md +263 -0
- package/commands/solveos/archive.md +106 -0
- package/commands/solveos/build.md +170 -0
- package/commands/solveos/fast.md +85 -0
- package/commands/solveos/new-cycle.md +165 -0
- package/commands/solveos/new.md +142 -0
- package/commands/solveos/next.md +86 -0
- package/commands/solveos/plan.md +139 -0
- package/commands/solveos/quick.md +109 -0
- package/commands/solveos/research.md +117 -0
- package/commands/solveos/review.md +198 -0
- package/commands/solveos/ship.md +129 -0
- package/commands/solveos/status.md +78 -0
- package/commands/solveos/validate-build.md +155 -0
- package/commands/solveos/validate-plan.md +115 -0
- package/dist/bin/install.d.ts +11 -0
- package/dist/bin/install.d.ts.map +1 -0
- package/dist/bin/install.js +158 -0
- package/dist/bin/install.js.map +1 -0
- package/dist/hooks/brief-anchor.d.ts +68 -0
- package/dist/hooks/brief-anchor.d.ts.map +1 -0
- package/dist/hooks/brief-anchor.js +236 -0
- package/dist/hooks/brief-anchor.js.map +1 -0
- package/dist/hooks/context-monitor.d.ts +70 -0
- package/dist/hooks/context-monitor.d.ts.map +1 -0
- package/dist/hooks/context-monitor.js +166 -0
- package/dist/hooks/context-monitor.js.map +1 -0
- package/dist/lib/artifacts.d.ts +63 -0
- package/dist/lib/artifacts.d.ts.map +1 -0
- package/dist/lib/artifacts.js +382 -0
- package/dist/lib/artifacts.js.map +1 -0
- package/dist/lib/config.d.ts +10 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +29 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/runtime-adapters/claude-code.d.ts +18 -0
- package/dist/lib/runtime-adapters/claude-code.d.ts.map +1 -0
- package/dist/lib/runtime-adapters/claude-code.js +125 -0
- package/dist/lib/runtime-adapters/claude-code.js.map +1 -0
- package/dist/lib/runtime-adapters/cursor.d.ts +18 -0
- package/dist/lib/runtime-adapters/cursor.d.ts.map +1 -0
- package/dist/lib/runtime-adapters/cursor.js +113 -0
- package/dist/lib/runtime-adapters/cursor.js.map +1 -0
- package/dist/lib/runtime-adapters/gemini-cli.d.ts +18 -0
- package/dist/lib/runtime-adapters/gemini-cli.d.ts.map +1 -0
- package/dist/lib/runtime-adapters/gemini-cli.js +127 -0
- package/dist/lib/runtime-adapters/gemini-cli.js.map +1 -0
- package/dist/lib/runtime-adapters/opencode.d.ts +14 -0
- package/dist/lib/runtime-adapters/opencode.d.ts.map +1 -0
- package/dist/lib/runtime-adapters/opencode.js +109 -0
- package/dist/lib/runtime-adapters/opencode.js.map +1 -0
- package/dist/lib/runtime-detect.d.ts +22 -0
- package/dist/lib/runtime-detect.d.ts.map +1 -0
- package/dist/lib/runtime-detect.js +73 -0
- package/dist/lib/runtime-detect.js.map +1 -0
- package/dist/lib/security.d.ts +88 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +230 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/types.d.ts +224 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +31 -0
- package/dist/types.js.map +1 -0
- package/dist/workflows/state-machine.d.ts +55 -0
- package/dist/workflows/state-machine.d.ts.map +1 -0
- package/dist/workflows/state-machine.js +271 -0
- package/dist/workflows/state-machine.js.map +1 -0
- package/dist/workflows/wave-executor.d.ts +112 -0
- package/dist/workflows/wave-executor.d.ts.map +1 -0
- package/dist/workflows/wave-executor.js +496 -0
- package/dist/workflows/wave-executor.js.map +1 -0
- package/package.json +58 -0
- package/templates/build-validation.md +82 -0
- package/templates/config-default.json +21 -0
- package/templates/plan-brief.md +106 -0
- package/templates/plan-validation-log.md +77 -0
- package/templates/post-ship-review.md +75 -0
- package/templates/pre-ship-review.md +56 -0
- package/templates/research-summary.md +30 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor runtime adapter for solveos-cli.
|
|
3
|
+
*
|
|
4
|
+
* Handles detection and installation of commands, agents, and hooks
|
|
5
|
+
* into Cursor's directory structure.
|
|
6
|
+
*
|
|
7
|
+
* Cursor conventions:
|
|
8
|
+
* - Skills (commands): .cursor/skills/<name>/SKILL.md -> /name
|
|
9
|
+
* - Agents: .cursor/agents/<name>.md -> auto-delegation
|
|
10
|
+
* - Hooks: .cursor/hooks.json -> event-based config
|
|
11
|
+
* - Rules: .cursor/rules/<name>.mdc -> contextual instructions
|
|
12
|
+
*
|
|
13
|
+
* Cursor also reads from .claude/ and .codex/ for cross-tool compatibility,
|
|
14
|
+
* but we install to .cursor/ for clarity and priority.
|
|
15
|
+
*/
|
|
16
|
+
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
17
|
+
import { join, basename } from "node:path";
|
|
18
|
+
import { detectRuntime } from "../runtime-detect.js";
|
|
19
|
+
/**
|
|
20
|
+
* Transform OpenCode-style command frontmatter to Cursor skill frontmatter.
|
|
21
|
+
*
|
|
22
|
+
* Cursor uses the same SKILL.md format as Claude Code skills.
|
|
23
|
+
* We set disable-model-invocation: true so skills are explicit-only.
|
|
24
|
+
*/
|
|
25
|
+
function transformCommandToSkill(content, commandName) {
|
|
26
|
+
const skillName = `solveos-${commandName}`;
|
|
27
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
28
|
+
if (!frontmatterMatch) {
|
|
29
|
+
return `---\nname: ${skillName}\ndescription: solveOS ${commandName} command\ndisable-model-invocation: true\n---\n\n${content}`;
|
|
30
|
+
}
|
|
31
|
+
const frontmatter = frontmatterMatch[1];
|
|
32
|
+
const body = content.slice(frontmatterMatch[0].length);
|
|
33
|
+
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
34
|
+
const description = descMatch ? descMatch[1].trim() : `solveOS ${commandName} command`;
|
|
35
|
+
return `---\nname: ${skillName}\ndescription: ${description}\ndisable-model-invocation: true\n---\n\n${body}`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Transform OpenCode-style agent frontmatter to Cursor agent frontmatter.
|
|
39
|
+
*
|
|
40
|
+
* Cursor agents use: name, description, model, readonly, is_background
|
|
41
|
+
*/
|
|
42
|
+
function transformAgentForCursor(content, agentName) {
|
|
43
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
44
|
+
if (!frontmatterMatch) {
|
|
45
|
+
return `---\nname: ${agentName}\ndescription: solveOS ${agentName} agent\n---\n\n${content}`;
|
|
46
|
+
}
|
|
47
|
+
const frontmatter = frontmatterMatch[1];
|
|
48
|
+
const body = content.slice(frontmatterMatch[0].length);
|
|
49
|
+
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
50
|
+
const description = descMatch ? descMatch[1].trim() : `solveOS ${agentName} agent`;
|
|
51
|
+
return `---\nname: ${agentName}\ndescription: ${description}\n---\n\n${body}`;
|
|
52
|
+
}
|
|
53
|
+
export const cursor = {
|
|
54
|
+
async detect() {
|
|
55
|
+
const cwd = process.cwd();
|
|
56
|
+
const result = await detectRuntime(cwd);
|
|
57
|
+
if (result && result.runtime === "cursor") {
|
|
58
|
+
return {
|
|
59
|
+
detected: true,
|
|
60
|
+
name: "Cursor",
|
|
61
|
+
configPath: result.configPath,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
detected: false,
|
|
66
|
+
name: "Cursor",
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
async installCommands(sourceDir) {
|
|
70
|
+
const cwd = process.cwd();
|
|
71
|
+
const skillsBase = join(cwd, ".cursor", "skills");
|
|
72
|
+
const files = await readdir(sourceDir);
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
if (!file.endsWith(".md"))
|
|
75
|
+
continue;
|
|
76
|
+
const commandName = basename(file, ".md");
|
|
77
|
+
const sourcePath = join(sourceDir, file);
|
|
78
|
+
// Cursor skills live in directories: .cursor/skills/solveos-{name}/SKILL.md
|
|
79
|
+
const skillDir = join(skillsBase, `solveos-${commandName}`);
|
|
80
|
+
await mkdir(skillDir, { recursive: true });
|
|
81
|
+
const content = await readFile(sourcePath, "utf-8");
|
|
82
|
+
const transformed = transformCommandToSkill(content, commandName);
|
|
83
|
+
await writeFile(join(skillDir, "SKILL.md"), transformed, "utf-8");
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
async installAgents(sourceDir) {
|
|
87
|
+
const cwd = process.cwd();
|
|
88
|
+
const targetDir = join(cwd, ".cursor", "agents");
|
|
89
|
+
await mkdir(targetDir, { recursive: true });
|
|
90
|
+
const files = await readdir(sourceDir);
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
if (!file.endsWith(".md"))
|
|
93
|
+
continue;
|
|
94
|
+
const agentName = basename(file, ".md");
|
|
95
|
+
const sourcePath = join(sourceDir, file);
|
|
96
|
+
const content = await readFile(sourcePath, "utf-8");
|
|
97
|
+
const transformed = transformAgentForCursor(content, agentName);
|
|
98
|
+
await writeFile(join(targetDir, file), transformed, "utf-8");
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
async installHooks(_sourceDir) {
|
|
102
|
+
// Cursor hooks are configured in .cursor/hooks.json with event-based
|
|
103
|
+
// handlers (shell commands). Similar to Claude Code, the mechanism is
|
|
104
|
+
// different from OpenCode's TypeScript plugins.
|
|
105
|
+
//
|
|
106
|
+
// The solveOS state files (.solveos/STATE.md, BRIEF.md) remain readable
|
|
107
|
+
// by any hook script the user configures.
|
|
108
|
+
//
|
|
109
|
+
// Future: generate .cursor/hooks.json with shell-based context monitor
|
|
110
|
+
// and brief anchor hooks.
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../src/lib/runtime-adapters/cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI runtime adapter for solveos-cli.
|
|
3
|
+
*
|
|
4
|
+
* Handles detection and installation of commands, agents, and hooks
|
|
5
|
+
* into Gemini CLI's directory structure.
|
|
6
|
+
*
|
|
7
|
+
* Gemini CLI conventions:
|
|
8
|
+
* - Commands: .gemini/commands/<name>.toml -> /<name>
|
|
9
|
+
* Subdirectories create namespaced commands: .gemini/commands/solveos/<name>.toml -> /solveos:<name>
|
|
10
|
+
* - Agents: .gemini/agents/<name>.md -> @name / auto-delegation
|
|
11
|
+
* - Hooks: .gemini/settings.json -> hooks object
|
|
12
|
+
*
|
|
13
|
+
* Key difference: Gemini CLI uses TOML for commands (not Markdown).
|
|
14
|
+
* We wrap our markdown prompt content in TOML's `prompt` field.
|
|
15
|
+
*/
|
|
16
|
+
import type { RuntimeAdapter } from "../../types.js";
|
|
17
|
+
export declare const gemini: RuntimeAdapter;
|
|
18
|
+
//# sourceMappingURL=gemini-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-cli.d.ts","sourceRoot":"","sources":["../../../src/lib/runtime-adapters/gemini-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,gBAAgB,CAAC;AA8DnE,eAAO,MAAM,MAAM,EAAE,cA8DpB,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI runtime adapter for solveos-cli.
|
|
3
|
+
*
|
|
4
|
+
* Handles detection and installation of commands, agents, and hooks
|
|
5
|
+
* into Gemini CLI's directory structure.
|
|
6
|
+
*
|
|
7
|
+
* Gemini CLI conventions:
|
|
8
|
+
* - Commands: .gemini/commands/<name>.toml -> /<name>
|
|
9
|
+
* Subdirectories create namespaced commands: .gemini/commands/solveos/<name>.toml -> /solveos:<name>
|
|
10
|
+
* - Agents: .gemini/agents/<name>.md -> @name / auto-delegation
|
|
11
|
+
* - Hooks: .gemini/settings.json -> hooks object
|
|
12
|
+
*
|
|
13
|
+
* Key difference: Gemini CLI uses TOML for commands (not Markdown).
|
|
14
|
+
* We wrap our markdown prompt content in TOML's `prompt` field.
|
|
15
|
+
*/
|
|
16
|
+
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
17
|
+
import { join, basename } from "node:path";
|
|
18
|
+
import { detectRuntime } from "../runtime-detect.js";
|
|
19
|
+
/**
|
|
20
|
+
* Convert a markdown command file to Gemini CLI's TOML format.
|
|
21
|
+
*
|
|
22
|
+
* Extracts the description from frontmatter for the TOML `description` field,
|
|
23
|
+
* and wraps the full markdown body in a multi-line TOML `prompt` string.
|
|
24
|
+
*/
|
|
25
|
+
function markdownToToml(content, commandName) {
|
|
26
|
+
let description = `solveOS ${commandName} command`;
|
|
27
|
+
let body = content;
|
|
28
|
+
// Extract and strip frontmatter
|
|
29
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
30
|
+
if (frontmatterMatch) {
|
|
31
|
+
const frontmatter = frontmatterMatch[1];
|
|
32
|
+
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
33
|
+
if (descMatch) {
|
|
34
|
+
description = descMatch[1].trim();
|
|
35
|
+
}
|
|
36
|
+
body = content.slice(frontmatterMatch[0].length);
|
|
37
|
+
}
|
|
38
|
+
// Escape triple quotes in the body (extremely unlikely but safe)
|
|
39
|
+
const escapedBody = body.replace(/"""/g, '"\\"\\""');
|
|
40
|
+
return `description = "${escapeTomlString(description)}"\nprompt = """\n${escapedBody}"""\n`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Escape a string for use in a TOML double-quoted string.
|
|
44
|
+
*/
|
|
45
|
+
function escapeTomlString(s) {
|
|
46
|
+
return s
|
|
47
|
+
.replace(/\\/g, "\\\\")
|
|
48
|
+
.replace(/"/g, '\\"')
|
|
49
|
+
.replace(/\n/g, "\\n")
|
|
50
|
+
.replace(/\t/g, "\\t");
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Transform OpenCode-style agent frontmatter to Gemini CLI agent frontmatter.
|
|
54
|
+
*
|
|
55
|
+
* Gemini CLI agents use: name, description, kind, tools, model, temperature,
|
|
56
|
+
* max_turns, timeout_mins.
|
|
57
|
+
*/
|
|
58
|
+
function transformAgentForGemini(content, agentName) {
|
|
59
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n/);
|
|
60
|
+
if (!frontmatterMatch) {
|
|
61
|
+
return `---\nname: ${agentName}\ndescription: solveOS ${agentName} agent\nkind: local\n---\n\n${content}`;
|
|
62
|
+
}
|
|
63
|
+
const frontmatter = frontmatterMatch[1];
|
|
64
|
+
const body = content.slice(frontmatterMatch[0].length);
|
|
65
|
+
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
66
|
+
const description = descMatch ? descMatch[1].trim() : `solveOS ${agentName} agent`;
|
|
67
|
+
return `---\nname: ${agentName}\ndescription: ${description}\nkind: local\n---\n\n${body}`;
|
|
68
|
+
}
|
|
69
|
+
export const gemini = {
|
|
70
|
+
async detect() {
|
|
71
|
+
const cwd = process.cwd();
|
|
72
|
+
const result = await detectRuntime(cwd);
|
|
73
|
+
if (result && result.runtime === "gemini") {
|
|
74
|
+
return {
|
|
75
|
+
detected: true,
|
|
76
|
+
name: "Gemini CLI",
|
|
77
|
+
configPath: result.configPath,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
detected: false,
|
|
82
|
+
name: "Gemini CLI",
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
async installCommands(sourceDir) {
|
|
86
|
+
const cwd = process.cwd();
|
|
87
|
+
// Use subdirectory for namespacing: .gemini/commands/solveos/<name>.toml -> /solveos:<name>
|
|
88
|
+
const targetDir = join(cwd, ".gemini", "commands", "solveos");
|
|
89
|
+
await mkdir(targetDir, { recursive: true });
|
|
90
|
+
const files = await readdir(sourceDir);
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
if (!file.endsWith(".md"))
|
|
93
|
+
continue;
|
|
94
|
+
const commandName = basename(file, ".md");
|
|
95
|
+
const sourcePath = join(sourceDir, file);
|
|
96
|
+
const content = await readFile(sourcePath, "utf-8");
|
|
97
|
+
const tomlContent = markdownToToml(content, commandName);
|
|
98
|
+
await writeFile(join(targetDir, `${commandName}.toml`), tomlContent, "utf-8");
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
async installAgents(sourceDir) {
|
|
102
|
+
const cwd = process.cwd();
|
|
103
|
+
const targetDir = join(cwd, ".gemini", "agents");
|
|
104
|
+
await mkdir(targetDir, { recursive: true });
|
|
105
|
+
const files = await readdir(sourceDir);
|
|
106
|
+
for (const file of files) {
|
|
107
|
+
if (!file.endsWith(".md"))
|
|
108
|
+
continue;
|
|
109
|
+
const agentName = basename(file, ".md");
|
|
110
|
+
const sourcePath = join(sourceDir, file);
|
|
111
|
+
const content = await readFile(sourcePath, "utf-8");
|
|
112
|
+
const transformed = transformAgentForGemini(content, agentName);
|
|
113
|
+
await writeFile(join(targetDir, file), transformed, "utf-8");
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
async installHooks(_sourceDir) {
|
|
117
|
+
// Gemini CLI hooks are configured in .gemini/settings.json with
|
|
118
|
+
// event-based handlers (shell commands). The mechanism is different
|
|
119
|
+
// from OpenCode's TypeScript plugins.
|
|
120
|
+
//
|
|
121
|
+
// The solveOS state files (.solveos/STATE.md, BRIEF.md) remain readable
|
|
122
|
+
// by any hook script the user configures.
|
|
123
|
+
//
|
|
124
|
+
// Future: generate shell-based hooks for context monitor and brief anchor.
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=gemini-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-cli.js","sourceRoot":"","sources":["../../../src/lib/runtime-adapters/gemini-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode runtime adapter for solveos-cli.
|
|
3
|
+
*
|
|
4
|
+
* Handles detection and installation of commands, agents, and hooks
|
|
5
|
+
* into OpenCode's directory structure.
|
|
6
|
+
*
|
|
7
|
+
* OpenCode conventions (from docs):
|
|
8
|
+
* - Commands: .opencode/commands/{name}.md -> /name
|
|
9
|
+
* - Agents: .opencode/agents/{name}.md -> @name
|
|
10
|
+
* - Plugins: .opencode/plugins/{name}.ts -> loaded at startup
|
|
11
|
+
*/
|
|
12
|
+
import type { RuntimeAdapter } from "../../types.js";
|
|
13
|
+
export declare const opencode: RuntimeAdapter;
|
|
14
|
+
//# sourceMappingURL=opencode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/lib/runtime-adapters/opencode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,gBAAgB,CAAC;AAmCnE,eAAO,MAAM,QAAQ,EAAE,cAmEtB,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode runtime adapter for solveos-cli.
|
|
3
|
+
*
|
|
4
|
+
* Handles detection and installation of commands, agents, and hooks
|
|
5
|
+
* into OpenCode's directory structure.
|
|
6
|
+
*
|
|
7
|
+
* OpenCode conventions (from docs):
|
|
8
|
+
* - Commands: .opencode/commands/{name}.md -> /name
|
|
9
|
+
* - Agents: .opencode/agents/{name}.md -> @name
|
|
10
|
+
* - Plugins: .opencode/plugins/{name}.ts -> loaded at startup
|
|
11
|
+
*/
|
|
12
|
+
import { mkdir, readdir, copyFile } from "node:fs/promises";
|
|
13
|
+
import { join, basename } from "node:path";
|
|
14
|
+
import { detectRuntime } from "../runtime-detect.js";
|
|
15
|
+
/**
|
|
16
|
+
* Maps our command directory structure (commands/solveos/*.md)
|
|
17
|
+
* to OpenCode's flat convention (.opencode/commands/solveos-{name}.md).
|
|
18
|
+
*
|
|
19
|
+
* e.g., commands/solveos/new.md -> .opencode/commands/solveos-new.md
|
|
20
|
+
* This becomes /solveos-new in OpenCode.
|
|
21
|
+
*/
|
|
22
|
+
function toOpencodeCommandName(filename) {
|
|
23
|
+
// Remove .md extension, prepend solveos- namespace
|
|
24
|
+
const name = basename(filename, ".md");
|
|
25
|
+
return `solveos-${name}.md`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Maps our agent files (agents/solveos-*.md)
|
|
29
|
+
* to OpenCode's convention (.opencode/agents/solveos-{name}.md).
|
|
30
|
+
*/
|
|
31
|
+
function toOpencodeAgentName(filename) {
|
|
32
|
+
return basename(filename);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Maps our hook files (src/hooks/*.ts compiled to dist/hooks/*.js)
|
|
36
|
+
* to OpenCode's plugin convention (.opencode/plugins/solveos-{name}.ts).
|
|
37
|
+
*
|
|
38
|
+
* Note: We copy the TypeScript source directly — OpenCode's plugin loader
|
|
39
|
+
* supports TypeScript files natively.
|
|
40
|
+
*/
|
|
41
|
+
function toOpencodePluginName(filename) {
|
|
42
|
+
return basename(filename);
|
|
43
|
+
}
|
|
44
|
+
export const opencode = {
|
|
45
|
+
async detect() {
|
|
46
|
+
// We detect OpenCode by checking for .opencode/ dir or opencode.json
|
|
47
|
+
// in the current working directory
|
|
48
|
+
const cwd = process.cwd();
|
|
49
|
+
const result = await detectRuntime(cwd);
|
|
50
|
+
if (result && result.runtime === "opencode") {
|
|
51
|
+
return {
|
|
52
|
+
detected: true,
|
|
53
|
+
name: "OpenCode",
|
|
54
|
+
configPath: result.configPath,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
detected: false,
|
|
59
|
+
name: "OpenCode",
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
async installCommands(sourceDir) {
|
|
63
|
+
const cwd = process.cwd();
|
|
64
|
+
const targetDir = join(cwd, ".opencode", "commands");
|
|
65
|
+
await mkdir(targetDir, { recursive: true });
|
|
66
|
+
// sourceDir is the commands/solveos/ directory
|
|
67
|
+
const files = await readdir(sourceDir);
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
if (!file.endsWith(".md"))
|
|
70
|
+
continue;
|
|
71
|
+
const sourcePath = join(sourceDir, file);
|
|
72
|
+
const targetName = toOpencodeCommandName(file);
|
|
73
|
+
const targetPath = join(targetDir, targetName);
|
|
74
|
+
await copyFile(sourcePath, targetPath);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
async installAgents(sourceDir) {
|
|
78
|
+
const cwd = process.cwd();
|
|
79
|
+
const targetDir = join(cwd, ".opencode", "agents");
|
|
80
|
+
await mkdir(targetDir, { recursive: true });
|
|
81
|
+
const files = await readdir(sourceDir);
|
|
82
|
+
for (const file of files) {
|
|
83
|
+
if (!file.endsWith(".md"))
|
|
84
|
+
continue;
|
|
85
|
+
const sourcePath = join(sourceDir, file);
|
|
86
|
+
const targetName = toOpencodeAgentName(file);
|
|
87
|
+
const targetPath = join(targetDir, targetName);
|
|
88
|
+
await copyFile(sourcePath, targetPath);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
async installHooks(sourceDir) {
|
|
92
|
+
const cwd = process.cwd();
|
|
93
|
+
const targetDir = join(cwd, ".opencode", "plugins");
|
|
94
|
+
await mkdir(targetDir, { recursive: true });
|
|
95
|
+
// sourceDir is src/hooks/ — contains TypeScript plugin files.
|
|
96
|
+
// OpenCode's plugin loader supports TypeScript natively.
|
|
97
|
+
const files = await readdir(sourceDir);
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
if (!file.endsWith(".ts"))
|
|
100
|
+
continue;
|
|
101
|
+
const sourcePath = join(sourceDir, file);
|
|
102
|
+
const targetName = toOpencodePluginName(file);
|
|
103
|
+
// Prefix with solveos- to namespace our plugins
|
|
104
|
+
const targetPath = join(targetDir, `solveos-${targetName}`);
|
|
105
|
+
await copyFile(sourcePath, targetPath);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=opencode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../../src/lib/runtime-adapters/opencode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime detection for solveos-cli.
|
|
3
|
+
*
|
|
4
|
+
* Checks for known AI coding assistant indicators in order of priority.
|
|
5
|
+
* Returns the first detected runtime, or null if none found.
|
|
6
|
+
*/
|
|
7
|
+
import type { Runtime } from "../types.js";
|
|
8
|
+
interface DetectionResult {
|
|
9
|
+
runtime: Runtime;
|
|
10
|
+
name: string;
|
|
11
|
+
configPath: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Attempt to detect which AI coding assistant runtime is active.
|
|
15
|
+
* Checks in priority order: OpenCode, Claude Code, Cursor, Gemini CLI.
|
|
16
|
+
*
|
|
17
|
+
* @param projectDir - The project directory to check.
|
|
18
|
+
* @returns Detection result, or null if no runtime found.
|
|
19
|
+
*/
|
|
20
|
+
export declare function detectRuntime(projectDir: string): Promise<DetectionResult | null>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=runtime-detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-detect.d.ts","sourceRoot":"","sources":["../../src/lib/runtime-detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,UAAU,eAAe;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAmDvF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime detection for solveos-cli.
|
|
3
|
+
*
|
|
4
|
+
* Checks for known AI coding assistant indicators in order of priority.
|
|
5
|
+
* Returns the first detected runtime, or null if none found.
|
|
6
|
+
*/
|
|
7
|
+
import { access } from "node:fs/promises";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
/**
|
|
10
|
+
* Attempt to detect which AI coding assistant runtime is active.
|
|
11
|
+
* Checks in priority order: OpenCode, Claude Code, Cursor, Gemini CLI.
|
|
12
|
+
*
|
|
13
|
+
* @param projectDir - The project directory to check.
|
|
14
|
+
* @returns Detection result, or null if no runtime found.
|
|
15
|
+
*/
|
|
16
|
+
export async function detectRuntime(projectDir) {
|
|
17
|
+
// OpenCode: .opencode/ directory or opencode.json
|
|
18
|
+
const opencodeDir = join(projectDir, ".opencode");
|
|
19
|
+
const opencodeJson = join(projectDir, "opencode.json");
|
|
20
|
+
for (const indicator of [opencodeDir, opencodeJson]) {
|
|
21
|
+
if (await exists(indicator)) {
|
|
22
|
+
return {
|
|
23
|
+
runtime: "opencode",
|
|
24
|
+
name: "OpenCode",
|
|
25
|
+
configPath: indicator,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Claude Code: .claude/ directory or CLAUDE.md
|
|
30
|
+
const claudeDir = join(projectDir, ".claude");
|
|
31
|
+
const claudeMd = join(projectDir, "CLAUDE.md");
|
|
32
|
+
for (const indicator of [claudeDir, claudeMd]) {
|
|
33
|
+
if (await exists(indicator)) {
|
|
34
|
+
return {
|
|
35
|
+
runtime: "claude-code",
|
|
36
|
+
name: "Claude Code",
|
|
37
|
+
configPath: indicator,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Cursor: .cursor/ directory or .cursorrules
|
|
42
|
+
const cursorDir = join(projectDir, ".cursor");
|
|
43
|
+
const cursorRules = join(projectDir, ".cursorrules");
|
|
44
|
+
for (const indicator of [cursorDir, cursorRules]) {
|
|
45
|
+
if (await exists(indicator)) {
|
|
46
|
+
return {
|
|
47
|
+
runtime: "cursor",
|
|
48
|
+
name: "Cursor",
|
|
49
|
+
configPath: indicator,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Gemini CLI: .gemini/ directory
|
|
54
|
+
const geminiDir = join(projectDir, ".gemini");
|
|
55
|
+
if (await exists(geminiDir)) {
|
|
56
|
+
return {
|
|
57
|
+
runtime: "gemini",
|
|
58
|
+
name: "Gemini CLI",
|
|
59
|
+
configPath: geminiDir,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
async function exists(path) {
|
|
65
|
+
try {
|
|
66
|
+
await access(path);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=runtime-detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-detect.js","sourceRoot":"","sources":["../../src/lib/runtime-detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AASjC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB;IACpD,kDAAkD;IAClD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IACvD,KAAK,MAAM,SAAS,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,EAAE,CAAC;QACpD,IAAI,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,OAAO;gBACL,OAAO,EAAE,UAAU;gBACnB,IAAI,EAAE,UAAU;gBAChB,UAAU,EAAE,SAAS;aACtB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,+CAA+C;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC/C,KAAK,MAAM,SAAS,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC9C,IAAI,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,OAAO;gBACL,OAAO,EAAE,aAAa;gBACtB,IAAI,EAAE,aAAa;gBACnB,UAAU,EAAE,SAAS;aACtB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACrD,KAAK,MAAM,SAAS,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC;QACjD,IAAI,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,OAAO;gBACL,OAAO,EAAE,QAAQ;gBACjB,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,SAAS;aACtB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC9C,IAAI,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,OAAO,EAAE,QAAQ;YACjB,IAAI,EAAE,YAAY;YAClB,UAAU,EAAE,SAAS;SACtB,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security module for solveos-cli.
|
|
3
|
+
*
|
|
4
|
+
* Protects against:
|
|
5
|
+
* 1. Path traversal attacks — ensures all file operations stay within .solveos/
|
|
6
|
+
* 2. Prompt injection — scans user-supplied content for injection patterns
|
|
7
|
+
* before it's used as AI agent context
|
|
8
|
+
*
|
|
9
|
+
* Since solveos-cli generates markdown that becomes LLM system prompts,
|
|
10
|
+
* indirect prompt injection is a real attack surface.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Validate that a target path resolves within a base directory.
|
|
14
|
+
* Prevents path traversal attacks (../../etc/passwd, symlinks, null bytes).
|
|
15
|
+
*
|
|
16
|
+
* @param basePath - The allowed root directory (e.g., /project/.solveos)
|
|
17
|
+
* @param targetPath - The path to validate (absolute or relative to basePath)
|
|
18
|
+
* @returns The resolved, validated absolute path
|
|
19
|
+
* @throws Error if the path escapes the base directory
|
|
20
|
+
*/
|
|
21
|
+
export declare function validatePath(basePath: string, targetPath: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Validate path with symlink resolution.
|
|
24
|
+
* Resolves symlinks first, then validates the real path.
|
|
25
|
+
* Use this for operations where the target file already exists.
|
|
26
|
+
*
|
|
27
|
+
* @param basePath - The allowed root directory
|
|
28
|
+
* @param targetPath - The path to validate
|
|
29
|
+
* @returns The resolved, validated real path
|
|
30
|
+
* @throws Error if the real path escapes the base directory
|
|
31
|
+
*/
|
|
32
|
+
export declare function validatePathWithSymlinks(basePath: string, targetPath: string): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Custom error for path traversal attempts.
|
|
35
|
+
*/
|
|
36
|
+
export declare class PathTraversalError extends Error {
|
|
37
|
+
readonly attemptedPath: string;
|
|
38
|
+
readonly basePath: string;
|
|
39
|
+
constructor(message: string, attemptedPath: string, basePath: string);
|
|
40
|
+
}
|
|
41
|
+
/** Severity level for injection detection. */
|
|
42
|
+
export type InjectionSeverity = "block" | "warn";
|
|
43
|
+
/** Result of scanning content for injection patterns. */
|
|
44
|
+
export interface InjectionScanResult {
|
|
45
|
+
/** Whether any patterns were detected. */
|
|
46
|
+
detected: boolean;
|
|
47
|
+
/** Highest severity of detected patterns. */
|
|
48
|
+
severity: InjectionSeverity | null;
|
|
49
|
+
/** List of matched patterns with descriptions. */
|
|
50
|
+
matches: InjectionMatch[];
|
|
51
|
+
}
|
|
52
|
+
/** A single injection pattern match. */
|
|
53
|
+
export interface InjectionMatch {
|
|
54
|
+
/** Human-readable description of the pattern. */
|
|
55
|
+
pattern: string;
|
|
56
|
+
/** The matched text (truncated to 100 chars). */
|
|
57
|
+
matched: string;
|
|
58
|
+
/** Severity level. */
|
|
59
|
+
severity: InjectionSeverity;
|
|
60
|
+
/** Line number where the match was found (1-indexed). */
|
|
61
|
+
line: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Scan content for prompt injection patterns.
|
|
65
|
+
*
|
|
66
|
+
* @param content - The text to scan (Plan Brief content, research summaries, etc.)
|
|
67
|
+
* @returns Scan result with detected patterns, severity, and matches.
|
|
68
|
+
*/
|
|
69
|
+
export declare function scanForInjection(content: string): InjectionScanResult;
|
|
70
|
+
/**
|
|
71
|
+
* Wrap user-supplied content with clear delimiters so the LLM can distinguish
|
|
72
|
+
* between system instructions and user content.
|
|
73
|
+
*
|
|
74
|
+
* This doesn't prevent injection but makes it harder for injected instructions
|
|
75
|
+
* to be confused with system-level prompts.
|
|
76
|
+
*
|
|
77
|
+
* @param content - The user-supplied content
|
|
78
|
+
* @param label - A label describing what this content is (e.g., "Plan Brief", "Research Summary")
|
|
79
|
+
* @returns Content wrapped with delimiters
|
|
80
|
+
*/
|
|
81
|
+
export declare function wrapUserContent(content: string, label: string): string;
|
|
82
|
+
/**
|
|
83
|
+
* Sanitize a filename to prevent path traversal via filename manipulation.
|
|
84
|
+
* Allows: alphanumeric, hyphens, underscores, dots.
|
|
85
|
+
* Rejects: slashes, null bytes, special characters.
|
|
86
|
+
*/
|
|
87
|
+
export declare function sanitizeFilename(name: string): string;
|
|
88
|
+
//# sourceMappingURL=security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/lib/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAkCzE;AAED;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAMrE;AAMD,8CAA8C;AAC9C,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjD,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB,6CAA6C;IAC7C,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnC,kDAAkD;IAClD,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,sBAAsB;IACtB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;CACd;AA8ED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAgCrE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMtE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWrD"}
|