zyn-ai 1.3.2 → 1.3.3
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/README.md +86 -93
- package/package.json +1 -1
- package/src/cli/commands.js +151 -48
- package/src/cli/print.js +4 -2
- package/src/config.js +7 -1
- package/src/core/agent.js +3 -4
- package/src/core/prompts.js +46 -9
- package/src/tools/index.js +140 -121
- package/src/tui/app.mjs +72 -21
- package/src/web/server.js +10 -5
package/README.md
CHANGED
|
@@ -6,55 +6,42 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<img src="https://img.shields.io/npm/v/zyn-ai?label=npm&color=%23CB3837" alt="NPM Version"/>
|
|
9
|
-
|
|
10
9
|
<img src="https://img.shields.io/github/v/release/SoyMaycol/Zyn?include_prereleases&sort=semver" alt="Latest Release"/>
|
|
11
|
-
|
|
12
10
|
<img src="https://img.shields.io/npm/dt/zyn-ai" alt="Downloads"/>
|
|
13
|
-
|
|
14
|
-
<img src="https://img.shields.io/github/forks/SoyMaycol/Zyn" alt="Forks"/>
|
|
15
11
|
</p>
|
|
16
12
|
|
|
17
13
|
<p align="center">
|
|
18
|
-
<b>Local
|
|
14
|
+
<b>Local AI agent for terminal, TUI, and web.</b>
|
|
19
15
|
</p>
|
|
20
16
|
|
|
21
17
|
<p align="center">
|
|
22
18
|
<a href="https://github.com/SoyMaycol/Zyn">Official repository</a>
|
|
23
19
|
</p>
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
---
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
## What is Zyn
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
Zyn is a local AI agent designed for terminal and web usage. It supports persistent sessions, system tools, multiple AI providers, session exports, and configurable models.
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
- Interactive terminal mode and classic CLI mode.
|
|
33
|
-
- Web mode for collaborative usage.
|
|
34
|
-
- Multiple providers and custom models.
|
|
35
|
-
- Modular skills system.
|
|
36
|
-
- Persistent sessions, history, and transcript export.
|
|
37
|
-
- Extensible architecture for tools and providers.
|
|
27
|
+
---
|
|
38
28
|
|
|
39
29
|
## Requirements
|
|
40
30
|
|
|
41
|
-
- Node.js 18
|
|
31
|
+
- Node.js 18+
|
|
42
32
|
- npm
|
|
43
33
|
- Internet connection for remote providers
|
|
44
34
|
- Optional: Ollama for local models
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
### Global install
|
|
36
|
+
---
|
|
49
37
|
|
|
50
|
-
|
|
51
|
-
npm install zyn-ai -g
|
|
52
|
-
```
|
|
38
|
+
## Installation
|
|
53
39
|
|
|
54
|
-
|
|
40
|
+
### Global install
|
|
55
41
|
|
|
56
42
|
```bash
|
|
57
|
-
|
|
43
|
+
npm install -g zyn-ai
|
|
44
|
+
zyn
|
|
58
45
|
```
|
|
59
46
|
|
|
60
47
|
### Local development
|
|
@@ -63,79 +50,116 @@ Zyn
|
|
|
63
50
|
git clone https://github.com/SoyMaycol/Zyn.git
|
|
64
51
|
cd Zyn
|
|
65
52
|
npm install
|
|
53
|
+
npm start
|
|
66
54
|
```
|
|
67
55
|
|
|
68
|
-
|
|
56
|
+
---
|
|
69
57
|
|
|
70
|
-
|
|
58
|
+
## Usage
|
|
71
59
|
|
|
72
60
|
```bash
|
|
73
|
-
|
|
61
|
+
zyn
|
|
62
|
+
zyn "Explain this project"
|
|
63
|
+
zyn --new
|
|
64
|
+
zyn --resume ID
|
|
74
65
|
```
|
|
75
66
|
|
|
76
|
-
|
|
67
|
+
---
|
|
77
68
|
|
|
78
|
-
|
|
79
|
-
Zyn "Explain this project"
|
|
80
|
-
```
|
|
69
|
+
## Web mode
|
|
81
70
|
|
|
82
|
-
|
|
71
|
+
Inside Zyn:
|
|
83
72
|
|
|
84
|
-
```
|
|
73
|
+
```text
|
|
85
74
|
/web
|
|
75
|
+
/web 0.0.0.0:3000
|
|
86
76
|
```
|
|
87
77
|
|
|
88
78
|
Or directly:
|
|
89
79
|
|
|
90
80
|
```bash
|
|
91
|
-
|
|
81
|
+
npm run web
|
|
92
82
|
```
|
|
93
83
|
|
|
94
|
-
|
|
84
|
+
---
|
|
95
85
|
|
|
96
|
-
|
|
86
|
+
## Language
|
|
97
87
|
|
|
98
|
-
|
|
99
|
-
/help
|
|
100
|
-
```
|
|
88
|
+
Supported languages:
|
|
101
89
|
|
|
102
|
-
|
|
90
|
+
- `en`
|
|
91
|
+
- `es`
|
|
103
92
|
|
|
104
|
-
|
|
93
|
+
Commands:
|
|
105
94
|
|
|
106
95
|
```text
|
|
96
|
+
/lang
|
|
107
97
|
/lang en
|
|
108
98
|
/lang es
|
|
109
99
|
```
|
|
110
100
|
|
|
111
|
-
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Main Commands
|
|
112
104
|
|
|
113
|
-
|
|
105
|
+
### Sessions
|
|
114
106
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
- `/reset` resets the current context.
|
|
107
|
+
| Command | Description |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `/help` | Show available commands |
|
|
110
|
+
| `/status` | Show current status |
|
|
111
|
+
| `/history` | Show recent actions |
|
|
112
|
+
| `/memory` | Show memory summary |
|
|
113
|
+
| `/sessions` | List saved sessions |
|
|
114
|
+
| `/new` | Create a new session |
|
|
115
|
+
| `/resume <ID>` | Resume a session |
|
|
116
|
+
| `/title <text>` | Rename session |
|
|
126
117
|
|
|
127
|
-
|
|
118
|
+
### Configuration
|
|
128
119
|
|
|
129
|
-
|
|
120
|
+
| Command | Description |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `/model` | Show or change model |
|
|
123
|
+
| `/models` | List models |
|
|
124
|
+
| `/providers` | List providers |
|
|
125
|
+
| `/lang <en\|es>` | Change language |
|
|
126
|
+
| `/config show` | Show config |
|
|
127
|
+
| `/auto on\|off` | Toggle auto approval |
|
|
128
|
+
| `/cwd <path>` | Change working directory |
|
|
130
129
|
|
|
131
|
-
|
|
132
|
-
- Zen
|
|
133
|
-
- Ollama
|
|
134
|
-
- OpenAI-compatible providers
|
|
130
|
+
### Tools
|
|
135
131
|
|
|
136
|
-
|
|
132
|
+
| Command | Description |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `/tools` | List tools |
|
|
135
|
+
| `/skills` | List skills |
|
|
136
|
+
| `/cwd` | Show working directory |
|
|
137
137
|
|
|
138
|
-
###
|
|
138
|
+
### Web & Export
|
|
139
|
+
|
|
140
|
+
| Command | Description |
|
|
141
|
+
|---|---|
|
|
142
|
+
| `/web` | Start web interface |
|
|
143
|
+
| `/transcript` | Show transcript |
|
|
144
|
+
| `/export` | Export session |
|
|
145
|
+
|
|
146
|
+
### Control
|
|
147
|
+
|
|
148
|
+
| Command | Description |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `/stop` | Stop current task |
|
|
151
|
+
| `/reset` | Reset session |
|
|
152
|
+
| `/exit` | Exit Zyn |
|
|
153
|
+
|
|
154
|
+
In the TUI, press `ESC` twice to stop the current task.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Models
|
|
159
|
+
|
|
160
|
+
Custom models can be added using `data/models.json`.
|
|
161
|
+
|
|
162
|
+
Example:
|
|
139
163
|
|
|
140
164
|
```json
|
|
141
165
|
{
|
|
@@ -154,34 +178,3 @@ Models can be extended from `data/models.json` or from the internal configuratio
|
|
|
154
178
|
}
|
|
155
179
|
}
|
|
156
180
|
```
|
|
157
|
-
|
|
158
|
-
## Web collaboration
|
|
159
|
-
|
|
160
|
-
The web version is designed for cross-review between multiple models. That helps one model correct or contrast what another generated, which is useful when consistency matters.
|
|
161
|
-
|
|
162
|
-
## Skills
|
|
163
|
-
|
|
164
|
-
The skills system breaks the agent behavior into focused pieces:
|
|
165
|
-
|
|
166
|
-
- `core`
|
|
167
|
-
- `reasoning`
|
|
168
|
-
- `methodology`
|
|
169
|
-
- `thinking`
|
|
170
|
-
- `tools`
|
|
171
|
-
- `web-agent`
|
|
172
|
-
- `debugging`
|
|
173
|
-
- `frontend_design`
|
|
174
|
-
- `code-style`
|
|
175
|
-
- `domains`
|
|
176
|
-
- `testing`
|
|
177
|
-
|
|
178
|
-
Each skill can evolve without breaking the rest of the project.
|
|
179
|
-
|
|
180
|
-
## License
|
|
181
|
-
|
|
182
|
-
This project includes an attribution-friendly license. Keep the credits, the repository link, and the license notices when redistributing or deriving the project.
|
|
183
|
-
|
|
184
|
-
## Credits
|
|
185
|
-
|
|
186
|
-
- Project: [SoyMaycol/Zyn](https://github.com/SoyMaycol/Zyn)
|
|
187
|
-
- Base authorship: Maycol and Ado
|
package/package.json
CHANGED
package/src/cli/commands.js
CHANGED
|
@@ -13,35 +13,39 @@ const { resolveInputPath } = require('../utils/pathUtils');
|
|
|
13
13
|
const { printTools } = require('../tools');
|
|
14
14
|
|
|
15
15
|
const SLASH_COMMANDS = [
|
|
16
|
-
{ name: 'help', desc: 'full help' },
|
|
17
|
-
{ name: 'status', desc: 'current status' },
|
|
18
|
-
{ name: 'history', desc: 'recent actions' },
|
|
19
|
-
{ name: 'memory', desc: 'memory summary' },
|
|
20
|
-
{ name: '
|
|
21
|
-
{ name: '
|
|
22
|
-
{ name: '
|
|
23
|
-
{ name: '
|
|
24
|
-
{ name: '
|
|
25
|
-
{ name: '
|
|
26
|
-
{ name: '
|
|
27
|
-
{ name: '
|
|
28
|
-
{ name: '
|
|
29
|
-
{ name: '
|
|
30
|
-
{ name: '
|
|
31
|
-
{ name: '
|
|
32
|
-
{ name: '
|
|
33
|
-
{ name: '
|
|
34
|
-
{ name: '
|
|
35
|
-
{ name: '
|
|
36
|
-
{ name: '
|
|
37
|
-
{ name: '
|
|
38
|
-
{ name: '
|
|
39
|
-
{ name: '
|
|
40
|
-
{ name: '
|
|
41
|
-
{ name: '
|
|
42
|
-
{ name: '
|
|
43
|
-
{ name: '
|
|
44
|
-
{ name: '
|
|
16
|
+
{ name: 'help', desc: 'full help', descEs: 'ayuda completa' },
|
|
17
|
+
{ name: 'status', desc: 'current status', descEs: 'estado actual' },
|
|
18
|
+
{ name: 'history', desc: 'recent actions', descEs: 'acciones recientes' },
|
|
19
|
+
{ name: 'memory', desc: 'memory summary', descEs: 'resumen de memoria' },
|
|
20
|
+
{ name: 'summary', desc: 'memory summary', descEs: 'resumen de memoria' },
|
|
21
|
+
{ name: 'session', desc: 'current session', descEs: 'sesión actual' },
|
|
22
|
+
{ name: 'sessions', desc: 'list sessions', descEs: 'listar sesiones' },
|
|
23
|
+
{ name: 'new', desc: 'new session', descEs: 'nueva sesión' },
|
|
24
|
+
{ name: 'resume', desc: 'resume session', descEs: 'reanudar sesión' },
|
|
25
|
+
{ name: 'title', desc: 'rename session', descEs: 'renombrar sesión' },
|
|
26
|
+
{ name: 'rename', desc: 'rename session', descEs: 'renombrar sesión' },
|
|
27
|
+
{ name: 'model', desc: 'view/change model', descEs: 'ver/cambiar modelo' },
|
|
28
|
+
{ name: 'models', desc: 'list models', descEs: 'listar modelos' },
|
|
29
|
+
{ name: 'providers', desc: 'list providers', descEs: 'listar proveedores' },
|
|
30
|
+
{ name: 'git', desc: 'configure git credentials', descEs: 'configurar credenciales git' },
|
|
31
|
+
{ name: 'persona', desc: 'set response tone/personality', descEs: 'definir tono/persona' },
|
|
32
|
+
{ name: 'lang', desc: 'change language', descEs: 'cambiar idioma' },
|
|
33
|
+
{ name: 'language', desc: 'change language', descEs: 'cambiar idioma' },
|
|
34
|
+
{ name: 'auto', desc: 'auto-approval', descEs: 'auto-aprobación' },
|
|
35
|
+
{ name: 'concuerdo', desc: 'group model mode', descEs: 'modo de grupo' },
|
|
36
|
+
{ name: 'tools', desc: 'tools', descEs: 'herramientas' },
|
|
37
|
+
{ name: 'skills', desc: 'agent skills', descEs: 'skills del agente' },
|
|
38
|
+
{ name: 'config', desc: 'view/change session settings', descEs: 'ver/cambiar configuración' },
|
|
39
|
+
{ name: 'web', desc: 'open web version', descEs: 'abrir versión web' },
|
|
40
|
+
{ name: 'stop', desc: 'stop agent', descEs: 'detener agente' },
|
|
41
|
+
{ name: 'abort', desc: 'stop agent', descEs: 'detener agente' },
|
|
42
|
+
{ name: 'reset', desc: 'reset context', descEs: 'reiniciar contexto' },
|
|
43
|
+
{ name: 'clear', desc: 'reset context', descEs: 'reiniciar contexto' },
|
|
44
|
+
{ name: 'cwd', desc: 'working directory', descEs: 'directorio de trabajo' },
|
|
45
|
+
{ name: 'transcript', desc: 'view transcript', descEs: 'ver transcripción' },
|
|
46
|
+
{ name: 'export', desc: 'export to txt', descEs: 'exportar a txt' },
|
|
47
|
+
{ name: 'exit', desc: 'exit', descEs: 'salir' },
|
|
48
|
+
{ name: 'quit', desc: 'exit', descEs: 'salir' },
|
|
45
49
|
];
|
|
46
50
|
|
|
47
51
|
function parseSlashCommand(input) {
|
|
@@ -57,6 +61,7 @@ function printHelp(state = {}) {
|
|
|
57
61
|
const { paint } = require('./print');
|
|
58
62
|
const lang = normalizeLanguage(state.language || DEFAULT_LANGUAGE);
|
|
59
63
|
const m = (value) => paint(value, 'dim');
|
|
64
|
+
const b = (value) => paint(value, 'cyan');
|
|
60
65
|
const providers = listProvidersFromModels(MODELS);
|
|
61
66
|
|
|
62
67
|
console.log('');
|
|
@@ -68,15 +73,77 @@ function printHelp(state = {}) {
|
|
|
68
73
|
console.log(` zyn --new ${m(t(lang, 'newSession'))}`);
|
|
69
74
|
console.log(` zyn --resume ID ${m(t(lang, 'resumeSession'))}`);
|
|
70
75
|
console.log('');
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
76
|
+
|
|
77
|
+
// Sessions
|
|
78
|
+
console.log(` ${paint('── Sessions ──', 'dim')}`);
|
|
79
|
+
console.log(` ${b('/help')} Show this help`);
|
|
80
|
+
console.log(` ${b('/status')} Show current status`);
|
|
81
|
+
console.log(` ${b('/history')} Recent actions (last 20)`);
|
|
82
|
+
console.log(` ${b('/memory')} Agent memory summary`);
|
|
83
|
+
console.log(` ${b('/summary')} Alias of /memory`);
|
|
84
|
+
console.log(` ${b('/session')} Current session info`);
|
|
85
|
+
console.log(` ${b('/sessions')} List all saved sessions`);
|
|
86
|
+
console.log(` ${b('/new')} Create a new session`);
|
|
87
|
+
console.log(` ${b('/resume <ID>')} Resume an existing session`);
|
|
88
|
+
console.log(` ${b('/title <text>')} Rename current session`);
|
|
89
|
+
console.log(` ${b('/rename <text>')} Alias of /title`);
|
|
90
|
+
console.log('');
|
|
91
|
+
|
|
92
|
+
// Configuration
|
|
93
|
+
console.log(` ${paint('── Configuration ──', 'dim')}`);
|
|
94
|
+
console.log(` ${b('/model')} Show active model`);
|
|
95
|
+
console.log(` ${b('/model <key>')} Change active model`);
|
|
96
|
+
console.log(` ${b('/models')} List available models`);
|
|
97
|
+
console.log(` ${b('/providers')} List detected providers`);
|
|
98
|
+
console.log(` ${b('/lang')} Show current language`);
|
|
99
|
+
console.log(` ${b('/lang <en|es>')} Change language`);
|
|
100
|
+
console.log(` ${b('/language <en|es>')} Alias of /lang`);
|
|
101
|
+
console.log(` ${b('/auto')} Show auto-approval status`);
|
|
102
|
+
console.log(` ${b('/auto on')} Enable auto-approval`);
|
|
103
|
+
console.log(` ${b('/auto off')} Disable auto-approval`);
|
|
104
|
+
console.log(` ${b('/concuerdo')} Toggle group model mode`);
|
|
105
|
+
console.log(` ${b('/persona set <text>')} Set response persona/tone`);
|
|
106
|
+
console.log(` ${b('/persona show')} Show active persona`);
|
|
107
|
+
console.log(` ${b('/persona reset')} Reset to default persona`);
|
|
108
|
+
console.log(` ${b('/config show')} Show session config`);
|
|
109
|
+
console.log(` ${b('/config lang <en|es>')} Change language from config`);
|
|
110
|
+
console.log(` ${b('/config model <key>')} Change model from config`);
|
|
111
|
+
console.log(` ${b('/config auto on|off')} Toggle auto from config`);
|
|
112
|
+
console.log(` ${b('/config group on|off')} Toggle group mode from config`);
|
|
113
|
+
console.log(` ${b('/config cwd <path>')} Change working dir from config`);
|
|
114
|
+
console.log('');
|
|
115
|
+
|
|
116
|
+
// Tools and Git
|
|
117
|
+
console.log(` ${paint('── Tools and Git ──', 'dim')}`);
|
|
118
|
+
console.log(` ${b('/tools')} List available agent tools`);
|
|
119
|
+
console.log(` ${b('/skills')} List loaded skills`);
|
|
120
|
+
console.log(` ${b('/git set <provider> <token>')} Configure git credentials`);
|
|
121
|
+
console.log(` ${b('/git set <provider> <token> [user] [apiBaseUrl:URL] [cloneBaseUrl:URL] [name:X]')}`);
|
|
122
|
+
console.log(` ${b('/git list')} List configured git profiles`);
|
|
123
|
+
console.log(` ${b('/git remove <provider> [name]')} Remove git credentials`);
|
|
124
|
+
console.log(` ${b('/cwd')} Show current working directory`);
|
|
125
|
+
console.log(` ${b('/cwd <path>')} Change working directory`);
|
|
126
|
+
console.log('');
|
|
127
|
+
|
|
128
|
+
// Web and export
|
|
129
|
+
console.log(` ${paint('── Web and Export ──', 'dim')}`);
|
|
130
|
+
console.log(` ${b('/web')} Open web version`);
|
|
131
|
+
console.log(` ${b('/web <host:port>')} Open web version on custom host:port`);
|
|
132
|
+
console.log(` ${b('/transcript')} View full session transcript`);
|
|
133
|
+
console.log(` ${b('/export')} Export session to txt`);
|
|
134
|
+
console.log(` ${b('/export <path>')} Export session to specific path`);
|
|
75
135
|
console.log('');
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
console.log(`
|
|
136
|
+
|
|
137
|
+
// Control
|
|
138
|
+
console.log(` ${paint('── Control ──', 'dim')}`);
|
|
139
|
+
console.log(` ${b('/stop')} Stop current agent turn`);
|
|
140
|
+
console.log(` ${b('/abort')} Alias of /stop`);
|
|
141
|
+
console.log(` ${b('/reset')} Reset context (clear history)`);
|
|
142
|
+
console.log(` ${b('/clear')} Alias of /reset`);
|
|
143
|
+
console.log(` ${b('/exit')} Exit Zyn`);
|
|
144
|
+
console.log(` ${b('/quit')} Alias of /exit`);
|
|
79
145
|
console.log('');
|
|
146
|
+
|
|
80
147
|
console.log(` ${m(t(lang, 'escTwice'))}`);
|
|
81
148
|
console.log(` ${m(t(lang, 'escTwiceDesc'))}`);
|
|
82
149
|
console.log('');
|
|
@@ -123,15 +190,16 @@ function printConfig(state) {
|
|
|
123
190
|
console.log('');
|
|
124
191
|
}
|
|
125
192
|
|
|
126
|
-
async function startWebVersion() {
|
|
193
|
+
async function startWebVersion(host = '127.0.0.1', port = 3000) {
|
|
127
194
|
const serverPath = path.join(__dirname, '..', 'web', 'server.js');
|
|
128
195
|
const child = spawn(process.execPath, [serverPath], {
|
|
129
196
|
detached: true,
|
|
130
197
|
stdio: 'ignore',
|
|
131
198
|
windowsHide: true,
|
|
199
|
+
env: { ...process.env, HOST: host, PORT: String(port) },
|
|
132
200
|
});
|
|
133
201
|
child.unref();
|
|
134
|
-
return
|
|
202
|
+
return `http://${host}:${port}`;
|
|
135
203
|
}
|
|
136
204
|
|
|
137
205
|
async function handleLocalCommand(input, state, deps) {
|
|
@@ -200,27 +268,50 @@ async function handleLocalCommand(input, state, deps) {
|
|
|
200
268
|
if (commandName === 'git') {
|
|
201
269
|
const [sub, ...rest] = args.split(' ').filter(Boolean);
|
|
202
270
|
if (!sub || sub === 'help') {
|
|
203
|
-
console.log('Uso: /git list
|
|
271
|
+
console.log('Uso: /git list');
|
|
272
|
+
console.log(' /git set <provider> <token> [username] [apiBaseUrl] [cloneBaseUrl] [name]');
|
|
273
|
+
console.log(' /git remove <provider> [name]');
|
|
274
|
+
console.log('');
|
|
275
|
+
console.log('Proveedores: github, gitlab, custom');
|
|
276
|
+
console.log('Para custom: apiBaseUrl y cloneBaseUrl son obligatorios para configurar la URL');
|
|
277
|
+
console.log('name: identificador para multiples perfiles custom');
|
|
278
|
+
console.log('');
|
|
279
|
+
console.log('Ejemplos:');
|
|
280
|
+
console.log(' /git set github ghp_xxxxx');
|
|
281
|
+
console.log(' /git set custom glpat_xxxxx - apiBaseUrl:https://git.empresa.com/api/v4 cloneBaseUrl:https://git.empresa.com name:empresa');
|
|
204
282
|
return true;
|
|
205
283
|
}
|
|
206
284
|
if (sub === 'list') {
|
|
207
285
|
const secrets = listGitSecrets();
|
|
208
286
|
if (!secrets.length) console.log('No hay credenciales git guardadas.');
|
|
209
|
-
else
|
|
287
|
+
else {
|
|
288
|
+
for (const s of secrets) {
|
|
289
|
+
console.log(`${s.key} user:${s.username || '-'} api:${s.apiBaseUrl || '-'} clone:${s.cloneBaseUrl || '-'}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
210
292
|
return true;
|
|
211
293
|
}
|
|
212
294
|
if (sub === 'set') {
|
|
295
|
+
if (rest.length < 2) throw new Error('Uso: /git set <provider> <token> [username] [apiBaseUrl] [cloneBaseUrl] [name]');
|
|
213
296
|
const [provider, token, username] = rest;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
297
|
+
let apiBaseUrl = '';
|
|
298
|
+
let cloneBaseUrl = '';
|
|
299
|
+
let name = '';
|
|
300
|
+
for (const part of rest.slice(3)) {
|
|
301
|
+
if (part.startsWith('apiBaseUrl:')) apiBaseUrl = part.slice('apiBaseUrl:'.length);
|
|
302
|
+
else if (part.startsWith('cloneBaseUrl:')) cloneBaseUrl = part.slice('cloneBaseUrl:'.length);
|
|
303
|
+
else if (part.startsWith('name:')) name = part.slice('name:'.length);
|
|
304
|
+
}
|
|
305
|
+
upsertGitSecret(provider, { provider, token, username: username || '', apiBaseUrl: apiBaseUrl || '', cloneBaseUrl: cloneBaseUrl || '', name });
|
|
306
|
+
console.log(`Credencial guardada para ${provider}${name ? `:${name}` : ''}`);
|
|
217
307
|
return true;
|
|
218
308
|
}
|
|
219
309
|
if (sub === 'remove') {
|
|
220
|
-
const [provider] = rest;
|
|
221
|
-
if (!provider) throw new Error('Uso: /git remove <provider>');
|
|
222
|
-
const
|
|
223
|
-
|
|
310
|
+
const [provider, namePart] = rest;
|
|
311
|
+
if (!provider) throw new Error('Uso: /git remove <provider> [name]');
|
|
312
|
+
const name = (namePart || '').startsWith('name:') ? namePart.slice('name:'.length) : '';
|
|
313
|
+
const removed = removeGitSecret(provider, name);
|
|
314
|
+
console.log(removed ? `Credencial eliminada: ${provider}${name ? `:${name}` : ''}` : `No existe credencial para ${provider}`);
|
|
224
315
|
return true;
|
|
225
316
|
}
|
|
226
317
|
throw new Error('Subcomando git no reconocido. Usa /git help');
|
|
@@ -451,7 +542,19 @@ async function handleLocalCommand(input, state, deps) {
|
|
|
451
542
|
}
|
|
452
543
|
|
|
453
544
|
if (commandName === 'web') {
|
|
454
|
-
|
|
545
|
+
let host = '127.0.0.1';
|
|
546
|
+
let port = 3000;
|
|
547
|
+
if (args) {
|
|
548
|
+
const parts = args.split(/[\s:]+/).filter(Boolean);
|
|
549
|
+
for (const part of parts) {
|
|
550
|
+
if (/^\d{1,5}$/.test(part)) {
|
|
551
|
+
port = Math.min(65535, Math.max(1, Number(part)));
|
|
552
|
+
} else if (/^[\d.]+$/.test(part) || part === 'localhost' || part === '0.0.0.0') {
|
|
553
|
+
host = part;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
const url = await startWebVersion(host, port);
|
|
455
558
|
console.log(`Web version started at ${url}`);
|
|
456
559
|
return true;
|
|
457
560
|
}
|
package/src/cli/print.js
CHANGED
|
@@ -57,7 +57,7 @@ function wrapLines(text, maxWidth, indent = '') {
|
|
|
57
57
|
continue;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
const words = rawLine.split(/(\s+)/);
|
|
60
|
+
const words = rawLine.split(/(\s+)/).filter(Boolean);
|
|
61
61
|
let line = '';
|
|
62
62
|
let lineLen = indentLen;
|
|
63
63
|
|
|
@@ -97,7 +97,9 @@ function wrapLines(text, maxWidth, indent = '') {
|
|
|
97
97
|
lineLen += word.length;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
if (line || rawLine.trim()) {
|
|
101
|
+
lines.push(indent + line);
|
|
102
|
+
}
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
return lines;
|
package/src/config.js
CHANGED
|
@@ -96,7 +96,7 @@ const QWEN_PASSWORD = process.env.ZYN_QWEN_PASSWORD || 'zyzz1234';
|
|
|
96
96
|
|
|
97
97
|
const MAX_TOOL_STEPS = Number.POSITIVE_INFINITY;
|
|
98
98
|
const MAX_OUTPUT_CHARS = 12000;
|
|
99
|
-
const MAX_FILE_LINES =
|
|
99
|
+
const MAX_FILE_LINES = 5000;
|
|
100
100
|
const ACTION_LOG_LIMIT = 40;
|
|
101
101
|
const REQUEST_TIMEOUT_MS = Number(process.env.ZYN_REQUEST_TIMEOUT_MS || 180000);
|
|
102
102
|
const MAX_HISTORY_CHARS = 24000;
|
|
@@ -108,6 +108,9 @@ const PERSISTENT_CONFIG_FILE = path.join(SESSION_ROOT, 'persistent-config.json')
|
|
|
108
108
|
const TRANSCRIPTS_DIR = path.join(SESSION_ROOT, 'transcripts');
|
|
109
109
|
const EXPORTS_DIR = path.join(SESSION_ROOT, 'exports');
|
|
110
110
|
const THINK_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
111
|
+
const USER_DATA_ROOT = path.join(os.homedir(), '.zyn');
|
|
112
|
+
const TASKS_FILE = path.join(USER_DATA_ROOT, 'tasks.json');
|
|
113
|
+
const PROVIDERS_FILE = path.join(DATA_ROOT, 'providers.json');
|
|
111
114
|
|
|
112
115
|
function listProvidersFromModels(models = MODELS) {
|
|
113
116
|
const grouped = new Map();
|
|
@@ -148,12 +151,15 @@ module.exports = {
|
|
|
148
151
|
MAX_TOOL_STEPS,
|
|
149
152
|
MODELS,
|
|
150
153
|
MODELS_FILE,
|
|
154
|
+
PROVIDERS_FILE,
|
|
151
155
|
QWEN_EMAIL,
|
|
152
156
|
QWEN_PASSWORD,
|
|
153
157
|
REQUEST_TIMEOUT_MS,
|
|
154
158
|
SESSION_ROOT,
|
|
155
159
|
SESSIONS_DIR,
|
|
160
|
+
TASKS_FILE,
|
|
156
161
|
THINK_FRAMES,
|
|
157
162
|
TRANSCRIPTS_DIR,
|
|
163
|
+
USER_DATA_ROOT,
|
|
158
164
|
listProvidersFromModels,
|
|
159
165
|
};
|
package/src/core/agent.js
CHANGED
|
@@ -251,7 +251,6 @@ async function runAgentTurn(input, state, ui, options = {}) {
|
|
|
251
251
|
const toolPathUsage = new Map();
|
|
252
252
|
let step = 0;
|
|
253
253
|
const turnLanguage = detectLanguage(input, state.language);
|
|
254
|
-
state.language = turnLanguage;
|
|
255
254
|
|
|
256
255
|
while (true) {
|
|
257
256
|
if (signal?.aborted) {
|
|
@@ -270,7 +269,7 @@ async function runAgentTurn(input, state, ui, options = {}) {
|
|
|
270
269
|
const messages = buildConversationMessages(
|
|
271
270
|
state,
|
|
272
271
|
turnMessages,
|
|
273
|
-
buildSystemPrompt(state.cwd, state, { input, language:
|
|
272
|
+
buildSystemPrompt(state.cwd, state, { input, language: turnLanguage }),
|
|
274
273
|
);
|
|
275
274
|
|
|
276
275
|
const primaryPromise = requestModel(messages, state, ui, {
|
|
@@ -419,7 +418,7 @@ async function runAgentTurn(input, state, ui, options = {}) {
|
|
|
419
418
|
const key = `${parsed.tool}:${targetPath}`;
|
|
420
419
|
const nextCount = (toolPathUsage.get(key) || 0) + 1;
|
|
421
420
|
toolPathUsage.set(key, nextCount);
|
|
422
|
-
if (nextCount >=
|
|
421
|
+
if (nextCount >= 20 && ['write_file', 'append_file', 'replace_in_file'].includes(parsed.tool)) {
|
|
423
422
|
ui.logEvent(state, 'warn', state.language === 'es' ? 'Posible loop detectado' : 'Possible loop detected', `${parsed.tool} → ${targetPath} x${nextCount}`);
|
|
424
423
|
turnMessages.push({
|
|
425
424
|
role: 'user',
|
|
@@ -433,7 +432,7 @@ async function runAgentTurn(input, state, ui, options = {}) {
|
|
|
433
432
|
}
|
|
434
433
|
if (fingerprint === lastFingerprint) {
|
|
435
434
|
repeatCount += 1;
|
|
436
|
-
if (repeatCount >=
|
|
435
|
+
if (repeatCount >= 19) {
|
|
437
436
|
ui.logEvent(state, 'warn', 'Loop detectado', `${parsed.tool} repetido ${repeatCount + 1}x`);
|
|
438
437
|
turnMessages.push({
|
|
439
438
|
role: 'user',
|