@meistrari/tela-cli 0.7.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 -241
  36. package/src/ink/components.tsx +0 -178
  37. package/src/ink/runtime.tsx +0 -90
  38. package/src/ink/screens/feedback-screen.tsx +0 -395
  39. package/src/ink/screens/push-screen.tsx +0 -232
  40. package/src/ink/screens/run-screen.tsx +0 -292
  41. package/src/ink/screens/setup/breadcrumb.tsx +0 -24
  42. package/src/ink/screens/setup/setup-screen.tsx +0 -612
  43. package/src/ink/screens/setup/step-frame.tsx +0 -31
  44. package/src/ink/screens/setup/steps/done-step.tsx +0 -29
  45. package/src/ink/screens/setup/steps/environment-step.tsx +0 -23
  46. package/src/ink/screens/setup/steps/organization-step.tsx +0 -35
  47. package/src/ink/screens/setup/steps/review-step.tsx +0 -78
  48. package/src/ink/screens/setup/steps/running-step.tsx +0 -37
  49. package/src/ink/screens/setup/steps/ssh-key-step.tsx +0 -48
  50. package/src/ink/screens/setup/steps/text-field-step.tsx +0 -18
  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 -283
  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,283 +0,0 @@
1
- /** @jsxImportSource react */
2
- import React, { useEffect, useMemo, useRef, useState } from 'react'
3
- import { Box, Text, useInput } from 'ink'
4
- import type { BatchSyncFailure, SyncResult } from '../../types.ts'
5
- import { AppFrame, Callout, KeyHints, Section, SelectList, SpinnerText, TextEntry } from '../components.tsx'
6
- import type { CommandScreenController } from '../runtime.tsx'
7
-
8
- export interface SyncScreenResult {
9
- ok: boolean
10
- mode: 'current' | 'repository' | 'all'
11
- successes: SyncResult[]
12
- failures: BatchSyncFailure[]
13
- nextActions: string[]
14
- }
15
-
16
- export interface SyncProgressItem {
17
- id: string
18
- label: string
19
- status: 'pending' | 'running' | 'success' | 'failure'
20
- detail?: string
21
- }
22
-
23
- export interface SyncScreenProps {
24
- controller: CommandScreenController<SyncScreenResult>
25
- mode: 'current' | 'repository' | 'all'
26
- dryRun?: boolean
27
- targetLabel: string
28
- cloneRoot?: string
29
- initialRepository?: string
30
- availableRepositories?: string[]
31
- execute: (
32
- repository: string | undefined,
33
- onProgress: (update: SyncProgressItem) => void,
34
- ) => Promise<SyncScreenResult>
35
- }
36
-
37
- type SyncPhase = 'pick' | 'manual' | 'preview' | 'running' | 'done'
38
-
39
- const MANUAL_ENTRY_VALUE = '__manual__'
40
-
41
- export function SyncScreen(props: SyncScreenProps): React.ReactElement {
42
- const [phase, setPhase] = useState<SyncPhase>(() => {
43
- if (props.mode === 'repository' && !props.initialRepository) {
44
- return props.availableRepositories && props.availableRepositories.length > 0 ? 'pick' : 'manual'
45
- }
46
-
47
- return 'preview'
48
- })
49
- const [repository, setRepository] = useState(props.initialRepository ?? '')
50
- const [manualRepository, setManualRepository] = useState('')
51
- const [currentIndex, setCurrentIndex] = useState(0)
52
- const [result, setResult] = useState<SyncScreenResult | null>(null)
53
- const [progressItems, setProgressItems] = useState<SyncProgressItem[]>(() => {
54
- if (props.mode === 'all' && props.availableRepositories) {
55
- return props.availableRepositories.map(repo => ({
56
- id: repo,
57
- label: repo,
58
- status: 'pending',
59
- }))
60
- }
61
-
62
- return []
63
- })
64
- const startedRef = useRef(false)
65
-
66
- const selectableRepositories = useMemo(() => {
67
- return [
68
- ...(props.availableRepositories ?? []).map(repo => ({
69
- key: repo,
70
- label: repo,
71
- })),
72
- { key: MANUAL_ENTRY_VALUE, label: 'Enter a repository name manually' },
73
- ]
74
- }, [props.availableRepositories])
75
-
76
- useEffect(() => {
77
- if (phase !== 'running' || startedRef.current) {
78
- return
79
- }
80
-
81
- startedRef.current = true
82
- void props.execute(
83
- repository || undefined,
84
- (update) => {
85
- setProgressItems(current => {
86
- const existingIndex = current.findIndex(item => item.id === update.id)
87
- if (existingIndex === -1) {
88
- return [...current, update]
89
- }
90
-
91
- return current.map(item => item.id === update.id ? update : item)
92
- })
93
- },
94
- )
95
- .then((nextResult) => {
96
- setResult(nextResult)
97
- setPhase('done')
98
- })
99
- .catch((error) => {
100
- props.controller.reject(error)
101
- })
102
- }, [phase, props, repository])
103
-
104
- useEffect(() => {
105
- if (phase === 'done' && result) {
106
- props.controller.resolve(result, 40)
107
- }
108
- }, [phase, props, result])
109
-
110
- useInput((input, key) => {
111
- if (phase === 'pick') {
112
- if (key.downArrow || input === 'j') {
113
- setCurrentIndex(current => Math.min(current + 1, selectableRepositories.length - 1))
114
- return
115
- }
116
-
117
- if (key.upArrow || input === 'k') {
118
- setCurrentIndex(current => Math.max(current - 1, 0))
119
- return
120
- }
121
-
122
- if (key.escape) {
123
- props.controller.reject(new Error('Sync cancelled.'))
124
- return
125
- }
126
-
127
- if (key.return) {
128
- const selected = selectableRepositories[currentIndex]?.key
129
- if (selected === MANUAL_ENTRY_VALUE) {
130
- setPhase('manual')
131
- return
132
- }
133
-
134
- setRepository(String(selected ?? ''))
135
- setPhase('preview')
136
- }
137
-
138
- return
139
- }
140
-
141
- if (phase === 'manual') {
142
- if (key.escape) {
143
- props.controller.reject(new Error('Sync cancelled.'))
144
- return
145
- }
146
-
147
- if (key.backspace || key.delete) {
148
- setManualRepository(current => current.slice(0, -1))
149
- return
150
- }
151
-
152
- if (key.return) {
153
- if (!manualRepository.trim()) {
154
- props.controller.reject(new Error('Repository name is required when not running inside a git repository.'))
155
- return
156
- }
157
-
158
- setRepository(manualRepository.trim())
159
- setPhase('preview')
160
- return
161
- }
162
-
163
- if (input) {
164
- setManualRepository(current => `${current}${input}`)
165
- }
166
- return
167
- }
168
-
169
- if (phase === 'preview') {
170
- if (key.escape) {
171
- props.controller.reject(new Error('Sync cancelled.'))
172
- return
173
- }
174
-
175
- if (key.return) {
176
- if (props.dryRun) {
177
- setResult({
178
- ok: true,
179
- mode: props.mode,
180
- successes: [],
181
- failures: [],
182
- nextActions: [
183
- props.mode === 'current'
184
- ? 'Run `tela sync --current` without `--dry-run` to update this checkout.'
185
- : props.mode === 'all'
186
- ? 'Run `tela sync --all` without `--dry-run` to refresh all repositories.'
187
- : `Run \`tela sync ${repository}\` without \`--dry-run\` to clone or update the repository.`,
188
- ],
189
- })
190
- setPhase('done')
191
- return
192
- }
193
-
194
- setPhase('running')
195
- }
196
- }
197
- })
198
-
199
- return (
200
- <AppFrame
201
- title="Sync"
202
- subtitle="Refresh the current checkout, a named repository, or the whole clone root."
203
- footer={
204
- phase === 'pick'
205
- ? '↑/↓ move · Enter select · Esc cancel'
206
- : phase === 'manual'
207
- ? 'Type repository name · Enter continue · Esc cancel'
208
- : phase === 'preview'
209
- ? 'Enter run · Esc cancel'
210
- : undefined
211
- }
212
- status={phase === 'running' ? 'Sync in progress' : undefined}
213
- >
214
- {phase === 'pick' ? (
215
- <>
216
- <Section title="Repository picker">
217
- <SelectList items={selectableRepositories} currentIndex={currentIndex} />
218
- </Section>
219
- <KeyHints items={['Select a repository from the clone root or switch to manual entry']} />
220
- </>
221
- ) : null}
222
-
223
- {phase === 'manual' ? (
224
- <Section title="Repository name">
225
- <TextEntry label="Repository" value={manualRepository} placeholder="agent-api" />
226
- </Section>
227
- ) : null}
228
-
229
- {phase === 'preview' ? (
230
- <>
231
- <Section title={props.dryRun ? 'Dry run' : 'Plan'}>
232
- <Text>Mode: {props.mode}</Text>
233
- <Text>Target: {props.mode === 'repository' ? repository : props.targetLabel}</Text>
234
- {props.cloneRoot ? <Text>Clone root: {props.cloneRoot}</Text> : null}
235
- </Section>
236
- {props.availableRepositories && props.mode === 'all' ? (
237
- <Section title="Repositories">
238
- {props.availableRepositories.map(repo => (
239
- <Text key={repo}>{repo}</Text>
240
- ))}
241
- </Section>
242
- ) : null}
243
- </>
244
- ) : null}
245
-
246
- {phase === 'running' ? (
247
- <>
248
- <SpinnerText label={`Syncing ${props.mode === 'repository' ? repository : props.targetLabel}`} />
249
- {progressItems.length > 0 ? (
250
- <Section title="Progress">
251
- {progressItems.map(item => (
252
- <Text key={item.id}>
253
- {`${item.label}: ${item.status}${item.detail ? ` — ${item.detail}` : ''}`}
254
- </Text>
255
- ))}
256
- </Section>
257
- ) : null}
258
- </>
259
- ) : null}
260
-
261
- {phase === 'done' && result ? (
262
- <>
263
- <Section title="Result">
264
- <Text>Mode: {result.mode}</Text>
265
- <Text>Successes: {result.successes.length}</Text>
266
- <Text>Failures: {result.failures.length}</Text>
267
- </Section>
268
- {result.failures.length > 0 ? (
269
- <Section title="Failures">
270
- {result.failures.map(failure => (
271
- <Text key={failure.targetDir} color="yellow">
272
- {`${failure.targetDir}: ${failure.error}`}
273
- </Text>
274
- ))}
275
- </Section>
276
- ) : (
277
- <Callout tone="success">Sync finished successfully.</Callout>
278
- )}
279
- </>
280
- ) : null}
281
- </AppFrame>
282
- )
283
- }
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
- }