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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# LitOpenCode Migration Guide
|
|
2
|
+
|
|
3
|
+
This guide describes the supported migration shape for moving an OpenCode workflow adapter to LitOpenCode. Keep this document brand-clean for public package payloads.
|
|
4
|
+
|
|
5
|
+
## Package
|
|
6
|
+
|
|
7
|
+
- Use `litopencode` as the npm package name, OpenCode plugin id, and CLI binary name.
|
|
8
|
+
- Configure OpenCode plugin loading with `litopencode`; do not carry old adapter package names into new runtime config.
|
|
9
|
+
- Treat upstream or sibling repositories as behavior references only. Product code in this package is OpenCode-native TypeScript authored for this repo.
|
|
10
|
+
|
|
11
|
+
## Environment And Config
|
|
12
|
+
|
|
13
|
+
- Use the `LITOPENCODE_` prefix for LitOpenCode-owned environment variables.
|
|
14
|
+
- Keep OpenCode plugin configuration in `opencode.json`.
|
|
15
|
+
- Keep LitOpenCode runtime config in `.litopencode/config.json` when project-local config is needed.
|
|
16
|
+
- Use `litopencode doctor --root <workspace>` to inspect package metadata, config source, runtime paths, and ledger location without writing files.
|
|
17
|
+
- Use `litopencode install --dry-run --root <workspace>` to preview the `opencode.json` plugin mutation before applying it manually or through a future write-enabled installer.
|
|
18
|
+
|
|
19
|
+
## Durable State
|
|
20
|
+
|
|
21
|
+
LitOpenCode runtime state is intentionally separated from planning and agent evidence:
|
|
22
|
+
|
|
23
|
+
- project runtime root: `.litopencode/`
|
|
24
|
+
- durable goal root: `.litopencode/litgoal/`
|
|
25
|
+
- durable ledger: `.litopencode/litgoal/lit-loop/ledger.jsonl`
|
|
26
|
+
- user/global root: `~/.litopencode`
|
|
27
|
+
- implementation evidence root: `.litcodex/lit-loop/evidence/`
|
|
28
|
+
|
|
29
|
+
The durable ledger uses atomic write behavior and recovery for partial temporary files. Existing state from a previous adapter should be copied only after a deliberate review, then represented as LitOpenCode ledger events rather than mixed directly into the new ledger file.
|
|
30
|
+
|
|
31
|
+
## Vocabulary
|
|
32
|
+
|
|
33
|
+
Use LitOpenCode vocabulary in docs, config, runtime output, and issue reports:
|
|
34
|
+
|
|
35
|
+
- product: `LitOpenCode`
|
|
36
|
+
- package, plugin, and binary: `litopencode`
|
|
37
|
+
- activation commands/tools: `lit`, `litwork`
|
|
38
|
+
- durable loop: `lit-loop`
|
|
39
|
+
- durable goal manager: `litgoal`
|
|
40
|
+
- planning/research workflow names: `lit-plan`, `litresearch`, `start-work`, `review-work`, `deep-interview`, `dynamic-workflow`
|
|
41
|
+
|
|
42
|
+
Legacy vocabulary may appear only in retained provenance files covered by the scanner allowlist and removal conditions. Do not introduce old package names, host claims, or environment prefixes into new product docs, tests, source, or release payloads.
|
|
43
|
+
|
|
44
|
+
## Migration Checklist
|
|
45
|
+
|
|
46
|
+
- Replace package/config references with `litopencode`.
|
|
47
|
+
- Move LitOpenCode-owned environment settings to `LITOPENCODE_` names.
|
|
48
|
+
- Keep runtime state under `.litopencode/litgoal` and implementation evidence under `.litcodex`.
|
|
49
|
+
- Keep visible static skills under `skills/*/SKILL.md`; they document workflow loop, durable litgoal, agent roster, doctor installer, release guardrails, litwork activation, and tool guards without dynamic execution.
|
|
50
|
+
- Run `npm run scan:legacy-tokens` after any migration wording change.
|
|
51
|
+
- Run `npm run check:pack-payload` and `npm pack --dry-run --json` before sharing a package artifact.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# LitOpenCode Release Checklist
|
|
2
|
+
|
|
3
|
+
This checklist is a readiness gate, not an instruction to distribute. The current CI and local workflow are no-publication by default.
|
|
4
|
+
|
|
5
|
+
## Preflight
|
|
6
|
+
|
|
7
|
+
- Confirm `package.json` name, binary, and plugin id are `litopencode`.
|
|
8
|
+
- Confirm `package.json` and `package-lock.json` versions are in lockstep.
|
|
9
|
+
- Confirm `README.md`, `docs/migration.md`, `docs/release-checklist.md`, and `HANDOFF.md` describe the current implementation state.
|
|
10
|
+
- Confirm `.litopencode/`, `.litcodex/`, generated evidence, reference archives, fixture-only paths, and package archives are excluded from the packed payload.
|
|
11
|
+
|
|
12
|
+
## Required Gates
|
|
13
|
+
|
|
14
|
+
Run these commands from the repository root:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm run build
|
|
18
|
+
npm test
|
|
19
|
+
npm test
|
|
20
|
+
npm run typecheck
|
|
21
|
+
npm run scan:legacy-tokens
|
|
22
|
+
npm run check:version
|
|
23
|
+
npm run check:pack-payload
|
|
24
|
+
npm pack --dry-run --json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Expected results:
|
|
28
|
+
|
|
29
|
+
- tests pass twice to reduce stale or misleading success output risk
|
|
30
|
+
- build emits `dist/*.js` and `dist/*.d.ts`
|
|
31
|
+
- typecheck exits cleanly
|
|
32
|
+
- scanner exits cleanly with only current allowlisted provenance hits
|
|
33
|
+
- version lockstep exits cleanly
|
|
34
|
+
- payload guard exits cleanly
|
|
35
|
+
- dry pack emits a manifest and leaves no root package archive behind
|
|
36
|
+
|
|
37
|
+
## Packed Artifact Probe
|
|
38
|
+
|
|
39
|
+
After the source gates pass, verify the packed artifact in a temporary directory:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
tmp="$(mktemp -d)"
|
|
43
|
+
npm pack --pack-destination "$tmp"
|
|
44
|
+
tar -xzf "$tmp"/litopencode-0.0.0.tgz -C "$tmp"
|
|
45
|
+
node --input-type=module -e "import('$tmp/package/dist/index.js').then((m) => console.log(m.default?.id ?? m.pluginId))"
|
|
46
|
+
(
|
|
47
|
+
cd "$tmp/package"
|
|
48
|
+
npm run scan:legacy-tokens
|
|
49
|
+
npm run check:version
|
|
50
|
+
npm run check:pack-payload
|
|
51
|
+
npm run typecheck
|
|
52
|
+
)
|
|
53
|
+
rm -rf "$tmp"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Expected results:
|
|
57
|
+
|
|
58
|
+
- plugin import prints `litopencode`
|
|
59
|
+
- package payload includes compiled `dist/*.js` and `dist/*.d.ts` files, not `src/*.ts`
|
|
60
|
+
- `scan:legacy-tokens` runs with an empty allowlist when the source-only allowlist is absent from the packed artifact
|
|
61
|
+
- `check:version` skips lockstep when the source-only lockfile is absent from the packed artifact
|
|
62
|
+
- `typecheck` skips when TypeScript dev dependencies are not installed in the packed artifact
|
|
63
|
+
- `check:pack-payload` still validates the packed manifest
|
|
64
|
+
|
|
65
|
+
## Installed Package Probe
|
|
66
|
+
|
|
67
|
+
After the packed artifact probe, verify the public install surface in a clean temporary npm project:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
tmp="$(mktemp -d)"
|
|
71
|
+
npm pack --pack-destination "$tmp"
|
|
72
|
+
mkdir "$tmp/consumer"
|
|
73
|
+
(
|
|
74
|
+
cd "$tmp/consumer"
|
|
75
|
+
npm init -y
|
|
76
|
+
npm install "$tmp"/litopencode-0.0.0.tgz
|
|
77
|
+
npm ls litopencode @opencode-ai/plugin --all
|
|
78
|
+
node --input-type=module -e "import('litopencode').then((m) => console.log(m.default.id))"
|
|
79
|
+
node_modules/.bin/litopencode --help
|
|
80
|
+
node_modules/.bin/litopencode doctor --root .
|
|
81
|
+
node_modules/.bin/litopencode install --dry-run --root .
|
|
82
|
+
)
|
|
83
|
+
rm -rf "$tmp"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Expected results:
|
|
87
|
+
|
|
88
|
+
- `@opencode-ai/plugin` is installed as a runtime dependency below `litopencode`
|
|
89
|
+
- package import prints `litopencode`
|
|
90
|
+
- CLI help, doctor, and install dry-run exit 0
|
|
91
|
+
- temporary project cleanup removes the probe directory
|
|
92
|
+
|
|
93
|
+
## OpenCode Host Probe
|
|
94
|
+
|
|
95
|
+
Use the installed temp-project package, not the source tree, to import `litopencode`, call the default plugin module\'s `server()` function, invoke the config hook, command hook, `lit` and `litwork` tools, and before/after tool guard hooks. Expected results:
|
|
96
|
+
|
|
97
|
+
- `server()` exposes config, tool, command activation, dispose, and non-enumerable tool guard hooks
|
|
98
|
+
- config registers the LitOpenCode agent roster
|
|
99
|
+
- `/litwork` command activation records a durable redacted ledger event
|
|
100
|
+
- `lit` and `litwork` tools record durable ledger events
|
|
101
|
+
- malformed LitOpenCode tool args are denied fail-closed
|
|
102
|
+
|
|
103
|
+
## No-Publication Guardrails
|
|
104
|
+
|
|
105
|
+
- Do not run the npm registry publication command in this workflow.
|
|
106
|
+
- Do not push repository refs from this workflow.
|
|
107
|
+
- Do not create or move repository tags from this workflow.
|
|
108
|
+
- Do not add npm or node auth token values, token variables, or registry credentials to local files, CI, evidence, or docs.
|
|
109
|
+
- Do not add a release job, deploy job, publication secret, write permission, or history rewrite step without explicit user authorization.
|
|
110
|
+
- CI must keep `permissions: contents: read` and must continue to run tests, typecheck, scanner, version lockstep, dry pack, and payload guard.
|
|
111
|
+
|
|
112
|
+
## Payload Review
|
|
113
|
+
|
|
114
|
+
Use `npm pack --dry-run --json --ignore-scripts` after `npm run build` as the observable package surface for payload checking. The manifest must include only public package files required for the compiled JavaScript plugin, type declarations, CLI, docs, and guard scripts. It must not include local state, implementation evidence, reference archives, temporary fixtures, dependency folders, source TypeScript, or generated package archives. Source-only guard inputs such as `package-lock.json` and `tools/legacy-token-allowlist.json` stay out of the public payload; their scripts must degrade explicitly and safely when those inputs are absent in the packed artifact.
|
|
115
|
+
|
|
116
|
+
## Final Verification Reminder
|
|
117
|
+
|
|
118
|
+
FV-ALL is complete from `.litcodex/plans/litopencode-rebrand-sdd.md`. Final evidence is recorded at `.litcodex/lit-loop/evidence/start-work/FV-ALL-final.txt`.
|
|
119
|
+
|
|
120
|
+
The completed final wave drove the package through an OpenCode-matching plugin surface and included `npm test`, `npm run typecheck`, `npm run scan:legacy-tokens`, `npm run check:version`, `npm run check:pack-payload`, `npm pack --dry-run --json`, CLI dry-run probes, durable ledger probes, docs tests, and runtime skills coverage.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generate the LitOpenCode cover image (2560x1280).
|
|
3
|
+
|
|
4
|
+
The visual style follows the sibling cover scripts in this workspace: a dark
|
|
5
|
+
gradient field, soft glow, monospace product title, and a compact subtitle.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from PIL import Image, ImageDraw, ImageFilter, ImageFont
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
W, H = 2560, 1280
|
|
15
|
+
CORNER_RADIUS = 80
|
|
16
|
+
ROOT = Path(__file__).resolve().parent
|
|
17
|
+
OUT_PATH = ROOT / "cover.png"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def make_blob(size, color_rgba, cx, cy, rx, ry):
|
|
21
|
+
layer = Image.new("RGBA", size, (0, 0, 0, 0))
|
|
22
|
+
draw = ImageDraw.Draw(layer)
|
|
23
|
+
draw.ellipse([cx - rx, cy - ry, cx + rx, cy + ry], fill=color_rgba)
|
|
24
|
+
return layer
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def load_font(size, bold=False):
|
|
28
|
+
try:
|
|
29
|
+
return ImageFont.truetype("/System/Library/Fonts/Menlo.ttc", size, index=1 if bold else 0)
|
|
30
|
+
except Exception:
|
|
31
|
+
fallback = "/System/Library/Fonts/Supplemental/Courier New Bold.ttf"
|
|
32
|
+
return ImageFont.truetype(fallback, size)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def draw_text_layer(text, x, y, font, color):
|
|
36
|
+
layer = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
|
37
|
+
ImageDraw.Draw(layer).text((x, y), text, font=font, fill=color)
|
|
38
|
+
return layer
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
base = Image.new("RGBA", (W, H), (9, 11, 18, 255))
|
|
42
|
+
|
|
43
|
+
blobs = [
|
|
44
|
+
(37, 99, 235, 150, 620, 360, 720, 520, 125),
|
|
45
|
+
(245, 158, 11, 150, 1880, 400, 620, 460, 105),
|
|
46
|
+
(20, 184, 166, 120, 1320, 1040, 980, 360, 95),
|
|
47
|
+
(168, 85, 247, 105, 1420, 650, 560, 380, 85),
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
canvas = base.copy()
|
|
51
|
+
for r, g, b, a, cx, cy, rx, ry, blur in blobs:
|
|
52
|
+
blob = make_blob((W, H), (r, g, b, a), cx, cy, rx, ry)
|
|
53
|
+
blob = blob.filter(ImageFilter.GaussianBlur(radius=blur))
|
|
54
|
+
canvas = Image.alpha_composite(canvas, blob)
|
|
55
|
+
|
|
56
|
+
canvas = canvas.filter(ImageFilter.GaussianBlur(radius=8))
|
|
57
|
+
|
|
58
|
+
rng = np.random.default_rng(42)
|
|
59
|
+
noise = rng.integers(0, 255, (H, W), dtype=np.uint8)
|
|
60
|
+
grain_alpha = (noise * 0.16).astype(np.uint8)
|
|
61
|
+
grain_layer = np.stack([noise, noise, noise, grain_alpha], axis=-1).astype(np.uint8)
|
|
62
|
+
canvas = Image.alpha_composite(canvas, Image.fromarray(grain_layer, "RGBA"))
|
|
63
|
+
|
|
64
|
+
TITLE_TEXT = "litopencode"
|
|
65
|
+
SUBTITLE_TEXT = "OpenCode plugin for lit-plan, lit-loop, and durable release gates."
|
|
66
|
+
|
|
67
|
+
tmp = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
|
68
|
+
d = ImageDraw.Draw(tmp)
|
|
69
|
+
|
|
70
|
+
font_title = load_font(224, bold=True)
|
|
71
|
+
font_subtitle = load_font(64)
|
|
72
|
+
|
|
73
|
+
t_bbox = d.textbbox((0, 0), TITLE_TEXT, font=font_title)
|
|
74
|
+
t_w = t_bbox[2] - t_bbox[0]
|
|
75
|
+
t_h = t_bbox[3] - t_bbox[1]
|
|
76
|
+
|
|
77
|
+
max_subtitle_width = W - 360
|
|
78
|
+
while True:
|
|
79
|
+
s_bbox = d.textbbox((0, 0), SUBTITLE_TEXT, font=font_subtitle)
|
|
80
|
+
s_w = s_bbox[2] - s_bbox[0]
|
|
81
|
+
if s_w <= max_subtitle_width:
|
|
82
|
+
break
|
|
83
|
+
current_size = getattr(font_subtitle, "size", 64)
|
|
84
|
+
if current_size <= 42:
|
|
85
|
+
break
|
|
86
|
+
font_subtitle = load_font(current_size - 2)
|
|
87
|
+
|
|
88
|
+
s_bbox = d.textbbox((0, 0), SUBTITLE_TEXT, font=font_subtitle)
|
|
89
|
+
s_w = s_bbox[2] - s_bbox[0]
|
|
90
|
+
s_h = s_bbox[3] - s_bbox[1]
|
|
91
|
+
|
|
92
|
+
gap = 52
|
|
93
|
+
total_h = t_h + gap + s_h
|
|
94
|
+
block_top = (H - total_h) // 2 - 20
|
|
95
|
+
|
|
96
|
+
title_x = (W - t_w) // 2 - t_bbox[0]
|
|
97
|
+
title_y = block_top - t_bbox[1]
|
|
98
|
+
subtitle_x = (W - s_w) // 2 - s_bbox[0]
|
|
99
|
+
subtitle_y = title_y + t_h + gap
|
|
100
|
+
|
|
101
|
+
for color, blur_r in [
|
|
102
|
+
((96, 165, 250, 70), 22),
|
|
103
|
+
((20, 184, 166, 90), 11),
|
|
104
|
+
((251, 191, 36, 110), 5),
|
|
105
|
+
]:
|
|
106
|
+
glow = draw_text_layer(TITLE_TEXT, title_x, title_y, font_title, color)
|
|
107
|
+
glow = glow.filter(ImageFilter.GaussianBlur(radius=blur_r))
|
|
108
|
+
canvas = Image.alpha_composite(canvas, glow)
|
|
109
|
+
|
|
110
|
+
canvas = Image.alpha_composite(
|
|
111
|
+
canvas,
|
|
112
|
+
draw_text_layer(TITLE_TEXT, title_x, title_y, font_title, (248, 250, 252, 246)),
|
|
113
|
+
)
|
|
114
|
+
canvas = Image.alpha_composite(
|
|
115
|
+
canvas,
|
|
116
|
+
draw_text_layer(SUBTITLE_TEXT, subtitle_x, subtitle_y, font_subtitle, (203, 213, 225, 222)),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
mask = Image.new("L", (W, H), 0)
|
|
120
|
+
ImageDraw.Draw(mask).rounded_rectangle([(0, 0), (W - 1, H - 1)], radius=CORNER_RADIUS, fill=255)
|
|
121
|
+
canvas.putalpha(mask)
|
|
122
|
+
canvas = canvas.filter(ImageFilter.GaussianBlur(radius=1))
|
|
123
|
+
|
|
124
|
+
canvas.save(OUT_PATH, "PNG", dpi=(400, 400))
|
|
125
|
+
print(f"Saved: {OUT_PATH}")
|
|
126
|
+
print(f"Size: {canvas.size}")
|
|
127
|
+
print(f"Mode: {canvas.mode}")
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "litopencode",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "LitOpenCode OpenCode plugin bootstrap package.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"litopencode": "bin/litopencode"
|
|
8
|
+
},
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "node tools/run-build.mjs",
|
|
15
|
+
"check:pack-payload": "npm run build && npm pack --dry-run --json --ignore-scripts | node tools/check-pack-payload.mjs --stdin",
|
|
16
|
+
"check:version": "node tools/check-version-lockstep.mjs",
|
|
17
|
+
"prepack": "npm run build",
|
|
18
|
+
"scan:legacy-tokens": "node tools/scan-legacy-tokens.mjs",
|
|
19
|
+
"test": "node --test",
|
|
20
|
+
"typecheck": "node tools/run-typecheck.mjs"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@opencode-ai/plugin": "^1.17.6"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^24.12.2",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Agent Roster
|
|
2
|
+
|
|
3
|
+
Use this LitOpenCode skill when a contributor needs the static agent-roster map for `agent-roster`.
|
|
4
|
+
|
|
5
|
+
## Covers
|
|
6
|
+
|
|
7
|
+
- Provide the two primary user-facing agents: `lit-plan` and `lit-loop`.
|
|
8
|
+
- Keep recommended role aliases available as explicit subagent choices.
|
|
9
|
+
- Keep specialist agents available as advanced explicit choices.
|
|
10
|
+
- Merge agent definitions through the OpenCode config hook without overwriting host agents.
|
|
11
|
+
- Keep prompts brand-clean and OpenCode-native.
|
|
12
|
+
|
|
13
|
+
## OpenCode Surfaces
|
|
14
|
+
|
|
15
|
+
- Config hook: LitOpenCode agent merge
|
|
16
|
+
- Primary default agents: `lit-plan`, `lit-loop`
|
|
17
|
+
- Recommended role aliases: `lit-architect`, `lit-forge`, `lit-oracle`, `lit-prover`, `lit-sentinel`, `lit-librarian`
|
|
18
|
+
- Specialist agents: explorer, archive researcher, verdict oracle, strategy planner, forge worker, systems architect, critical reviewer, context cartographer, persistence runner
|
|
19
|
+
- Runtime feature id: `agent-roster`
|
|
20
|
+
- Runtime feature id: `planning-start-work-loop`
|
|
21
|
+
|
|
22
|
+
## Safety
|
|
23
|
+
|
|
24
|
+
- This file is static documentation.
|
|
25
|
+
- Do not execute commands from this file automatically.
|
|
26
|
+
- Preserve existing host agent entries during registration.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Doctor Installer
|
|
2
|
+
|
|
3
|
+
Use this LitOpenCode skill when a contributor needs the static CLI health and installer map for `doctor-install`.
|
|
4
|
+
|
|
5
|
+
## Covers
|
|
6
|
+
|
|
7
|
+
- Inspect package metadata, config source, runtime paths, and state presence.
|
|
8
|
+
- Preview OpenCode plugin configuration changes without writing files.
|
|
9
|
+
- Keep malformed config handling fail-closed.
|
|
10
|
+
- Keep installer output bounded and avoid leaking existing user config content.
|
|
11
|
+
|
|
12
|
+
## OpenCode Surfaces
|
|
13
|
+
|
|
14
|
+
- CLI: `litopencode doctor`
|
|
15
|
+
- CLI: `litopencode install --dry-run`
|
|
16
|
+
- Runtime feature id: `doctor-install`
|
|
17
|
+
|
|
18
|
+
## Safety
|
|
19
|
+
|
|
20
|
+
- This file is static documentation.
|
|
21
|
+
- Do not execute commands from this file automatically.
|
|
22
|
+
- Treat dry-run output as a summary of intended mutation, not as a full config dump.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Durable LitGoal
|
|
2
|
+
|
|
3
|
+
Use this LitOpenCode skill when a contributor needs the static durable-goal map for `durable-ledger`.
|
|
4
|
+
|
|
5
|
+
## Covers
|
|
6
|
+
|
|
7
|
+
- Store resumable goal and loop state under `.litopencode/litgoal`.
|
|
8
|
+
- Append durable JSONL events for command, tool, and goal activity.
|
|
9
|
+
- Recover temporary ledger files before status reads.
|
|
10
|
+
- Fail closed when durable ledger lines are malformed.
|
|
11
|
+
|
|
12
|
+
## OpenCode Surfaces
|
|
13
|
+
|
|
14
|
+
- Tool: `litwork` with `status`
|
|
15
|
+
- Runtime state: `.litopencode/litgoal/lit-loop/ledger.jsonl`
|
|
16
|
+
- Runtime feature id: `durable-ledger`
|
|
17
|
+
|
|
18
|
+
## Safety
|
|
19
|
+
|
|
20
|
+
- This file is static documentation.
|
|
21
|
+
- Do not execute commands from this file automatically.
|
|
22
|
+
- Keep ledger events JSON-serializable and redact command arguments before persistence.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Litwork Activation
|
|
2
|
+
|
|
3
|
+
Use this LitOpenCode skill when a contributor needs the static activation-surface map for `lit-litwork-activation`.
|
|
4
|
+
|
|
5
|
+
## Covers
|
|
6
|
+
|
|
7
|
+
- Expose `/lit` and `/litwork` as command activation points.
|
|
8
|
+
- Expose `lit` and `litwork` as OpenCode tools.
|
|
9
|
+
- Record activation as durable ledger metadata without persisting raw command arguments.
|
|
10
|
+
- Keep activation text observable through command hooks.
|
|
11
|
+
|
|
12
|
+
## OpenCode Surfaces
|
|
13
|
+
|
|
14
|
+
- Command: `/lit`
|
|
15
|
+
- Command: `/litwork`
|
|
16
|
+
- Tool: `lit`
|
|
17
|
+
- Tool: `litwork`
|
|
18
|
+
- Hook: `command.execute.before`
|
|
19
|
+
- Runtime feature id: `lit-litwork-activation`
|
|
20
|
+
|
|
21
|
+
## Safety
|
|
22
|
+
|
|
23
|
+
- This file is static documentation.
|
|
24
|
+
- Do not execute commands from this file automatically.
|
|
25
|
+
- Persist redacted argument metadata only.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Release Guardrails
|
|
2
|
+
|
|
3
|
+
Use this LitOpenCode skill when a contributor needs the static release-readiness map for `guardrails`.
|
|
4
|
+
|
|
5
|
+
## Covers
|
|
6
|
+
|
|
7
|
+
- Run scanner, version lockstep, payload guard, dry pack, typecheck, and tests before release-oriented work.
|
|
8
|
+
- Keep CI no-publication by default.
|
|
9
|
+
- Keep package payloads free of local state, evidence, reference archives, fixtures, dependency folders, and package archives.
|
|
10
|
+
- Keep guarded vocabulary exceptions exact, reviewed, and removal-conditioned.
|
|
11
|
+
|
|
12
|
+
## OpenCode Surfaces
|
|
13
|
+
|
|
14
|
+
- npm script: `scan:legacy-tokens`
|
|
15
|
+
- npm script: `check:version`
|
|
16
|
+
- npm script: `check:pack-payload`
|
|
17
|
+
- Runtime feature id: `guardrails`
|
|
18
|
+
|
|
19
|
+
## Safety
|
|
20
|
+
|
|
21
|
+
- This file is static documentation.
|
|
22
|
+
- Do not execute commands from this file automatically.
|
|
23
|
+
- Do not publish, push refs, create tags, or add auth tokens without explicit authorization.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Tool Guards
|
|
2
|
+
|
|
3
|
+
Use this LitOpenCode skill when a contributor needs the static tool-guard map for OpenCode hook behavior.
|
|
4
|
+
|
|
5
|
+
## Covers
|
|
6
|
+
|
|
7
|
+
- Inspect tool execution before and after supported tool calls.
|
|
8
|
+
- Allow unrelated tool calls to pass through unchanged.
|
|
9
|
+
- Deny unsafe guarded tool requests fail-closed.
|
|
10
|
+
- Add structured metadata after supported tool calls complete.
|
|
11
|
+
|
|
12
|
+
## OpenCode Surfaces
|
|
13
|
+
|
|
14
|
+
- Hook: `tool.execute.before`
|
|
15
|
+
- Hook: `tool.execute.after`
|
|
16
|
+
- Runtime feature area: tool guards
|
|
17
|
+
|
|
18
|
+
## Safety
|
|
19
|
+
|
|
20
|
+
- This file is static documentation.
|
|
21
|
+
- Do not execute commands from this file automatically.
|
|
22
|
+
- Keep guard decisions explicit and auditable.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Workflow Loop
|
|
2
|
+
|
|
3
|
+
Use this LitOpenCode skill when a contributor needs the static workflow-loop map for `planning-start-work-loop`.
|
|
4
|
+
|
|
5
|
+
## Covers
|
|
6
|
+
|
|
7
|
+
- Start with a concrete plan before implementation work begins.
|
|
8
|
+
- Keep implementation slices scoped to the active task.
|
|
9
|
+
- Record observable progress through evidence and durable ledger entries.
|
|
10
|
+
- Pair worker changes with independent verification before completion claims.
|
|
11
|
+
|
|
12
|
+
## OpenCode Surfaces
|
|
13
|
+
|
|
14
|
+
- Command: `/litwork`
|
|
15
|
+
- Primary agents: `lit-plan`, `lit-loop`
|
|
16
|
+
- Recommended role aliases: `lit-architect`, `lit-forge`, `lit-oracle`, `lit-prover`, `lit-sentinel`, `lit-librarian`
|
|
17
|
+
- Runtime feature id: `planning-start-work-loop`
|
|
18
|
+
- Runtime feature id: `lit-litwork-activation`
|
|
19
|
+
|
|
20
|
+
## Safety
|
|
21
|
+
|
|
22
|
+
- This file is static documentation.
|
|
23
|
+
- Do not execute commands from this file automatically.
|
|
24
|
+
- Keep project progress in durable state rather than chat-only notes.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
|
|
6
|
+
const FORBIDDEN_PATH_RULES = [
|
|
7
|
+
{ label: "original prompt provenance", pattern: /^INITIAL_PROMPT\.md$/u },
|
|
8
|
+
{ label: "handoff state document", pattern: /^HANDOFF\.md$/u },
|
|
9
|
+
{ label: "repository automation", pattern: /(^|\/)\.github(\/|$)/u },
|
|
10
|
+
{ label: "internal recon/spec document", pattern: /^docs\/(?:recon|spec)(\/|$)/u },
|
|
11
|
+
{ label: "implementation plan", pattern: /(^|\/)plans(\/|$)/u },
|
|
12
|
+
{ label: ".litcodex runtime state", pattern: /(^|\/)\.litcodex(\/|$)/u },
|
|
13
|
+
{ label: ".litopencode runtime state", pattern: /(^|\/)\.litopencode(\/|$)/u },
|
|
14
|
+
{ label: "evidence artifact", pattern: /(^|\/)evidence(\/|$)/u },
|
|
15
|
+
{ label: "test file", pattern: /(^|\/)tests?(\/|$)/u },
|
|
16
|
+
{ label: "reference archive", pattern: /(^|\/)# REFERENCE(\/|$)/u },
|
|
17
|
+
{ label: "package archive", pattern: /\.tgz$/u },
|
|
18
|
+
{ label: "dependency directory", pattern: /(^|\/)node_modules(\/|$)/u },
|
|
19
|
+
{ label: "git directory", pattern: /(^|\/)\.git(\/|$)/u },
|
|
20
|
+
{ label: "local scanner allowlist", pattern: /^tools\/legacy-token-allowlist\.json$/u }
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
class PackPayloadError extends Error {
|
|
24
|
+
constructor(message, exitCode) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "PackPayloadError";
|
|
27
|
+
this.exitCode = exitCode;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseArgs(argv) {
|
|
32
|
+
const options = {
|
|
33
|
+
mode: "stdin",
|
|
34
|
+
filePath: undefined
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
38
|
+
const arg = argv[index];
|
|
39
|
+
const next = argv[index + 1];
|
|
40
|
+
if (arg === "--stdin") {
|
|
41
|
+
options.mode = "stdin";
|
|
42
|
+
} else if (arg === "--file" && next !== undefined) {
|
|
43
|
+
options.mode = "file";
|
|
44
|
+
options.filePath = next;
|
|
45
|
+
index += 1;
|
|
46
|
+
} else {
|
|
47
|
+
throw new PackPayloadError(`unknown or incomplete argument: ${arg}`, 2);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return options;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function readStdin() {
|
|
55
|
+
const chunks = [];
|
|
56
|
+
for await (const chunk of process.stdin) {
|
|
57
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
58
|
+
}
|
|
59
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function readManifestText(options) {
|
|
63
|
+
if (options.mode === "file") {
|
|
64
|
+
return fs.readFile(path.resolve(options.filePath), "utf8");
|
|
65
|
+
}
|
|
66
|
+
return readStdin();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseManifest(text) {
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(text);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
74
|
+
throw new PackPayloadError(`cannot read or parse manifest: ${detail}`, 2);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function requireObject(value, label) {
|
|
79
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
80
|
+
throw new PackPayloadError(`${label} must be a JSON object`, 2);
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normalizeManifestPath(value) {
|
|
86
|
+
return value.split(path.sep).join("/");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readManifestFiles(parsed) {
|
|
90
|
+
if (!Array.isArray(parsed) || parsed.length !== 1) {
|
|
91
|
+
throw new PackPayloadError("manifest must be the one-package array emitted by npm pack --dry-run --json", 2);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const packageEntry = requireObject(parsed[0], "manifest package");
|
|
95
|
+
const files = packageEntry.files;
|
|
96
|
+
if (!Array.isArray(files)) {
|
|
97
|
+
throw new PackPayloadError("manifest package files must be an array", 2);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return files.map((entry, index) => {
|
|
101
|
+
const fileEntry = requireObject(entry, `manifest package files[${index}]`);
|
|
102
|
+
if (typeof fileEntry.path !== "string" || fileEntry.path.trim() === "") {
|
|
103
|
+
throw new PackPayloadError(`manifest package files[${index}].path must be a non-empty string`, 2);
|
|
104
|
+
}
|
|
105
|
+
return normalizeManifestPath(fileEntry.path);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function findForbiddenPaths(paths) {
|
|
110
|
+
const matches = [];
|
|
111
|
+
for (const manifestPath of paths) {
|
|
112
|
+
const rule = FORBIDDEN_PATH_RULES.find((candidate) => candidate.pattern.test(manifestPath));
|
|
113
|
+
if (rule !== undefined) {
|
|
114
|
+
matches.push({ path: manifestPath, reason: rule.label });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return matches;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function printForbiddenPaths(matches) {
|
|
121
|
+
for (const match of matches) {
|
|
122
|
+
console.log(`${match.path} :: ${match.reason}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function main() {
|
|
127
|
+
try {
|
|
128
|
+
const options = parseArgs(process.argv.slice(2));
|
|
129
|
+
const manifestText = await readManifestText(options);
|
|
130
|
+
const manifest = parseManifest(manifestText);
|
|
131
|
+
const files = readManifestFiles(manifest);
|
|
132
|
+
const forbidden = findForbiddenPaths(files);
|
|
133
|
+
|
|
134
|
+
if (forbidden.length > 0) {
|
|
135
|
+
const suffix = forbidden.length === 1 ? "path" : "paths";
|
|
136
|
+
console.log(`pack payload guard failed: ${forbidden.length} forbidden ${suffix}`);
|
|
137
|
+
printForbiddenPaths(forbidden);
|
|
138
|
+
process.exitCode = 1;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const suffix = files.length === 1 ? "file" : "files";
|
|
143
|
+
console.log(`pack payload guard passed: ${files.length} ${suffix} checked`);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (error instanceof PackPayloadError) {
|
|
146
|
+
console.error(`pack payload guard error: ${error.message}`);
|
|
147
|
+
process.exitCode = error.exitCode;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
151
|
+
console.error(`pack payload guard error: ${message}`);
|
|
152
|
+
process.exitCode = 2;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await main();
|