@litfamily/litopencode 1.0.17 → 1.0.19

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README-Ko-KR.md +56 -86
  3. package/README.md +57 -86
  4. package/dist/activation-routing.js +3 -1
  5. package/dist/cli/args.js +5 -1
  6. package/dist/cli/auto-update.js +1 -1
  7. package/dist/cli/doctor.js +7 -5
  8. package/dist/cli/host-limits.js +2 -5
  9. package/dist/cli/install-config.d.ts +4 -0
  10. package/dist/cli/install-config.js +27 -1
  11. package/dist/cli/install-report.js +4 -1
  12. package/dist/cli/install.js +89 -28
  13. package/dist/cli/json.d.ts +1 -0
  14. package/dist/cli/json.js +10 -4
  15. package/dist/cli/jsonc.d.ts +17 -0
  16. package/dist/cli/jsonc.js +174 -0
  17. package/dist/cli/loop.d.ts +1 -1
  18. package/dist/cli/loop.js +81 -1
  19. package/dist/cli/motion-runtime.d.ts +13 -1
  20. package/dist/cli/motion-runtime.js +89 -2
  21. package/dist/cli/plugin-mutation.js +11 -6
  22. package/dist/cli/tui-config.d.ts +52 -0
  23. package/dist/cli/tui-config.js +198 -0
  24. package/dist/cli/types.d.ts +4 -0
  25. package/dist/cli.js +8 -3
  26. package/dist/deliverable-hedge-guard.js +5 -3
  27. package/dist/hooks.js +2 -1
  28. package/dist/tui/mission-band.d.ts +29 -0
  29. package/dist/tui/mission-band.js +116 -0
  30. package/dist/tui/mission-model.d.ts +139 -0
  31. package/dist/tui/mission-model.js +318 -0
  32. package/dist/tui/mission-motion.d.ts +66 -0
  33. package/dist/tui/mission-motion.js +147 -0
  34. package/dist/tui/mission-source.d.ts +56 -0
  35. package/dist/tui/mission-source.js +88 -0
  36. package/dist/tui/mission-text.d.ts +103 -0
  37. package/dist/tui/mission-text.js +613 -0
  38. package/dist/tui.d.ts +6 -0
  39. package/dist/tui.js +317 -0
  40. package/docs/assets/readme/badge-version.svg +1 -1
  41. package/docs/reference-Ko-KR.md +72 -1
  42. package/docs/reference.md +82 -1
  43. package/package.json +21 -2
  44. package/skills/lit-docx/scripts/docx_design.py +1 -1
  45. package/skills/lit-docx/templates/tonalities/brief.yaml +0 -1
  46. package/skills/lit-docx/templates/tonalities/journal.yaml +0 -1
  47. package/skills/lit-docx/templates/tonalities/manual.yaml +0 -1
  48. package/skills/lit-docx/templates/tonalities/memo.yaml +0 -1
  49. package/skills/lit-docx/templates/tonalities/proposal.yaml +0 -1
  50. package/skills/lit-docx/templates/tonalities/report.yaml +0 -1
  51. package/skills/lit-pptx/references/tonalities/atlas.md +3 -4
  52. package/skills/lit-pptx/references/tonalities/chalk.md +2 -2
  53. package/skills/lit-pptx/references/tonalities/gazette.md +3 -3
  54. package/skills/lit-pptx/references/tonalities/ledger.md +6 -7
  55. package/skills/lit-pptx/references/tonalities/night.md +3 -3
  56. package/skills/lit-pptx/references/tonalities/paper.md +4 -5
  57. package/skills/lit-pptx/references/tonalities/signal.md +3 -4
  58. package/skills/lit-pptx/references/tonalities/studio.md +3 -3
  59. package/skills/lit-pptx/scripts/lib/template-registry.js +1 -1
  60. package/skills/lit-pptx/scripts/validate_pptx.py +10 -4
  61. package/skills/lit-pptx/templates/tonalities/atlas/pack.yaml +0 -12
  62. package/skills/lit-pptx/templates/tonalities/chalk/pack.yaml +0 -12
  63. package/skills/lit-pptx/templates/tonalities/gazette/pack.yaml +0 -12
  64. package/skills/lit-pptx/templates/tonalities/ledger/pack.yaml +2 -14
  65. package/skills/lit-pptx/templates/tonalities/night/pack.yaml +0 -12
  66. package/skills/lit-pptx/templates/tonalities/paper/pack.yaml +0 -12
  67. package/skills/lit-pptx/templates/tonalities/signal/pack.yaml +0 -12
  68. package/skills/lit-pptx/templates/tonalities/studio/pack.yaml +0 -12
  69. package/skills/managed-skill-manifest.json +25 -25
  70. package/tools/check-pack-payload.mjs +20 -5
  71. package/tools/version-manifests.json +18 -6
@@ -0,0 +1,56 @@
1
+ import { type MissionInput, type TodoInput } from "./mission-model.ts";
2
+ type MessageLike = {
3
+ readonly id: string;
4
+ readonly role: string;
5
+ readonly agent?: unknown;
6
+ readonly time?: {
7
+ readonly created?: unknown;
8
+ };
9
+ readonly providerID?: unknown;
10
+ readonly modelID?: unknown;
11
+ readonly tokens?: {
12
+ readonly input?: unknown;
13
+ readonly output?: unknown;
14
+ readonly reasoning?: unknown;
15
+ readonly cache?: {
16
+ readonly read?: unknown;
17
+ readonly write?: unknown;
18
+ };
19
+ };
20
+ };
21
+ export type MissionStateSource = {
22
+ readonly path: {
23
+ readonly worktree?: string;
24
+ readonly directory?: string;
25
+ };
26
+ readonly provider: ReadonlyArray<{
27
+ readonly id: string;
28
+ readonly models: Readonly<Record<string, {
29
+ readonly limit?: {
30
+ readonly context?: number;
31
+ };
32
+ }>>;
33
+ }>;
34
+ readonly session: {
35
+ messages(sessionID: string): ReadonlyArray<MessageLike>;
36
+ todo(sessionID: string): ReadonlyArray<TodoInput>;
37
+ status(sessionID: string): {
38
+ readonly type: string;
39
+ readonly message?: string;
40
+ } | undefined;
41
+ permission(sessionID: string): ReadonlyArray<unknown>;
42
+ question(sessionID: string): ReadonlyArray<unknown>;
43
+ };
44
+ part(messageID: string): ReadonlyArray<{
45
+ readonly metadata?: unknown;
46
+ }>;
47
+ };
48
+ /** The same root the server plugin uses: the worktree unless it is the filesystem root, else the directory. */
49
+ export declare function missionProjectRoot(paths: MissionStateSource["path"]): string | undefined;
50
+ /**
51
+ * Reads goals.json under `root`, refusing symbolic links and oversized files. The result is cached
52
+ * on mtime and size, so calling it on every refresh costs one lstat.
53
+ */
54
+ export declare function createLedgerReader(): (root: string | undefined) => unknown;
55
+ export declare function readMissionInput(state: MissionStateSource, sessionID: string, ledger: unknown, now: number): MissionInput;
56
+ export {};
@@ -0,0 +1,88 @@
1
+ // Gathers the mission input from the OpenCode TUI state and the project's evidence ledger. The state
2
+ // shape is the slice of `api.state` the plugin reads, so tests can pass a plain object.
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { readLitMark } from "./mission-model.js";
6
+ const ledgerByteLimit = 1024 * 1024;
7
+ /** The same root the server plugin uses: the worktree unless it is the filesystem root, else the directory. */
8
+ export function missionProjectRoot(paths) {
9
+ const worktree = paths.worktree?.trim();
10
+ if (worktree !== undefined && worktree !== "" && worktree !== "/")
11
+ return worktree;
12
+ const directory = paths.directory?.trim();
13
+ return directory === undefined || directory === "" ? undefined : directory;
14
+ }
15
+ /**
16
+ * Reads goals.json under `root`, refusing symbolic links and oversized files. The result is cached
17
+ * on mtime and size, so calling it on every refresh costs one lstat.
18
+ */
19
+ export function createLedgerReader() {
20
+ let cache;
21
+ return (root) => {
22
+ if (root === undefined)
23
+ return undefined;
24
+ const file = path.join(root, ".litopencode", "litgoal", "lit-loop", "goals.json");
25
+ let stat;
26
+ try {
27
+ stat = fs.lstatSync(file);
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ if (!stat.isFile() || stat.size > ledgerByteLimit)
33
+ return undefined;
34
+ const stamp = `${stat.mtimeMs}:${stat.size}`;
35
+ if (cache?.file === file && cache.stamp === stamp)
36
+ return cache.value;
37
+ let value;
38
+ try {
39
+ value = JSON.parse(fs.readFileSync(file, "utf8"));
40
+ }
41
+ catch {
42
+ value = undefined;
43
+ }
44
+ cache = { file, stamp, value };
45
+ return value;
46
+ };
47
+ }
48
+ function num(value) {
49
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
50
+ }
51
+ function contextPercent(state, messages) {
52
+ const last = [...messages].reverse().find((message) => message.role === "assistant" && num(message.tokens?.output) > 0);
53
+ if (last === undefined)
54
+ return null;
55
+ const tokens = num(last.tokens?.input) + num(last.tokens?.output) + num(last.tokens?.reasoning) + num(last.tokens?.cache?.read) + num(last.tokens?.cache?.write);
56
+ const limit = state.provider.find((provider) => provider.id === last.providerID)?.models[String(last.modelID)]?.limit?.context;
57
+ return typeof limit === "number" && limit > 0 ? (tokens / limit) * 100 : null;
58
+ }
59
+ export function readMissionInput(state, sessionID, ledger, now) {
60
+ const messages = state.session.messages(sessionID);
61
+ const marks = [];
62
+ let agent;
63
+ for (const message of messages) {
64
+ if (message.role !== "user")
65
+ continue;
66
+ if (typeof message.agent === "string")
67
+ agent = message.agent;
68
+ const at = num(message.time?.created);
69
+ for (const part of state.part(message.id)) {
70
+ const mark = readLitMark(part.metadata, at);
71
+ if (mark !== undefined)
72
+ marks.push(mark);
73
+ }
74
+ }
75
+ const status = state.session.status(sessionID);
76
+ return {
77
+ marks,
78
+ ...(agent === undefined ? {} : { agent }),
79
+ todos: state.session.todo(sessionID),
80
+ ledger,
81
+ ...(status === undefined ? {} : { sessionStatus: status.type }),
82
+ ...(status?.type === "retry" && typeof status.message === "string" ? { retryMessage: status.message } : {}),
83
+ pendingPermissions: state.session.permission(sessionID).length,
84
+ pendingQuestions: state.session.question(sessionID).length,
85
+ contextPercent: contextPercent(state, messages),
86
+ now
87
+ };
88
+ }
@@ -0,0 +1,103 @@
1
+ import { type MotionFrame } from "./mission-motion.ts";
2
+ import { type MissionView } from "./mission-model.ts";
3
+ export type Segment = {
4
+ readonly text: string;
5
+ readonly fg: string;
6
+ readonly bg?: string;
7
+ readonly bold?: boolean;
8
+ };
9
+ export type Line = readonly Segment[];
10
+ export type MissionPalette = {
11
+ readonly primary: string;
12
+ readonly accent: string;
13
+ readonly info: string;
14
+ readonly warning: string;
15
+ readonly success: string;
16
+ readonly error: string;
17
+ readonly text: string;
18
+ readonly muted: string;
19
+ readonly borderActive: string;
20
+ readonly borderSubtle: string;
21
+ /** Opaque fills only; undefined when the theme leaves that surface transparent. */
22
+ readonly background?: string;
23
+ readonly panel?: string;
24
+ readonly element?: string;
25
+ /** What fades and tints mix toward: the panel, else the background, else a neutral for the mode. */
26
+ readonly mixBase: string;
27
+ /** Text drawn on a filled pill. */
28
+ readonly onFill: string;
29
+ };
30
+ /** Panel box width in cells, and the content width inside its border and one cell of padding per side. */
31
+ export declare const panelWidth = 36;
32
+ export declare const panelInnerWidth: number;
33
+ /** Theme tokens arrive as RGBA objects; plain hex strings pass through. Alpha is ignored here. */
34
+ export declare function colorToHex(value: unknown, fallback: string): string;
35
+ /**
36
+ * Builds the palette from `api.theme.current`. A surface token with alpha 0 (the system and
37
+ * lucent-orng themes) means "draw no background", never black.
38
+ */
39
+ export declare function paletteFromTheme(theme: Record<string, unknown> | undefined, mode?: "dark" | "light"): MissionPalette;
40
+ /**
41
+ * Columns the prompt row leaves for the chip. OpenCode shows the sidebar when the session is not a
42
+ * child, the stored `sidebar` setting is "auto" and the terminal is wider than 120 columns. An unknown
43
+ * setting is treated as open, the narrower and safe guess.
44
+ */
45
+ export declare function chipBudget(input: {
46
+ readonly width: number;
47
+ readonly sidebar: string | undefined;
48
+ readonly child: boolean;
49
+ }): number;
50
+ export declare function cellWidth(value: string): number;
51
+ /** Cuts `value` to at most `max` cells, ending in an ellipsis when anything was dropped. */
52
+ export declare function truncateCells(value: string, max: number): string;
53
+ export declare function padCells(value: string, width: number): string;
54
+ export declare function lineWidth(line: Line): number;
55
+ export declare function gradientAt(stops: readonly string[], position: number): string;
56
+ export declare function shimmerAt(frame: MotionFrame): number;
57
+ /**
58
+ * A bar of `cells` cells filled to `ratio` with eighth-cell blocks, each cell its own step of the
59
+ * primary-to-accent gradient. The empty track is a thin muted rule, never a dither.
60
+ */
61
+ export declare function progressBar(ratio: number, cells: number, palette: MissionPalette, sweep?: number): Segment[];
62
+ export type ChipTier = "full" | "medium" | "tight";
63
+ export type ChipGroup = readonly Segment[];
64
+ /** The chip's pill groups for a tier; the five-segment bar is appended unless `withMicroBar` is false. */
65
+ export declare function chipGroups(view: MissionView, frame: MotionFrame, palette: MissionPalette, tier: ChipTier, withMicroBar?: boolean): ChipGroup[];
66
+ export declare function groupsWidth(groups: readonly ChipGroup[]): number;
67
+ export declare function fade(segment: Segment, alpha: number, palette: MissionPalette): Segment;
68
+ export declare const chipSparkWidth = 7;
69
+ export declare function chipSparkCells(t: number, palette: MissionPalette): Segment[] | undefined;
70
+ export declare function chipTier(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number): ChipTier;
71
+ /**
72
+ * The phase chip on the right of the prompt's agent/model row: `🔥 LIT` `PLAN` › `WORK n/m` › `REVIEW`
73
+ * and a five-segment bar, shedding labels before values as `budget` shrinks. Idle sessions render nothing.
74
+ */
75
+ export declare function chipLine(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number): Line;
76
+ /** The header status: a glyph and words, or the COMPLETE badge that pops in and settles to green text. */
77
+ export declare function statusSegments(view: MissionView, frame: MotionFrame, palette: MissionPalette): {
78
+ readonly segments: Segment[];
79
+ readonly offset: number;
80
+ };
81
+ export type MissionPanel = {
82
+ readonly border: string;
83
+ readonly title: string;
84
+ readonly titleColor: string;
85
+ readonly bottomTitle?: string;
86
+ readonly background?: string;
87
+ readonly rows: readonly Line[];
88
+ };
89
+ /**
90
+ * The sidebar mission panel: a rounded box whose border carries the state (accent while working,
91
+ * warning on a warning, success when complete), a tiny LIT header, the goal title, the phase line,
92
+ * the bar, the next item, criteria, warnings and the evidence timeline. Rows are at most
93
+ * `panelInnerWidth` cells.
94
+ */
95
+ export declare function missionPanel(view: MissionView, frame: MotionFrame, palette: MissionPalette): MissionPanel | undefined;
96
+ export type LastMission = {
97
+ readonly title: string;
98
+ readonly passed: number;
99
+ readonly total: number;
100
+ readonly complete: boolean;
101
+ };
102
+ /** The home-screen line under the prompt: `🔥 lit ready · last: <goal> ✓ n/m`, labels shed first when narrow. */
103
+ export declare function homeLine(last: LastMission | undefined, palette: MissionPalette, budget: number): Line;