litopencode 0.0.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/README.md +124 -0
- package/bin/litopencode +4 -0
- package/cover.png +0 -0
- package/dist/agents/defaults.d.ts +15 -0
- package/dist/agents/defaults.js +42 -0
- package/dist/agents/registry.d.ts +64 -0
- package/dist/agents/registry.js +31 -0
- package/dist/agents/specialists.d.ts +49 -0
- package/dist/agents/specialists.js +198 -0
- package/dist/agents/types.d.ts +30 -0
- package/dist/agents/types.js +4 -0
- package/dist/agents.d.ts +4 -0
- package/dist/agents.js +3 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +204 -0
- package/dist/commands.d.ts +30 -0
- package/dist/commands.js +59 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +61 -0
- package/dist/features.d.ts +108 -0
- package/dist/features.js +161 -0
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.js +11 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +45 -0
- package/dist/ledger.d.ts +39 -0
- package/dist/ledger.js +147 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.js +21 -0
- package/dist/skills.d.ts +47 -0
- package/dist/skills.js +60 -0
- package/dist/state.d.ts +14 -0
- package/dist/state.js +20 -0
- package/dist/tool-guards.d.ts +24 -0
- package/dist/tool-guards.js +82 -0
- package/dist/tools.d.ts +19 -0
- package/dist/tools.js +134 -0
- package/docs/migration.md +51 -0
- package/docs/release-checklist.md +120 -0
- package/generate_cover.py +127 -0
- package/package.json +29 -0
- package/skills/agent-roster/SKILL.md +26 -0
- package/skills/doctor-installer/SKILL.md +22 -0
- package/skills/durable-litgoal/SKILL.md +22 -0
- package/skills/litwork-activation/SKILL.md +25 -0
- package/skills/release-guardrails/SKILL.md +23 -0
- package/skills/tool-guards/SKILL.md +22 -0
- package/skills/workflow-loop/SKILL.md +24 -0
- package/tools/check-pack-payload.mjs +156 -0
- package/tools/check-version-lockstep.mjs +144 -0
- package/tools/run-build.mjs +34 -0
- package/tools/run-typecheck.mjs +25 -0
- package/tools/scan-legacy-tokens.mjs +211 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +13 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { LitOpenCodeConfigError, loadConfig } from "./config.js";
|
|
5
|
+
import { createRuntimePaths } from "./state.js";
|
|
6
|
+
function isRecord(value) {
|
|
7
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
async function readPackageMetadata() {
|
|
10
|
+
const packagePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../package.json");
|
|
11
|
+
const parsed = JSON.parse(await fs.readFile(packagePath, "utf8"));
|
|
12
|
+
if (!isRecord(parsed) || typeof parsed.name !== "string" || typeof parsed.version !== "string") {
|
|
13
|
+
throw new Error(`Malformed package metadata at ${packagePath}`);
|
|
14
|
+
}
|
|
15
|
+
return { name: parsed.name, version: parsed.version };
|
|
16
|
+
}
|
|
17
|
+
function parseArgs(argv) {
|
|
18
|
+
const parsed = {
|
|
19
|
+
command: argv[0],
|
|
20
|
+
root: process.cwd(),
|
|
21
|
+
dryRun: false
|
|
22
|
+
};
|
|
23
|
+
if (parsed.command === "--help" || parsed.command === "-h") {
|
|
24
|
+
parsed.command = "help";
|
|
25
|
+
}
|
|
26
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
27
|
+
const arg = argv[index];
|
|
28
|
+
if (arg === "--root" || arg === "--workdir") {
|
|
29
|
+
const value = argv[index + 1];
|
|
30
|
+
if (!value)
|
|
31
|
+
throw new Error(`${arg} requires a value`);
|
|
32
|
+
parsed.root = value;
|
|
33
|
+
index += 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (arg === "--dry-run") {
|
|
37
|
+
parsed.dryRun = true;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (arg === "--help" || arg === "-h") {
|
|
41
|
+
parsed.command = "help";
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
45
|
+
}
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
function helpText() {
|
|
49
|
+
return [
|
|
50
|
+
"litopencode",
|
|
51
|
+
"",
|
|
52
|
+
"Usage:",
|
|
53
|
+
" litopencode doctor [--root <dir>]",
|
|
54
|
+
" litopencode install --dry-run [--root <dir>]",
|
|
55
|
+
"",
|
|
56
|
+
"Commands:",
|
|
57
|
+
" doctor Report package, config, and runtime path status without writing files.",
|
|
58
|
+
" install Print the exact opencode.json mutation for adding this plugin."
|
|
59
|
+
].join("\n");
|
|
60
|
+
}
|
|
61
|
+
async function readJsonObjectIfPresent(filePath) {
|
|
62
|
+
let raw;
|
|
63
|
+
try {
|
|
64
|
+
raw = await fs.readFile(filePath, "utf8");
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
68
|
+
return null;
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
const parsed = JSON.parse(raw);
|
|
72
|
+
if (!isRecord(parsed)) {
|
|
73
|
+
throw new Error(`Malformed JSON at ${filePath}: expected an object.`);
|
|
74
|
+
}
|
|
75
|
+
return parsed;
|
|
76
|
+
}
|
|
77
|
+
function describePluginMutation(config) {
|
|
78
|
+
const pluginValue = config?.plugin;
|
|
79
|
+
const pluginIsArray = Array.isArray(pluginValue);
|
|
80
|
+
const alreadyPresent = pluginIsArray && pluginValue.includes("litopencode");
|
|
81
|
+
const currentCount = pluginIsArray ? pluginValue.length : 0;
|
|
82
|
+
if (alreadyPresent) {
|
|
83
|
+
return {
|
|
84
|
+
changed: false,
|
|
85
|
+
plugin: {
|
|
86
|
+
alreadyPresent: true,
|
|
87
|
+
currentCount,
|
|
88
|
+
resultCount: currentCount,
|
|
89
|
+
add: []
|
|
90
|
+
},
|
|
91
|
+
patch: []
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const add = ["litopencode"];
|
|
95
|
+
const resultCount = currentCount + add.length;
|
|
96
|
+
if (pluginIsArray) {
|
|
97
|
+
return {
|
|
98
|
+
changed: true,
|
|
99
|
+
plugin: {
|
|
100
|
+
alreadyPresent: false,
|
|
101
|
+
currentCount,
|
|
102
|
+
resultCount,
|
|
103
|
+
add
|
|
104
|
+
},
|
|
105
|
+
patch: [{ op: "add", path: "/plugin/-", value: "litopencode" }]
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const op = config !== null && Object.hasOwn(config, "plugin") ? "replace" : "add";
|
|
109
|
+
return {
|
|
110
|
+
changed: true,
|
|
111
|
+
plugin: {
|
|
112
|
+
alreadyPresent: false,
|
|
113
|
+
currentCount,
|
|
114
|
+
resultCount: 1,
|
|
115
|
+
add
|
|
116
|
+
},
|
|
117
|
+
patch: [{ op, path: "/plugin", value: ["litopencode"] }]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async function doctor(root) {
|
|
121
|
+
const metadata = await readPackageMetadata();
|
|
122
|
+
const loaded = await loadConfig(root);
|
|
123
|
+
const runtimeExists = await fs
|
|
124
|
+
.stat(loaded.paths.runtimeDir)
|
|
125
|
+
.then(() => true)
|
|
126
|
+
.catch((error) => {
|
|
127
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
128
|
+
return false;
|
|
129
|
+
throw error;
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
exitCode: 0,
|
|
133
|
+
stdout: JSON.stringify({
|
|
134
|
+
package: metadata,
|
|
135
|
+
config: {
|
|
136
|
+
source: loaded.source,
|
|
137
|
+
path: loaded.paths.configFile,
|
|
138
|
+
enabled: loaded.config.enabled,
|
|
139
|
+
logLevel: loaded.config.logLevel
|
|
140
|
+
},
|
|
141
|
+
state: {
|
|
142
|
+
runtimeDir: loaded.paths.runtimeDir,
|
|
143
|
+
runtimeExists,
|
|
144
|
+
stateFile: loaded.paths.stateFile,
|
|
145
|
+
logFile: loaded.paths.logFile,
|
|
146
|
+
ledgerFile: loaded.paths.ledgerFile
|
|
147
|
+
}
|
|
148
|
+
}, null, 2)
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async function install(root, dryRun) {
|
|
152
|
+
if (!dryRun) {
|
|
153
|
+
return {
|
|
154
|
+
exitCode: 2,
|
|
155
|
+
stderr: "litopencode install currently requires --dry-run; no files were written."
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const paths = createRuntimePaths(root);
|
|
159
|
+
const before = await readJsonObjectIfPresent(paths.opencodeConfigFile);
|
|
160
|
+
const mutation = describePluginMutation(before);
|
|
161
|
+
return {
|
|
162
|
+
exitCode: 0,
|
|
163
|
+
stdout: JSON.stringify({
|
|
164
|
+
dryRun: true,
|
|
165
|
+
path: paths.opencodeConfigFile,
|
|
166
|
+
plugin: mutation.plugin,
|
|
167
|
+
patch: mutation.patch,
|
|
168
|
+
changed: mutation.changed
|
|
169
|
+
}, null, 2)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export async function runCli(argv = process.argv.slice(2)) {
|
|
173
|
+
let parsed;
|
|
174
|
+
try {
|
|
175
|
+
parsed = parseArgs(argv);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
179
|
+
return { exitCode: 2, stderr: message };
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
if (!parsed.command || parsed.command === "help") {
|
|
183
|
+
return { exitCode: 0, stdout: helpText() };
|
|
184
|
+
}
|
|
185
|
+
if (parsed.command === "doctor")
|
|
186
|
+
return await doctor(parsed.root);
|
|
187
|
+
if (parsed.command === "install")
|
|
188
|
+
return await install(parsed.root, parsed.dryRun);
|
|
189
|
+
return { exitCode: 2, stderr: `Unknown command: ${parsed.command}` };
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
const prefix = error instanceof LitOpenCodeConfigError ? "CONFIG_ERROR" : "ERROR";
|
|
193
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
194
|
+
return { exitCode: 1, stderr: `${prefix}: ${message}` };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
198
|
+
const result = await runCli(argv);
|
|
199
|
+
if (result.stdout)
|
|
200
|
+
process.stdout.write(`${result.stdout}\n`);
|
|
201
|
+
if (result.stderr)
|
|
202
|
+
process.stderr.write(`${result.stderr}\n`);
|
|
203
|
+
process.exitCode = result.exitCode;
|
|
204
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Hooks } from "@opencode-ai/plugin";
|
|
2
|
+
export type LitOpenCodeCommandId = "lit" | "litwork";
|
|
3
|
+
export type LitOpenCodeCommand = {
|
|
4
|
+
readonly id: LitOpenCodeCommandId;
|
|
5
|
+
readonly slash: string;
|
|
6
|
+
readonly title: string;
|
|
7
|
+
readonly description: string;
|
|
8
|
+
readonly banner: string;
|
|
9
|
+
readonly activationText: string;
|
|
10
|
+
};
|
|
11
|
+
type CommandExecuteBeforeHook = NonNullable<Hooks["command.execute.before"]>;
|
|
12
|
+
export declare const litActivationBanner = "LITWORK ACTIVE: LitOpenCode durable goal loop is ready under .litopencode/litgoal.";
|
|
13
|
+
export declare const litOpenCodeCommands: readonly ({
|
|
14
|
+
id: "lit";
|
|
15
|
+
slash: string;
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
banner: string;
|
|
19
|
+
activationText: string;
|
|
20
|
+
} | {
|
|
21
|
+
id: "litwork";
|
|
22
|
+
slash: string;
|
|
23
|
+
title: string;
|
|
24
|
+
description: string;
|
|
25
|
+
banner: string;
|
|
26
|
+
activationText: string;
|
|
27
|
+
})[];
|
|
28
|
+
export declare function findLitOpenCodeCommand(command: string): LitOpenCodeCommand | undefined;
|
|
29
|
+
export declare function createCommandActivationHook(projectRoot: string): CommandExecuteBeforeHook;
|
|
30
|
+
export {};
|
package/dist/commands.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createLitGoalOperations } from "./ledger.js";
|
|
2
|
+
export const litActivationBanner = "LITWORK ACTIVE: LitOpenCode durable goal loop is ready under .litopencode/litgoal.";
|
|
3
|
+
export const litOpenCodeCommands = Object.freeze([
|
|
4
|
+
{
|
|
5
|
+
id: "lit",
|
|
6
|
+
slash: "/lit",
|
|
7
|
+
title: "LitOpenCode",
|
|
8
|
+
description: "Activate the LitOpenCode workflow banner and durable goal loop.",
|
|
9
|
+
banner: litActivationBanner,
|
|
10
|
+
activationText: "LITWORK ACTIVE. Use the durable ledger for goal and loop progress, keep evidence attached to each step, and route implementation through the active LitOpenCode workflow."
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "litwork",
|
|
14
|
+
slash: "/litwork",
|
|
15
|
+
title: "LitOpenCode Work Loop",
|
|
16
|
+
description: "Resume a LitOpenCode work loop with durable ledger-backed progress.",
|
|
17
|
+
banner: litActivationBanner,
|
|
18
|
+
activationText: "LITWORK RESUME. Continue from the durable ledger, preserve unrelated workspace changes, and verify each completed slice before advancing."
|
|
19
|
+
}
|
|
20
|
+
]);
|
|
21
|
+
export function findLitOpenCodeCommand(command) {
|
|
22
|
+
const normalized = command.trim().replace(/^\//, "").toLowerCase();
|
|
23
|
+
return litOpenCodeCommands.find((candidate) => candidate.id === normalized);
|
|
24
|
+
}
|
|
25
|
+
function summarizeCommandArguments(value) {
|
|
26
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
27
|
+
return { present: false, redacted: false, length: 0 };
|
|
28
|
+
}
|
|
29
|
+
return { present: true, redacted: true, length: value.length };
|
|
30
|
+
}
|
|
31
|
+
export function createCommandActivationHook(projectRoot) {
|
|
32
|
+
const operations = createLitGoalOperations(projectRoot);
|
|
33
|
+
return async (input, output) => {
|
|
34
|
+
const command = findLitOpenCodeCommand(input.command);
|
|
35
|
+
if (!command)
|
|
36
|
+
return;
|
|
37
|
+
await operations.append({
|
|
38
|
+
type: "command.activated",
|
|
39
|
+
command: command.id,
|
|
40
|
+
arguments: summarizeCommandArguments(input.arguments),
|
|
41
|
+
sessionID: input.sessionID,
|
|
42
|
+
timestamp: new Date().toISOString()
|
|
43
|
+
});
|
|
44
|
+
output.parts.push({
|
|
45
|
+
id: `litopencode-${command.id}-activation`,
|
|
46
|
+
sessionID: input.sessionID,
|
|
47
|
+
messageID: `litopencode-${command.id}-activation`,
|
|
48
|
+
type: "text",
|
|
49
|
+
text: command.activationText,
|
|
50
|
+
synthetic: true,
|
|
51
|
+
metadata: {
|
|
52
|
+
litopencode: {
|
|
53
|
+
command: command.id,
|
|
54
|
+
banner: command.banner
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type RuntimePaths } from "./state.ts";
|
|
2
|
+
export type LogLevel = "silent" | "error" | "info" | "debug";
|
|
3
|
+
export type LitOpenCodeConfig = {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
logLevel: LogLevel;
|
|
6
|
+
};
|
|
7
|
+
export type LoadedConfig = {
|
|
8
|
+
config: LitOpenCodeConfig;
|
|
9
|
+
paths: RuntimePaths;
|
|
10
|
+
source: "defaults" | "file";
|
|
11
|
+
};
|
|
12
|
+
export declare class LitOpenCodeConfigError extends Error {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
export declare const defaultConfig: LitOpenCodeConfig;
|
|
16
|
+
export declare function loadConfig(projectRoot: string): Promise<LoadedConfig>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { createRuntimePaths } from "./state.js";
|
|
3
|
+
export class LitOpenCodeConfigError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "LitOpenCodeConfigError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export const defaultConfig = Object.freeze({
|
|
10
|
+
enabled: true,
|
|
11
|
+
logLevel: "info"
|
|
12
|
+
});
|
|
13
|
+
const logLevels = new Set(["silent", "error", "info", "debug"]);
|
|
14
|
+
function isRecord(value) {
|
|
15
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16
|
+
}
|
|
17
|
+
function parseConfig(value, filePath) {
|
|
18
|
+
if (!isRecord(value)) {
|
|
19
|
+
throw new LitOpenCodeConfigError(`Malformed LitOpenCode config at ${filePath}: expected a JSON object.`);
|
|
20
|
+
}
|
|
21
|
+
const config = { ...defaultConfig };
|
|
22
|
+
if ("enabled" in value) {
|
|
23
|
+
if (typeof value.enabled !== "boolean") {
|
|
24
|
+
throw new LitOpenCodeConfigError(`Malformed LitOpenCode config at ${filePath}: enabled must be boolean.`);
|
|
25
|
+
}
|
|
26
|
+
config.enabled = value.enabled;
|
|
27
|
+
}
|
|
28
|
+
if ("logLevel" in value) {
|
|
29
|
+
if (typeof value.logLevel !== "string" || !logLevels.has(value.logLevel)) {
|
|
30
|
+
throw new LitOpenCodeConfigError(`Malformed LitOpenCode config at ${filePath}: logLevel must be one of silent, error, info, debug.`);
|
|
31
|
+
}
|
|
32
|
+
config.logLevel = value.logLevel;
|
|
33
|
+
}
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
36
|
+
export async function loadConfig(projectRoot) {
|
|
37
|
+
const paths = createRuntimePaths(projectRoot);
|
|
38
|
+
let raw;
|
|
39
|
+
try {
|
|
40
|
+
raw = await fs.readFile(paths.configFile, "utf8");
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
44
|
+
return { config: { ...defaultConfig }, paths, source: "defaults" };
|
|
45
|
+
}
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(raw);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
const reason = error instanceof Error ? error.message : "invalid JSON";
|
|
54
|
+
throw new LitOpenCodeConfigError(`Malformed LitOpenCode config at ${paths.configFile}: ${reason}`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
config: parseConfig(parsed, paths.configFile),
|
|
58
|
+
paths,
|
|
59
|
+
source: "file"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export type LitOpenCodeBindingKind = "agent" | "cli" | "command" | "config" | "hook" | "tool";
|
|
2
|
+
export type LitOpenCodeFeatureId = "planning-start-work-loop" | "lit-litwork-activation" | "durable-ledger" | "agent-roster" | "doctor-install" | "guardrails";
|
|
3
|
+
export type LitOpenCodeFeatureBinding = {
|
|
4
|
+
readonly kind: LitOpenCodeBindingKind;
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly surface: string;
|
|
7
|
+
readonly description: string;
|
|
8
|
+
};
|
|
9
|
+
export type LitOpenCodeFeature = {
|
|
10
|
+
readonly id: LitOpenCodeFeatureId;
|
|
11
|
+
readonly title: string;
|
|
12
|
+
readonly summary: string;
|
|
13
|
+
readonly bindings: readonly LitOpenCodeFeatureBinding[];
|
|
14
|
+
readonly verification: readonly string[];
|
|
15
|
+
};
|
|
16
|
+
export declare const litOpenCodeFeatures: readonly ({
|
|
17
|
+
id: "planning-start-work-loop";
|
|
18
|
+
title: string;
|
|
19
|
+
summary: string;
|
|
20
|
+
bindings: ({
|
|
21
|
+
kind: "command";
|
|
22
|
+
id: string;
|
|
23
|
+
surface: string;
|
|
24
|
+
description: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "agent";
|
|
27
|
+
id: string;
|
|
28
|
+
surface: string;
|
|
29
|
+
description: string;
|
|
30
|
+
})[];
|
|
31
|
+
verification: string[];
|
|
32
|
+
} | {
|
|
33
|
+
id: "lit-litwork-activation";
|
|
34
|
+
title: string;
|
|
35
|
+
summary: string;
|
|
36
|
+
bindings: ({
|
|
37
|
+
kind: "command";
|
|
38
|
+
id: string;
|
|
39
|
+
surface: string;
|
|
40
|
+
description: string;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "tool";
|
|
43
|
+
id: string;
|
|
44
|
+
surface: string;
|
|
45
|
+
description: string;
|
|
46
|
+
} | {
|
|
47
|
+
kind: "hook";
|
|
48
|
+
id: string;
|
|
49
|
+
surface: string;
|
|
50
|
+
description: string;
|
|
51
|
+
})[];
|
|
52
|
+
verification: string[];
|
|
53
|
+
} | {
|
|
54
|
+
id: "durable-ledger";
|
|
55
|
+
title: string;
|
|
56
|
+
summary: string;
|
|
57
|
+
bindings: ({
|
|
58
|
+
kind: "tool";
|
|
59
|
+
id: string;
|
|
60
|
+
surface: string;
|
|
61
|
+
description: string;
|
|
62
|
+
} | {
|
|
63
|
+
kind: "config";
|
|
64
|
+
id: string;
|
|
65
|
+
surface: string;
|
|
66
|
+
description: string;
|
|
67
|
+
})[];
|
|
68
|
+
verification: string[];
|
|
69
|
+
} | {
|
|
70
|
+
id: "agent-roster";
|
|
71
|
+
title: string;
|
|
72
|
+
summary: string;
|
|
73
|
+
bindings: ({
|
|
74
|
+
kind: "hook";
|
|
75
|
+
id: string;
|
|
76
|
+
surface: string;
|
|
77
|
+
description: string;
|
|
78
|
+
} | {
|
|
79
|
+
kind: "agent";
|
|
80
|
+
id: string;
|
|
81
|
+
surface: string;
|
|
82
|
+
description: string;
|
|
83
|
+
})[];
|
|
84
|
+
verification: string[];
|
|
85
|
+
} | {
|
|
86
|
+
id: "doctor-install";
|
|
87
|
+
title: string;
|
|
88
|
+
summary: string;
|
|
89
|
+
bindings: {
|
|
90
|
+
kind: "cli";
|
|
91
|
+
id: string;
|
|
92
|
+
surface: string;
|
|
93
|
+
description: string;
|
|
94
|
+
}[];
|
|
95
|
+
verification: string[];
|
|
96
|
+
} | {
|
|
97
|
+
id: "guardrails";
|
|
98
|
+
title: string;
|
|
99
|
+
summary: string;
|
|
100
|
+
bindings: {
|
|
101
|
+
kind: "cli";
|
|
102
|
+
id: string;
|
|
103
|
+
surface: string;
|
|
104
|
+
description: string;
|
|
105
|
+
}[];
|
|
106
|
+
verification: string[];
|
|
107
|
+
})[];
|
|
108
|
+
export declare function findLitOpenCodeFeature(id: string): LitOpenCodeFeature | undefined;
|
package/dist/features.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export const litOpenCodeFeatures = Object.freeze([
|
|
2
|
+
{
|
|
3
|
+
id: "planning-start-work-loop",
|
|
4
|
+
title: "Planning And Work Loop",
|
|
5
|
+
summary: "Coordinates plan-first work, scoped implementation, independent verification, and evidence-backed progress.",
|
|
6
|
+
bindings: [
|
|
7
|
+
{
|
|
8
|
+
kind: "command",
|
|
9
|
+
id: "litwork",
|
|
10
|
+
surface: "OpenCode command /litwork",
|
|
11
|
+
description: "Resumes the work loop and writes a command activation event to the durable ledger."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
kind: "agent",
|
|
15
|
+
id: "lit-plan",
|
|
16
|
+
surface: "OpenCode agent lit-plan",
|
|
17
|
+
description: "Provides the primary planning surface for executable, verified work slices."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
kind: "agent",
|
|
21
|
+
id: "lit-loop",
|
|
22
|
+
surface: "OpenCode agent lit-loop",
|
|
23
|
+
description: "Provides the primary execution-loop surface for scoped edits and command-backed delivery."
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
verification: ["node --test test/litwork.test.mjs", "node --test test/agent-roster.test.mjs"]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "lit-litwork-activation",
|
|
30
|
+
title: "Lit Activation Surface",
|
|
31
|
+
summary: "Exposes LitOpenCode activation through command and tool surfaces backed by observable ledger events.",
|
|
32
|
+
bindings: [
|
|
33
|
+
{
|
|
34
|
+
kind: "command",
|
|
35
|
+
id: "lit",
|
|
36
|
+
surface: "OpenCode command /lit",
|
|
37
|
+
description: "Activates the workflow banner and records command activation."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
kind: "tool",
|
|
41
|
+
id: "lit",
|
|
42
|
+
surface: "OpenCode tool lit",
|
|
43
|
+
description: "Initializes the runtime ledger and reports status."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
kind: "tool",
|
|
47
|
+
id: "litwork",
|
|
48
|
+
surface: "OpenCode tool litwork",
|
|
49
|
+
description: "Starts or inspects the current work loop through durable ledger operations."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
kind: "hook",
|
|
53
|
+
id: "command.execute.before",
|
|
54
|
+
surface: "OpenCode hook command.execute.before",
|
|
55
|
+
description: "Injects activation text before supported slash commands execute."
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
verification: ["node --test test/litwork.test.mjs"]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "durable-ledger",
|
|
62
|
+
title: "Durable LitGoal Ledger",
|
|
63
|
+
summary: "Persists loop events under .litopencode/litgoal with atomic writes and temp-file recovery.",
|
|
64
|
+
bindings: [
|
|
65
|
+
{
|
|
66
|
+
kind: "tool",
|
|
67
|
+
id: "litwork.status",
|
|
68
|
+
surface: "OpenCode tool litwork action=status",
|
|
69
|
+
description: "Reads durable ledger events for status output."
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
kind: "config",
|
|
73
|
+
id: ".litopencode/litgoal",
|
|
74
|
+
surface: "litopencode runtime state path",
|
|
75
|
+
description: "Owns the durable ledger directory used by commands, tools, and goal operations."
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
verification: ["node --test test/ledger.test.mjs", "node --test test/litwork.test.mjs"]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "agent-roster",
|
|
82
|
+
title: "Agent Roster",
|
|
83
|
+
summary: "Registers the two primary LitOpenCode agents, recommended role aliases, and specialist roster as static OpenCode agent definitions.",
|
|
84
|
+
bindings: [
|
|
85
|
+
{
|
|
86
|
+
kind: "hook",
|
|
87
|
+
id: "config",
|
|
88
|
+
surface: "OpenCode config hook",
|
|
89
|
+
description: "Merges LitOpenCode agent definitions into the host config without overwriting existing agents."
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
kind: "agent",
|
|
93
|
+
id: "primary-defaults",
|
|
94
|
+
surface: "OpenCode agent registry",
|
|
95
|
+
description: "Provides the lit-plan and lit-loop primary default agents."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
kind: "agent",
|
|
99
|
+
id: "recommended-roles",
|
|
100
|
+
surface: "OpenCode agent registry",
|
|
101
|
+
description: "Provides architect, forge, oracle, prover, sentinel, and librarian role aliases."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
kind: "agent",
|
|
105
|
+
id: "specialists",
|
|
106
|
+
surface: "OpenCode agent registry",
|
|
107
|
+
description: "Provides the adapted specialist set for advanced discovery, review, planning, and persistence."
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
verification: ["node --test test/agent-roster.test.mjs"]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "doctor-install",
|
|
114
|
+
title: "Doctor And Installer CLI",
|
|
115
|
+
summary: "Reports package, config, and state health and previews the OpenCode plugin config mutation.",
|
|
116
|
+
bindings: [
|
|
117
|
+
{
|
|
118
|
+
kind: "cli",
|
|
119
|
+
id: "litopencode doctor",
|
|
120
|
+
surface: "litopencode CLI doctor command",
|
|
121
|
+
description: "Reports package metadata, config source, runtime paths, and state presence without writes."
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
kind: "cli",
|
|
125
|
+
id: "litopencode install --dry-run",
|
|
126
|
+
surface: "litopencode CLI install command",
|
|
127
|
+
description: "Prints the opencode.json plugin mutation without modifying the workspace."
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
verification: ["node --test test/cli.test.mjs", "node --test test/config-state.test.mjs"]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: "guardrails",
|
|
134
|
+
title: "Release Guardrails",
|
|
135
|
+
summary: "Keeps release-sensitive checks visible through npm-script and CI gates before package publication work.",
|
|
136
|
+
bindings: [
|
|
137
|
+
{
|
|
138
|
+
kind: "cli",
|
|
139
|
+
id: "npm run scan:legacy-tokens",
|
|
140
|
+
surface: "npm guard command",
|
|
141
|
+
description: "Fails on guarded vocabulary unless an exact line-level exception is documented."
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
kind: "cli",
|
|
145
|
+
id: "npm run check:version",
|
|
146
|
+
surface: "npm guard command",
|
|
147
|
+
description: "Verifies package metadata and lockfile version lockstep."
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
kind: "cli",
|
|
151
|
+
id: "npm run check:pack-payload",
|
|
152
|
+
surface: "npm guard command",
|
|
153
|
+
description: "Verifies the dry pack manifest excludes local state, evidence, archives, and fixtures."
|
|
154
|
+
}
|
|
155
|
+
],
|
|
156
|
+
verification: ["npm run scan:legacy-tokens", "npm run check:version", "npm run check:pack-payload"]
|
|
157
|
+
}
|
|
158
|
+
]);
|
|
159
|
+
export function findLitOpenCodeFeature(id) {
|
|
160
|
+
return litOpenCodeFeatures.find((feature) => feature.id === id);
|
|
161
|
+
}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Hooks } from "@opencode-ai/plugin";
|
|
2
|
+
type ToolExecuteBeforeHook = NonNullable<Hooks["tool.execute.before"]>;
|
|
3
|
+
type ToolExecuteAfterHook = NonNullable<Hooks["tool.execute.after"]>;
|
|
4
|
+
export declare function createToolExecuteBeforeHook(): ToolExecuteBeforeHook;
|
|
5
|
+
export declare function createToolExecuteAfterHook(): ToolExecuteAfterHook;
|
|
6
|
+
export {};
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { applyLitOpenCodeToolAfterHook, applyLitOpenCodeToolBeforeHook } from "./tool-guards.js";
|
|
2
|
+
export function createToolExecuteBeforeHook() {
|
|
3
|
+
return async (input, output) => {
|
|
4
|
+
applyLitOpenCodeToolBeforeHook(input, output);
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
export function createToolExecuteAfterHook() {
|
|
8
|
+
return async (input, output) => {
|
|
9
|
+
applyLitOpenCodeToolAfterHook(input, output);
|
|
10
|
+
};
|
|
11
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Hooks, PluginInput } from "@opencode-ai/plugin";
|
|
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
|
+
export { litActivationBanner, litOpenCodeCommands } from "./commands.ts";
|
|
4
|
+
export { createToolExecuteAfterHook, createToolExecuteBeforeHook } from "./hooks.ts";
|
|
5
|
+
export { applyLitOpenCodeToolAfterHook, applyLitOpenCodeToolBeforeHook, type LitOpenCodeToolAction, type LitOpenCodeToolId, type ToolGuardAfterOutput, type ToolGuardBeforeOutput, type ToolGuardDecision, type ToolGuardMarker, type ToolGuardRequest } from "./tool-guards.ts";
|
|
6
|
+
export { litOpenCodeTools, litTool, litworkTool } from "./tools.ts";
|
|
7
|
+
export { findLitOpenCodeFeature, litOpenCodeFeatures, type LitOpenCodeBindingKind, type LitOpenCodeFeature, type LitOpenCodeFeatureBinding, type LitOpenCodeFeatureId } from "./features.ts";
|
|
8
|
+
export { findLitOpenCodeRuntimeSkill, litOpenCodeRuntimeSkills, type LitOpenCodeRuntimeSkill, type LitOpenCodeRuntimeSkillId } from "./skills.ts";
|
|
9
|
+
export declare const pluginId = "litopencode";
|
|
10
|
+
export declare const server: (input?: PluginInput) => Promise<Hooks>;
|
|
11
|
+
declare const pluginModule: {
|
|
12
|
+
id: string;
|
|
13
|
+
server: (input?: PluginInput) => Promise<Hooks>;
|
|
14
|
+
};
|
|
15
|
+
export default pluginModule;
|