@parallel-cli/parallel 0.4.3 → 0.4.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.
package/dist/config.js CHANGED
@@ -280,8 +280,38 @@ export function isLocalProvider(p) {
280
280
  export function providerNeedsApiKey(p) {
281
281
  return p.requiresApiKey !== false && !isLocalProvider(p);
282
282
  }
283
+ export function isPlaceholderModel(model) {
284
+ return !model.trim() || /^your-model-here$/i.test(model.trim());
285
+ }
286
+ export function providerHasUsableModel(p) {
287
+ return !isPlaceholderModel(p.defaultModel || p.models[0] || '');
288
+ }
283
289
  export function providerReady(p) {
284
- return !providerNeedsApiKey(p) || p.apiKey.trim().length > 0;
290
+ return (!providerNeedsApiKey(p) || p.apiKey.trim().length > 0) && providerHasUsableModel(p);
291
+ }
292
+ export async function detectProviderModels(provider, timeoutMs = 2000) {
293
+ let timeout;
294
+ try {
295
+ const controller = new AbortController();
296
+ timeout = setTimeout(() => controller.abort(), timeoutMs);
297
+ const resp = await fetch(provider.baseUrl.replace(/\/+$/, '') + '/models', { signal: controller.signal });
298
+ if (!resp.ok)
299
+ return null;
300
+ const data = (await resp.json());
301
+ const models = [
302
+ ...(data.data?.map((m) => m.id || m.name).filter(Boolean) ?? []),
303
+ ...(data.models?.map((m) => m.name).filter(Boolean) ?? []),
304
+ ];
305
+ const unique = [...new Set(models.filter((m) => !isPlaceholderModel(m)))];
306
+ return unique.length > 0 ? { models: unique, defaultModel: unique[0] } : null;
307
+ }
308
+ catch {
309
+ return null;
310
+ }
311
+ finally {
312
+ if (timeout)
313
+ clearTimeout(timeout);
314
+ }
285
315
  }
286
316
  export function getProvider(cfg, name) {
287
317
  const n = (name ?? cfg.defaultProvider).toLowerCase();
@@ -72,6 +72,7 @@ export class Controller extends EventEmitter {
72
72
  conversationFiles = new Map();
73
73
  /** The session restored at startup (source of /restore conversations). */
74
74
  loadedSession = null;
75
+ sessionOnlyProvider = null;
75
76
  constructor(config, projectRoot) {
76
77
  super();
77
78
  this.config = config;
@@ -108,6 +109,10 @@ export class Controller extends EventEmitter {
108
109
  // ---------- providers / models ----------
109
110
  /** Provider used by the current session (falls back to the global default). */
110
111
  sessionProvider() {
112
+ if (this.sessionOnlyProvider &&
113
+ this.session.providerName.toLowerCase() === this.sessionOnlyProvider.name.toLowerCase()) {
114
+ return this.sessionOnlyProvider;
115
+ }
111
116
  return getProvider(this.config, this.session.providerName || undefined);
112
117
  }
113
118
  /** Resolve "model" or "provider:model" against the configured providers. */
@@ -115,7 +120,10 @@ export class Controller extends EventEmitter {
115
120
  const trimmed = spec.trim();
116
121
  const sep = trimmed.indexOf(':');
117
122
  if (sep > 0) {
118
- const provider = getProvider(this.config, trimmed.slice(0, sep).trim());
123
+ const left = trimmed.slice(0, sep).trim();
124
+ const provider = this.sessionOnlyProvider && this.sessionOnlyProvider.name.toLowerCase() === left.toLowerCase()
125
+ ? this.sessionOnlyProvider
126
+ : getProvider(this.config, left);
119
127
  if (provider)
120
128
  return { provider, model: trimmed.slice(sep + 1).trim() };
121
129
  }
@@ -231,7 +239,7 @@ export class Controller extends EventEmitter {
231
239
  return;
232
240
  const [q] = this.questions.splice(idx, 1);
233
241
  this.board.log('', 'system', auto ? t('m.qAuto', { name: q.agentName, answer }) : t('m.qAnswered', { name: q.agentName, answer }));
234
- q.resolve(answer);
242
+ q.resolve({ answer, auto });
235
243
  this.emit('update');
236
244
  }
237
245
  // ---------- agents ----------
@@ -382,7 +390,7 @@ export class Controller extends EventEmitter {
382
390
  respawnAgent(name) {
383
391
  const sa = this.loadedSession?.agents.find((a) => a.name.toLowerCase() === name.toLowerCase());
384
392
  if (!sa)
385
- return 'no-conversation';
393
+ return 'no-agent';
386
394
  if (!sa.conversation || !fs.existsSync(sa.conversation))
387
395
  return 'no-conversation';
388
396
  let history;
@@ -427,7 +435,7 @@ export class Controller extends EventEmitter {
427
435
  for (const req of this.approvals.splice(0))
428
436
  req.resolve(false);
429
437
  for (const q of this.questions.splice(0))
430
- q.resolve(q.options[q.recommended] ?? '');
438
+ q.resolve({ answer: q.options[q.recommended] ?? '', auto: true });
431
439
  }
432
440
  sendToAgent(name, content) {
433
441
  const a = this.findAgent(name);
@@ -438,6 +446,15 @@ export class Controller extends EventEmitter {
438
446
  }
439
447
  broadcast(content) {
440
448
  this.board.addNote('user', 'all', content);
449
+ let n = 0;
450
+ for (const [id, agent] of this.agents.entries()) {
451
+ const info = this.board.agents.get(id);
452
+ if (!info || ['done', 'error', 'stopped'].includes(info.state))
453
+ continue;
454
+ agent.instruct(content);
455
+ n++;
456
+ }
457
+ return n;
441
458
  }
442
459
  hasRunningAgents() {
443
460
  return [...this.board.agents.values()].some((a) => ['working', 'thinking', 'listening', 'waiting', 'paused', 'idle'].includes(a.state));
@@ -464,8 +481,10 @@ export class Controller extends EventEmitter {
464
481
  continue;
465
482
  const conflict = this.board.changes.some((c2) => c2.id > c.id && c2.path === c.path && c2.agentId !== info.id);
466
483
  try {
467
- const abs = path.resolve(this.projectRoot, c.path);
468
- if (!abs.startsWith(path.resolve(this.projectRoot)))
484
+ const root = path.resolve(this.projectRoot);
485
+ const abs = path.resolve(root, c.path);
486
+ const rel = path.relative(root, abs);
487
+ if (rel.startsWith('..') || path.isAbsolute(rel))
469
488
  return 'none';
470
489
  fs.writeFileSync(abs, c.before);
471
490
  }
@@ -630,7 +649,7 @@ export class Controller extends EventEmitter {
630
649
  return { file, data: JSON.parse(fs.readFileSync(file, 'utf8')) };
631
650
  })
632
651
  .sort((a, b) => (a.data.savedAt < b.data.savedAt ? 1 : -1))
633
- .slice(0, 8);
652
+ .slice(0, 20);
634
653
  }
635
654
  catch {
636
655
  return [];
@@ -671,11 +690,19 @@ export class Controller extends EventEmitter {
671
690
  const p = getProvider(this.config, name);
672
691
  if (!p)
673
692
  return false;
693
+ this.sessionOnlyProvider = null;
674
694
  this.session.providerName = p.name;
675
695
  this.session.model = p.defaultModel || p.models[0] || '';
676
696
  this.emit('update');
677
697
  return true;
678
698
  }
699
+ setSessionProviderConfig(p) {
700
+ this.sessionOnlyProvider = p;
701
+ this.session.providerName = p.name;
702
+ this.session.model = p.defaultModel || p.models[0] || '';
703
+ this.llmCache.clear();
704
+ this.emit('update');
705
+ }
679
706
  setSessionApprovalMode(mode) {
680
707
  this.session.approvalMode = mode;
681
708
  this.emit('update');
@@ -687,6 +714,8 @@ export class Controller extends EventEmitter {
687
714
  // ---------- GLOBAL settings (/settings) — persisted ----------
688
715
  saveProvider(p) {
689
716
  upsertProvider(this.config, p);
717
+ if (this.sessionOnlyProvider?.name.toLowerCase() === p.name.toLowerCase())
718
+ this.sessionOnlyProvider = null;
690
719
  this.llmCache.clear();
691
720
  // if the session points at this provider, refresh its view
692
721
  if (this.session.providerName.toLowerCase() === p.name.toLowerCase()) {
package/dist/i18n.js CHANGED
@@ -105,7 +105,7 @@ const en = {
105
105
  'notes.empty': 'No notes exchanged yet.',
106
106
  'sessions.title': '📂 SAVED SESSIONS',
107
107
  'sessions.empty': 'No saved sessions for this project yet.',
108
- 'sessions.item': '{date} · {agents} agent(s)',
108
+ 'sessions.item': '{name}{date} · {agents} agent(s)',
109
109
  'sessions.hint': 'Restore one with /session <n> or /session latest. New launches start fresh by default.',
110
110
  'diff.title': '± LIVE DIFFS — {total} change(s) total (last 4 shown)',
111
111
  'diff.empty': 'No file changes yet.',
@@ -121,7 +121,7 @@ const en = {
121
121
  'help.l2d': ' for everyone).',
122
122
  'help.l3': "Agents see each other's statuses and diffs before every action: they co-edit the same files without blocking and without breaking each other's work.",
123
123
  'help.states': 'States: 🔨 working · 👂 listening to other agents (double border) · 🧠 thinking · ✋ waiting for your approval · ⏹ stop. Sounds: 1 beep = agent launched/finished, 2 beeps = approval required (/sound off to mute).',
124
- 'help.keys': 'Esc: back to agents view · Tab: completion · ↑/↓: suggestions, history, or view scroll · Ctrl+V: paste image',
124
+ 'help.keys': 'Esc: back to agents view · Tab: completion · PgUp/PgDn: hub/focus scroll · ↑/↓: suggestions, history, or view scroll · Ctrl+V: paste image',
125
125
  // commands (descriptions)
126
126
  'cmd.ask': 'Ask-only agent: answers and advises without editing',
127
127
  'cmd.task': 'Task agent: executes, edits, validates, summarizes',
@@ -156,7 +156,7 @@ const en = {
156
156
  'cmd.model': 'Show/change the session model ([provider:]model)',
157
157
  'cmd.sessions': 'List saved sessions for this project',
158
158
  'cmd.session': 'Restore a saved session by number, or latest',
159
- 'cmd.doctor': 'Check provider/model/API key configuration and repair hints',
159
+ 'cmd.doctor': 'Run local readiness diagnostics',
160
160
  'cmd.settings': 'Global settings — providers, API keys, language, defaults (persisted)',
161
161
  'cmd.ssettings': 'Session settings — model, approvals, sound (this session only)',
162
162
  'cmd.project': 'Change project folder or reopen folder picker',
@@ -175,7 +175,7 @@ const en = {
175
175
  'm.usageAsk': 'Usage: /ask [Name:] <question> [--model=m] — advisory only, no edits.',
176
176
  'm.usageSpawn': 'Usage: /task [Name:] <task> [--model=m]',
177
177
  'm.usageAt': 'Usage: @Agent <message> (or @all <message>)',
178
- 'm.broadcast': '✉ Instruction broadcast to all agents.',
178
+ 'm.broadcast': '✉ Instruction sent to {n} active agent(s).',
179
179
  'm.sent': '✉ Instruction sent to {target}.',
180
180
  'm.notFound': 'Agent not found: {target} (agents: {list})',
181
181
  'm.none': 'none',
@@ -206,14 +206,15 @@ const en = {
206
206
  'm.focusHint': '🎯 focus {name} · type to talk to it · PgUp/PgDn scroll · Esc to exit',
207
207
  'm.usageRestore': 'Usage: /restore <agent> (after loading a session via /session)',
208
208
  'm.restored': '⏪ {name} relaunched with its previous conversation.',
209
+ 'm.noRestoredAgent': 'No agent named "{name}" in the restored session.',
209
210
  'm.noConversation': 'No saved conversation for "{name}" in the restored session.',
210
211
  'm.conflict': '⚠ {path}: repeated collisions between agents — consider arbitrating (@agent or /focus).',
211
212
  'status.bar': '⚡ {agents} agent(s) · {active} active · {cost}',
212
213
  'cmd.plan': 'Plan agent: presents its plan, edits only after your approval',
213
- 'cmd.issue': 'spawn an agent on a GitHub issue (needs the gh CLI)',
214
+ 'cmd.issue': 'spawn an agent on a GitHub issue (needs gh, a GitHub repo, and auth)',
214
215
  'cmd.undo': "revert the agent's last file change",
215
- 'cmd.commit': 'git-commit the files touched by an agent (or all)',
216
- 'cmd.autocommit': 'auto-commit each agent’s files when it finishes',
216
+ 'cmd.commit': 'git-commit only files touched by an agent (or all)',
217
+ 'cmd.autocommit': 'session-only auto-commit of each agent’s files when it finishes',
217
218
  'm.usagePlan': 'Usage: /plan [Name:] <task> — the agent presents its plan and waits for your approval before editing.',
218
219
  'm.usageIssue': 'Usage: /issue <number> — spawns an agent on this GitHub issue (gh CLI required).',
219
220
  'm.ghMissing': '✗ gh CLI not found. Install it (https://cli.github.com) then `gh auth login`.',
@@ -253,14 +254,34 @@ const en = {
253
254
  'm.copyNone': 'No completed agent output to copy.',
254
255
  'm.copyDone': 'Copied latest result from {name}.',
255
256
  'm.unknown': 'Unknown command: {cmd} — type /help',
257
+ 'm.projectActive': 'Active agents are running. Save/stop them first, or rerun with /project [folder] --force.',
258
+ 'm.wizardActive': 'Active agents are running. Save/stop them first, or rerun with /wizard --force.',
256
259
  'm.imagesIgnored': 'Note: attached images are only supported when launching a new agent.',
257
260
  'm.sessionRestored': '📂 Session from {date} restored.',
258
261
  'm.usageSession': 'Usage: /sessions to list, then /session <n> or /session latest',
259
262
  'm.sessionLoaded': '📂 Session from {date} loaded.',
263
+ 'm.sessionActive': 'Active agents are running. Save/stop them first, or rerun with /session <n|latest> --force.',
264
+ 'm.sessionRestoreHint': 'Run /restore <agent> to relaunch a saved agent conversation.',
260
265
  'm.missingProvider': 'No provider configured. Open /settings → Add a provider, or restart with a first-run config.',
261
266
  'm.missingKey': 'Provider {name} has no API key. Use /settings → Providers → API key.',
262
267
  'm.missingModel': 'Provider {name} has no default model. Use /settings → Provider models.',
263
268
  'm.doctorOk': '✓ Configuration ready: {pm}. Changes: /settings · temporary model: /model [provider:]model',
269
+ 'm.doctorReport': 'Doctor\n{lines}',
270
+ 'm.doctorNoProvider': '✗ No provider configured. Open /settings → Add a provider.',
271
+ 'm.doctorProvider': '• Provider: {provider}:{model}',
272
+ 'm.doctorKeyMissing': '✗ API key missing. Open /settings → Providers → API key.',
273
+ 'm.doctorKeyOk': '✓ API key present.',
274
+ 'm.doctorKeySkipped': '✓ API key not required for this local provider.',
275
+ 'm.doctorModelMissing': '✗ Model missing or still set to placeholder. Choose a real model in /settings or /model.',
276
+ 'm.doctorModelOk': '✓ Model ready: {model}',
277
+ 'm.doctorEndpointOk': '✓ Local endpoint reachable: {url}',
278
+ 'm.doctorEndpointFail': '⚠ Local endpoint not reachable: {url}',
279
+ 'm.doctorAttachOk': '✓ Attach socket is available.',
280
+ 'm.doctorAttachMissing': '⚠ Attach socket is not running yet.',
281
+ 'm.doctorGitOk': '✓ git CLI found.',
282
+ 'm.doctorGitMissing': '⚠ git CLI not found.',
283
+ 'm.doctorGhOk': '✓ gh CLI found for /issue.',
284
+ 'm.doctorGhMissing': '⚠ gh CLI not found; /issue needs gh and GitHub auth.',
264
285
  'm.spawnFail': 'Cannot launch the agent: no usable provider/model. Configure one via /settings.',
265
286
  // settings panels
266
287
  'set.title': '⚙ SETTINGS — global (persisted in ~/.parallel/config.json)',
@@ -277,8 +298,13 @@ const en = {
277
298
  'set.sound': 'Default sound: {state}',
278
299
  'set.back': '← Back',
279
300
  'set.saved': '✓ Saved to ~/.parallel/config.json',
301
+ 'set.sessionProviderReady': '✓ {name} is ready for this session only.',
302
+ 'set.setupScope.title': 'Use {name} only now, or save it globally?',
303
+ 'set.setupScope.session': 'Use this session only',
304
+ 'set.setupScope.global': 'Save globally',
280
305
  'set.chooseProvider': 'Choose a provider',
281
306
  'set.chooseModel': 'Model ({name}) — pick or type a name from the provider docs',
307
+ 'set.modelPlaceholder': 'Choose or type a real model name first. `your-model-here` is only a placeholder.',
282
308
  'sset.model': 'Session model: {pm}',
283
309
  'sset.approvals': 'Shell approvals (session): {mode}',
284
310
  'sset.sound': 'Sound (session): {state}',
@@ -453,7 +479,7 @@ const fr = {
453
479
  'notes.empty': 'Aucune note échangée pour le moment.',
454
480
  'sessions.title': '📂 SESSIONS SAUVEGARDÉES',
455
481
  'sessions.empty': 'Aucune session sauvegardée pour ce projet.',
456
- 'sessions.item': '{date} · {agents} agent(s)',
482
+ 'sessions.item': '{name}{date} · {agents} agent(s)',
457
483
  'sessions.hint': 'Restaure avec /session <n> ou /session latest. Les nouveaux lancements démarrent vierges par défaut.',
458
484
  'diff.title': '± DIFFS EN DIRECT — {total} modification(s) au total (4 dernières affichées)',
459
485
  'diff.empty': 'Aucune modification de fichier pour le moment.',
@@ -469,7 +495,7 @@ const fr = {
469
495
  'help.l2d': ' pour tous).',
470
496
  'help.l3': 'Les agents voient mutuellement leurs statuts et leurs diffs avant chaque action : ils co-éditent les mêmes fichiers sans se bloquer et sans casser le travail des autres.',
471
497
  'help.states': "États : 🔨 travaille · 👂 écoute les autres agents (double bordure) · 🧠 réfléchit · ✋ attend ton approbation · ⏹ stop. Sons : 1 bip = agent lancé/terminé, 2 bips = approbation requise (/sound off pour couper).",
472
- 'help.keys': 'Esc : revenir à la vue agents · Tab : complétion · ↑/↓ : suggestions, historique ou scroll selon la vue · Ctrl+V : coller une image',
498
+ 'help.keys': 'Esc : revenir à la vue agents · Tab : complétion · PgUp/PgDn : scroll hub/focus · ↑/↓ : suggestions, historique ou scroll selon la vue · Ctrl+V : coller une image',
473
499
  'cmd.ask': 'Agent ask : répond et conseille sans modifier',
474
500
  'cmd.task': 'Agent task : exécute, modifie, valide, résume',
475
501
  'cmd.agents': 'Vue panneaux agents (temps réel)',
@@ -503,7 +529,7 @@ const fr = {
503
529
  'cmd.model': 'Afficher/changer le modèle de la session ([provider:]modèle)',
504
530
  'cmd.sessions': 'Lister les sessions sauvegardées de ce projet',
505
531
  'cmd.session': 'Restaurer une session sauvegardée par numéro, ou latest',
506
- 'cmd.doctor': 'Vérifier provider/modèle/clef API et afficher les corrections',
532
+ 'cmd.doctor': 'Lancer les diagnostics locaux de readiness',
507
533
  'cmd.settings': 'Réglages globaux — providers, clefs API, langue, défauts (persistés)',
508
534
  'cmd.ssettings': 'Réglages de session — modèle, approbations, son (cette session)',
509
535
  'cmd.project': 'Changer de dossier projet ou rouvrir le choix de dossier',
@@ -521,7 +547,7 @@ const fr = {
521
547
  'm.usageAsk': 'Usage : /ask [Nom:] <question> [--model=m] — conseil uniquement, aucune modification.',
522
548
  'm.usageSpawn': 'Usage : /task [Nom:] <tâche> [--model=m]',
523
549
  'm.usageAt': 'Usage : @Agent <message> (ou @all <message>)',
524
- 'm.broadcast': '✉ Instruction diffusée à tous les agents.',
550
+ 'm.broadcast': '✉ Instruction envoyée à {n} agent(s) actif(s).',
525
551
  'm.sent': '✉ Instruction envoyée à {target}.',
526
552
  'm.notFound': 'Agent introuvable : {target} (agents : {list})',
527
553
  'm.none': 'aucun',
@@ -552,14 +578,15 @@ const fr = {
552
578
  'm.focusHint': '🎯 focus {name} · tapez pour lui parler · PgUp/PgDn défilement · Échap pour sortir',
553
579
  'm.usageRestore': 'Usage : /restore <agent> (après avoir chargé une session via /session)',
554
580
  'm.restored': '⏪ {name} relancé avec sa conversation précédente.',
581
+ 'm.noRestoredAgent': 'Aucun agent nommé « {name} » dans la session restaurée.',
555
582
  'm.noConversation': 'Aucune conversation sauvegardée pour « {name} » dans la session restaurée.',
556
583
  'm.conflict': '⚠ {path} : collisions répétées entre agents — pensez à arbitrer (@agent ou /focus).',
557
584
  'status.bar': '⚡ {agents} agent(s) · {active} actif(s) · {cost}',
558
585
  'cmd.plan': "Agent plan : présente son plan, ne modifie qu'après votre accord",
559
- 'cmd.issue': 'lance un agent sur une issue GitHub (CLI gh requise)',
586
+ 'cmd.issue': 'lance un agent sur une issue GitHub (gh, repo GitHub et auth requis)',
560
587
  'cmd.undo': "annule la dernière modification de fichier de l'agent",
561
- 'cmd.commit': 'git-commit des fichiers touchés par un agent (ou tous)',
562
- 'cmd.autocommit': 'commit auto des fichiers de chaque agent quand il termine',
588
+ 'cmd.commit': 'git-commit seulement les fichiers touchés par un agent (ou tous)',
589
+ 'cmd.autocommit': 'commit auto session-only des fichiers de chaque agent quand il termine',
563
590
  'm.usagePlan': "Usage : /plan [Nom:] <tâche> — l'agent présente son plan et attend votre accord avant de modifier.",
564
591
  'm.usageIssue': 'Usage : /issue <numéro> — lance un agent sur cette issue GitHub (CLI gh requise).',
565
592
  'm.ghMissing': '✗ CLI gh introuvable. Installez-la (https://cli.github.com) puis `gh auth login`.',
@@ -599,14 +626,34 @@ const fr = {
599
626
  'm.copyNone': "Aucun résultat d'agent à copier.",
600
627
  'm.copyDone': 'Dernier résultat de {name} copié.',
601
628
  'm.unknown': 'Commande inconnue : {cmd} — tape /help',
629
+ 'm.projectActive': 'Des agents sont actifs. Sauvegarde/arrête-les d’abord, ou relance avec /project [dossier] --force.',
630
+ 'm.wizardActive': 'Des agents sont actifs. Sauvegarde/arrête-les d’abord, ou relance avec /wizard --force.',
602
631
  'm.imagesIgnored': "Note : les images jointes ne sont prises en compte qu'au lancement d'un nouvel agent.",
603
632
  'm.sessionRestored': '📂 Session du {date} restaurée.',
604
633
  'm.usageSession': 'Usage : /sessions pour lister, puis /session <n> ou /session latest',
605
634
  'm.sessionLoaded': '📂 Session du {date} chargée.',
635
+ 'm.sessionActive': 'Des agents sont actifs. Sauvegarde/arrête-les d’abord, ou relance avec /session <n|latest> --force.',
636
+ 'm.sessionRestoreHint': 'Lance /restore <agent> pour relancer une conversation agent sauvegardée.',
606
637
  'm.missingProvider': 'Aucun provider configuré. Ouvre /settings → Ajouter un provider, ou relance une première config.',
607
638
  'm.missingKey': 'Le provider {name} n’a pas de clef API. Utilise /settings → Providers → Clef API.',
608
639
  'm.missingModel': 'Le provider {name} n’a pas de modèle par défaut. Utilise /settings → Modèles du provider.',
609
640
  'm.doctorOk': '✓ Configuration prête : {pm}. Changements : /settings · modèle temporaire : /model [provider:]modèle',
641
+ 'm.doctorReport': 'Doctor\n{lines}',
642
+ 'm.doctorNoProvider': '✗ Aucun provider configuré. Ouvre /settings → Add a provider.',
643
+ 'm.doctorProvider': '• Provider : {provider}:{model}',
644
+ 'm.doctorKeyMissing': '✗ Clef API manquante. Ouvre /settings → Providers → Clef API.',
645
+ 'm.doctorKeyOk': '✓ Clef API présente.',
646
+ 'm.doctorKeySkipped': '✓ Clef API non requise pour ce provider local.',
647
+ 'm.doctorModelMissing': '✗ Modèle manquant ou encore sur le placeholder. Choisis un vrai modèle dans /settings ou /model.',
648
+ 'm.doctorModelOk': '✓ Modèle prêt : {model}',
649
+ 'm.doctorEndpointOk': '✓ Endpoint local joignable : {url}',
650
+ 'm.doctorEndpointFail': '⚠ Endpoint local non joignable : {url}',
651
+ 'm.doctorAttachOk': '✓ Socket attach disponible.',
652
+ 'm.doctorAttachMissing': '⚠ Socket attach pas encore lancé.',
653
+ 'm.doctorGitOk': '✓ CLI git trouvé.',
654
+ 'm.doctorGitMissing': '⚠ CLI git introuvable.',
655
+ 'm.doctorGhOk': '✓ CLI gh trouvé pour /issue.',
656
+ 'm.doctorGhMissing': '⚠ CLI gh introuvable ; /issue nécessite gh et une auth GitHub.',
610
657
  'm.spawnFail': "Impossible de lancer l'agent : aucun provider/modèle utilisable. Configure-en un via /settings.",
611
658
  'set.title': '⚙ RÉGLAGES — globaux (persistés dans ~/.parallel/config.json)',
612
659
  'sset.title': '⚙ RÉGLAGES DE SESSION — cette session uniquement (globaux : /settings)',
@@ -622,8 +669,13 @@ const fr = {
622
669
  'set.sound': 'Son par défaut : {state}',
623
670
  'set.back': '← Retour',
624
671
  'set.saved': '✓ Enregistré dans ~/.parallel/config.json',
672
+ 'set.sessionProviderReady': '✓ {name} est prêt pour cette session seulement.',
673
+ 'set.setupScope.title': 'Utiliser {name} seulement maintenant, ou le sauvegarder globalement ?',
674
+ 'set.setupScope.session': 'Utiliser pour cette session seulement',
675
+ 'set.setupScope.global': 'Sauvegarder globalement',
625
676
  'set.chooseProvider': 'Choisis un provider',
626
677
  'set.chooseModel': 'Modèle ({name}) — choisis ou tape un nom selon la doc du provider',
678
+ 'set.modelPlaceholder': 'Choisis ou tape d’abord un vrai nom de modèle. `your-model-here` est seulement un placeholder.',
627
679
  'sset.model': 'Modèle de session : {pm}',
628
680
  'sset.approvals': 'Approbations shell (session) : {mode}',
629
681
  'sset.sound': 'Son (session) : {state}',
@@ -792,7 +844,7 @@ const es = {
792
844
  'notes.empty': 'Aún no se han intercambiado notas.',
793
845
  'sessions.title': '📂 SESIONES GUARDADAS',
794
846
  'sessions.empty': 'Aún no hay sesiones guardadas para este proyecto.',
795
- 'sessions.item': '{date} · {agents} agente(s)',
847
+ 'sessions.item': '{name}{date} · {agents} agente(s)',
796
848
  'sessions.hint': 'Restaura una con /session <n> o /session latest. Los nuevos lanzamientos empiezan vacíos por defecto.',
797
849
  'diff.title': '± DIFFS EN VIVO — {total} cambio(s) en total (últimos 4 mostrados)',
798
850
  'diff.empty': 'Sin cambios de archivos por ahora.',
@@ -808,7 +860,7 @@ const es = {
808
860
  'help.l2d': ' para todos).',
809
861
  'help.l3': 'Los agentes ven los estados y diffs de los demás antes de cada acción: co-editan los mismos archivos sin bloquearse y sin romper el trabajo ajeno.',
810
862
  'help.states': 'Estados: 🔨 trabajando · 👂 escuchando a otros agentes (borde doble) · 🧠 pensando · ✋ esperando tu aprobación · ⏹ stop. Sonidos: 1 bip = agente lanzado/terminado, 2 bips = aprobación requerida (/sound off para silenciar).',
811
- 'help.keys': 'Esc: volver a la vista de agentes · Tab: autocompletar · ↑/↓: sugerencias, historial o scroll de vista · Ctrl+V: pegar imagen',
863
+ 'help.keys': 'Esc: volver a la vista de agentes · Tab: autocompletar · PgUp/PgDn: scroll hub/focus · ↑/↓: sugerencias, historial o scroll de vista · Ctrl+V: pegar imagen',
812
864
  'cmd.ask': 'Agente ask: responde y aconseja sin editar',
813
865
  'cmd.task': 'Agente task: ejecuta, edita, valida y resume',
814
866
  'cmd.agents': 'Vista de paneles de agentes (tiempo real)',
@@ -842,7 +894,7 @@ const es = {
842
894
  'cmd.model': 'Mostrar/cambiar el modelo de la sesión ([proveedor:]modelo)',
843
895
  'cmd.sessions': 'Listar las sesiones guardadas de este proyecto',
844
896
  'cmd.session': 'Restaurar una sesión guardada por número, o latest',
845
- 'cmd.doctor': 'Comprobar proveedor/modelo/clave API y mostrar correcciones',
897
+ 'cmd.doctor': 'Ejecutar diagnósticos locales de readiness',
846
898
  'cmd.settings': 'Ajustes globales — proveedores, claves API, idioma, valores por defecto (persistentes)',
847
899
  'cmd.ssettings': 'Ajustes de sesión — modelo, aprobaciones, sonido (solo esta sesión)',
848
900
  'cmd.project': 'Cambiar carpeta del proyecto o reabrir selector de carpeta',
@@ -860,7 +912,7 @@ const es = {
860
912
  'm.usageAsk': 'Uso: /ask [Nombre:] <pregunta> [--model=m] — solo asesoría, sin ediciones.',
861
913
  'm.usageSpawn': 'Uso: /task [Nombre:] <tarea> [--model=m]',
862
914
  'm.usageAt': 'Uso: @Agente <mensaje> (o @all <mensaje>)',
863
- 'm.broadcast': '✉ Instrucción difundida a todos los agentes.',
915
+ 'm.broadcast': '✉ Instrucción enviada a {n} agente(s) activo(s).',
864
916
  'm.sent': '✉ Instrucción enviada a {target}.',
865
917
  'm.notFound': 'Agente no encontrado: {target} (agentes: {list})',
866
918
  'm.none': 'ninguno',
@@ -891,14 +943,15 @@ const es = {
891
943
  'm.focusHint': '🎯 foco {name} · escribe para hablarle · PgUp/PgDn desplazamiento · Esc para salir',
892
944
  'm.usageRestore': 'Uso: /restore <agente> (tras cargar una sesión con /session)',
893
945
  'm.restored': '⏪ {name} relanzado con su conversación anterior.',
946
+ 'm.noRestoredAgent': 'No hay ningún agente llamado "{name}" en la sesión restaurada.',
894
947
  'm.noConversation': 'No hay conversación guardada para "{name}" en la sesión restaurada.',
895
948
  'm.conflict': '⚠ {path}: colisiones repetidas entre agentes — considera arbitrar (@agente o /focus).',
896
949
  'status.bar': '⚡ {agents} agente(s) · {active} activo(s) · {cost}',
897
950
  'cmd.plan': 'Agente plan: presenta su plan, solo edita tras tu aprobación',
898
- 'cmd.issue': 'lanza un agente sobre una issue de GitHub (requiere la CLI gh)',
951
+ 'cmd.issue': 'lanza un agente sobre una issue de GitHub (requiere gh, repo GitHub y auth)',
899
952
  'cmd.undo': 'revierte el último cambio de archivo del agente',
900
- 'cmd.commit': 'git-commit de los archivos tocados por un agente (o todos)',
901
- 'cmd.autocommit': 'commit automático de los archivos de cada agente al terminar',
953
+ 'cmd.commit': 'git-commit solo de archivos tocados por un agente (o todos)',
954
+ 'cmd.autocommit': 'commit automático session-only de archivos de cada agente al terminar',
902
955
  'm.usagePlan': 'Uso: /plan [Nombre:] <tarea> — el agente presenta su plan y espera tu aprobación antes de editar.',
903
956
  'm.usageIssue': 'Uso: /issue <número> — lanza un agente sobre esa issue de GitHub (requiere la CLI gh).',
904
957
  'm.ghMissing': '✗ CLI gh no encontrada. Instálala (https://cli.github.com) y ejecuta `gh auth login`.',
@@ -938,14 +991,34 @@ const es = {
938
991
  'm.copyNone': 'No hay resultado de agente que copiar.',
939
992
  'm.copyDone': 'Último resultado de {name} copiado.',
940
993
  'm.unknown': 'Comando desconocido: {cmd} — escribe /help',
994
+ 'm.projectActive': 'Hay agentes activos. Guarda/deténlos primero, o vuelve a ejecutar con /project [carpeta] --force.',
995
+ 'm.wizardActive': 'Hay agentes activos. Guarda/deténlos primero, o vuelve a ejecutar con /wizard --force.',
941
996
  'm.imagesIgnored': 'Nota: las imágenes adjuntas solo se tienen en cuenta al lanzar un agente nuevo.',
942
997
  'm.sessionRestored': '📂 Sesión del {date} restaurada.',
943
998
  'm.usageSession': 'Uso: /sessions para listar, luego /session <n> o /session latest',
944
999
  'm.sessionLoaded': '📂 Sesión del {date} cargada.',
1000
+ 'm.sessionActive': 'Hay agentes activos. Guárdalos/deténlos primero, o vuelve a ejecutar con /session <n|latest> --force.',
1001
+ 'm.sessionRestoreHint': 'Ejecuta /restore <agente> para relanzar una conversación guardada.',
945
1002
  'm.missingProvider': 'No hay proveedor configurado. Abre /settings → Añadir proveedor, o reinicia con configuración inicial.',
946
1003
  'm.missingKey': 'El proveedor {name} no tiene clave API. Usa /settings → Providers → Clave API.',
947
1004
  'm.missingModel': 'El proveedor {name} no tiene modelo por defecto. Usa /settings → Modelos del proveedor.',
948
1005
  'm.doctorOk': '✓ Configuración lista: {pm}. Cambios: /settings · modelo temporal: /model [proveedor:]modelo',
1006
+ 'm.doctorReport': 'Doctor\n{lines}',
1007
+ 'm.doctorNoProvider': '✗ No hay proveedor configurado. Abre /settings → Add a provider.',
1008
+ 'm.doctorProvider': '• Proveedor: {provider}:{model}',
1009
+ 'm.doctorKeyMissing': '✗ Falta la clave API. Abre /settings → Providers → Clave API.',
1010
+ 'm.doctorKeyOk': '✓ Clave API presente.',
1011
+ 'm.doctorKeySkipped': '✓ Este proveedor local no requiere clave API.',
1012
+ 'm.doctorModelMissing': '✗ Falta el modelo o sigue usando el placeholder. Elige un modelo real en /settings o /model.',
1013
+ 'm.doctorModelOk': '✓ Modelo listo: {model}',
1014
+ 'm.doctorEndpointOk': '✓ Endpoint local accesible: {url}',
1015
+ 'm.doctorEndpointFail': '⚠ Endpoint local no accesible: {url}',
1016
+ 'm.doctorAttachOk': '✓ Socket attach disponible.',
1017
+ 'm.doctorAttachMissing': '⚠ Socket attach aún no está activo.',
1018
+ 'm.doctorGitOk': '✓ CLI git encontrado.',
1019
+ 'm.doctorGitMissing': '⚠ CLI git no encontrado.',
1020
+ 'm.doctorGhOk': '✓ CLI gh encontrado para /issue.',
1021
+ 'm.doctorGhMissing': '⚠ CLI gh no encontrado; /issue necesita gh y auth de GitHub.',
949
1022
  'm.spawnFail': 'No se puede lanzar el agente: ningún provider/modelo utilizable. Configura uno con /settings.',
950
1023
  'set.title': '⚙ AJUSTES — globales (persistentes en ~/.parallel/config.json)',
951
1024
  'sset.title': '⚙ AJUSTES DE SESIÓN — solo esta sesión (globales: /settings)',
@@ -961,8 +1034,13 @@ const es = {
961
1034
  'set.sound': 'Sonido por defecto: {state}',
962
1035
  'set.back': '← Volver',
963
1036
  'set.saved': '✓ Guardado en ~/.parallel/config.json',
1037
+ 'set.sessionProviderReady': '✓ {name} está listo solo para esta sesión.',
1038
+ 'set.setupScope.title': '¿Usar {name} solo ahora o guardarlo globalmente?',
1039
+ 'set.setupScope.session': 'Usar solo en esta sesión',
1040
+ 'set.setupScope.global': 'Guardar globalmente',
964
1041
  'set.chooseProvider': 'Elige un proveedor',
965
1042
  'set.chooseModel': 'Modelo ({name}) — elige o escribe un nombre según la doc del proveedor',
1043
+ 'set.modelPlaceholder': 'Elige o escribe primero un modelo real. `your-model-here` es solo un placeholder.',
966
1044
  'sset.model': 'Modelo de sesión: {pm}',
967
1045
  'sset.approvals': 'Aprobaciones shell (sesión): {mode}',
968
1046
  'sset.sound': 'Sonido (sesión): {state}',
@@ -1131,7 +1209,7 @@ const zh = {
1131
1209
  'notes.empty': '尚未交换任何便签。',
1132
1210
  'sessions.title': '📂 已保存会话',
1133
1211
  'sessions.empty': '此项目暂无已保存会话。',
1134
- 'sessions.item': '{date} · {agents} 个智能体',
1212
+ 'sessions.item': '{name}{date} · {agents} 个智能体',
1135
1213
  'sessions.hint': '用 /session <n> 或 /session latest 恢复。新的启动默认从空会话开始。',
1136
1214
  'diff.title': '± 实时差异 — 共 {total} 处修改(显示最近 4 处)',
1137
1215
  'diff.empty': '暂无文件修改。',
@@ -1147,7 +1225,7 @@ const zh = {
1147
1225
  'help.l2d': ' 发给所有)。',
1148
1226
  'help.l3': '每次行动前,智能体都能看到彼此的状态和差异:它们共同编辑同一批文件,互不阻塞,也不破坏彼此的工作。',
1149
1227
  'help.states': '状态:🔨 工作中 · 👂 倾听其他智能体(双边框)· 🧠 思考中 · ✋ 等待你的批准 · ⏹ 停止。声音:1 声 = 智能体启动/完成,2 声 = 需要批准(/sound off 静音)。',
1150
- 'help.keys': 'Esc:返回智能体视图 · Tab:补全 · ↑/↓:建议、历史或视图滚动 · Ctrl+V:粘贴图片',
1228
+ 'help.keys': 'Esc:返回智能体视图 · Tab:补全 · PgUp/PgDn:hub/focus 滚动 · ↑/↓:建议、历史或视图滚动 · Ctrl+V:粘贴图片',
1151
1229
  'cmd.ask': 'Ask 智能体:只回答和建议,不编辑',
1152
1230
  'cmd.task': 'Task 智能体:执行、编辑、验证并总结',
1153
1231
  'cmd.agents': '智能体面板视图(实时)',
@@ -1181,7 +1259,7 @@ const zh = {
1181
1259
  'cmd.model': '显示/更改会话模型([提供商:]模型)',
1182
1260
  'cmd.sessions': '列出此项目的已保存会话',
1183
1261
  'cmd.session': '按编号或 latest 恢复已保存会话',
1184
- 'cmd.doctor': '检查提供商/模型/API 密钥配置并显示修复提示',
1262
+ 'cmd.doctor': '运行本地就绪诊断',
1185
1263
  'cmd.settings': '全局设置 — 提供商、API 密钥、语言、默认值(持久化)',
1186
1264
  'cmd.ssettings': '会话设置 — 模型、批准、声音(仅本会话)',
1187
1265
  'cmd.project': '更改项目文件夹或重新打开文件夹选择器',
@@ -1199,7 +1277,7 @@ const zh = {
1199
1277
  'm.usageAsk': '用法:/ask [名称:] <问题> [--model=m] — 仅建议,不修改。',
1200
1278
  'm.usageSpawn': '用法:/task [名称:] <任务> [--model=m]',
1201
1279
  'm.usageAt': '用法:@智能体 <消息>(或 @all <消息>)',
1202
- 'm.broadcast': '✉ 指令已广播给所有智能体。',
1280
+ 'm.broadcast': '✉ 指令已发送给 {n} 个活动智能体。',
1203
1281
  'm.sent': '✉ 指令已发送给 {target}。',
1204
1282
  'm.notFound': '找不到智能体:{target}(现有:{list})',
1205
1283
  'm.none': '无',
@@ -1230,14 +1308,15 @@ const zh = {
1230
1308
  'm.focusHint': '🎯 聚焦 {name} · 直接输入与它对话 · PgUp/PgDn 滚动 · Esc 退出',
1231
1309
  'm.usageRestore': '用法:/restore <代理>(先用 /session 加载会话)',
1232
1310
  'm.restored': '⏪ {name} 已带着之前的对话重新启动。',
1311
+ 'm.noRestoredAgent': '恢复的会话中没有名为「{name}」的代理。',
1233
1312
  'm.noConversation': '恢复的会话中没有「{name}」的已保存对话。',
1234
1313
  'm.conflict': '⚠ {path}:代理之间反复冲突 — 建议你介入仲裁(@代理 或 /focus)。',
1235
1314
  'status.bar': '⚡ {agents} 个代理 · {active} 个活跃 · {cost}',
1236
1315
  'cmd.plan': 'Plan 智能体:先给出计划,你批准后才开始修改',
1237
- 'cmd.issue': '基于 GitHub issue 启动代理(需要 gh CLI)',
1316
+ 'cmd.issue': '基于 GitHub issue 启动代理(需要 gh、GitHub 仓库和登录)',
1238
1317
  'cmd.undo': '撤销该代理最近一次文件修改',
1239
- 'cmd.commit': '把某个代理(或全部)改动的文件做 git 提交',
1240
- 'cmd.autocommit': '每个代理完成任务时自动提交其文件',
1318
+ 'cmd.commit': '仅提交某个代理(或全部)改动过的文件',
1319
+ 'cmd.autocommit': '会话内自动提交每个代理完成时改动过的文件',
1241
1320
  'm.usagePlan': '用法:/plan [名称:] <任务> — 代理先呈现计划,获得你的批准后才开始修改。',
1242
1321
  'm.usageIssue': '用法:/issue <编号> — 基于该 GitHub issue 启动代理(需要 gh CLI)。',
1243
1322
  'm.ghMissing': '✗ 未找到 gh CLI。请安装(https://cli.github.com)并执行 `gh auth login`。',
@@ -1277,14 +1356,34 @@ const zh = {
1277
1356
  'm.copyNone': '没有可复制的已完成智能体输出。',
1278
1357
  'm.copyDone': '已复制 {name} 的最新结果。',
1279
1358
  'm.unknown': '未知命令:{cmd} — 输入 /help',
1359
+ 'm.projectActive': '仍有活动智能体。请先保存/停止,或使用 /project [文件夹] --force。',
1360
+ 'm.wizardActive': '仍有活动智能体。请先保存/停止,或使用 /wizard --force。',
1280
1361
  'm.imagesIgnored': '注意:附加的图片仅在启动新智能体时生效。',
1281
1362
  'm.sessionRestored': '📂 已恢复 {date} 的会话。',
1282
1363
  'm.usageSession': '用法:先用 /sessions 列出,然后 /session <n> 或 /session latest',
1283
1364
  'm.sessionLoaded': '📂 已加载 {date} 的会话。',
1365
+ 'm.sessionActive': '仍有活动代理。请先保存/停止,或使用 /session <n|latest> --force。',
1366
+ 'm.sessionRestoreHint': '运行 /restore <代理> 重新启动已保存的代理对话。',
1284
1367
  'm.missingProvider': '未配置提供商。打开 /settings → 添加提供商,或重新进行首次配置。',
1285
1368
  'm.missingKey': '提供商 {name} 没有 API 密钥。使用 /settings → Providers → API 密钥。',
1286
1369
  'm.missingModel': '提供商 {name} 没有默认模型。使用 /settings → 提供商模型。',
1287
1370
  'm.doctorOk': '✓ 配置就绪:{pm}。修改:/settings · 临时模型:/model [提供商:]模型',
1371
+ 'm.doctorReport': 'Doctor\n{lines}',
1372
+ 'm.doctorNoProvider': '✗ 未配置提供商。打开 /settings → Add a provider。',
1373
+ 'm.doctorProvider': '• 提供商:{provider}:{model}',
1374
+ 'm.doctorKeyMissing': '✗ 缺少 API 密钥。打开 /settings → Providers → API 密钥。',
1375
+ 'm.doctorKeyOk': '✓ API 密钥已设置。',
1376
+ 'm.doctorKeySkipped': '✓ 此本地提供商不需要 API 密钥。',
1377
+ 'm.doctorModelMissing': '✗ 模型缺失或仍是占位符。请在 /settings 或 /model 选择真实模型。',
1378
+ 'm.doctorModelOk': '✓ 模型就绪:{model}',
1379
+ 'm.doctorEndpointOk': '✓ 本地端点可访问:{url}',
1380
+ 'm.doctorEndpointFail': '⚠ 本地端点不可访问:{url}',
1381
+ 'm.doctorAttachOk': '✓ attach socket 可用。',
1382
+ 'm.doctorAttachMissing': '⚠ attach socket 尚未运行。',
1383
+ 'm.doctorGitOk': '✓ 找到 git CLI。',
1384
+ 'm.doctorGitMissing': '⚠ 未找到 git CLI。',
1385
+ 'm.doctorGhOk': '✓ 找到 gh CLI,可用于 /issue。',
1386
+ 'm.doctorGhMissing': '⚠ 未找到 gh CLI;/issue 需要 gh 和 GitHub 登录。',
1288
1387
  'm.spawnFail': '无法启动智能体:没有可用的 provider/模型。请通过 /settings 配置。',
1289
1388
  'set.title': '⚙ 设置 — 全局(持久化于 ~/.parallel/config.json)',
1290
1389
  'sset.title': '⚙ 会话设置 — 仅本会话(全局设置:/settings)',
@@ -1300,8 +1399,13 @@ const zh = {
1300
1399
  'set.sound': '默认声音:{state}',
1301
1400
  'set.back': '← 返回',
1302
1401
  'set.saved': '✓ 已保存到 ~/.parallel/config.json',
1402
+ 'set.sessionProviderReady': '✓ {name} 仅在本会话中可用。',
1403
+ 'set.setupScope.title': '仅现在使用 {name},还是全局保存?',
1404
+ 'set.setupScope.session': '仅用于本会话',
1405
+ 'set.setupScope.global': '全局保存',
1303
1406
  'set.chooseProvider': '选择提供商',
1304
1407
  'set.chooseModel': '模型({name})— 选择或按提供商文档输入名称',
1408
+ 'set.modelPlaceholder': '请先选择或输入真实模型名。`your-model-here` 只是占位符。',
1305
1409
  'sset.model': '会话模型:{pm}',
1306
1410
  'sset.approvals': 'Shell 批准(会话):{mode}',
1307
1411
  'sset.sound': '声音(会话):{state}',
package/dist/index.js CHANGED
@@ -44,8 +44,8 @@ Usage:
44
44
  auto-approved commands, summary (or JSON) on stdout — for CI
45
45
 
46
46
  Environment variables:
47
- PARALLEL_API_KEY / DEEPSEEK_API_KEY API key
48
- PARALLEL_MODEL Default model (e.g. deepseek-chat)
47
+ PARALLEL_API_KEY API key for the default provider
48
+ PARALLEL_MODEL Default model
49
49
  PARALLEL_BASE_URL OpenAI-compatible endpoint
50
50
  PARALLEL_NO_ALT_SCREEN=1 Disable the alternate terminal screen.
51
51
 
@@ -108,7 +108,7 @@ if (headless) {
108
108
  ctl.setSessionApprovalMode('yolo');
109
109
  const provider = ctl.sessionProvider();
110
110
  if (!provider || !providerReady(provider)) {
111
- console.error('Headless mode needs a configured provider + API key. Run `parallel` interactively once, or set PARALLEL_API_KEY.');
111
+ console.error('Headless mode needs a ready provider and model. Run `parallel` interactively once, or set PARALLEL_API_KEY / PARALLEL_MODEL.');
112
112
  process.exit(1);
113
113
  }
114
114
  // Agent questions cannot be asked: auto-answer with the recommended option.