@litfamily/litopencode 1.0.19 → 1.0.21
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/CHANGELOG.md +14 -0
- package/README-Ko-KR.md +6 -6
- package/README.md +6 -6
- package/dist/agents/registry.js +2 -2
- package/dist/cli/doctor.d.ts +5 -0
- package/dist/cli/doctor.js +27 -6
- package/dist/cli/install-config.d.ts +6 -0
- package/dist/cli/install-config.js +32 -2
- package/dist/cli/install.js +4 -1
- package/dist/cli/model-catalog.d.ts +6 -1
- package/dist/cli/model-catalog.js +32 -15
- package/dist/cli/model-routing.js +14 -9
- package/dist/cli/types.d.ts +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +10 -2
- package/dist/model-route-policy.d.ts +9 -1
- package/dist/model-route-policy.js +46 -47
- package/dist/tui/mission-band.d.ts +28 -8
- package/dist/tui/mission-band.js +223 -39
- package/dist/tui/mission-skills.d.ts +38 -0
- package/dist/tui/mission-skills.js +81 -0
- package/dist/tui/mission-source.d.ts +17 -0
- package/dist/tui/mission-source.js +30 -0
- package/dist/tui/mission-text.d.ts +4 -1
- package/dist/tui/mission-text.js +2 -2
- package/dist/tui.js +47 -21
- package/docs/assets/readme/badge-version.svg +1 -1
- package/docs/migration.md +1 -1
- package/docs/reference-Ko-KR.md +36 -13
- package/docs/reference.md +37 -14
- package/package.json +1 -1
- package/skills/lit-pptx/package-lock.json +167 -119
- package/skills/lit-pptx/package.json +1 -1
- package/skills/managed-skill-manifest.json +4 -4
- package/skills/readme-studio/templates/hyperframes/package-lock.json +169 -121
- package/skills/readme-studio/templates/remotion/package-lock.json +3 -3
- package/tools/check-pack-payload.mjs +2 -2
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { readLitMark } from "./mission-model.js";
|
|
6
|
+
import { readSkillUse, skillChips } from "./mission-skills.js";
|
|
6
7
|
const ledgerByteLimit = 1024 * 1024;
|
|
7
8
|
/** The same root the server plugin uses: the worktree unless it is the filesystem root, else the directory. */
|
|
8
9
|
export function missionProjectRoot(paths) {
|
|
@@ -86,3 +87,32 @@ export function readMissionInput(state, sessionID, ledger, now) {
|
|
|
86
87
|
now
|
|
87
88
|
};
|
|
88
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* What the band card shows beside the mission: the session's skill chips, whether a turn is running,
|
|
92
|
+
* context use and git branch. OpenCode keeps each session's last `session.status` ({ type: "busy" },
|
|
93
|
+
* "retry" or "idle"); a session it has not seen run has none, which counts as not running.
|
|
94
|
+
*/
|
|
95
|
+
export function readCardInput(state, sessionID) {
|
|
96
|
+
const messages = state.session.messages(sessionID);
|
|
97
|
+
const uses = [];
|
|
98
|
+
let since;
|
|
99
|
+
for (const message of messages) {
|
|
100
|
+
const at = num(message.time?.created);
|
|
101
|
+
if (message.role === "user")
|
|
102
|
+
since = at;
|
|
103
|
+
for (const part of state.part(message.id)) {
|
|
104
|
+
const use = readSkillUse(part, at);
|
|
105
|
+
if (use !== undefined)
|
|
106
|
+
uses.push(use);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const status = state.session.status(sessionID)?.type;
|
|
110
|
+
const busy = status === "busy" || status === "retry";
|
|
111
|
+
const branch = state.vcs?.branch?.trim();
|
|
112
|
+
return {
|
|
113
|
+
chips: skillChips(uses, { since, busy }),
|
|
114
|
+
busy,
|
|
115
|
+
contextPercent: contextPercent(state, messages),
|
|
116
|
+
...(branch === undefined || branch === "" ? {} : { branch })
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type MotionFrame } from "./mission-motion.ts";
|
|
2
|
-
import { type MissionView } from "./mission-model.ts";
|
|
2
|
+
import { type MissionStep, type MissionView } from "./mission-model.ts";
|
|
3
3
|
export type Segment = {
|
|
4
4
|
readonly text: string;
|
|
5
5
|
readonly fg: string;
|
|
@@ -53,12 +53,15 @@ export declare function truncateCells(value: string, max: number): string;
|
|
|
53
53
|
export declare function padCells(value: string, width: number): string;
|
|
54
54
|
export declare function lineWidth(line: Line): number;
|
|
55
55
|
export declare function gradientAt(stops: readonly string[], position: number): string;
|
|
56
|
+
/** A light pass along a row: `sweep` is its centre in 0..1 of the row, or negative when off. */
|
|
57
|
+
export declare function shine(color: string, palette: MissionPalette, position: number, sweep: number, reach: number): string;
|
|
56
58
|
export declare function shimmerAt(frame: MotionFrame): number;
|
|
57
59
|
/**
|
|
58
60
|
* A bar of `cells` cells filled to `ratio` with eighth-cell blocks, each cell its own step of the
|
|
59
61
|
* primary-to-accent gradient. The empty track is a thin muted rule, never a dither.
|
|
60
62
|
*/
|
|
61
63
|
export declare function progressBar(ratio: number, cells: number, palette: MissionPalette, sweep?: number): Segment[];
|
|
64
|
+
export declare function stepPill(step: MissionStep, view: MissionView, frame: MotionFrame, palette: MissionPalette): Segment;
|
|
62
65
|
export type ChipTier = "full" | "medium" | "tight";
|
|
63
66
|
export type ChipGroup = readonly Segment[];
|
|
64
67
|
/** The chip's pill groups for a tier; the five-segment bar is appended unless `withMicroBar` is false. */
|
package/dist/tui/mission-text.js
CHANGED
|
@@ -139,7 +139,7 @@ export function gradientAt(stops, position) {
|
|
|
139
139
|
return mixHex(stops[index], stops[index + 1], scaled - index);
|
|
140
140
|
}
|
|
141
141
|
/** A light pass along a row: `sweep` is its centre in 0..1 of the row, or negative when off. */
|
|
142
|
-
function shine(color, palette, position, sweep, reach) {
|
|
142
|
+
export function shine(color, palette, position, sweep, reach) {
|
|
143
143
|
if (sweep < 0)
|
|
144
144
|
return color;
|
|
145
145
|
const distance = Math.abs(position - sweep);
|
|
@@ -232,7 +232,7 @@ function stepLabel(step, state, view) {
|
|
|
232
232
|
return [name, "⚑", count].filter((part) => part !== "").join(" ");
|
|
233
233
|
return [name, state === "now" ? count : ""].filter((part) => part !== "").join(" ");
|
|
234
234
|
}
|
|
235
|
-
function stepPill(step, view, frame, palette) {
|
|
235
|
+
export function stepPill(step, view, frame, palette) {
|
|
236
236
|
const { state, doneMix } = shownState(step, view, frame);
|
|
237
237
|
const before = pillStyle(step.id === "work" ? "now" : "todo", palette, frame.warn);
|
|
238
238
|
const style = state === "done" && doneMix < 1 ? blendStyle(before, pillStyle("done", palette, frame.warn), doneMix, palette) : pillStyle(state, palette, frame.warn);
|
package/dist/tui.js
CHANGED
|
@@ -4,8 +4,9 @@ import { jsx } from "@opentui/solid/jsx-runtime";
|
|
|
4
4
|
import { buildMissionView, lastMission, leadingNotice, missionNotices } from "./tui/mission-model.js";
|
|
5
5
|
import { idleMotion, motionTiming, planMotion, sampleMotion } from "./tui/mission-motion.js";
|
|
6
6
|
import { chipBudget, chipLine, homeLine, missionPanel, paletteFromTheme, panelWidth } from "./tui/mission-text.js";
|
|
7
|
-
import { createLedgerReader, missionProjectRoot, readMissionInput } from "./tui/mission-source.js";
|
|
8
|
-
import {
|
|
7
|
+
import { createLedgerReader, missionProjectRoot, readCardInput, readMissionInput } from "./tui/mission-source.js";
|
|
8
|
+
import { chipShimmer, idleChipMotion, planChipMotion } from "./tui/mission-skills.js";
|
|
9
|
+
import { bandCard, readBandOptions, statusCard } from "./tui/mission-band.js";
|
|
9
10
|
export const litOpenCodeTuiPluginId = "litopencode";
|
|
10
11
|
const homeLineWidth = 69;
|
|
11
12
|
const refreshDelayMs = 80;
|
|
@@ -61,7 +62,7 @@ const tui = async (api, options) => {
|
|
|
61
62
|
}
|
|
62
63
|
function anyMoving(now) {
|
|
63
64
|
for (const entry of sessions.values())
|
|
64
|
-
if (sampleMotion(entry.motion, now).moving)
|
|
65
|
+
if (sampleMotion(entry.motion, now).moving || chipShimmer(entry.chipMotion, now) !== undefined)
|
|
65
66
|
return true;
|
|
66
67
|
return false;
|
|
67
68
|
}
|
|
@@ -83,15 +84,22 @@ const tui = async (api, options) => {
|
|
|
83
84
|
const source = state();
|
|
84
85
|
const ledger = readLedger(missionProjectRoot(source.path));
|
|
85
86
|
const next = buildMissionView(readMissionInput(source, sessionID, ledger, now));
|
|
87
|
+
const card = readCardInput(source, sessionID);
|
|
86
88
|
const entry = sessions.get(sessionID);
|
|
87
|
-
|
|
89
|
+
const viewChanged = entry === undefined || JSON.stringify(entry.view) !== JSON.stringify(next);
|
|
90
|
+
if (!viewChanged && JSON.stringify(entry.card) === JSON.stringify(card))
|
|
88
91
|
return;
|
|
89
|
-
if (entry !== undefined) {
|
|
92
|
+
if (entry !== undefined && viewChanged) {
|
|
90
93
|
const notice = leadingNotice(missionNotices(entry.view, next));
|
|
91
94
|
if (notice !== undefined)
|
|
92
95
|
api.ui.toast({ variant: notice.variant, title: notice.title, message: notice.message, duration: toastMs });
|
|
93
96
|
}
|
|
94
|
-
sessions.set(sessionID, {
|
|
97
|
+
sessions.set(sessionID, {
|
|
98
|
+
view: next,
|
|
99
|
+
card,
|
|
100
|
+
motion: viewChanged ? planMotion(entry?.view, next, now, entry?.motion ?? idleMotion, motionEnabled) : entry.motion,
|
|
101
|
+
chipMotion: planChipMotion(entry?.card.chips, card.chips, now, entry?.chipMotion ?? idleChipMotion, motionEnabled)
|
|
102
|
+
});
|
|
95
103
|
setClock(now);
|
|
96
104
|
setRevision(revision() + 1);
|
|
97
105
|
ensureFrames();
|
|
@@ -129,14 +137,13 @@ const tui = async (api, options) => {
|
|
|
129
137
|
});
|
|
130
138
|
}
|
|
131
139
|
// Opening a session loads its messages and parts over HTTP, which fires no bus event. Reading the
|
|
132
|
-
// reactive store here re-runs this effect as they arrive, so a resumed session lights up too
|
|
140
|
+
// reactive store here re-runs this effect as they arrive, so a resumed session lights up too and its
|
|
141
|
+
// skill calls come back as chips.
|
|
133
142
|
function watchSession(sessionID) {
|
|
134
143
|
createEffect(() => {
|
|
135
144
|
const source = state();
|
|
136
|
-
for (const message of source.session.messages(sessionID))
|
|
137
|
-
|
|
138
|
-
void source.part(message.id).length;
|
|
139
|
-
}
|
|
145
|
+
for (const message of source.session.messages(sessionID))
|
|
146
|
+
void source.part(message.id).length;
|
|
140
147
|
void source.session.todo(sessionID).length;
|
|
141
148
|
void source.session.status(sessionID)?.type;
|
|
142
149
|
scheduleRefresh();
|
|
@@ -151,6 +158,15 @@ const tui = async (api, options) => {
|
|
|
151
158
|
return { view: entry.view, frame: sampleMotion(entry.motion, clock()) };
|
|
152
159
|
};
|
|
153
160
|
}
|
|
161
|
+
// The band shows every session, lit or not, so it reads the entry whether or not the mission is active.
|
|
162
|
+
function cardView(sessionID) {
|
|
163
|
+
return () => {
|
|
164
|
+
revision();
|
|
165
|
+
const entry = sessions.get(sessionID);
|
|
166
|
+
const now = clock();
|
|
167
|
+
return entry === undefined ? undefined : { ...entry, frame: sampleMotion(entry.motion, now), now };
|
|
168
|
+
};
|
|
169
|
+
}
|
|
154
170
|
for (const type of refreshEvents) {
|
|
155
171
|
api.event.on(type, scheduleRefresh);
|
|
156
172
|
}
|
|
@@ -222,8 +238,9 @@ const tui = async (api, options) => {
|
|
|
222
238
|
}
|
|
223
239
|
}
|
|
224
240
|
});
|
|
225
|
-
// The band:
|
|
226
|
-
// screen; a child (subagent) session shows its parent's mission as a lane.
|
|
241
|
+
// The band: a four-row status card under every session route in app_bottom, idle or working. It
|
|
242
|
+
// follows the session on screen; a child (subagent) session shows its parent's mission as a lane.
|
|
243
|
+
// Home shows no band.
|
|
227
244
|
if (band.enabled) {
|
|
228
245
|
api.slots.register({
|
|
229
246
|
order: 50,
|
|
@@ -247,7 +264,7 @@ const tui = async (api, options) => {
|
|
|
247
264
|
});
|
|
248
265
|
const current = createMemo(() => {
|
|
249
266
|
const place = target();
|
|
250
|
-
const snapshot = place === undefined ? undefined :
|
|
267
|
+
const snapshot = place === undefined ? undefined : cardView(place.sessionID)();
|
|
251
268
|
return snapshot === undefined || place === undefined ? undefined : { ...snapshot, inLane: place.inLane };
|
|
252
269
|
});
|
|
253
270
|
const visible = createMemo(() => current() !== undefined);
|
|
@@ -257,17 +274,28 @@ const tui = async (api, options) => {
|
|
|
257
274
|
return jsx("box", {
|
|
258
275
|
flexDirection: "column",
|
|
259
276
|
flexShrink: 0,
|
|
277
|
+
paddingLeft: 1,
|
|
278
|
+
paddingRight: 1,
|
|
260
279
|
get backgroundColor() { return palette().panel ?? "transparent"; },
|
|
261
280
|
children: () => {
|
|
262
281
|
const snapshot = current();
|
|
263
282
|
if (snapshot === undefined)
|
|
264
283
|
return null;
|
|
265
284
|
const colors = palette();
|
|
266
|
-
const width = dimensions().width;
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
285
|
+
const width = dimensions().width - 2;
|
|
286
|
+
const last = snapshot.view.active ? undefined : lastMission(readLedger(missionProjectRoot(state().path)));
|
|
287
|
+
const shimmer = chipShimmer(snapshot.chipMotion, snapshot.now);
|
|
288
|
+
const lines = statusCard({
|
|
289
|
+
view: snapshot.view,
|
|
290
|
+
frame: snapshot.frame,
|
|
291
|
+
chips: snapshot.card.chips,
|
|
292
|
+
busy: snapshot.card.busy,
|
|
293
|
+
...(shimmer === undefined ? {} : { shimmer }),
|
|
294
|
+
figures: { contextPercent: snapshot.card.contextPercent, ...(snapshot.card.branch === undefined ? {} : { branch: snapshot.card.branch }), inLane: snapshot.inLane },
|
|
295
|
+
...(last === undefined ? {} : { last })
|
|
296
|
+
}, colors, width);
|
|
297
|
+
const rows = lines.map(lineElement);
|
|
298
|
+
const card = band.card ? bandCard(snapshot.view, snapshot.frame, colors, width) : undefined;
|
|
271
299
|
if (card !== undefined) {
|
|
272
300
|
rows.push(jsx("box", {
|
|
273
301
|
border: true,
|
|
@@ -275,8 +303,6 @@ const tui = async (api, options) => {
|
|
|
275
303
|
borderColor: card.border,
|
|
276
304
|
title: card.title,
|
|
277
305
|
titleColor: colors.text,
|
|
278
|
-
marginLeft: 1,
|
|
279
|
-
marginRight: 1,
|
|
280
306
|
paddingLeft: 1,
|
|
281
307
|
paddingRight: 1,
|
|
282
308
|
flexShrink: 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.21"><title>release: 1.0.21</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.21</text></g></svg>
|
package/docs/migration.md
CHANGED
|
@@ -23,7 +23,7 @@ This guide describes the supported migration shape for moving an OpenCode workfl
|
|
|
23
23
|
- Use `litopencode doctor --root <workspace>` to inspect `litopencode.json` route validity and effective `lit-plan` / `lit-loop` settings without writing files.
|
|
24
24
|
- Use `litopencode install --dry-run --root <workspace>` to preview the `opencode.json` plugin mutation without writing files.
|
|
25
25
|
- Route fields include top-level `permissionMode` (`safe`, `balanced`, or `yolo`) plus per-agent/per-category `provider`, `model`, `variant`, `category`, `reasoningEffort`, `textVerbosity`, `thinking`, `temperature`, `topP`, `maxTokens`, `promptAppend`, `tools`, `permission`, and `providerOptions`; generated route files use `variant` as the primary effort knob while `reasoningEffort` stays available for explicit provider-level overrides. LitOpenCode keeps `topP` as its config spelling and projects it to OpenCode's native `top_p` field; this preserves nonreasoning model sampling while Astra's native Responses capability omits unsupported sampling fields.
|
|
26
|
-
- Global route config loads first, project-local route config merges afterward, and effective per-agent fields override category fields. Fresh/reset lead routes use Astra with native `reasoningEffort`. GPT-6 Astra and Sol accept `low`, `medium`, `high`, `xhigh`, `max
|
|
26
|
+
- Global route config loads first, project-local route config merges afterward, and effective per-agent fields override category fields. Fresh/reset lead routes use Astra with native `reasoningEffort`. Each model accepts exactly the efforts OpenCode lists for it: GPT-6 Astra and GPT-6.1 Sol accept `low`, `medium`, `high`, `xhigh`, and `max`; GPT-6 Luna, GPT-6 Sol, and the GPT-5.6 rows accept `none`, `low`, `medium`, `high`, `xhigh`, and `max`. No model accepts `ultra`. A saved route set to `ultra` keeps running as `max`, `litopencode doctor` warns about it, and the next install rewrites it to `max` with a one-line notice after copying the file to `litopencode.json.litopencode-backup`. Astra has no invented `-fast` alias. No-selection installs preserve user route bytes but fail before success output when a managed route is unsafe. Other model IDs remain user-owned custom routes. Existing host entries named `momus` or `litwork-reviewer` preserve explicit routes and receive Astra/xhigh only when route fields are absent. LitOpenCode does not expose Hermes-specific per-subagent overrides.
|
|
27
27
|
- Use `litopencode install --permission-prompt`, `--permission-mode balanced`, or `--yolo` only when you explicitly want LitOpenCode to reduce OpenCode approval prompts. Default installs do not relax permissions. Balanced mode allows routine automation while keeping dangerous bash patterns on ask; YOLO mode writes `permissionMode: "yolo"` and the plugin config hook allows OpenCode `bash`, `edit`, `webfetch`, and `external_directory` globally. In both balanced and YOLO modes, `task` delegation is granted only to execution-capable primary agents; subagents and `lit-plan` stay task-denied so delegation never recurses and the planner cannot select an implementation child. Existing global string/object and per-primary task policies are preserved unless they would weaken those hard deny guards; absent global task policy is not invented, and the runtime lineage guard blocks child task calls even under an explicit global allow. `lit-plan` remains agent-level edit/bash/task `deny`.
|
|
28
28
|
- Restart OpenCode after changing `permissionMode`; OpenCode loads config at startup and running sessions keep the already-loaded permission policy.
|
|
29
29
|
- Planning routes now produce proportionate objective-achievable checklists rather than relying on a later review pass to discover missing execution detail. Each retained item carries action/output/verification, while risky or multi-stage work receives SDD-like gates only when justified. `/review-work` accepts both draft plans (read-only `PASS`/`ITERATE`/`NEEDS-CONTEXT`) and completed work (the existing five-lane review); it never implements a draft plan.
|
package/docs/reference-Ko-KR.md
CHANGED
|
@@ -89,11 +89,14 @@ CI와 비대화형 터미널에서는 자동 질문을 생략하며, 명시한
|
|
|
89
89
|
새로 설치하거나 초기화하면 계획·검토 및 `lit-loop`는 `openai/gpt-6-astra`/`xhigh`,
|
|
90
90
|
실행·연구 helper는 새 기본값인 `openai/gpt-6-luna`/`max`를 사용합니다. `gpt-6.1-sol`/`xhigh`는
|
|
91
91
|
코딩 리드 대안입니다. `gpt-6-sol`은 이전 세대로 계속 선택할 수 있어 이 모델을 쓰는 기존 설정도 그대로 동작하며,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
`-fast` 별칭은 없습니다. 모델마다 OpenCode가 그 모델에 내놓는 추론 수준을 그대로 고를 수 있습니다
|
|
93
|
+
(`opencode models openai --verbose`의 `variants`, OpenCode 1.18.35에서 확인). GPT-6 Astra와 GPT-6.1 Sol은
|
|
94
|
+
`low`, `medium`, `high`, `xhigh`, `max`를 지원합니다. GPT-6 Luna, GPT-6 Sol, GPT-5.6 계열은 `none`, `low`,
|
|
95
|
+
`medium`, `high`, `xhigh`, `max`를 지원합니다. `ultra`를 지원하는 모델은 없습니다. `ultra`로 저장된 설정은 `max`로 동작하고, doctor가 `retiredEfforts`에 알려 주며,
|
|
96
|
+
다음 설치가 백업을 남긴 뒤 `max`로 바꿉니다. 선택 메뉴는 모델을 고른
|
|
97
|
+
뒤 그 역할의 추론 수준을 묻고, 모델이 지원하는 수준을 모두 보여 주며, 추천 수준에 표시를 붙여 기본값으로
|
|
98
|
+
둡니다. 지금 설정의 수준이 여전히 지원되면 그 수준이 기본값입니다. 이전 세대 `gpt-5.6-sol`, `gpt-5.6-terra`,
|
|
99
|
+
`gpt-5.6-luna`는 계속 선택할 수 있고, 현재 호스트 카탈로그에는 이 모델들의 지원 종료일이 등록되어 있지 않습니다.
|
|
97
100
|
일반 설치와
|
|
98
101
|
업데이트는 이미 설정된 모델을 보존합니다. 명시적인 `--model-prompt`만 관리 모델 키를 다시 씁니다.
|
|
99
102
|
기존 GPT-5.6 계열의 `-fast` 의미는 유지하지만 `gpt-6-astra-fast` 별칭은 추가하지 않습니다.
|
|
@@ -263,12 +266,27 @@ OpenCode 1.18은 `tui.json`이나 `tui.jsonc`의 `plugin` 배열에 적힌 TUI
|
|
|
263
266
|
경고가 있으면 warning, 끝나면 success 색입니다. 안에는 두 줄짜리 `LIT`과 그 옆의 작업 이름·상태,
|
|
264
267
|
굵은 목표 제목, 단계 줄, 1/8칸 블록으로 칠하고 빈 곳은 가는 선으로 둔 그라데이션 막대, 다음 할 일이나
|
|
265
268
|
아직 통과하지 못한 기준, 기준 목록, 경고, 최근 증거 네 개가 들어갑니다.
|
|
266
|
-
- 띠(band): 화면 맨
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
269
|
+
- 띠(band): 화면 맨 아래에 붙는 상태 카드입니다(OpenCode의 `app_bottom` 자리). 세션과 사이드바
|
|
270
|
+
아래를 모두 가로지릅니다. lit을 쓰든 안 쓰든 세션 내내 보이고, 쉴 때나 일할 때나 똑같이 네 줄이라
|
|
271
|
+
입력창이 움직이지 않습니다.
|
|
272
|
+
- 위 테두리 왼쪽에 `🔥 LIT · <작업 이름>`, 오른쪽에 단계가 붙습니다(`● WORK`, `⚑ review blocked`,
|
|
273
|
+
`✓ COMPLETE`, lit 턴 전과 세션이 턴을 돌리지 않는 동안에는 `○ IDLE`. 이때는 OpenCode의 `session.status`를
|
|
274
|
+
읽고, 밝게 켜진 단계가 없습니다).
|
|
275
|
+
- 첫째 줄은 칩과 같은 알약으로 그린 단계 줄 `PLAN ━ WORK ━ REVIEW`, 1/8칸 막대, 목표의
|
|
276
|
+
`n/m criteria`, 다음 할 일입니다. 경고가 열려 있는 동안에는 `▸ 다음 일` 자리에 `⚑ <내용>`이
|
|
277
|
+
들어갑니다. 쉬는 세션에서는 흐린 단계 줄 옆에 프로젝트의 마지막 목표를, 목표가 없으면
|
|
278
|
+
`add lit to a prompt to start`를 보여 줍니다.
|
|
279
|
+
- 둘째 줄은 스킬 칩입니다. 세션에서 실행된 LitOpenCode 스킬이 여러 개 함께 보입니다. 지금 요청에서
|
|
280
|
+
실행된 스킬은 요청이 도는 동안 accent 색 알약으로 밝게 보이고, 새로 들어온 칩에는 빛이 짧게 한 번
|
|
281
|
+
지나갑니다. 턴이 끝나면 흐려져 최근 목록으로 남고(최근 것이 앞), 다시 실행되면 다시 밝아집니다.
|
|
282
|
+
자리가 모자라면 남은 칩을 `+N`으로 접습니다.
|
|
283
|
+
- 아래 테두리에는 context 사용량(75%부터 주황), 첫 lit 턴부터 지난 시간(`⟳ 7m`), git 브랜치가
|
|
284
|
+
붙습니다.
|
|
285
|
+
|
|
286
|
+
하위(subagent) 세션에서는 부모 세션의 카드를 `· in lane`과 함께 보여 주고, 홈 화면에는 띠가
|
|
287
|
+
없습니다. 폭이 좁으면 줄마다 덜 중요한 것부터 덜어 냅니다. 단계 줄은 다음 할 일 글자, 막대 길이,
|
|
288
|
+
막대, 기준 개수, 단계 이름 순서로, 아래 테두리는 브랜치, 시간, context 사용량 순서로 뺍니다. 줄을
|
|
289
|
+
넘기지는 않습니다. 완료 순간도 여기서 함께 보입니다.
|
|
272
290
|
- 홈 화면 입력창 아래, OpenCode 팁 다음 줄: `🔥 lit ready · last: <목표> ✓ n/m`. 프로젝트 ledger에
|
|
273
291
|
목표가 있을 때만 보입니다.
|
|
274
292
|
- 경고는 색만 바꾸지 않고 늘 `⚑`와 글자를 함께 붙입니다. accent와 warning이 같은 색인 테마가 있기
|
|
@@ -281,6 +299,11 @@ OpenCode 1.18은 `tui.json`이나 `tui.jsonc`의 `plugin` 배열에 적힌 TUI
|
|
|
281
299
|
|
|
282
300
|
데이터는 이미 있는 것만 읽고, TUI를 위해 새로 쓰는 파일은 없습니다.
|
|
283
301
|
|
|
302
|
+
- 스킬 칩은 OpenCode가 이미 저장하는 세 곳에서 읽습니다. 이름이 LitOpenCode가 싣고 다니는 스킬인
|
|
303
|
+
내장 `skill` 도구 호출(다른 스킬은 빼고, 불러오기에 실패한 호출과 상태 조회는 실행으로 치지 않음),
|
|
304
|
+
이 플러그인의 `lit`, `litwork`, `start-work`, `review-work`, `wikify` 도구 호출, 그리고 맨 `lit`
|
|
305
|
+
라우팅과 슬래시 명령에 붙는 아래의 `metadata.litopencode` 표시입니다. "지금 요청"은 세션이 일하는
|
|
306
|
+
동안 마지막 사용자 메시지 이후에 실행된 것을 뜻합니다.
|
|
284
307
|
- `lit`이 모드를 켜거나 명령이 실행될 때(`save-plan` 포함) 이 플러그인이 사용자 메시지 part에 붙이는
|
|
285
308
|
`metadata.litopencode` 표시. 표시가 없는 세션은 쉬는 상태로 보고, 기본 `lit-loop` agent여도 칩과
|
|
286
309
|
패널을 숨깁니다.
|
|
@@ -299,7 +322,7 @@ ease-out-back으로 160ms씩, 60ms 간격으로 들어오고, 막대는 300ms에
|
|
|
299
322
|
띠 설정도 같은 옵션 객체에 적습니다.
|
|
300
323
|
|
|
301
324
|
- `{ "band": false }`는 띠만 없애고 칩, 패널, 홈 줄은 그대로 둡니다.
|
|
302
|
-
- `{ "band": { "card": true } }`는
|
|
325
|
+
- `{ "band": { "card": true } }`는 상태 카드 아래에 경고, 다음 할 일, 목표를 담은 둥근 상세 카드를 붙입니다.
|
|
303
326
|
목표가 있는 동안 카드는 네 줄로 고정되어, 경고가 생기거나 사라져도 입력창이 움직이지 않습니다.
|
|
304
327
|
카드는 기본으로 꺼져 있습니다.
|
|
305
328
|
|
|
@@ -343,7 +366,7 @@ OpenCode는 세션이 바쁜 동안 들어온 `summarize` 호출을 줄 세웠
|
|
|
343
366
|
|
|
344
367
|
### Doctor
|
|
345
368
|
|
|
346
|
-
`litopencode doctor`의 출력에 `autoHandoff` 블록이 추가됩니다. 실제로 적용되는 스위치와 퍼센트, 각각의 출처, 상태 파일 경로, 두 변수 이름, 호스트의 압축 지점, `warnings`가 들어 있습니다. 호스트 지점은 LitOpenCode가 설치하는 창(372,000토큰, 압축은 334,800토큰, 곧 90%)에서만 알 수 있습니다. 그 지점과 같거나 높은 퍼센트는 OpenCode가 먼저 압축하므로 경고가 납니다. 다른 모델은 창과 여유분을 호스트가 정하므로 doctor가 숫자를 내놓지 않습니다.
|
|
369
|
+
`litopencode doctor`의 출력에 `autoHandoff` 블록이 추가됩니다. 플러그인과 같은 프로젝트(작업 폴더 위의 git 루트, 없으면 그 폴더)에서 읽습니다. 실제로 적용되는 스위치와 퍼센트, 각각의 출처, 프로젝트, 상태 파일 경로, 두 변수 이름, 호스트의 압축 지점, `warnings`가 들어 있습니다. 호스트 지점은 LitOpenCode가 설치하는 창(372,000토큰, 압축은 334,800토큰, 곧 90%)에서만 알 수 있습니다. 그 지점과 같거나 높은 퍼센트는 OpenCode가 먼저 압축하므로 경고가 납니다. 다른 모델은 창과 여유분을 호스트가 정하므로 doctor가 숫자를 내놓지 않습니다.
|
|
347
370
|
|
|
348
371
|
### 디스크에 남는 것
|
|
349
372
|
|
package/docs/reference.md
CHANGED
|
@@ -91,11 +91,15 @@ may use a different provider. New installs default to GPT-6 Astra/xhigh for lead
|
|
|
91
91
|
review roles; GPT-6.1 Sol/xhigh (`gpt-6.1-sol`) is the coding-lead alternative, and GPT-6 Luna/max is the helper
|
|
92
92
|
default for execution, research, and ordinary workers. `gpt-6-sol` stays selectable as the previous
|
|
93
93
|
generation, so existing configs that name it keep working; it accepts the same efforts and has no `-fast` alias.
|
|
94
|
-
|
|
95
|
-
`
|
|
94
|
+
Each model offers exactly the reasoning efforts OpenCode lists for it (the model's `variants` in
|
|
95
|
+
`opencode models openai --verbose`, checked on OpenCode 1.18.35). GPT-6 Astra and GPT-6.1 Sol accept
|
|
96
|
+
`low`, `medium`, `high`, `xhigh`, and `max`. GPT-6 Luna, GPT-6 Sol, and the GPT-5.6 rows accept `none`,
|
|
97
|
+
`low`, `medium`, `high`, `xhigh`, and `max`. No model accepts `ultra`; a saved `ultra` route runs as `max`,
|
|
98
|
+
doctor lists it under `retiredEfforts`, and the next install rewrites it to `max` after a backup. After each model the picker
|
|
99
|
+
asks for that role's effort, lists every effort the model accepts, marks the recommended one, and
|
|
100
|
+
defaults to it, or to the current route's effort when that is still offered. The previous-generation
|
|
96
101
|
`gpt-5.6-sol`, `gpt-5.6-terra`, and `gpt-5.6-luna` remain selectable, and the current host catalog
|
|
97
|
-
has no retirement metadata for them.
|
|
98
|
-
unsupported for that model. This previous-generation restriction does not apply to GPT-6 Luna.
|
|
102
|
+
has no retirement metadata for them.
|
|
99
103
|
xAI rows are the reasoning-capable Grok models from the host catalog, `grok-4.6` first.
|
|
100
104
|
`--subagent-model <provider/id>` and `--subagent-effort <e>` set the helper route from flags.
|
|
101
105
|
Legacy OpenAI `<id>-fast` aliases remain accepted and policy-checked as their base ids;
|
|
@@ -366,13 +370,27 @@ What it draws:
|
|
|
366
370
|
Inside: a two-row `LIT` with the discipline and a status beside it, the goal title in bold, the
|
|
367
371
|
phase line, a gradient bar drawn in eighth-cell blocks over a thin rule, the next todo or open
|
|
368
372
|
criterion, the criteria, warnings, and the last four evidence events.
|
|
369
|
-
- A band:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
373
|
+
- A band: a status card along the bottom of the screen (OpenCode's `app_bottom` slot), under the
|
|
374
|
+
session and the sidebar alike. It is on screen for the whole session, lit or not, and keeps the
|
|
375
|
+
same four rows idle or working, so the prompt never moves:
|
|
376
|
+
- the top border carries `🔥 LIT · <discipline>` on the left and the phase on the right
|
|
377
|
+
(`● WORK`, `⚑ review blocked`, `✓ COMPLETE`, or `○ IDLE` before any lit turn and whenever the
|
|
378
|
+
session is not running a turn, read from OpenCode's own `session.status`; at rest no step is lit);
|
|
379
|
+
- row one is the phase track `PLAN ━ WORK ━ REVIEW` in the chip's pills, an eighth-block bar,
|
|
380
|
+
`n/m criteria` for the goal and the next task. While a warning is open, `⚑ <text>` takes the
|
|
381
|
+
place of `▸ next`. An idle session shows the dim track with the project's last goal, or
|
|
382
|
+
`add lit to a prompt to start`;
|
|
383
|
+
- row two holds the skill chips: every LitOpenCode skill that fired in the session, several at
|
|
384
|
+
once. Skills fired by the current request are bright accent pills while it runs, and a new one
|
|
385
|
+
gets a short light sweep as it arrives; when the turn ends they dim and stay as the recent list,
|
|
386
|
+
newest first, and firing one again brightens it. Chips that do not fit collapse into `+N`;
|
|
387
|
+
- the bottom border holds the context use (amber from 75%), the time since the first lit turn
|
|
388
|
+
(`⟳ 7m`) and the git branch.
|
|
389
|
+
|
|
390
|
+
A subagent session shows its parent's card with `· in lane`; the home screen has no band. When the
|
|
391
|
+
card is narrow each row drops its own low-priority parts: the track drops the next-task text, bar
|
|
392
|
+
width, the bar, the criteria count and then the step labels; the bottom border drops the branch,
|
|
393
|
+
then the time, then the context use. It never wraps. The completion moment plays here too.
|
|
376
394
|
- A line under the home prompt, after OpenCode's tips: `🔥 lit ready · last: <goal> ✓ n/m`. It
|
|
377
395
|
appears only when the project ledger holds a goal.
|
|
378
396
|
- Warnings always carry `⚑` and words, not colour alone, because some themes use one colour for
|
|
@@ -387,6 +405,11 @@ What it draws:
|
|
|
387
405
|
|
|
388
406
|
Where the data comes from, with nothing new written for the TUI:
|
|
389
407
|
|
|
408
|
+
- The skill chips come from three places OpenCode already stores: the built-in `skill` tool's calls
|
|
409
|
+
whose name is a skill LitOpenCode ships (other skills are left out, and a failed load or a status
|
|
410
|
+
read is not a firing), this plugin's own `lit`, `litwork`, `start-work`, `review-work` and `wikify`
|
|
411
|
+
tool calls, and the `metadata.litopencode` marks below for bare `lit` routing and slash commands.
|
|
412
|
+
"Current" means fired since the latest user message while the session is busy.
|
|
390
413
|
- The `metadata.litopencode` marks this plugin already attaches to user-message parts when `lit`
|
|
391
414
|
activates a mode or a command runs (`save-plan` included). A session with no mark is idle and the
|
|
392
415
|
chip and panel stay hidden, even on the default `lit-loop` agent.
|
|
@@ -407,8 +430,8 @@ manager or with `plugin_enabled`.
|
|
|
407
430
|
Band options go in the same options object:
|
|
408
431
|
|
|
409
432
|
- `{ "band": false }` removes the band and leaves the chip, the panel and the home line.
|
|
410
|
-
- `{ "band": { "card": true } }` adds a rounded card under the
|
|
411
|
-
task and the goal. While a goal exists the card keeps a fixed four rows, so a warning arriving or
|
|
433
|
+
- `{ "band": { "card": true } }` adds a rounded detail card under the status card with the warnings,
|
|
434
|
+
the next task and the goal. While a goal exists the card keeps a fixed four rows, so a warning arriving or
|
|
412
435
|
clearing never moves the prompt. The card is off by default.
|
|
413
436
|
|
|
414
437
|
A ledger written from another terminal (for example `litopencode record-evidence`) raises no
|
|
@@ -453,7 +476,7 @@ The order is environment, then saved setting, then config file, then the default
|
|
|
453
476
|
|
|
454
477
|
### Doctor
|
|
455
478
|
|
|
456
|
-
`litopencode doctor` adds an `autoHandoff` block: the effective switch and percent, where each came from, the state file path, the two variable names, the host compaction point, and `warnings`. The host point is known for the window LitOpenCode installs (372,000 tokens with compaction at 334,800, which is 90%). A percent at or above it produces a warning, because OpenCode would compact first. For other models the host decides the window and the reserve, so doctor reports no number.
|
|
479
|
+
`litopencode doctor` adds an `autoHandoff` block, read from the same project the plugin uses (the git root above the working directory, or the directory itself): the effective switch and percent, where each came from, the project, the state file path, the two variable names, the host compaction point, and `warnings`. The host point is known for the window LitOpenCode installs (372,000 tokens with compaction at 334,800, which is 90%). A percent at or above it produces a warning, because OpenCode would compact first. For other models the host decides the window and the reserve, so doctor reports no number.
|
|
457
480
|
|
|
458
481
|
### What is written
|
|
459
482
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litfamily/litopencode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
4
4
|
"description": "Workflow plugin for OpenCode: add lit to a prompt and the work is planned, built, checked and written down so the next session can pick it up.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|