@patze/code-cli 0.16.2 → 0.17.2
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 +33 -0
- package/VERSION +1 -1
- package/dist/backend/run-stream-client.d.ts +1 -0
- package/dist/backend/run-stream-client.d.ts.map +1 -1
- package/dist/backend/run-stream-client.js +17 -0
- package/dist/backend/run-stream-client.js.map +1 -1
- package/dist/cli/interactive/agent-execute-turn.d.ts +1 -0
- package/dist/cli/interactive/agent-execute-turn.d.ts.map +1 -1
- package/dist/cli/interactive/agent-execute-turn.js +24 -1
- package/dist/cli/interactive/agent-execute-turn.js.map +1 -1
- package/dist/cli/interactive/agent-turn.d.ts +1 -0
- package/dist/cli/interactive/agent-turn.d.ts.map +1 -1
- package/dist/cli/interactive/agent-turn.js +1 -0
- package/dist/cli/interactive/agent-turn.js.map +1 -1
- package/dist/cli/interactive/composer-keys.d.ts +11 -0
- package/dist/cli/interactive/composer-keys.d.ts.map +1 -0
- package/dist/cli/interactive/composer-keys.js +19 -0
- package/dist/cli/interactive/composer-keys.js.map +1 -0
- package/dist/cli/interactive/header.d.ts.map +1 -1
- package/dist/cli/interactive/header.js +0 -1
- package/dist/cli/interactive/header.js.map +1 -1
- package/dist/cli/interactive/line-editor.d.ts +23 -0
- package/dist/cli/interactive/line-editor.d.ts.map +1 -1
- package/dist/cli/interactive/line-editor.js +95 -24
- package/dist/cli/interactive/line-editor.js.map +1 -1
- package/dist/cli/interactive/session-controller.d.ts +4 -1
- package/dist/cli/interactive/session-controller.d.ts.map +1 -1
- package/dist/cli/interactive/session-controller.js +2 -1
- package/dist/cli/interactive/session-controller.js.map +1 -1
- package/dist/cli/interactive/shell.d.ts.map +1 -1
- package/dist/cli/interactive/shell.js +0 -1
- package/dist/cli/interactive/shell.js.map +1 -1
- package/dist/cli/interactive/slash-dispatch.d.ts +1 -0
- package/dist/cli/interactive/slash-dispatch.d.ts.map +1 -1
- package/dist/cli/interactive/slash-dispatch.js +1 -0
- package/dist/cli/interactive/slash-dispatch.js.map +1 -1
- package/dist/cli/interactive/transcript-upsert.d.ts +12 -0
- package/dist/cli/interactive/transcript-upsert.d.ts.map +1 -0
- package/dist/cli/interactive/transcript-upsert.js +33 -0
- package/dist/cli/interactive/transcript-upsert.js.map +1 -0
- package/opentui/package.json +18 -0
- package/opentui/scripts/assert-parse.mjs +21 -0
- package/opentui/src/App.tsx +806 -0
- package/opentui/src/line-parse.ts +54 -0
- package/opentui/src/main.tsx +45 -0
- package/opentui/src/patze-dist.ts +11 -0
- package/opentui/src/transcript-render.ts +218 -0
- package/opentui/src/tui-sink.ts +78 -0
- package/opentui/tsconfig.json +13 -0
- package/package.json +6 -2
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { TranscriptEntryKind } from "./transcript-render.js"
|
|
2
|
+
|
|
3
|
+
const ANSI_PATTERN = /\x1b\[[0-9;]*m/g
|
|
4
|
+
const LABEL_WIDTH = 7
|
|
5
|
+
|
|
6
|
+
const ROLE_MAP: Record<string, TranscriptEntryKind> = {
|
|
7
|
+
you: "user",
|
|
8
|
+
agent: "assistant",
|
|
9
|
+
tool: "tool",
|
|
10
|
+
run: "status",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function stripAnsi(value: string): string {
|
|
14
|
+
return value.replace(ANSI_PATTERN, "")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function parseTranscriptLine(raw: string): {
|
|
18
|
+
kind: TranscriptEntryKind
|
|
19
|
+
label: string
|
|
20
|
+
text: string
|
|
21
|
+
continuation: boolean
|
|
22
|
+
} | null {
|
|
23
|
+
const line = stripAnsi(raw)
|
|
24
|
+
if (!line.trim()) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (line.startsWith(" ".repeat(LABEL_WIDTH))) {
|
|
29
|
+
return {
|
|
30
|
+
kind: "assistant",
|
|
31
|
+
label: "",
|
|
32
|
+
text: line.slice(LABEL_WIDTH),
|
|
33
|
+
continuation: true,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const match = line.match(/^(you|agent|tool|run)\s*(.*)$/i)
|
|
38
|
+
if (!match) {
|
|
39
|
+
return {
|
|
40
|
+
kind: "status",
|
|
41
|
+
label: "info",
|
|
42
|
+
text: line,
|
|
43
|
+
continuation: false,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const role = match[1].toLowerCase()
|
|
48
|
+
return {
|
|
49
|
+
kind: ROLE_MAP[role] ?? "status",
|
|
50
|
+
label: role,
|
|
51
|
+
text: match[2] ?? "",
|
|
52
|
+
continuation: false,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patze Code OpenTUI shell (cookbook-style TUI).
|
|
3
|
+
* Default when Bun + deps are available; opt out with PATZE_USE_JS=1 or PATZE_USE_OPENTUI=0.
|
|
4
|
+
*/
|
|
5
|
+
import { CliRenderEvents, type CliRenderer, createCliRenderer } from "@opentui/core"
|
|
6
|
+
import { createRoot } from "@opentui/react"
|
|
7
|
+
import { createElement } from "react"
|
|
8
|
+
|
|
9
|
+
import { App, parseCwdArg } from "./App.tsx"
|
|
10
|
+
|
|
11
|
+
function waitUntilDestroyed(renderer: CliRenderer) {
|
|
12
|
+
if (renderer.isDestroyed) {
|
|
13
|
+
return Promise.resolve()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return new Promise<void>((resolve) => {
|
|
17
|
+
renderer.once(CliRenderEvents.DESTROY, () => resolve())
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function main() {
|
|
22
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
23
|
+
throw new Error("OpenTUI requires a TTY. Use PATZE_USE_JS=1 for piped/non-TTY shells.")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const cwd = parseCwdArg()
|
|
27
|
+
const renderer = await createCliRenderer({
|
|
28
|
+
exitOnCtrlC: false,
|
|
29
|
+
maxFps: 30,
|
|
30
|
+
screenMode: "alternate-screen",
|
|
31
|
+
})
|
|
32
|
+
const root = createRoot(renderer)
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
root.render(createElement(App, { cwd }))
|
|
36
|
+
await waitUntilDestroyed(renderer)
|
|
37
|
+
} finally {
|
|
38
|
+
root.unmount()
|
|
39
|
+
if (!renderer.isDestroyed) {
|
|
40
|
+
renderer.destroy()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
await main()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { resolve } from "node:path"
|
|
2
|
+
import { pathToFileURL } from "node:url"
|
|
3
|
+
|
|
4
|
+
export function resolvePatzePackageRoot(): string {
|
|
5
|
+
return process.env.PATZE_CLI_PACKAGE_ROOT || resolve(import.meta.dir, "../..")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function importPatzeDist<T>(relativePath: string): Promise<T> {
|
|
9
|
+
const modulePath = resolve(resolvePatzePackageRoot(), "dist", relativePath)
|
|
10
|
+
return import(pathToFileURL(modulePath).href) as Promise<T>
|
|
11
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
export type TranscriptEntryKind =
|
|
2
|
+
| "assistant"
|
|
3
|
+
| "error"
|
|
4
|
+
| "meta"
|
|
5
|
+
| "status"
|
|
6
|
+
| "tool"
|
|
7
|
+
| "user"
|
|
8
|
+
|
|
9
|
+
export type TranscriptEntry = {
|
|
10
|
+
id: string
|
|
11
|
+
kind: TranscriptEntryKind
|
|
12
|
+
label: string
|
|
13
|
+
text: string
|
|
14
|
+
upsertKey?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type TranscriptPart = {
|
|
18
|
+
text: string
|
|
19
|
+
bold?: boolean
|
|
20
|
+
color?: "blue" | "cyan" | "gray" | "green" | "magenta" | "red" | "white" | "yellow"
|
|
21
|
+
dimColor?: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type TranscriptLine = {
|
|
25
|
+
id: string
|
|
26
|
+
kind: TranscriptEntryKind
|
|
27
|
+
label: string
|
|
28
|
+
parts: TranscriptPart[]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function buildTranscriptLines(entries: TranscriptEntry[], columns: number): TranscriptLine[] {
|
|
32
|
+
const labelWidth = 7
|
|
33
|
+
const textWidth = Math.max(20, columns - labelWidth - 4)
|
|
34
|
+
|
|
35
|
+
return entries.flatMap((entry) => {
|
|
36
|
+
const renderedLines = trimTrailingBlankLines(
|
|
37
|
+
entry.kind === "assistant"
|
|
38
|
+
? renderMarkdownLines(entry.text || "...", textWidth)
|
|
39
|
+
: wrapText(entry.text || "...", textWidth).map((text) => ({
|
|
40
|
+
parts: [{ text }],
|
|
41
|
+
}))
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return renderedLines.map((line, index) => ({
|
|
45
|
+
id: `${entry.id}-${index}`,
|
|
46
|
+
kind: entry.kind,
|
|
47
|
+
label: index === 0 ? entry.label : "",
|
|
48
|
+
parts: line.parts,
|
|
49
|
+
}))
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function trimTrailingBlankLines(lines: Array<{ parts: TranscriptPart[] }>) {
|
|
54
|
+
let end = lines.length
|
|
55
|
+
|
|
56
|
+
while (end > 1 && isBlankRenderedLine(lines[end - 1])) {
|
|
57
|
+
end -= 1
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return lines.slice(0, end)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isBlankRenderedLine(line: { parts: TranscriptPart[] }) {
|
|
64
|
+
return line.parts.every((part) => part.text.trim() === "")
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function renderMarkdownLines(value: string, width: number) {
|
|
68
|
+
const lines: Array<{ parts: TranscriptPart[] }> = []
|
|
69
|
+
let inCodeBlock = false
|
|
70
|
+
|
|
71
|
+
for (const rawLine of value.split("\n")) {
|
|
72
|
+
const line = rawLine.trimEnd()
|
|
73
|
+
const fence = line.match(/^```\s*(.*)$/)
|
|
74
|
+
|
|
75
|
+
if (fence) {
|
|
76
|
+
inCodeBlock = !inCodeBlock
|
|
77
|
+
const language = fence[1]?.trim()
|
|
78
|
+
lines.push({
|
|
79
|
+
parts: [
|
|
80
|
+
{
|
|
81
|
+
text: inCodeBlock
|
|
82
|
+
? `--- code${language ? `: ${language}` : ""}`
|
|
83
|
+
: "--- end code",
|
|
84
|
+
color: "gray",
|
|
85
|
+
dimColor: true,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
})
|
|
89
|
+
continue
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (inCodeBlock) {
|
|
93
|
+
for (const text of wrapText(line || " ", width - 2)) {
|
|
94
|
+
lines.push({
|
|
95
|
+
parts: [{ text: `| ${text}`, color: "gray" }],
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const heading = line.match(/^(#{1,6})\s+(.+)$/)
|
|
102
|
+
if (heading) {
|
|
103
|
+
for (const text of wrapText(heading[2] ?? "", width - 2)) {
|
|
104
|
+
lines.push({
|
|
105
|
+
parts: [{ text: `# ${text}`, bold: true, color: "cyan" }],
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const blockquote = line.match(/^>\s?(.*)$/)
|
|
112
|
+
if (blockquote) {
|
|
113
|
+
for (const text of wrapText(blockquote[1] || " ", width - 2)) {
|
|
114
|
+
lines.push({
|
|
115
|
+
parts: [
|
|
116
|
+
{ text: "> ", color: "gray", dimColor: true },
|
|
117
|
+
...parseInlineMarkdown(text),
|
|
118
|
+
],
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
continue
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const unordered = line.match(/^\s*[-*+]\s+(.+)$/)
|
|
125
|
+
if (unordered) {
|
|
126
|
+
for (const [index, text] of wrapText(unordered[1] ?? "", width - 2).entries()) {
|
|
127
|
+
lines.push({
|
|
128
|
+
parts: [
|
|
129
|
+
{ text: index === 0 ? "- " : " ", color: "cyan" },
|
|
130
|
+
...parseInlineMarkdown(text),
|
|
131
|
+
],
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
continue
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const ordered = line.match(/^\s*(\d+)[.)]\s+(.+)$/)
|
|
138
|
+
if (ordered) {
|
|
139
|
+
const marker = `${ordered[1]}. `
|
|
140
|
+
for (const [index, text] of wrapText(ordered[2] ?? "", width - marker.length).entries()) {
|
|
141
|
+
lines.push({
|
|
142
|
+
parts: [
|
|
143
|
+
{ text: index === 0 ? marker : " ".repeat(marker.length), color: "cyan" },
|
|
144
|
+
...parseInlineMarkdown(text),
|
|
145
|
+
],
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (const text of wrapText(line, width)) {
|
|
152
|
+
lines.push({ parts: parseInlineMarkdown(text) })
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return lines
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function wrapText(value: string, width: number) {
|
|
160
|
+
const lines: string[] = []
|
|
161
|
+
|
|
162
|
+
for (const rawLine of value.split("\n")) {
|
|
163
|
+
let line = rawLine
|
|
164
|
+
|
|
165
|
+
if (!line) {
|
|
166
|
+
lines.push("")
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
while (line.length > width) {
|
|
171
|
+
let breakAt = line.lastIndexOf(" ", width)
|
|
172
|
+
if (breakAt < width * 0.5) {
|
|
173
|
+
breakAt = width
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
lines.push(line.slice(0, breakAt).trimEnd())
|
|
177
|
+
line = line.slice(breakAt).trimStart()
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
lines.push(line)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return lines
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function parseInlineMarkdown(value: string): TranscriptPart[] {
|
|
187
|
+
const parts: TranscriptPart[] = []
|
|
188
|
+
const pattern = /(\*\*[^*]+\*\*|`[^`]+`|\*[^*]+\*)/g
|
|
189
|
+
let lastIndex = 0
|
|
190
|
+
let match: RegExpExecArray | null
|
|
191
|
+
|
|
192
|
+
while ((match = pattern.exec(value)) !== null) {
|
|
193
|
+
if (match.index > lastIndex) {
|
|
194
|
+
parts.push({ text: value.slice(lastIndex, match.index) })
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const token = match[0]
|
|
198
|
+
if (token.startsWith("**")) {
|
|
199
|
+
parts.push({ text: token.slice(2, -2), bold: true })
|
|
200
|
+
} else if (token.startsWith("`")) {
|
|
201
|
+
parts.push({ text: token.slice(1, -1), color: "magenta" })
|
|
202
|
+
} else {
|
|
203
|
+
parts.push({ text: token.slice(1, -1), bold: true, color: "cyan" })
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
lastIndex = match.index + token.length
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (lastIndex < value.length) {
|
|
210
|
+
parts.push({ text: value.slice(lastIndex) })
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!parts.length) {
|
|
214
|
+
parts.push({ text: value })
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return parts
|
|
218
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { TranscriptEntry, TranscriptEntryKind } from "./transcript-render.js"
|
|
2
|
+
import { deriveTranscriptUpsertKey } from "../../dist/cli/interactive/transcript-upsert.js"
|
|
3
|
+
|
|
4
|
+
type TextWriter = {
|
|
5
|
+
line: (text?: string) => void
|
|
6
|
+
}
|
|
7
|
+
import { parseTranscriptLine } from "./line-parse.js"
|
|
8
|
+
|
|
9
|
+
export type TranscriptSinkCallbacks = {
|
|
10
|
+
addEntry: (entry: Omit<TranscriptEntry, "id"> & { upsertKey?: string }) => void
|
|
11
|
+
upsertEntry: (upsertKey: string, entry: Omit<TranscriptEntry, "id">) => void
|
|
12
|
+
appendToLast: (text: string) => void
|
|
13
|
+
upsertStreaming: (text: string) => void
|
|
14
|
+
clearStreaming: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createTranscriptSink(callbacks: TranscriptSinkCallbacks): TextWriter {
|
|
18
|
+
return {
|
|
19
|
+
line(text = "") {
|
|
20
|
+
callbacks.clearStreaming()
|
|
21
|
+
const parsed = parseTranscriptLine(text)
|
|
22
|
+
if (!parsed) {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (parsed.continuation) {
|
|
27
|
+
callbacks.appendToLast(parsed.text)
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const entry = {
|
|
32
|
+
kind: parsed.kind,
|
|
33
|
+
label: parsed.label,
|
|
34
|
+
text: parsed.text,
|
|
35
|
+
}
|
|
36
|
+
const upsertKey = deriveTranscriptUpsertKey(parsed.kind, parsed.label, parsed.text)
|
|
37
|
+
if (upsertKey) {
|
|
38
|
+
callbacks.upsertEntry(upsertKey, entry)
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
callbacks.addEntry(entry)
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function classifyPlainOutput(lines: string[]): Array<Omit<TranscriptEntry, "id">> {
|
|
48
|
+
const entries: Array<Omit<TranscriptEntry, "id">> = []
|
|
49
|
+
|
|
50
|
+
for (const raw of lines) {
|
|
51
|
+
const parsed = parseTranscriptLine(raw)
|
|
52
|
+
if (!parsed) {
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (parsed.continuation && entries.length > 0) {
|
|
57
|
+
const last = entries[entries.length - 1]
|
|
58
|
+
last.text = `${last.text}\n${parsed.text}`
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
entries.push({
|
|
63
|
+
kind: parsed.kind,
|
|
64
|
+
label: parsed.label,
|
|
65
|
+
text: parsed.text,
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return entries
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function inferErrorKind(text: string): TranscriptEntryKind {
|
|
73
|
+
const lower = text.toLowerCase()
|
|
74
|
+
if (lower.includes("error") || lower.includes("failed") || lower.includes("unknown command")) {
|
|
75
|
+
return "error"
|
|
76
|
+
}
|
|
77
|
+
return "status"
|
|
78
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"jsxImportSource": "@opentui/react",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"noEmit": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*"]
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patze/code-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.2",
|
|
4
4
|
"description": "Patze Code — local terminal coding agent client for PatzeAgents",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"CHANGELOG.md",
|
|
18
18
|
"NOTICE.md",
|
|
19
19
|
"README.md",
|
|
20
|
-
"VERSION"
|
|
20
|
+
"VERSION",
|
|
21
|
+
"opentui/package.json",
|
|
22
|
+
"opentui/tsconfig.json",
|
|
23
|
+
"opentui/scripts",
|
|
24
|
+
"opentui/src"
|
|
21
25
|
],
|
|
22
26
|
"scripts": {
|
|
23
27
|
"build": "tsc -p tsconfig.json",
|