litopencode 0.0.2 → 0.0.4
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/README.md +4 -4
- package/dist/agents/registry.js +20 -3
- package/dist/agents/types.d.ts +12 -2
- package/dist/cli/args.js +1 -1
- package/dist/cli/install.js +121 -7
- package/dist/cli/json.js +3 -2
- package/dist/cli/types.d.ts +2 -1
- package/dist/features.js +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +4 -35
- package/dist/server.d.ts +3 -0
- package/dist/server.js +33 -0
- package/dist/skills.js +2 -1
- package/docs/migration.md +2 -2
- package/docs/release-checklist.md +4 -4
- package/package.json +9 -2
- package/skills/doctor-installer/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
|
|
35
35
|
npx litopencode install
|
|
36
36
|
|
|
37
|
-
The installer
|
|
37
|
+
The installer delegates the default install path to OpenCode's own plugin installer. Published installs register a version-pinned entry such as <code>litopencode@0.0.4</code>; local checkout installs register the local package path for development. Then restart OpenCode. The agent switcher should show <code>lit-plan</code> and <code>lit-loop</code>.
|
|
38
38
|
|
|
39
|
-
For a preview without writing
|
|
39
|
+
For a preview without writing OpenCode config:
|
|
40
40
|
|
|
41
41
|
npx litopencode install --dry-run
|
|
42
42
|
|
|
@@ -44,7 +44,7 @@ For health checks:
|
|
|
44
44
|
|
|
45
45
|
npx litopencode doctor
|
|
46
46
|
|
|
47
|
-
By default,
|
|
47
|
+
By default, OpenCode manages <code>~/.config/opencode/opencode.jsonc</code>, or <code>$XDG_CONFIG_HOME/opencode/opencode.jsonc</code> when <code>XDG_CONFIG_HOME</code> is set. Use <code>--root <dir></code> for a custom OpenCode config directory; custom roots use a direct <code>opencode.json</code> patch for deterministic tests and previews.
|
|
48
48
|
|
|
49
49
|
### Local Source Probe
|
|
50
50
|
|
|
@@ -72,7 +72,7 @@ By default, the installer targets <code>~/.config/opencode/opencode.json</code>,
|
|
|
72
72
|
|
|
73
73
|
## OpenCode Plugin
|
|
74
74
|
|
|
75
|
-
The default export is an OpenCode <code>
|
|
75
|
+
The default export is an OpenCode plugin function, and <code>litopencode/server</code> points at the same compiled server entrypoint for OpenCode's plugin installer. The packed artifact exports compiled JavaScript from <code>dist/index.js</code>, so installed npm consumers do not rely on Node's TypeScript stripping behavior for files under <code>node_modules</code>.
|
|
76
76
|
|
|
77
77
|
The server surface registers:
|
|
78
78
|
|
package/dist/agents/registry.js
CHANGED
|
@@ -11,20 +11,37 @@ function toolsToConfig(tools) {
|
|
|
11
11
|
}
|
|
12
12
|
return enabledTools;
|
|
13
13
|
}
|
|
14
|
+
function toolsToPermission(tools) {
|
|
15
|
+
const permission = {};
|
|
16
|
+
if (tools.includes("edit") || tools.includes("write"))
|
|
17
|
+
permission.edit = "allow";
|
|
18
|
+
if (tools.includes("bash"))
|
|
19
|
+
permission.bash = "allow";
|
|
20
|
+
if (tools.includes("webfetch"))
|
|
21
|
+
permission.webfetch = "allow";
|
|
22
|
+
return Object.keys(permission).length > 0 ? permission : undefined;
|
|
23
|
+
}
|
|
14
24
|
export function toOpenCodeAgentConfig(agent) {
|
|
15
25
|
return {
|
|
16
26
|
description: agent.summary,
|
|
17
27
|
prompt: agent.prompt,
|
|
18
|
-
mode: agent.mode,
|
|
28
|
+
mode: agent.mode === "primary" ? "all" : "subagent",
|
|
19
29
|
tools: toolsToConfig(agent.tools),
|
|
20
30
|
color: agent.color,
|
|
21
|
-
maxSteps: agent.maxSteps
|
|
31
|
+
maxSteps: agent.maxSteps,
|
|
32
|
+
...(agent.mode === "subagent" ? { hidden: true } : {}),
|
|
33
|
+
...(toolsToPermission(agent.tools) ? { permission: toolsToPermission(agent.tools) } : {})
|
|
22
34
|
};
|
|
23
35
|
}
|
|
24
36
|
export function registerLitOpenCodeAgents(config) {
|
|
37
|
+
const existingBuild = config.agent?.build ?? {};
|
|
38
|
+
const existingPlan = config.agent?.plan ?? {};
|
|
25
39
|
config.agent = {
|
|
26
|
-
...config.agent
|
|
40
|
+
...config.agent,
|
|
41
|
+
build: { ...existingBuild, mode: "subagent", hidden: true },
|
|
42
|
+
plan: { ...existingPlan, mode: "subagent", hidden: true }
|
|
27
43
|
};
|
|
44
|
+
config.default_agent = "lit-loop";
|
|
28
45
|
for (const agent of litOpenCodeAgents) {
|
|
29
46
|
config.agent[agent.id] = toOpenCodeAgentConfig(agent);
|
|
30
47
|
}
|
package/dist/agents/types.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Config } from "@opencode-ai/plugin";
|
|
2
2
|
export type AgentTier = "default" | "role" | "specialist";
|
|
3
3
|
export type AgentMode = "primary" | "subagent";
|
|
4
|
+
export type OpenCodeAgentMode = "all" | "subagent";
|
|
4
5
|
export type AgentToolId = "read" | "write" | "edit" | "bash" | "webfetch" | "grep";
|
|
6
|
+
export type AgentPermission = "ask" | "allow" | "deny";
|
|
5
7
|
export type LitOpenCodeAgent = {
|
|
6
8
|
readonly id: string;
|
|
7
9
|
readonly name: string;
|
|
@@ -18,12 +20,20 @@ export type LitOpenCodeAgent = {
|
|
|
18
20
|
export type OpenCodeAgentConfig = {
|
|
19
21
|
readonly description: string;
|
|
20
22
|
readonly prompt: string;
|
|
21
|
-
readonly mode:
|
|
23
|
+
readonly mode: OpenCodeAgentMode;
|
|
22
24
|
readonly tools: Record<string, boolean>;
|
|
23
25
|
readonly color: string;
|
|
24
26
|
readonly maxSteps: number;
|
|
27
|
+
readonly hidden?: boolean;
|
|
28
|
+
readonly permission?: {
|
|
29
|
+
readonly edit?: AgentPermission;
|
|
30
|
+
readonly bash?: AgentPermission;
|
|
31
|
+
readonly webfetch?: AgentPermission;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export type AgentConfigTarget = Pick<Config, "agent"> & {
|
|
35
|
+
default_agent?: string;
|
|
25
36
|
};
|
|
26
|
-
export type AgentConfigTarget = Pick<Config, "agent">;
|
|
27
37
|
export declare const planningTools: readonly ("read" | "grep")[];
|
|
28
38
|
export declare const workerTools: readonly ("read" | "write" | "edit" | "bash" | "grep")[];
|
|
29
39
|
export declare const reviewTools: readonly ("read" | "bash" | "grep")[];
|
package/dist/cli/args.js
CHANGED
|
@@ -49,7 +49,7 @@ export function helpText() {
|
|
|
49
49
|
" litopencode doctor [--root <dir>]",
|
|
50
50
|
"",
|
|
51
51
|
"Commands:",
|
|
52
|
-
" install
|
|
52
|
+
" install Register litopencode with OpenCode using a branded installer UI.",
|
|
53
53
|
" doctor Report package, config, and runtime path status without writing files.",
|
|
54
54
|
"",
|
|
55
55
|
"Default root:",
|
package/dist/cli/install.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
2
4
|
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
3
6
|
import { createRuntimePaths } from "../state.js";
|
|
4
7
|
import { readJsonObjectIfPresent, readPackageMetadata } from "./json.js";
|
|
5
8
|
const pluginName = "litopencode";
|
|
6
9
|
const reset = "\u001b[0m";
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
7
11
|
function useColor() {
|
|
8
12
|
return process.stdout.isTTY === true && process.env.NO_COLOR === undefined;
|
|
9
13
|
}
|
|
@@ -24,6 +28,109 @@ function pluginSpec(metadata) {
|
|
|
24
28
|
function isLitOpenCodeEntry(value) {
|
|
25
29
|
return typeof value === "string" && (value === pluginName || value.startsWith(pluginName + "@"));
|
|
26
30
|
}
|
|
31
|
+
function defaultOpenCodeRoot() {
|
|
32
|
+
const configHome = process.env.XDG_CONFIG_HOME;
|
|
33
|
+
if (configHome && configHome.length > 0)
|
|
34
|
+
return path.join(configHome, "opencode");
|
|
35
|
+
return path.join(os.homedir(), ".config", "opencode");
|
|
36
|
+
}
|
|
37
|
+
function shouldUseOpenCodeInstaller(root) {
|
|
38
|
+
return path.resolve(root) === path.resolve(defaultOpenCodeRoot());
|
|
39
|
+
}
|
|
40
|
+
function openCodeConfigCandidates(root) {
|
|
41
|
+
const resolvedRoot = path.resolve(root);
|
|
42
|
+
return [path.join(resolvedRoot, "opencode.json"), path.join(resolvedRoot, "opencode.jsonc")];
|
|
43
|
+
}
|
|
44
|
+
async function openCodeManagedConfigFile(root) {
|
|
45
|
+
const [jsonFile, jsoncFile] = openCodeConfigCandidates(root);
|
|
46
|
+
if (await pathExists(jsonFile))
|
|
47
|
+
return jsonFile;
|
|
48
|
+
if (await pathExists(jsoncFile))
|
|
49
|
+
return jsoncFile;
|
|
50
|
+
return jsoncFile;
|
|
51
|
+
}
|
|
52
|
+
function outputText(value) {
|
|
53
|
+
if (typeof value === "string")
|
|
54
|
+
return value;
|
|
55
|
+
if (value instanceof Uint8Array)
|
|
56
|
+
return Buffer.from(value).toString("utf8");
|
|
57
|
+
return "";
|
|
58
|
+
}
|
|
59
|
+
function stripAnsi(value) {
|
|
60
|
+
return value.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
61
|
+
}
|
|
62
|
+
function commandFailureDetail(error) {
|
|
63
|
+
if (!(error instanceof Error))
|
|
64
|
+
return String(error);
|
|
65
|
+
const failure = error;
|
|
66
|
+
const parts = [];
|
|
67
|
+
const stderr = stripAnsi(outputText(failure.stderr)).trim();
|
|
68
|
+
const stdout = stripAnsi(outputText(failure.stdout)).trim();
|
|
69
|
+
const code = typeof failure.code === "number" || typeof failure.code === "string" ? String(failure.code) : "";
|
|
70
|
+
const signal = typeof failure.signal === "string" ? failure.signal : "";
|
|
71
|
+
if (stderr.length > 0)
|
|
72
|
+
parts.push("stderr:\n" + stderr);
|
|
73
|
+
if (stdout.length > 0)
|
|
74
|
+
parts.push("stdout:\n" + stdout);
|
|
75
|
+
if (code.length > 0)
|
|
76
|
+
parts.push("exit code: " + code);
|
|
77
|
+
if (signal.length > 0)
|
|
78
|
+
parts.push("signal: " + signal);
|
|
79
|
+
if (parts.length === 0 && error.message.length > 0)
|
|
80
|
+
parts.push(error.message);
|
|
81
|
+
return parts.join("\n\n");
|
|
82
|
+
}
|
|
83
|
+
async function pathExists(filePath) {
|
|
84
|
+
try {
|
|
85
|
+
await fs.stat(filePath);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
90
|
+
return false;
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function isLocalSourcePackage(packageRoot) {
|
|
95
|
+
const realPackageRoot = await fs.realpath(packageRoot);
|
|
96
|
+
return (await pathExists(path.join(realPackageRoot, ".git"))) && (await pathExists(path.join(realPackageRoot, "src")));
|
|
97
|
+
}
|
|
98
|
+
async function openCodeInstallTarget(metadata) {
|
|
99
|
+
if (await isLocalSourcePackage(metadata.packageRoot)) {
|
|
100
|
+
const realPackageRoot = await fs.realpath(metadata.packageRoot);
|
|
101
|
+
return { value: realPackageRoot, label: metadata.name + "@" + metadata.version + " (local checkout)" };
|
|
102
|
+
}
|
|
103
|
+
const spec = pluginSpec(metadata);
|
|
104
|
+
return { value: spec, label: spec };
|
|
105
|
+
}
|
|
106
|
+
async function installWithOpenCode(target) {
|
|
107
|
+
try {
|
|
108
|
+
await execFileAsync("opencode", ["plugin", target.value, "--global", "--force"], {
|
|
109
|
+
timeout: 120_000,
|
|
110
|
+
maxBuffer: 1024 * 1024 * 4
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const detail = commandFailureDetail(error);
|
|
115
|
+
throw new Error("OpenCode plugin install failed for " + target.label + (detail ? ":\n" + detail : ""));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function removeStaleOpenCodeEntries(root, target) {
|
|
119
|
+
for (const configFile of openCodeConfigCandidates(root)) {
|
|
120
|
+
const config = await readJsonObjectIfPresent(configFile);
|
|
121
|
+
const pluginValue = config?.plugin;
|
|
122
|
+
if (!Array.isArray(pluginValue))
|
|
123
|
+
continue;
|
|
124
|
+
const nextPlugin = pluginValue.filter((entry) => {
|
|
125
|
+
if (entry === target.value)
|
|
126
|
+
return true;
|
|
127
|
+
return !isLitOpenCodeEntry(entry);
|
|
128
|
+
});
|
|
129
|
+
if (nextPlugin.length === pluginValue.length)
|
|
130
|
+
continue;
|
|
131
|
+
await fs.writeFile(configFile, JSON.stringify({ ...config, plugin: nextPlugin }, null, 2) + "\n");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
27
134
|
function describePluginMutation(config, target) {
|
|
28
135
|
const pluginValue = config?.plugin;
|
|
29
136
|
const pluginIsArray = Array.isArray(pluginValue);
|
|
@@ -106,22 +213,29 @@ function renderInstallReport(report) {
|
|
|
106
213
|
export async function install(root, dryRun) {
|
|
107
214
|
const metadata = await readPackageMetadata();
|
|
108
215
|
const paths = createRuntimePaths(root);
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const
|
|
216
|
+
const useOpenCodeInstaller = shouldUseOpenCodeInstaller(root);
|
|
217
|
+
const configFile = useOpenCodeInstaller ? await openCodeManagedConfigFile(root) : paths.opencodeConfigFile;
|
|
218
|
+
const before = useOpenCodeInstaller ? null : await readJsonObjectIfPresent(paths.opencodeConfigFile);
|
|
219
|
+
const spec = pluginSpec(metadata);
|
|
220
|
+
const target = useOpenCodeInstaller ? await openCodeInstallTarget(metadata) : { value: spec, label: spec };
|
|
221
|
+
const mutation = describePluginMutation(before, target.value);
|
|
112
222
|
const report = {
|
|
113
223
|
dryRun,
|
|
114
|
-
path:
|
|
224
|
+
path: configFile,
|
|
115
225
|
plugin: mutation.plugin,
|
|
116
226
|
patch: mutation.patch,
|
|
117
227
|
changed: mutation.changed,
|
|
118
|
-
package: metadata
|
|
228
|
+
package: { name: metadata.name, version: metadata.version }
|
|
119
229
|
};
|
|
120
230
|
if (dryRun) {
|
|
121
231
|
return { exitCode: 0, stdout: JSON.stringify(report, null, 2) };
|
|
122
232
|
}
|
|
123
|
-
if (
|
|
124
|
-
|
|
233
|
+
if (useOpenCodeInstaller) {
|
|
234
|
+
await removeStaleOpenCodeEntries(root, target);
|
|
235
|
+
await installWithOpenCode(target);
|
|
236
|
+
}
|
|
237
|
+
else if (mutation.changed) {
|
|
238
|
+
const next = applyPluginMutation(before, mutation, target.value);
|
|
125
239
|
await fs.mkdir(path.dirname(paths.opencodeConfigFile), { recursive: true });
|
|
126
240
|
await fs.writeFile(paths.opencodeConfigFile, JSON.stringify(next, null, 2) + "\n");
|
|
127
241
|
}
|
package/dist/cli/json.js
CHANGED
|
@@ -5,12 +5,13 @@ export function isRecord(value) {
|
|
|
5
5
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
6
|
}
|
|
7
7
|
export async function readPackageMetadata() {
|
|
8
|
-
const
|
|
8
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
9
|
+
const packagePath = path.join(packageRoot, "package.json");
|
|
9
10
|
const parsed = JSON.parse(await fs.readFile(packagePath, "utf8"));
|
|
10
11
|
if (!isRecord(parsed) || typeof parsed.name !== "string" || typeof parsed.version !== "string") {
|
|
11
12
|
throw new Error(`Malformed package metadata at ${packagePath}`);
|
|
12
13
|
}
|
|
13
|
-
return { name: parsed.name, version: parsed.version };
|
|
14
|
+
return { name: parsed.name, version: parsed.version, packageRoot };
|
|
14
15
|
}
|
|
15
16
|
export async function readJsonObjectIfPresent(filePath) {
|
|
16
17
|
let raw;
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type CliResult = {
|
|
|
6
6
|
export type PackageMetadata = {
|
|
7
7
|
readonly name: string;
|
|
8
8
|
readonly version: string;
|
|
9
|
+
readonly packageRoot: string;
|
|
9
10
|
};
|
|
10
11
|
export type ParsedArgs = {
|
|
11
12
|
readonly command?: string;
|
|
@@ -45,5 +46,5 @@ export type InstallReport = {
|
|
|
45
46
|
readonly plugin: PluginMutation["plugin"];
|
|
46
47
|
readonly patch: readonly JsonPatchOperation[];
|
|
47
48
|
readonly changed: boolean;
|
|
48
|
-
readonly package: PackageMetadata
|
|
49
|
+
readonly package: Pick<PackageMetadata, "name" | "version">;
|
|
49
50
|
};
|
package/dist/features.js
CHANGED
|
@@ -124,7 +124,7 @@ export const litOpenCodeFeatures = Object.freeze([
|
|
|
124
124
|
kind: "cli",
|
|
125
125
|
id: "npx litopencode install",
|
|
126
126
|
surface: "litopencode CLI install command",
|
|
127
|
-
description: "
|
|
127
|
+
description: "Delegates default setup to OpenCode's plugin installer, or adds a version-pinned litopencode entry for custom roots; --dry-run prints the mutation only."
|
|
128
128
|
}
|
|
129
129
|
],
|
|
130
130
|
verification: ["node --test test/cli.test.mjs", "node --test test/config-state.test.mjs"]
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import litOpenCodePlugin from "./server.ts";
|
|
2
2
|
export { appendLedgerEvent, createLitGoalOperations, initializeLitGoal, readLedgerEvents, recoverLedgerTemps, LedgerIoError, LedgerParseError, type JsonValue, type LedgerAppendResult, type LedgerEvent, type LedgerRecoveryReport, type LitGoalInitialization, type LitGoalOperations } from "./ledger.ts";
|
|
3
3
|
export { litActivationBanner, litOpenCodeCommands } from "./commands.ts";
|
|
4
4
|
export { createToolExecuteAfterHook, createToolExecuteBeforeHook } from "./hooks.ts";
|
|
@@ -7,9 +7,8 @@ export { litOpenCodeTools, litTool, litworkTool } from "./tools.ts";
|
|
|
7
7
|
export { findLitOpenCodeFeature, litOpenCodeFeatures, type LitOpenCodeBindingKind, type LitOpenCodeFeature, type LitOpenCodeFeatureBinding, type LitOpenCodeFeatureId } from "./features.ts";
|
|
8
8
|
export { findLitOpenCodeRuntimeSkill, litOpenCodeRuntimeSkills, type LitOpenCodeRuntimeSkill, type LitOpenCodeRuntimeSkillId } from "./skills.ts";
|
|
9
9
|
export declare const pluginId = "litopencode";
|
|
10
|
-
export declare const
|
|
11
|
-
declare const pluginModule: {
|
|
10
|
+
export declare const pluginModule: {
|
|
12
11
|
id: string;
|
|
13
|
-
server: (input?: PluginInput) => Promise<Hooks>;
|
|
12
|
+
server: (input?: import("@opencode-ai/plugin").PluginInput) => Promise<import("@opencode-ai/plugin").Hooks>;
|
|
14
13
|
};
|
|
15
|
-
export default
|
|
14
|
+
export default litOpenCodePlugin;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { createCommandActivationHook, litActivationBanner, litOpenCodeCommands } from "./commands.js";
|
|
3
|
-
import { loadConfig } from "./config.js";
|
|
4
|
-
import { createToolExecuteAfterHook, createToolExecuteBeforeHook } from "./hooks.js";
|
|
1
|
+
import litOpenCodePlugin from "./server.js";
|
|
5
2
|
export { appendLedgerEvent, createLitGoalOperations, initializeLitGoal, readLedgerEvents, recoverLedgerTemps, LedgerIoError, LedgerParseError } from "./ledger.js";
|
|
6
|
-
import { createLogger } from "./logger.js";
|
|
7
|
-
import { litOpenCodeTools } from "./tools.js";
|
|
8
3
|
export { litActivationBanner, litOpenCodeCommands } from "./commands.js";
|
|
9
4
|
export { createToolExecuteAfterHook, createToolExecuteBeforeHook } from "./hooks.js";
|
|
10
5
|
export { applyLitOpenCodeToolAfterHook, applyLitOpenCodeToolBeforeHook } from "./tool-guards.js";
|
|
@@ -12,34 +7,8 @@ export { litOpenCodeTools, litTool, litworkTool } from "./tools.js";
|
|
|
12
7
|
export { findLitOpenCodeFeature, litOpenCodeFeatures } from "./features.js";
|
|
13
8
|
export { findLitOpenCodeRuntimeSkill, litOpenCodeRuntimeSkills } from "./skills.js";
|
|
14
9
|
export const pluginId = "litopencode";
|
|
15
|
-
export const
|
|
16
|
-
const root = input?.worktree ?? input?.directory ?? ".";
|
|
17
|
-
const loaded = await loadConfig(root);
|
|
18
|
-
const logger = createLogger(loaded.paths);
|
|
19
|
-
const hooks = {
|
|
20
|
-
config: async (config) => {
|
|
21
|
-
registerLitOpenCodeAgents(config);
|
|
22
|
-
},
|
|
23
|
-
tool: litOpenCodeTools,
|
|
24
|
-
"command.execute.before": createCommandActivationHook(root),
|
|
25
|
-
dispose: async () => {
|
|
26
|
-
await logger.dispose();
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
Object.defineProperties(hooks, {
|
|
30
|
-
"tool.execute.before": {
|
|
31
|
-
value: createToolExecuteBeforeHook(),
|
|
32
|
-
enumerable: false
|
|
33
|
-
},
|
|
34
|
-
"tool.execute.after": {
|
|
35
|
-
value: createToolExecuteAfterHook(),
|
|
36
|
-
enumerable: false
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
return hooks;
|
|
40
|
-
};
|
|
41
|
-
const pluginModule = {
|
|
10
|
+
export const pluginModule = {
|
|
42
11
|
id: pluginId,
|
|
43
|
-
server
|
|
12
|
+
server: litOpenCodePlugin
|
|
44
13
|
};
|
|
45
|
-
export default
|
|
14
|
+
export default litOpenCodePlugin;
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { registerLitOpenCodeAgents } from "./agents.js";
|
|
2
|
+
import { createCommandActivationHook } from "./commands.js";
|
|
3
|
+
import { loadConfig } from "./config.js";
|
|
4
|
+
import { createToolExecuteAfterHook, createToolExecuteBeforeHook } from "./hooks.js";
|
|
5
|
+
import { createLogger } from "./logger.js";
|
|
6
|
+
import { litOpenCodeTools } from "./tools.js";
|
|
7
|
+
const litOpenCodePlugin = async (input) => {
|
|
8
|
+
const root = input?.worktree ?? input?.directory ?? ".";
|
|
9
|
+
const loaded = await loadConfig(root);
|
|
10
|
+
const logger = createLogger(loaded.paths);
|
|
11
|
+
const hooks = {
|
|
12
|
+
config: async (config) => {
|
|
13
|
+
registerLitOpenCodeAgents(config);
|
|
14
|
+
},
|
|
15
|
+
tool: litOpenCodeTools,
|
|
16
|
+
"command.execute.before": createCommandActivationHook(root),
|
|
17
|
+
dispose: async () => {
|
|
18
|
+
await logger.dispose();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperties(hooks, {
|
|
22
|
+
"tool.execute.before": {
|
|
23
|
+
value: createToolExecuteBeforeHook(),
|
|
24
|
+
enumerable: false
|
|
25
|
+
},
|
|
26
|
+
"tool.execute.after": {
|
|
27
|
+
value: createToolExecuteAfterHook(),
|
|
28
|
+
enumerable: false
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return hooks;
|
|
32
|
+
};
|
|
33
|
+
export default litOpenCodePlugin;
|
package/dist/skills.js
CHANGED
|
@@ -39,7 +39,8 @@ export const litOpenCodeRuntimeSkills = Object.freeze([
|
|
|
39
39
|
featureIds: ["doctor-install"],
|
|
40
40
|
discovery: "Run npx litopencode install, litopencode doctor, or litopencode install --dry-run.",
|
|
41
41
|
safety: [
|
|
42
|
-
"Default installation
|
|
42
|
+
"Default installation delegates to the OpenCode plugin installer; custom roots write only the version-pinned opencode.json plugin entry.",
|
|
43
|
+
"Local checkout installs hand OpenCode the package path so unpublished versions can be tested before npm publish.",
|
|
43
44
|
"Malformed config fails closed with a typed config error."
|
|
44
45
|
]
|
|
45
46
|
},
|
package/docs/migration.md
CHANGED
|
@@ -11,9 +11,9 @@ This guide describes the supported migration shape for moving an OpenCode workfl
|
|
|
11
11
|
## Environment And Config
|
|
12
12
|
|
|
13
13
|
- Use the `LITOPENCODE_` prefix for LitOpenCode-owned environment variables.
|
|
14
|
-
- Keep OpenCode plugin configuration in `opencode.json`.
|
|
14
|
+
- Keep default OpenCode plugin configuration in OpenCode's managed `opencode.jsonc`; custom-root LitOpenCode previews use `opencode.json`.
|
|
15
15
|
- Keep LitOpenCode runtime config in `.litopencode/config.json` when project-local config is needed.
|
|
16
|
-
- Use `npx litopencode install` to
|
|
16
|
+
- Use `npx litopencode install` to delegate default setup to `opencode plugin <target> --global --force`; published installs use `litopencode@0.0.4`, while local checkout installs use the package path.
|
|
17
17
|
- Use `litopencode doctor --root <workspace>` to inspect package metadata, config source, runtime paths, and ledger location without writing files.
|
|
18
18
|
- Use `litopencode install --dry-run --root <workspace>` to preview the `opencode.json` plugin mutation without writing files.
|
|
19
19
|
|
|
@@ -41,7 +41,7 @@ After the source gates pass, verify the packed artifact in a temporary directory
|
|
|
41
41
|
```sh
|
|
42
42
|
tmp="$(mktemp -d)"
|
|
43
43
|
npm pack --pack-destination "$tmp"
|
|
44
|
-
tar -xzf "$tmp"/litopencode-0.0.
|
|
44
|
+
tar -xzf "$tmp"/litopencode-0.0.4.tgz -C "$tmp"
|
|
45
45
|
node --input-type=module -e "import('$tmp/package/dist/index.js').then((m) => console.log(m.default?.id ?? m.pluginId))"
|
|
46
46
|
(
|
|
47
47
|
cd "$tmp/package"
|
|
@@ -73,7 +73,7 @@ mkdir "$tmp/consumer"
|
|
|
73
73
|
(
|
|
74
74
|
cd "$tmp/consumer"
|
|
75
75
|
npm init -y
|
|
76
|
-
npm install "$tmp"/litopencode-0.0.
|
|
76
|
+
npm install "$tmp"/litopencode-0.0.4.tgz
|
|
77
77
|
npm ls litopencode --all
|
|
78
78
|
node --input-type=module -e "import('litopencode').then((m) => console.log(m.default.id))"
|
|
79
79
|
node_modules/.bin/litopencode --help
|
|
@@ -89,12 +89,12 @@ Expected results:
|
|
|
89
89
|
- `@opencode-ai/plugin` is declared as an optional peer for TypeScript host typings without forcing npx/global installs to pull its transitive runtime tree
|
|
90
90
|
- package import prints `litopencode`
|
|
91
91
|
- CLI help, doctor, install dry-run, and write-enabled install exit 0
|
|
92
|
-
- write-enabled install creates or updates `opencode.json` without echoing unrelated config secrets
|
|
92
|
+
- write-enabled custom-root install creates or updates `opencode.json` without echoing unrelated config secrets
|
|
93
93
|
- temporary project cleanup removes the probe directory
|
|
94
94
|
|
|
95
95
|
## OpenCode Host Probe
|
|
96
96
|
|
|
97
|
-
Use the installed temp-project package, not the source tree, to import `litopencode`, call the
|
|
97
|
+
Use the installed temp-project package, not the source tree, to import `litopencode`, import `litopencode/server`, call the plugin function, invoke the config hook, command hook, `lit` and `litwork` tools, and before/after tool guard hooks. Expected results:
|
|
98
98
|
|
|
99
99
|
- `server()` exposes config, tool, command activation, dispose, and non-enumerable tool guard hooks
|
|
100
100
|
- config registers the LitOpenCode agent roster
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "litopencode",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "LitOpenCode OpenCode plugin bootstrap package.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,14 @@
|
|
|
8
8
|
},
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"exports": {
|
|
11
|
-
".":
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./server": {
|
|
16
|
+
"types": "./dist/server.d.ts",
|
|
17
|
+
"import": "./dist/server.js"
|
|
18
|
+
}
|
|
12
19
|
},
|
|
13
20
|
"scripts": {
|
|
14
21
|
"build": "node tools/run-build.mjs",
|
|
@@ -5,7 +5,7 @@ Use this LitOpenCode skill when a contributor needs the static CLI health and in
|
|
|
5
5
|
## Covers
|
|
6
6
|
|
|
7
7
|
- Inspect package metadata, config source, runtime paths, and state presence.
|
|
8
|
-
- Install or update the version-pinned OpenCode plugin
|
|
8
|
+
- Install or update the version-pinned OpenCode plugin through the OpenCode plugin installer with a bounded branded terminal flow.
|
|
9
9
|
- Preview OpenCode plugin configuration changes without writing files when `--dry-run` is set.
|
|
10
10
|
- Keep malformed config handling fail-closed.
|
|
11
11
|
- Keep installer output bounded and avoid leaking existing user config content.
|