@meistrari/tela-cli 0.6.0 → 0.7.4

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 (59) hide show
  1. package/README.md +56 -106
  2. package/dist/commands/auth.d.ts +2 -0
  3. package/dist/commands/context.d.ts +2 -0
  4. package/dist/commands/doctor-auth.d.ts +2 -0
  5. package/dist/commands/env.d.ts +2 -0
  6. package/dist/credentials.d.ts +2 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +7027 -0
  9. package/dist/output.d.ts +13 -0
  10. package/dist/program.d.ts +2 -0
  11. package/package.json +17 -24
  12. package/scripts/install-skills.cjs +127 -0
  13. package/skills/tela-cli/SKILL.md +20 -0
  14. package/skills/tela-cli/references/agent.md +111 -0
  15. package/cli.ts +0 -25
  16. package/postinstall.sh +0 -19
  17. package/src/agent-api.ts +0 -300
  18. package/src/assets.d.ts +0 -4
  19. package/src/auth.ts +0 -432
  20. package/src/claude.ts +0 -101
  21. package/src/commands/doctor.ts +0 -35
  22. package/src/commands/feedback.ts +0 -701
  23. package/src/commands/metadata.ts +0 -242
  24. package/src/commands/push.ts +0 -208
  25. package/src/commands/run.ts +0 -1699
  26. package/src/commands/status.ts +0 -109
  27. package/src/commands/sync.ts +0 -245
  28. package/src/commit-message.ts +0 -69
  29. package/src/config.ts +0 -799
  30. package/src/diagnostics.ts +0 -813
  31. package/src/errors.ts +0 -122
  32. package/src/forgejo.ts +0 -233
  33. package/src/git.ts +0 -457
  34. package/src/ink/assets/160092__jorickhoofd__dog-bark-1.mp3 +0 -0
  35. package/src/ink/bark.tsx +0 -240
  36. package/src/ink/components.tsx +0 -177
  37. package/src/ink/runtime.tsx +0 -89
  38. package/src/ink/screens/feedback-screen.tsx +0 -394
  39. package/src/ink/screens/push-screen.tsx +0 -231
  40. package/src/ink/screens/run-screen.tsx +0 -291
  41. package/src/ink/screens/setup/breadcrumb.tsx +0 -23
  42. package/src/ink/screens/setup/setup-screen.tsx +0 -611
  43. package/src/ink/screens/setup/step-frame.tsx +0 -30
  44. package/src/ink/screens/setup/steps/done-step.tsx +0 -28
  45. package/src/ink/screens/setup/steps/environment-step.tsx +0 -22
  46. package/src/ink/screens/setup/steps/organization-step.tsx +0 -34
  47. package/src/ink/screens/setup/steps/review-step.tsx +0 -77
  48. package/src/ink/screens/setup/steps/running-step.tsx +0 -36
  49. package/src/ink/screens/setup/steps/ssh-key-step.tsx +0 -47
  50. package/src/ink/screens/setup/steps/text-field-step.tsx +0 -17
  51. package/src/ink/screens/setup/steps.ts +0 -156
  52. package/src/ink/screens/setup/types.ts +0 -65
  53. package/src/ink/screens/sync-screen.tsx +0 -282
  54. package/src/process.ts +0 -251
  55. package/src/program.ts +0 -2105
  56. package/src/runtime.ts +0 -147
  57. package/src/types.ts +0 -165
  58. package/src/ui.ts +0 -295
  59. package/src/version.ts +0 -3
@@ -1,282 +0,0 @@
1
- import React, { useEffect, useMemo, useRef, useState } from 'react'
2
- import { Box, Text, useInput } from 'ink'
3
- import type { BatchSyncFailure, SyncResult } from '../../types.ts'
4
- import { AppFrame, Callout, KeyHints, Section, SelectList, SpinnerText, TextEntry } from '../components.tsx'
5
- import type { CommandScreenController } from '../runtime.tsx'
6
-
7
- export interface SyncScreenResult {
8
- ok: boolean
9
- mode: 'current' | 'repository' | 'all'
10
- successes: SyncResult[]
11
- failures: BatchSyncFailure[]
12
- nextActions: string[]
13
- }
14
-
15
- export interface SyncProgressItem {
16
- id: string
17
- label: string
18
- status: 'pending' | 'running' | 'success' | 'failure'
19
- detail?: string
20
- }
21
-
22
- export interface SyncScreenProps {
23
- controller: CommandScreenController<SyncScreenResult>
24
- mode: 'current' | 'repository' | 'all'
25
- dryRun?: boolean
26
- targetLabel: string
27
- cloneRoot?: string
28
- initialRepository?: string
29
- availableRepositories?: string[]
30
- execute: (
31
- repository: string | undefined,
32
- onProgress: (update: SyncProgressItem) => void,
33
- ) => Promise<SyncScreenResult>
34
- }
35
-
36
- type SyncPhase = 'pick' | 'manual' | 'preview' | 'running' | 'done'
37
-
38
- const MANUAL_ENTRY_VALUE = '__manual__'
39
-
40
- export function SyncScreen(props: SyncScreenProps): React.ReactElement {
41
- const [phase, setPhase] = useState<SyncPhase>(() => {
42
- if (props.mode === 'repository' && !props.initialRepository) {
43
- return props.availableRepositories && props.availableRepositories.length > 0 ? 'pick' : 'manual'
44
- }
45
-
46
- return 'preview'
47
- })
48
- const [repository, setRepository] = useState(props.initialRepository ?? '')
49
- const [manualRepository, setManualRepository] = useState('')
50
- const [currentIndex, setCurrentIndex] = useState(0)
51
- const [result, setResult] = useState<SyncScreenResult | null>(null)
52
- const [progressItems, setProgressItems] = useState<SyncProgressItem[]>(() => {
53
- if (props.mode === 'all' && props.availableRepositories) {
54
- return props.availableRepositories.map(repo => ({
55
- id: repo,
56
- label: repo,
57
- status: 'pending',
58
- }))
59
- }
60
-
61
- return []
62
- })
63
- const startedRef = useRef(false)
64
-
65
- const selectableRepositories = useMemo(() => {
66
- return [
67
- ...(props.availableRepositories ?? []).map(repo => ({
68
- key: repo,
69
- label: repo,
70
- })),
71
- { key: MANUAL_ENTRY_VALUE, label: 'Enter a repository name manually' },
72
- ]
73
- }, [props.availableRepositories])
74
-
75
- useEffect(() => {
76
- if (phase !== 'running' || startedRef.current) {
77
- return
78
- }
79
-
80
- startedRef.current = true
81
- void props.execute(
82
- repository || undefined,
83
- (update) => {
84
- setProgressItems(current => {
85
- const existingIndex = current.findIndex(item => item.id === update.id)
86
- if (existingIndex === -1) {
87
- return [...current, update]
88
- }
89
-
90
- return current.map(item => item.id === update.id ? update : item)
91
- })
92
- },
93
- )
94
- .then((nextResult) => {
95
- setResult(nextResult)
96
- setPhase('done')
97
- })
98
- .catch((error) => {
99
- props.controller.reject(error)
100
- })
101
- }, [phase, props, repository])
102
-
103
- useEffect(() => {
104
- if (phase === 'done' && result) {
105
- props.controller.resolve(result, 40)
106
- }
107
- }, [phase, props, result])
108
-
109
- useInput((input, key) => {
110
- if (phase === 'pick') {
111
- if (key.downArrow || input === 'j') {
112
- setCurrentIndex(current => Math.min(current + 1, selectableRepositories.length - 1))
113
- return
114
- }
115
-
116
- if (key.upArrow || input === 'k') {
117
- setCurrentIndex(current => Math.max(current - 1, 0))
118
- return
119
- }
120
-
121
- if (key.escape) {
122
- props.controller.reject(new Error('Sync cancelled.'))
123
- return
124
- }
125
-
126
- if (key.return) {
127
- const selected = selectableRepositories[currentIndex]?.key
128
- if (selected === MANUAL_ENTRY_VALUE) {
129
- setPhase('manual')
130
- return
131
- }
132
-
133
- setRepository(String(selected ?? ''))
134
- setPhase('preview')
135
- }
136
-
137
- return
138
- }
139
-
140
- if (phase === 'manual') {
141
- if (key.escape) {
142
- props.controller.reject(new Error('Sync cancelled.'))
143
- return
144
- }
145
-
146
- if (key.backspace || key.delete) {
147
- setManualRepository(current => current.slice(0, -1))
148
- return
149
- }
150
-
151
- if (key.return) {
152
- if (!manualRepository.trim()) {
153
- props.controller.reject(new Error('Repository name is required when not running inside a git repository.'))
154
- return
155
- }
156
-
157
- setRepository(manualRepository.trim())
158
- setPhase('preview')
159
- return
160
- }
161
-
162
- if (input) {
163
- setManualRepository(current => `${current}${input}`)
164
- }
165
- return
166
- }
167
-
168
- if (phase === 'preview') {
169
- if (key.escape) {
170
- props.controller.reject(new Error('Sync cancelled.'))
171
- return
172
- }
173
-
174
- if (key.return) {
175
- if (props.dryRun) {
176
- setResult({
177
- ok: true,
178
- mode: props.mode,
179
- successes: [],
180
- failures: [],
181
- nextActions: [
182
- props.mode === 'current'
183
- ? 'Run `tela sync --current` without `--dry-run` to update this checkout.'
184
- : props.mode === 'all'
185
- ? 'Run `tela sync --all` without `--dry-run` to refresh all repositories.'
186
- : `Run \`tela sync ${repository}\` without \`--dry-run\` to clone or update the repository.`,
187
- ],
188
- })
189
- setPhase('done')
190
- return
191
- }
192
-
193
- setPhase('running')
194
- }
195
- }
196
- })
197
-
198
- return (
199
- <AppFrame
200
- title="Sync"
201
- subtitle="Refresh the current checkout, a named repository, or the whole clone root."
202
- footer={
203
- phase === 'pick'
204
- ? '↑/↓ move · Enter select · Esc cancel'
205
- : phase === 'manual'
206
- ? 'Type repository name · Enter continue · Esc cancel'
207
- : phase === 'preview'
208
- ? 'Enter run · Esc cancel'
209
- : undefined
210
- }
211
- status={phase === 'running' ? 'Sync in progress' : undefined}
212
- >
213
- {phase === 'pick' ? (
214
- <>
215
- <Section title="Repository picker">
216
- <SelectList items={selectableRepositories} currentIndex={currentIndex} />
217
- </Section>
218
- <KeyHints items={['Select a repository from the clone root or switch to manual entry']} />
219
- </>
220
- ) : null}
221
-
222
- {phase === 'manual' ? (
223
- <Section title="Repository name">
224
- <TextEntry label="Repository" value={manualRepository} placeholder="agent-api" />
225
- </Section>
226
- ) : null}
227
-
228
- {phase === 'preview' ? (
229
- <>
230
- <Section title={props.dryRun ? 'Dry run' : 'Plan'}>
231
- <Text>Mode: {props.mode}</Text>
232
- <Text>Target: {props.mode === 'repository' ? repository : props.targetLabel}</Text>
233
- {props.cloneRoot ? <Text>Clone root: {props.cloneRoot}</Text> : null}
234
- </Section>
235
- {props.availableRepositories && props.mode === 'all' ? (
236
- <Section title="Repositories">
237
- {props.availableRepositories.map(repo => (
238
- <Text key={repo}>{repo}</Text>
239
- ))}
240
- </Section>
241
- ) : null}
242
- </>
243
- ) : null}
244
-
245
- {phase === 'running' ? (
246
- <>
247
- <SpinnerText label={`Syncing ${props.mode === 'repository' ? repository : props.targetLabel}`} />
248
- {progressItems.length > 0 ? (
249
- <Section title="Progress">
250
- {progressItems.map(item => (
251
- <Text key={item.id}>
252
- {`${item.label}: ${item.status}${item.detail ? ` — ${item.detail}` : ''}`}
253
- </Text>
254
- ))}
255
- </Section>
256
- ) : null}
257
- </>
258
- ) : null}
259
-
260
- {phase === 'done' && result ? (
261
- <>
262
- <Section title="Result">
263
- <Text>Mode: {result.mode}</Text>
264
- <Text>Successes: {result.successes.length}</Text>
265
- <Text>Failures: {result.failures.length}</Text>
266
- </Section>
267
- {result.failures.length > 0 ? (
268
- <Section title="Failures">
269
- {result.failures.map(failure => (
270
- <Text key={failure.targetDir} color="yellow">
271
- {`${failure.targetDir}: ${failure.error}`}
272
- </Text>
273
- ))}
274
- </Section>
275
- ) : (
276
- <Callout tone="success">Sync finished successfully.</Callout>
277
- )}
278
- </>
279
- ) : null}
280
- </AppFrame>
281
- )
282
- }
package/src/process.ts DELETED
@@ -1,251 +0,0 @@
1
- export interface CommandResult {
2
- command: string[]
3
- exitCode: number
4
- stdout: string
5
- stderr: string
6
- }
7
-
8
- export interface RunCommandOptions {
9
- cwd?: string
10
- env?: Record<string, string | undefined>
11
- stdin?: 'pipe' | 'inherit' | 'ignore'
12
- input?: string
13
- allowFailure?: boolean
14
- }
15
-
16
- export interface RunStreamingCommandOptions extends RunCommandOptions {
17
- onStdoutLine?: (line: string) => void | Promise<void>
18
- onStderrLine?: (line: string) => void | Promise<void>
19
- }
20
-
21
- export interface StreamingCommandHandle {
22
- result: Promise<CommandResult>
23
- interrupt(): void
24
- kill(): void
25
- }
26
-
27
- export class CommandError extends Error {
28
- constructor(
29
- message: string,
30
- public readonly result: CommandResult,
31
- ) {
32
- super(message)
33
- }
34
- }
35
-
36
- async function readStream(stream: ReadableStream<Uint8Array> | null | undefined): Promise<string> {
37
- if (!stream) {
38
- return ''
39
- }
40
-
41
- return await new Response(stream).text()
42
- }
43
-
44
- async function readLineStream(
45
- stream: ReadableStream<Uint8Array> | null | undefined,
46
- onLine?: (line: string) => void | Promise<void>,
47
- ): Promise<string> {
48
- if (!stream) {
49
- return ''
50
- }
51
-
52
- const reader = stream.getReader()
53
- const decoder = new TextDecoder()
54
- let buffer = ''
55
- let output = ''
56
-
57
- while (true) {
58
- const { done, value } = await reader.read()
59
- if (done) {
60
- break
61
- }
62
-
63
- const chunk = decoder.decode(value, { stream: true })
64
- output += chunk
65
- buffer += chunk
66
-
67
- let newlineIndex = buffer.indexOf('\n')
68
- while (newlineIndex >= 0) {
69
- const line = buffer.slice(0, newlineIndex).replace(/\r$/, '')
70
- if (onLine) {
71
- await onLine(line)
72
- }
73
- buffer = buffer.slice(newlineIndex + 1)
74
- newlineIndex = buffer.indexOf('\n')
75
- }
76
- }
77
-
78
- const remainder = decoder.decode()
79
- if (remainder) {
80
- output += remainder
81
- buffer += remainder
82
- }
83
-
84
- if (buffer.length > 0 && onLine) {
85
- await onLine(buffer.replace(/\r$/, ''))
86
- }
87
-
88
- return output
89
- }
90
-
91
- export async function runCommand(
92
- command: string[],
93
- options: RunCommandOptions = {},
94
- ): Promise<CommandResult> {
95
- const stdin = options.input !== undefined
96
- ? new Blob([options.input])
97
- : (options.stdin ?? 'pipe')
98
-
99
- const proc = Bun.spawn(command, {
100
- cwd: options.cwd,
101
- env: options.env ? { ...process.env, ...options.env } : process.env,
102
- stdin,
103
- stdout: 'pipe',
104
- stderr: 'pipe',
105
- })
106
-
107
- const [stdout, stderr, exitCode] = await Promise.all([
108
- readStream(proc.stdout),
109
- readStream(proc.stderr),
110
- proc.exited,
111
- ])
112
-
113
- const result: CommandResult = {
114
- command,
115
- exitCode: exitCode ?? 1,
116
- stdout,
117
- stderr,
118
- }
119
-
120
- if (result.exitCode !== 0 && !options.allowFailure) {
121
- throw new CommandError(
122
- `Command failed (${result.exitCode}): ${command.join(' ')}`,
123
- result,
124
- )
125
- }
126
-
127
- return result
128
- }
129
-
130
- export async function runShell(
131
- script: string,
132
- options: Omit<RunCommandOptions, 'input'> = {},
133
- ): Promise<CommandResult> {
134
- return await runCommand(
135
- ['/bin/bash', '-lc', script],
136
- options,
137
- )
138
- }
139
-
140
- export async function commandExists(command: string): Promise<boolean> {
141
- const result = await runCommand(['which', command], { allowFailure: true })
142
- return result.exitCode === 0
143
- }
144
-
145
- export async function runInteractiveCommand(
146
- command: string[],
147
- options: Omit<RunCommandOptions, 'stdin' | 'input'> = {},
148
- ): Promise<CommandResult> {
149
- const proc = Bun.spawn(command, {
150
- cwd: options.cwd,
151
- env: options.env ? { ...process.env, ...options.env } : process.env,
152
- stdin: 'inherit',
153
- stdout: 'inherit',
154
- stderr: 'inherit',
155
- })
156
-
157
- const exitCode = await proc.exited
158
- const result: CommandResult = {
159
- command,
160
- exitCode: exitCode ?? 1,
161
- stdout: '',
162
- stderr: '',
163
- }
164
-
165
- if (result.exitCode !== 0 && !options.allowFailure) {
166
- throw new CommandError(
167
- `Command failed (${result.exitCode}): ${command.join(' ')}`,
168
- result,
169
- )
170
- }
171
-
172
- return result
173
- }
174
-
175
- export async function runStreamingCommand(
176
- command: string[],
177
- options: RunStreamingCommandOptions = {},
178
- ): Promise<CommandResult> {
179
- return await spawnStreamingCommand(command, options).result
180
- }
181
-
182
- export function spawnStreamingCommand(
183
- command: string[],
184
- options: RunStreamingCommandOptions = {},
185
- ): StreamingCommandHandle {
186
- const stdin = options.input !== undefined
187
- ? new Blob([options.input])
188
- : (options.stdin ?? 'pipe')
189
-
190
- const proc = Bun.spawn(command, {
191
- cwd: options.cwd,
192
- env: options.env ? { ...process.env, ...options.env } : process.env,
193
- stdin,
194
- stdout: 'pipe',
195
- stderr: 'pipe',
196
- })
197
-
198
- const sendSignal = (signal?: string) => {
199
- try {
200
- ;(proc as { kill: (signal?: string) => void }).kill(signal)
201
- }
202
- catch {
203
- try {
204
- proc.kill()
205
- }
206
- catch {
207
- // Best-effort shutdown only.
208
- }
209
- }
210
- }
211
-
212
- const result = (async () => {
213
- try {
214
- const [stdout, stderr, exitCode] = await Promise.all([
215
- readLineStream(proc.stdout, options.onStdoutLine),
216
- readLineStream(proc.stderr, options.onStderrLine),
217
- proc.exited,
218
- ])
219
-
220
- const commandResult: CommandResult = {
221
- command,
222
- exitCode: exitCode ?? 1,
223
- stdout,
224
- stderr,
225
- }
226
-
227
- if (commandResult.exitCode !== 0 && !options.allowFailure) {
228
- throw new CommandError(
229
- `Command failed (${commandResult.exitCode}): ${command.join(' ')}`,
230
- commandResult,
231
- )
232
- }
233
-
234
- return commandResult
235
- }
236
- catch (error) {
237
- sendSignal()
238
- throw error
239
- }
240
- })()
241
-
242
- return {
243
- result,
244
- interrupt() {
245
- sendSignal('SIGINT')
246
- },
247
- kill() {
248
- sendSignal('SIGKILL')
249
- },
250
- }
251
- }