@parallel-cli/parallel 0.4.9 → 0.5.0
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/README.md +23 -6
- package/dist/agents/agent.js +194 -25
- package/dist/agents/execution-policy.js +58 -0
- package/dist/agents/tools.js +71 -17
- package/dist/commands.js +62 -5
- package/dist/controller.js +136 -5
- package/dist/diagnostics.js +209 -0
- package/dist/i18n.js +40 -4
- package/dist/index.js +12 -2
- package/dist/llm/client.js +7 -3
- package/dist/project-context.js +477 -0
- package/dist/project-index.js +186 -0
- package/dist/ui/AgentPanel.js +5 -2
- package/dist/ui/App.js +4 -2
- package/dist/ui/SettingsPanel.js +22 -23
- package/dist/ui/Wizard.js +49 -21
- package/dist/ui/views.js +4 -2
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/ui/App.js
CHANGED
|
@@ -644,7 +644,7 @@ function MainScreen({ ctl, folder, view, focus, rawLogs, systemLines, agentNames
|
|
|
644
644
|
specialists: 'specialists',
|
|
645
645
|
};
|
|
646
646
|
const viewLabel = VIEW_LABEL[view] ?? 'control room';
|
|
647
|
-
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: headerColor, children: ["\u256D", '─'.repeat(cols - 2), "\u256E"] }), _jsxs(Box, { flexDirection: "row", width: cols, children: [_jsx(Text, { color: headerColor, children: "\u2502 " }), _jsxs(Box, { flexDirection: "row", width: cols - 4, justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { bold: true, color: BRAND.primary, children: "PARALLEL" }), _jsx(Text, { color: globalDotColor, children: " \u25CF" }), _jsxs(Text, { color: view === 'agents' ? CHROME.muted : BRAND.muted, children: [" ", agents.length === 0 ? 'ready' : viewLabel] }), rawLogs && focused ? _jsx(Text, { color: UI.warn, children: " [RAW]" }) : null] }), _jsx(Text, { color: CHROME.muted, children: middleTruncate(folder, folderMax) })] }), _jsx(Text, { color: headerColor, children: " \u2502" })] }), _jsxs(Box, { flexDirection: "row", width: cols, children: [_jsx(Text, { color: headerColor, children: "\u2502 " }), _jsxs(Box, { flexDirection: "row", width: cols - 4, justifyContent: "space-between", children: [agents.length > 0 ? (_jsx(Box, { flexDirection: "row", children: _jsxs(Text, { children: [_jsxs(Text, { color: CHROME.muted, children: ["\u25C7 ", idleCount, " idle"] }), ' · ', _jsxs(Text, { color: workingCount > 0 ? STATE.working : CHROME.muted, children: ["\u25CF ", workingCount, " active"] }), ' · ', _jsxs(Text, { color: doneCount > 0 ? STATE.done : CHROME.muted, children: ["\u2713 ", doneCount, " done"] }), ' · ', _jsxs(Text, { color: errorCount > 0 ? STATE.error : CHROME.muted, children: ["\u2717 ", errorCount, " err"] }), workMapAlerts.length > 0 ? (_jsxs(Text, { color: conflictAlerts.length > 0 ? UI.danger : UI.warn, children: [" \u00B7 \u26A0 work-map ", workMapAlerts.length] })) : null] }) })) : (_jsx(Text, { color: CHROME.muted, children: providerModel })), _jsxs(Text, { color: CHROME.muted, children: ["v", VERSION] })] }), _jsx(Text, { color: headerColor, children: " \u2502" })] }), _jsxs(Text, { color: headerColor, children: ["\u2570", '─'.repeat(cols - 2), "\u256F"] })] }), _jsx(Box, { height: bodyHeight, overflow: "hidden", flexDirection: "column", children: view === 'settings' ? (_jsx(SettingsPanel, { ctl: ctl, scope: "global", height: bodyHeight, onClose: onEscape })) : view === 'settings-session' ? (_jsx(SettingsPanel, { ctl: ctl, scope: "session", height: bodyHeight, onClose: onEscape })) : view === 'board' ? (_jsx(BoardView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'notes' ? (_jsx(NotesView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'sessions' ? (_jsx(SessionsView, { projectRoot: ctl.projectRoot, bodyHeight: bodyHeight })) : view === 'diff' ? (_jsx(DiffView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'cost' ? (_jsx(CostView, {
|
|
647
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: headerColor, children: ["\u256D", '─'.repeat(cols - 2), "\u256E"] }), _jsxs(Box, { flexDirection: "row", width: cols, children: [_jsx(Text, { color: headerColor, children: "\u2502 " }), _jsxs(Box, { flexDirection: "row", width: cols - 4, justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { bold: true, color: BRAND.primary, children: "PARALLEL" }), _jsx(Text, { color: globalDotColor, children: " \u25CF" }), _jsxs(Text, { color: view === 'agents' ? CHROME.muted : BRAND.muted, children: [" ", agents.length === 0 ? 'ready' : viewLabel] }), rawLogs && focused ? _jsx(Text, { color: UI.warn, children: " [RAW]" }) : null] }), _jsx(Text, { color: CHROME.muted, children: middleTruncate(folder, folderMax) })] }), _jsx(Text, { color: headerColor, children: " \u2502" })] }), _jsxs(Box, { flexDirection: "row", width: cols, children: [_jsx(Text, { color: headerColor, children: "\u2502 " }), _jsxs(Box, { flexDirection: "row", width: cols - 4, justifyContent: "space-between", children: [agents.length > 0 ? (_jsx(Box, { flexDirection: "row", children: _jsxs(Text, { children: [_jsxs(Text, { color: CHROME.muted, children: ["\u25C7 ", idleCount, " idle"] }), ' · ', _jsxs(Text, { color: workingCount > 0 ? STATE.working : CHROME.muted, children: ["\u25CF ", workingCount, " active"] }), ' · ', _jsxs(Text, { color: doneCount > 0 ? STATE.done : CHROME.muted, children: ["\u2713 ", doneCount, " done"] }), ' · ', _jsxs(Text, { color: errorCount > 0 ? STATE.error : CHROME.muted, children: ["\u2717 ", errorCount, " err"] }), workMapAlerts.length > 0 ? (_jsxs(Text, { color: conflictAlerts.length > 0 ? UI.danger : UI.warn, children: [" \u00B7 \u26A0 work-map ", workMapAlerts.length] })) : null] }) })) : (_jsx(Text, { color: CHROME.muted, children: providerModel })), _jsxs(Text, { color: CHROME.muted, children: ["v", VERSION] })] }), _jsx(Text, { color: headerColor, children: " \u2502" })] }), _jsxs(Text, { color: headerColor, children: ["\u2570", '─'.repeat(cols - 2), "\u256F"] })] }), _jsx(Box, { height: bodyHeight, overflow: "hidden", flexDirection: "column", children: view === 'settings' ? (_jsx(SettingsPanel, { ctl: ctl, scope: "global", height: bodyHeight, onClose: onEscape })) : view === 'settings-session' ? (_jsx(SettingsPanel, { ctl: ctl, scope: "session", height: bodyHeight, onClose: onEscape })) : view === 'board' ? (_jsx(BoardView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'notes' ? (_jsx(NotesView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'sessions' ? (_jsx(SessionsView, { projectRoot: ctl.projectRoot, bodyHeight: bodyHeight })) : view === 'diff' ? (_jsx(DiffView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'cost' ? (_jsx(CostView, { ctl: ctl, bodyHeight: bodyHeight })) : view === 'skills' ? (_jsx(SkillsView, { skills: ctl.getSkills(), bodyHeight: bodyHeight })) : view === 'specialists' ? (_jsx(SpecialistsView, { specialists: ctl.getSpecialists(), bodyHeight: bodyHeight })) : view === 'help' ? (_jsx(HelpView, { bodyHeight: bodyHeight, onSelect: (cmd) => onInput(cmd) })) : agents.length === 0 ? (_jsx(EmptyHub, { bodyHeight: bodyHeight })) : focused ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(AgentTranscript, { agent: focused, logs: visibleLogs, raw: rawLogs, scrolled: clampedScroll, cols: cols }), !focusFollowTail ? _jsx(Text, { color: UI.warn, children: "Viewing older \u00B7 PgDn to latest" }) : null] })) : (_jsx(AgentHub, { agents: agents, ctl: ctl, cols: cols, scroll: clampedHub, visibleRows: hubRows })) }), systemLines.length > 0 && !settingsOpen && (_jsxs(Box, { flexDirection: "column", children: [agents.length > 0 ? _jsx(Text, { color: UI.muted, bold: true, children: "Session" }) : null, (agents.length > 0
|
|
648
648
|
? systemLines
|
|
649
649
|
.filter((l) => !/^Ready|^Type a task|^⚡ Ready|^Default \/task|^Agent .* launched/.test(l.text))
|
|
650
650
|
.slice(-2)
|
|
@@ -658,7 +658,9 @@ function MainScreen({ ctl, folder, view, focus, rawLogs, systemLines, agentNames
|
|
|
658
658
|
return lines.map((line, j) => (_jsx(Text, { color: levelColor, wrap: "truncate-end", children: line }, `${i}-${j}`)));
|
|
659
659
|
})] })), approval && (_jsx(ApprovalPrompt, { request: approval, pendingCount: ctl.approvals.length, onAnswer: (id, ok, always) => ctl.answerApproval(id, ok, always) })), question && (_jsx(QuestionPrompt, { question: question, pendingCount: ctl.questions.length, onAnswer: (id, answer, auto) => ctl.answerQuestion(id, answer, auto) }, question.id)), _jsx(Text, { children: " " }), _jsx(CommandInput, { active: inputActive, placeholder: focus ? `Message ${focus} or /command` : t('main.prompt'), context: focus ? 'focus' : 'hub', targetAgent: focused?.name, modelLabel: ctl.sessionProvider() ? `${ctl.sessionProvider()?.name}:${ctl.session.model}` : undefined, agentNames: agentNames, agents: agents, width: cols, onHeightChange: setInputRows, onSubmit: onInput, onEscape: onEscape, notify: notify }), _jsx(Text, { children: " " }), _jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: [_jsx(Text, { color: CHROME.muted, children: "/ for commands" }), _jsx(Text, { color: CHROME.muted, children: " \u00B7 Shell " }), _jsx(Text, { color: ctl.session.approvalMode === 'ask' ? UI.warn :
|
|
660
660
|
ctl.session.approvalMode === 'yolo' ? UI.danger :
|
|
661
|
-
UI.ok, children: ctl.session.approvalMode === 'auto-safe' ? 'auto' : ctl.session.approvalMode }), agents.length > 0 ? _jsxs(Text, { color: CHROME.muted, children: [" \u00B7 Sessions ", Controller.listSessions(ctl.projectRoot).length] }) : null,
|
|
661
|
+
UI.ok, children: ctl.session.approvalMode === 'auto-safe' ? 'auto' : ctl.session.approvalMode }), agents.length > 0 ? _jsxs(Text, { color: CHROME.muted, children: [" \u00B7 Sessions ", Controller.listSessions(ctl.projectRoot).length] }) : null, _jsxs(Text, { color: ctl.projectContextStatus().status === 'ready' ? UI.ok :
|
|
662
|
+
ctl.projectContextStatus().status === 'indexing' ? UI.warn :
|
|
663
|
+
CHROME.muted, children: [" \u00B7 ", t('memory.label'), " ", ctl.projectContextStatus().status] }), _jsxs(Text, { color: CHROME.muted, children: [" \u00B7 index ", ctl.projectIndexStatus().files] }), ctl.questions.length > 0 ? (_jsxs(Text, { color: UI.warn, children: [" \u00B7 \u2753", ctl.questions.length] })) : null, ctl.approvals.length > 0 ? (_jsxs(Text, { color: UI.warn, children: [" \u00B7 \u23F3", ctl.approvals.length] })) : null, workMapAlerts.length > 0 ? (_jsx(Text, { color: conflictAlerts.length > 0 ? UI.danger : UI.warn, children: " \u00B7 \u26A0 /board" })) : null, conflictAlerts.length > 0 ? (_jsx(Text, { color: UI.danger, children: " \u00B7 run /review all" })) : null, focused ? (_jsxs(Text, { color: BRAND.muted, children: [" \u00B7 \uD83C\uDFAF ", focused.name] })) : null] }) })] }));
|
|
662
664
|
}
|
|
663
665
|
function groupAgents(agents) {
|
|
664
666
|
const needs = agents.filter((a) => ['waiting', 'paused'].includes(a.state));
|
package/dist/ui/SettingsPanel.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { createSkillTemplate, createSpecialistTemplate } from '../skills.js';
|
|
5
5
|
import { priceFor } from '../pricing.js';
|
|
@@ -7,6 +7,9 @@ import { SelectList as BaseSelectList } from './Wizard.js';
|
|
|
7
7
|
import { LANGS, getLang, setLang, t } from '../i18n.js';
|
|
8
8
|
import { detectProviderModels, isLocalProvider, isPlaceholderModel, providerNeedsApiKey, PROVIDER_PRESETS } from '../config.js';
|
|
9
9
|
import { BRAND } from './tokens.js';
|
|
10
|
+
function SettingsSelectList({ defaultBack, onBack, ...rest }) {
|
|
11
|
+
return _jsx(BaseSelectList, { ...rest, onBack: onBack ?? defaultBack });
|
|
12
|
+
}
|
|
10
13
|
function masked(key) {
|
|
11
14
|
if (!key)
|
|
12
15
|
return '—';
|
|
@@ -40,16 +43,12 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
40
43
|
const saved = () => setFlash(t('set.saved'));
|
|
41
44
|
const cfg = ctl.config;
|
|
42
45
|
const listHeight = height ? Math.max(3, height - 5) : undefined;
|
|
43
|
-
const goBack = () => {
|
|
46
|
+
const goBack = useCallback(() => {
|
|
44
47
|
if (step.id === 'root')
|
|
45
48
|
return onClose();
|
|
46
49
|
setStep(returnStep ?? { id: 'root' });
|
|
47
50
|
setReturnStep(null);
|
|
48
|
-
};
|
|
49
|
-
const SelectList = (props) => {
|
|
50
|
-
const { onBack, ...rest } = props;
|
|
51
|
-
return _jsx(BaseSelectList, { ...rest, onBack: onBack ?? goBack });
|
|
52
|
-
};
|
|
51
|
+
}, [onClose, returnStep, step.id]);
|
|
53
52
|
// ---- root menu items ----
|
|
54
53
|
const rootItems = scope === 'global'
|
|
55
54
|
? [
|
|
@@ -164,12 +163,12 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
164
163
|
setStep(next);
|
|
165
164
|
};
|
|
166
165
|
// ---- render ----
|
|
167
|
-
return (_jsxs(Box, { borderStyle: "round", borderColor: BRAND.muted, flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: BRAND.primary, children: scope === 'global' ? t('set.title') : t('sset.title') }), flash ? _jsx(Text, { color: "green", children: flash }) : null, _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [step.id === 'root' && _jsx(
|
|
166
|
+
return (_jsxs(Box, { borderStyle: "round", borderColor: BRAND.muted, flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: BRAND.primary, children: scope === 'global' ? t('set.title') : t('sset.title') }), flash ? _jsx(Text, { color: "green", children: flash }) : null, _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [step.id === 'root' && _jsx(SettingsSelectList, { defaultBack: goBack, items: rootItems, height: listHeight, onSelect: chooseRoot }), step.id === 'lang' && (_jsx(SettingsSelectList, { defaultBack: goBack, items: LANGS.map((l) => ({ label: l.label, value: l.code })), height: listHeight, onSelect: (code) => {
|
|
168
167
|
setLang(code);
|
|
169
168
|
ctl.setLanguage(code);
|
|
170
169
|
saved();
|
|
171
170
|
setStep({ id: 'root' });
|
|
172
|
-
} })), step.id === 'pickProvider' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.chooseProvider') }), _jsx(
|
|
171
|
+
} })), step.id === 'pickProvider' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.chooseProvider') }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [
|
|
173
172
|
...cfg.providers.map((p) => ({ label: p.name, value: p.name, hint: `(${p.baseUrl})` })),
|
|
174
173
|
{ label: t('set.back'), value: '__back__' },
|
|
175
174
|
], height: listHeight, onSelect: (v) => {
|
|
@@ -183,7 +182,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
183
182
|
if (step.next === 'models')
|
|
184
183
|
return setStep({ id: 'modelList', provider: p });
|
|
185
184
|
setStep({ id: 'model', provider: p });
|
|
186
|
-
} })] })), step.id === 'model' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.chooseModel', { name: step.provider.name }) }), _jsx(
|
|
185
|
+
} })] })), step.id === 'model' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.chooseModel', { name: step.provider.name }) }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [
|
|
187
186
|
...step.provider.models.map((m) => ({
|
|
188
187
|
label: m,
|
|
189
188
|
value: m,
|
|
@@ -197,7 +196,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
197
196
|
return;
|
|
198
197
|
}
|
|
199
198
|
pickModel(step.provider, v);
|
|
200
|
-
}, onInput: (m) => pickModel(step.provider, m) })] })), step.id === 'endpoint' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.endpoint.title', { name: step.provider.name }) }), _jsx(Text, { color: "gray", children: step.provider.baseUrl }), _jsx(Text, { color: "gray", children: t('wiz.provider.endpoint.model', { model: step.provider.defaultModel || step.provider.models[0] || '—' }) }), _jsx(
|
|
199
|
+
}, onInput: (m) => pickModel(step.provider, m) })] })), step.id === 'endpoint' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.endpoint.title', { name: step.provider.name }) }), _jsx(Text, { color: "gray", children: step.provider.baseUrl }), _jsx(Text, { color: "gray", children: t('wiz.provider.endpoint.model', { model: step.provider.defaultModel || step.provider.models[0] || '—' }) }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [
|
|
201
200
|
{ label: t('wiz.provider.endpoint.use'), value: 'use' },
|
|
202
201
|
{ label: t('wiz.provider.endpoint.edit'), value: 'edit' },
|
|
203
202
|
{ label: t('set.back'), value: '__back__' },
|
|
@@ -220,7 +219,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
220
219
|
saved();
|
|
221
220
|
setStep(returnStep ?? { id: 'providerDetail', provider: step.provider, scope });
|
|
222
221
|
setReturnStep(null);
|
|
223
|
-
} })] })), step.id === 'setupScope' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.setupScope.title', { name: step.provider.name }) }), _jsx(
|
|
222
|
+
} })] })), step.id === 'setupScope' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.setupScope.title', { name: step.provider.name }) }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [
|
|
224
223
|
{ label: t('set.setupScope.session'), value: 'session' },
|
|
225
224
|
{ label: t('set.setupScope.global'), value: 'global' },
|
|
226
225
|
{ label: t('set.back'), value: '__back__' },
|
|
@@ -228,10 +227,10 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
228
227
|
if (v === '__back__')
|
|
229
228
|
return setStep({ id: 'endpoint', provider: step.provider, setup: true });
|
|
230
229
|
finishProviderSetup(step.provider, v === 'global');
|
|
231
|
-
} })] })), step.id === 'editEndpoint' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.url.title') }), _jsx(
|
|
230
|
+
} })] })), step.id === 'editEndpoint' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.url.title') }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [], height: listHeight, allowInput: true, inputPlaceholder: step.provider.baseUrl, onInput: (url) => {
|
|
232
231
|
const provider = { ...step.provider, baseUrl: url.trim() };
|
|
233
232
|
setStep({ id: 'endpoint', provider, setup: step.setup });
|
|
234
|
-
} })] })), step.id === 'modelList' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.modelsFor', { name: step.provider.name }) }), _jsx(
|
|
233
|
+
} })] })), step.id === 'modelList' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.modelsFor', { name: step.provider.name }) }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [
|
|
235
234
|
...step.provider.models.map((m) => ({
|
|
236
235
|
label: m,
|
|
237
236
|
value: m,
|
|
@@ -262,7 +261,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
262
261
|
saved();
|
|
263
262
|
setStep(returnStep ?? { id: 'root' });
|
|
264
263
|
setReturnStep(null);
|
|
265
|
-
} })] })), step.id === 'priceModel' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.priceModel', { name: step.provider.name }) }), _jsx(
|
|
264
|
+
} })] })), step.id === 'priceModel' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.priceModel', { name: step.provider.name }) }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [
|
|
266
265
|
...step.provider.models.map((m) => {
|
|
267
266
|
const pr = priceFor(step.provider, m);
|
|
268
267
|
return {
|
|
@@ -281,7 +280,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
281
280
|
return;
|
|
282
281
|
}
|
|
283
282
|
goSub({ id: 'priceValue', provider: step.provider, model: v });
|
|
284
|
-
}, onInput: (m) => goSub({ id: 'priceValue', provider: step.provider, model: m.trim() }) })] })), step.id === 'priceValue' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.priceValue', { model: step.model }) }), _jsx(
|
|
283
|
+
}, onInput: (m) => goSub({ id: 'priceValue', provider: step.provider, model: m.trim() }) })] })), step.id === 'priceValue' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.priceValue', { model: step.model }) }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [], height: listHeight, allowInput: true, inputPlaceholder: "0.27, 1.10", onInput: (v) => {
|
|
285
284
|
const m = v.match(/^\s*([\d.]+)\s*[,;\s]\s*([\d.]+)\s*$/);
|
|
286
285
|
if (!m)
|
|
287
286
|
return setFlash(t('set.priceBad'));
|
|
@@ -293,7 +292,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
293
292
|
saved();
|
|
294
293
|
setStep(returnStep ?? { id: 'root' });
|
|
295
294
|
setReturnStep(null);
|
|
296
|
-
} })] })), step.id === 'key' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.key.title', { name: step.provider.name }) }), _jsx(
|
|
295
|
+
} })] })), step.id === 'key' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.key.title', { name: step.provider.name }) }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [], height: listHeight, allowInput: true, mask: true, inputPlaceholder: "sk-\u2026", onInput: (k) => {
|
|
297
296
|
const provider = { ...step.provider, apiKey: k.trim() };
|
|
298
297
|
if (step.setup) {
|
|
299
298
|
if (scope === 'session') {
|
|
@@ -309,7 +308,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
309
308
|
}
|
|
310
309
|
setStep(returnStep ?? { id: 'root' });
|
|
311
310
|
setReturnStep(null);
|
|
312
|
-
} })] })), step.id === 'newName' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.name.title') }), _jsx(
|
|
311
|
+
} })] })), step.id === 'newName' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.name.title') }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [], height: listHeight, allowInput: true, inputPlaceholder: t('wiz.provider.name.ph'), onInput: (name) => setStep({ id: 'newUrl', name }) })] })), step.id === 'newUrl' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.url.title') }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [], height: listHeight, allowInput: true, inputPlaceholder: t('wiz.provider.url.ph'), onInput: (url) => setStep({ id: 'newModel', name: step.name, url }) })] })), step.id === 'newModel' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.model.title') }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [], height: listHeight, allowInput: true, inputPlaceholder: t('wiz.provider.model.ph'), onInput: (model) => {
|
|
313
312
|
const trimmed = model.trim();
|
|
314
313
|
if (isPlaceholderModel(trimmed)) {
|
|
315
314
|
setFlash(t('set.modelPlaceholder'));
|
|
@@ -328,7 +327,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
328
327
|
requiresApiKey: !local,
|
|
329
328
|
},
|
|
330
329
|
});
|
|
331
|
-
} })] })), step.id === 'newKey' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.key.title', { name: step.name }) }), _jsx(
|
|
330
|
+
} })] })), step.id === 'newKey' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('wiz.provider.key.title', { name: step.name }) }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [], height: listHeight, allowInput: true, mask: true, inputPlaceholder: "sk-\u2026", onInput: (key) => finishProviderSetup({ name: step.name, baseUrl: step.url, apiKey: key.trim(), models: [step.model], defaultModel: step.model }) })] })), step.id === 'newSkill' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.newSkillName') }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [], height: listHeight, allowInput: true, inputPlaceholder: "review, deploy, tests\u2026", onInput: (name) => {
|
|
332
331
|
try {
|
|
333
332
|
const file = createSkillTemplate(name.trim(), '', 'global', ctl.projectRoot);
|
|
334
333
|
setFlash(t('m.skillCreated', { file }));
|
|
@@ -337,7 +336,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
337
336
|
setFlash(t('m.alreadyExists', { msg: e?.message ?? '' }));
|
|
338
337
|
}
|
|
339
338
|
setStep({ id: 'root' });
|
|
340
|
-
} })] })), step.id === 'newSpecialist' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.newSpecialistName') }), _jsx(
|
|
339
|
+
} })] })), step.id === 'newSpecialist' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.newSpecialistName') }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [], height: listHeight, allowInput: true, inputPlaceholder: "reviewer, architect, tester\u2026", onInput: (name) => {
|
|
341
340
|
try {
|
|
342
341
|
const file = createSpecialistTemplate(name.trim(), '', 'global', ctl.projectRoot);
|
|
343
342
|
setFlash(t('m.specCreated', { file }));
|
|
@@ -346,7 +345,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
346
345
|
setFlash(t('m.alreadyExists', { msg: e?.message ?? '' }));
|
|
347
346
|
}
|
|
348
347
|
setStep({ id: 'root' });
|
|
349
|
-
} })] })), step.id === 'providers' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: step.scope === 'global' ? t('set.providers.title') : t('sset.providers.title') }), _jsx(
|
|
348
|
+
} })] })), step.id === 'providers' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: step.scope === 'global' ? t('set.providers.title') : t('sset.providers.title') }), _jsx(SettingsSelectList, { defaultBack: goBack, items: (() => {
|
|
350
349
|
const configuredNames = new Set(cfg.providers.map((p) => p.name.toLowerCase()));
|
|
351
350
|
const items = [];
|
|
352
351
|
// Section: Configured
|
|
@@ -443,7 +442,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
443
442
|
else {
|
|
444
443
|
setStep({ id: 'providerDetail', provider: p, scope: 'global' });
|
|
445
444
|
}
|
|
446
|
-
} })] })), step.id === 'providerDetail' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.providerDetail.title', { name: step.provider.name }) }), _jsx(
|
|
445
|
+
} })] })), step.id === 'providerDetail' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.providerDetail.title', { name: step.provider.name }) }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [
|
|
447
446
|
{
|
|
448
447
|
label: t('set.providerDetail.key'),
|
|
449
448
|
value: 'key',
|
|
@@ -508,7 +507,7 @@ export function SettingsPanel({ ctl, scope, height, onClose, }) {
|
|
|
508
507
|
}
|
|
509
508
|
if (v === 'remove')
|
|
510
509
|
return setStep({ id: 'removeProvider', provider: step.provider, scope: step.scope });
|
|
511
|
-
} })] })), step.id === 'removeProvider' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.removeProvider.title', { name: step.provider.name }) }), _jsx(Text, { color: "yellow", children: t('set.removeProvider.confirm') }), _jsx(
|
|
510
|
+
} })] })), step.id === 'removeProvider' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: t('set.removeProvider.title', { name: step.provider.name }) }), _jsx(Text, { color: "yellow", children: t('set.removeProvider.confirm') }), _jsx(SettingsSelectList, { defaultBack: goBack, items: [
|
|
512
511
|
{ label: t('set.removeProvider.yes'), value: 'yes' },
|
|
513
512
|
{ label: t('set.removeProvider.no'), value: 'no' },
|
|
514
513
|
], height: listHeight, onSelect: (v) => {
|
package/dist/ui/Wizard.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import { t } from '../i18n.js';
|
|
5
5
|
import { BRAND, COLOR } from './tokens.js';
|
|
@@ -8,6 +8,16 @@ function clampIndex(index, count) {
|
|
|
8
8
|
return 0;
|
|
9
9
|
return Math.max(0, Math.min(index, count - 1));
|
|
10
10
|
}
|
|
11
|
+
export function selectableIndexes(items) {
|
|
12
|
+
return items.map((it, i) => (it.section ? -1 : i)).filter((i) => i >= 0);
|
|
13
|
+
}
|
|
14
|
+
export function selectListWindow(itemsLength, selectedRealIndex, maxVisible) {
|
|
15
|
+
const visible = Math.max(1, maxVisible);
|
|
16
|
+
if (itemsLength <= visible || selectedRealIndex < 0)
|
|
17
|
+
return { start: 0, end: Math.min(itemsLength, visible) };
|
|
18
|
+
const start = Math.max(0, Math.min(selectedRealIndex - Math.floor(visible / 2), itemsLength - visible));
|
|
19
|
+
return { start, end: start + visible };
|
|
20
|
+
}
|
|
11
21
|
/**
|
|
12
22
|
* Simple ↑/↓ + Entrée select list. If `allowInput` is set, the user can also
|
|
13
23
|
* type a free value (e.g. a folder path or a custom model name) — typing
|
|
@@ -17,9 +27,25 @@ export function SelectList({ items, allowInput, inputPlaceholder, mask, height,
|
|
|
17
27
|
const [idx, setIdx] = useState(0);
|
|
18
28
|
const [typed, setTyped] = useState('');
|
|
19
29
|
const typing = allowInput && typed.length > 0;
|
|
30
|
+
const selectable = useMemo(() => selectableIndexes(items), [items]);
|
|
31
|
+
const safeLogicalIdx = clampIndex(idx, selectable.length);
|
|
32
|
+
const safeRealIdx = selectable.length > 0 ? selectable[safeLogicalIdx] : -1;
|
|
33
|
+
const maxVisible = height ? Math.max(1, height - (allowInput ? 2 : 0)) : items.length;
|
|
34
|
+
const window = selectListWindow(items.length, safeRealIdx, maxVisible);
|
|
35
|
+
const visibleItems = items.slice(window.start, window.end);
|
|
36
|
+
const above = window.start;
|
|
37
|
+
const below = Math.max(0, items.length - window.end);
|
|
38
|
+
const pageStep = Math.max(1, Math.floor(Math.max(1, maxVisible) / 2));
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (safeLogicalIdx !== idx)
|
|
41
|
+
setIdx(safeLogicalIdx);
|
|
42
|
+
}, [idx, safeLogicalIdx]);
|
|
43
|
+
const chooseCurrent = () => {
|
|
44
|
+
const realIdx = selectable[safeLogicalIdx];
|
|
45
|
+
if (realIdx !== undefined && items[realIdx])
|
|
46
|
+
onSelect?.(items[realIdx].value);
|
|
47
|
+
};
|
|
20
48
|
useInput((input, key) => {
|
|
21
|
-
// Build selectable index list each render (cheap — items is small).
|
|
22
|
-
const selectable = items.map((it, i) => (it.section ? -1 : i)).filter((i) => i >= 0);
|
|
23
49
|
if (key.escape) {
|
|
24
50
|
if (typed)
|
|
25
51
|
setTyped('');
|
|
@@ -27,6 +53,13 @@ export function SelectList({ items, allowInput, inputPlaceholder, mask, height,
|
|
|
27
53
|
onBack?.();
|
|
28
54
|
return;
|
|
29
55
|
}
|
|
56
|
+
if (key.leftArrow) {
|
|
57
|
+
if (typed)
|
|
58
|
+
setTyped('');
|
|
59
|
+
else
|
|
60
|
+
onBack?.();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
30
63
|
if (key.return) {
|
|
31
64
|
if (typing) {
|
|
32
65
|
const v = typed.trim();
|
|
@@ -35,14 +68,19 @@ export function SelectList({ items, allowInput, inputPlaceholder, mask, height,
|
|
|
35
68
|
onInput?.(v);
|
|
36
69
|
}
|
|
37
70
|
else {
|
|
38
|
-
|
|
39
|
-
if (realIdx !== undefined && items[realIdx])
|
|
40
|
-
onSelect?.(items[realIdx].value);
|
|
71
|
+
chooseCurrent();
|
|
41
72
|
}
|
|
42
73
|
return;
|
|
43
74
|
}
|
|
75
|
+
if ((key.tab || key.rightArrow) && !typing) {
|
|
76
|
+
chooseCurrent();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
44
79
|
if (key.backspace || key.delete) {
|
|
45
|
-
|
|
80
|
+
if (typed)
|
|
81
|
+
setTyped((v) => v.slice(0, -1));
|
|
82
|
+
else
|
|
83
|
+
onBack?.();
|
|
46
84
|
return;
|
|
47
85
|
}
|
|
48
86
|
if (key.upArrow) {
|
|
@@ -57,12 +95,12 @@ export function SelectList({ items, allowInput, inputPlaceholder, mask, height,
|
|
|
57
95
|
}
|
|
58
96
|
if (key.pageUp) {
|
|
59
97
|
if (!typing)
|
|
60
|
-
setIdx((i) =>
|
|
98
|
+
setIdx((i) => clampIndex(i - pageStep, selectable.length));
|
|
61
99
|
return;
|
|
62
100
|
}
|
|
63
101
|
if (key.pageDown) {
|
|
64
102
|
if (!typing)
|
|
65
|
-
setIdx((i) =>
|
|
103
|
+
setIdx((i) => clampIndex(i + pageStep, selectable.length));
|
|
66
104
|
return;
|
|
67
105
|
}
|
|
68
106
|
if (key.home) {
|
|
@@ -89,19 +127,9 @@ export function SelectList({ items, allowInput, inputPlaceholder, mask, height,
|
|
|
89
127
|
}
|
|
90
128
|
setTyped((v) => v + input);
|
|
91
129
|
});
|
|
92
|
-
// Build a separate index map so up/down skip section headers.
|
|
93
|
-
const selectable = items.map((it, i) => (it.section ? -1 : i)).filter((i) => i >= 0);
|
|
94
|
-
const safeIdx = selectable.length > 0 ? selectable[Math.min(idx, selectable.length - 1)] : -1;
|
|
95
|
-
const maxVisible = height ? Math.max(1, height - (allowInput ? 2 : 0)) : items.length;
|
|
96
|
-
const start = items.length > maxVisible && safeIdx >= 0
|
|
97
|
-
? Math.max(0, Math.min(safeIdx - Math.floor(maxVisible / 2), items.length - maxVisible))
|
|
98
|
-
: 0;
|
|
99
|
-
const visibleItems = items.slice(start, start + maxVisible);
|
|
100
|
-
const above = start;
|
|
101
|
-
const below = Math.max(0, items.length - start - visibleItems.length);
|
|
102
130
|
return (_jsxs(Box, { flexDirection: "column", children: [above > 0 ? _jsxs(Text, { color: "gray", children: ["\u25B2 ", above] }) : null, visibleItems.map((it, localIdx) => {
|
|
103
|
-
const i = start + localIdx;
|
|
104
|
-
return (it.section ? (_jsx(Box, { marginTop: i > 0 ? 1 : 0, children: _jsx(Text, { bold: true, color: "white", children: it.label }) }, it.label)) : (_jsxs(Text, { children: [_jsxs(Text, { color: !typing && i ===
|
|
131
|
+
const i = window.start + localIdx;
|
|
132
|
+
return (it.section ? (_jsx(Box, { marginTop: i > 0 ? 1 : 0, children: _jsx(Text, { bold: true, color: "white", children: it.label }) }, it.label)) : (_jsxs(Text, { children: [_jsxs(Text, { color: !typing && i === safeRealIdx ? COLOR.cream : 'gray', bold: !typing && i === safeRealIdx, children: [!typing && i === safeRealIdx ? '❯ ' : ' ', it.label] }), it.hint ? _jsxs(Text, { color: "gray", children: [" ", it.hint] }) : null, it.detail ? _jsxs(Text, { color: "gray", children: [" \u2014 ", it.detail] }) : null] }, it.value + i)));
|
|
105
133
|
}), below > 0 ? _jsxs(Text, { color: "gray", children: ["\u25BC ", below] }) : null, allowInput && (_jsx(Box, { marginTop: items.length > 0 ? 1 : 0, children: _jsxs(Text, { color: typing ? COLOR.cream : 'gray', children: ["\u270E", ' ', typing ? (_jsx(Text, { color: "white", children: mask ? '•'.repeat(typed.length) : typed })) : (_jsx(Text, { color: "gray", children: inputPlaceholder ?? '…' })), typing ? _jsx(Text, { color: COLOR.cream, children: "\u2588" }) : null] }) }))] }));
|
|
106
134
|
}
|
|
107
135
|
export function WizardStep({ step, total, title, children, footer, }) {
|
package/dist/ui/views.js
CHANGED
|
@@ -121,14 +121,16 @@ export function DiffView({ board, bodyHeight }) {
|
|
|
121
121
|
}), _jsx(Below, { n: below })] }))] }));
|
|
122
122
|
}
|
|
123
123
|
/** Financial view: live cost / steps / tokens per agent + session total. */
|
|
124
|
-
export function CostView({
|
|
124
|
+
export function CostView({ ctl, bodyHeight }) {
|
|
125
|
+
const board = ctl.board;
|
|
125
126
|
const agents = [...board.agents.values()];
|
|
126
127
|
const fallbackVisible = useVisibleRows(8);
|
|
127
128
|
const visible = bodyHeight ? Math.max(3, bodyHeight - 7) : fallbackVisible;
|
|
128
129
|
const { slice, above, below } = useScrollWindow(agents, visible, 'top');
|
|
129
130
|
const total = agents.reduce((s, a) => s + (a.cost ?? 0), 0);
|
|
131
|
+
const memory = ctl.projectContextStatus();
|
|
130
132
|
const unknown = agents.some((a) => a.cost === null);
|
|
131
|
-
return (_jsxs(Box, { borderStyle: "round", borderColor: "greenBright", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "greenBright", children: t('cost.title') }), agents.length === 0 ? (_jsx(Text, { color: "gray", children: t('cost.empty') })) : (_jsxs(_Fragment, { children: [_jsx(Above, { n: above }), slice.map((a) => (_jsxs(Text, { wrap: "truncate-end", children: [' ', _jsx(Text, { color: a.color, bold: true, children: a.name.padEnd(12) }), _jsxs(Text, { color: "gray", children: [a.model.padEnd(24).slice(0, 24), " "] }), _jsxs(Text, { children: [String(a.steps).padStart(3), " steps "] }), _jsxs(Text, { color: BRAND.primary, children: [String(Math.round(a.tokensIn / 1000)).padStart(5), "k in ", String(Math.round(a.tokensOut / 1000)).padStart(4), "k out", ' '] }), _jsx(Text, { color: "greenBright", bold: true, children: a.cost === null ? ' $—' : fmtCost(a.cost).padStart(8) }), a.cost === null ? _jsxs(Text, { color: "gray", children: [" ", t('cost.unknown')] }) : null] }, a.id))), _jsx(Below, { n: below }), _jsx(Text, { children: " " }), _jsxs(Text, { bold: true, children: [' ', t('cost.total'), " ", _jsx(Text, { color: "greenBright", children: fmtCost(total) }), unknown ? _jsxs(Text, { color: "gray", children: [" ", t('cost.partial')] }) : null] })] })), _jsx(Text, { color: "gray", children: t('cost.hint') })] }));
|
|
133
|
+
return (_jsxs(Box, { borderStyle: "round", borderColor: "greenBright", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "greenBright", children: t('cost.title') }), agents.length === 0 ? (_jsx(Text, { color: "gray", children: t('cost.empty') })) : (_jsxs(_Fragment, { children: [_jsx(Above, { n: above }), slice.map((a) => (_jsxs(Text, { wrap: "truncate-end", children: [' ', _jsx(Text, { color: a.color, bold: true, children: a.name.padEnd(12) }), _jsxs(Text, { color: "gray", children: [a.model.padEnd(24).slice(0, 24), " "] }), _jsxs(Text, { color: "gray", children: [(a.profile ?? 'standard').padEnd(8), " "] }), _jsxs(Text, { children: [String(a.steps).padStart(3), " steps "] }), _jsxs(Text, { color: BRAND.primary, children: [String(Math.round(a.tokensIn / 1000)).padStart(5), "k in ", String(Math.round(a.tokensOut / 1000)).padStart(4), "k out", ' '] }), _jsx(Text, { color: "greenBright", bold: true, children: a.cost === null ? ' $—' : fmtCost(a.cost).padStart(8) }), a.cost === null ? _jsxs(Text, { color: "gray", children: [" ", t('cost.unknown')] }) : null] }, a.id))), _jsx(Below, { n: below }), _jsx(Text, { children: " " }), _jsxs(Text, { bold: true, children: [' ', t('cost.total'), " ", _jsx(Text, { color: "greenBright", children: fmtCost(total) }), unknown ? _jsxs(Text, { color: "gray", children: [" ", t('cost.partial')] }) : null] })] })), _jsxs(Text, { children: [' ', t('cost.memory'), " ", _jsx(Text, { color: BRAND.primary, children: memory.model ?? '—' }), ' ', _jsx(Text, { color: "greenBright", children: memory.cost === null ? '$—' : fmtCost(memory.cost) }), ' ', _jsxs(Text, { color: "gray", children: ["(", memory.status, ", ", memory.tokensIn + memory.tokensOut, " tokens)"] })] }), _jsx(Text, { color: "gray", children: t('cost.hint') })] }));
|
|
132
134
|
}
|
|
133
135
|
/** Skills catalog: user-authored markdown instructions agents can load. */
|
|
134
136
|
export function SkillsView({ skills, bodyHeight }) {
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const PACKAGE_NAME = '@parallel-cli/parallel';
|
|
2
|
-
export const VERSION = '0.
|
|
2
|
+
export const VERSION = '0.5.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parallel-cli/parallel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Real-time coding agents that work like a live team on one shared repository.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "tsc && chmod +x dist/index.js",
|
|
50
50
|
"dev": "tsc --watch",
|
|
51
|
-
"test": "npm run build &&
|
|
51
|
+
"test": "npm run build && if [ -d test ]; then node --test test/*.test.mjs && npm run test:pty; else echo 'test/ directory not found, skipping tests'; fi",
|
|
52
52
|
"test:pty": "sh test/pty.sh",
|
|
53
53
|
"start": "node dist/index.js",
|
|
54
54
|
"prepublishOnly": "npm run build"
|