zyn-ai 1.2.0 → 1.3.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/README.md +1 -1
- package/package.json +43 -11
- package/src/cli/commands.js +2 -0
- package/src/cli/runtime.js +3 -2
- package/src/config.js +3 -3
- package/src/core/agent.js +4 -2
- package/src/core/prompts.js +12 -4
- package/src/i18n.js +6 -0
- package/src/providers/catalog.js +302 -0
- package/src/tools/index.js +276 -0
- package/src/tui/app.mjs +51 -28
- package/src/utils/secretStorage.js +223 -0
- package/src/utils/taskStorage.js +192 -0
- package/src/web/webAgent.js +2 -2
- package/.github/workflows/publish.yml +0 -23
- package/data/models.example.json +0 -15
- package/data/skills/code-style.md +0 -79
- package/data/skills/completion.md +0 -20
- package/data/skills/core.md +0 -35
- package/data/skills/debugging.md +0 -279
- package/data/skills/domains.md +0 -83
- package/data/skills/frontend_design.md +0 -33
- package/data/skills/methodology.md +0 -84
- package/data/skills/reasoning.md +0 -62
- package/data/skills/testing.md +0 -24
- package/data/skills/thinking.md +0 -146
- package/data/skills/tools.md +0 -102
- package/data/skills/web-agent.md +0 -67
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const HOME_DIR = os.homedir() || '/root';
|
|
6
|
+
const DATA_ROOT = path.join(HOME_DIR, '.zyn');
|
|
7
|
+
const GIT_SECRETS_FILE = path.join(DATA_ROOT, 'git-secrets.json');
|
|
8
|
+
|
|
9
|
+
function readJson(filePath) {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function writeJson(filePath, data) {
|
|
18
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
19
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function loadGitSecrets() {
|
|
23
|
+
const raw = readJson(GIT_SECRETS_FILE);
|
|
24
|
+
if (!raw || typeof raw !== 'object') {
|
|
25
|
+
return { github: null, gitlab: null, custom: {} };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
github: raw.github || null,
|
|
30
|
+
gitlab: raw.gitlab || null,
|
|
31
|
+
custom: raw.custom && typeof raw.custom === 'object' ? raw.custom : {},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function saveGitSecrets(data) {
|
|
36
|
+
writeJson(GIT_SECRETS_FILE, {
|
|
37
|
+
github: data.github || null,
|
|
38
|
+
gitlab: data.gitlab || null,
|
|
39
|
+
custom: data.custom && typeof data.custom === 'object' ? data.custom : {},
|
|
40
|
+
updatedAt: new Date().toISOString(),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeProfileName(value) {
|
|
45
|
+
return String(value || '').trim().toLowerCase();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeBaseUrl(value) {
|
|
49
|
+
return String(value || '').trim().replace(/\/+$/, '');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function sanitizeSecretConfig(input = {}) {
|
|
53
|
+
const output = {};
|
|
54
|
+
if (input.token !== undefined) output.token = String(input.token || '').trim();
|
|
55
|
+
if (input.username !== undefined) output.username = String(input.username || '').trim();
|
|
56
|
+
if (input.apiBaseUrl !== undefined) output.apiBaseUrl = normalizeBaseUrl(input.apiBaseUrl);
|
|
57
|
+
if (input.cloneBaseUrl !== undefined) output.cloneBaseUrl = normalizeBaseUrl(input.cloneBaseUrl);
|
|
58
|
+
if (input.authHeader !== undefined) output.authHeader = String(input.authHeader || '').trim();
|
|
59
|
+
if (input.name !== undefined) output.name = String(input.name || '').trim();
|
|
60
|
+
return output;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function upsertGitSecret(provider, input = {}) {
|
|
64
|
+
const key = normalizeProfileName(provider);
|
|
65
|
+
const config = sanitizeSecretConfig(input);
|
|
66
|
+
const store = loadGitSecrets();
|
|
67
|
+
|
|
68
|
+
if (key === 'github') {
|
|
69
|
+
store.github = {
|
|
70
|
+
...store.github,
|
|
71
|
+
...config,
|
|
72
|
+
provider: 'github',
|
|
73
|
+
apiBaseUrl: config.apiBaseUrl || store.github?.apiBaseUrl || 'https://api.github.com',
|
|
74
|
+
cloneBaseUrl: config.cloneBaseUrl || store.github?.cloneBaseUrl || 'https://github.com',
|
|
75
|
+
updatedAt: new Date().toISOString(),
|
|
76
|
+
};
|
|
77
|
+
saveGitSecrets(store);
|
|
78
|
+
return store.github;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (key === 'gitlab') {
|
|
82
|
+
store.gitlab = {
|
|
83
|
+
...store.gitlab,
|
|
84
|
+
...config,
|
|
85
|
+
provider: 'gitlab',
|
|
86
|
+
apiBaseUrl: config.apiBaseUrl || store.gitlab?.apiBaseUrl || 'https://gitlab.com/api/v4',
|
|
87
|
+
cloneBaseUrl: config.cloneBaseUrl || store.gitlab?.cloneBaseUrl || 'https://gitlab.com',
|
|
88
|
+
updatedAt: new Date().toISOString(),
|
|
89
|
+
};
|
|
90
|
+
saveGitSecrets(store);
|
|
91
|
+
return store.gitlab;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const name = config.name || key || 'custom';
|
|
95
|
+
store.custom[name] = {
|
|
96
|
+
...store.custom[name],
|
|
97
|
+
...config,
|
|
98
|
+
provider: 'custom',
|
|
99
|
+
name,
|
|
100
|
+
updatedAt: new Date().toISOString(),
|
|
101
|
+
};
|
|
102
|
+
saveGitSecrets(store);
|
|
103
|
+
return store.custom[name];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function removeGitSecret(provider, name = '') {
|
|
107
|
+
const key = normalizeProfileName(provider);
|
|
108
|
+
const store = loadGitSecrets();
|
|
109
|
+
if (key === 'github') {
|
|
110
|
+
store.github = null;
|
|
111
|
+
saveGitSecrets(store);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
if (key === 'gitlab') {
|
|
115
|
+
store.gitlab = null;
|
|
116
|
+
saveGitSecrets(store);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
const customName = String(name || '').trim();
|
|
120
|
+
if (!customName) return false;
|
|
121
|
+
if (store.custom[customName]) {
|
|
122
|
+
delete store.custom[customName];
|
|
123
|
+
saveGitSecrets(store);
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function listGitSecrets() {
|
|
130
|
+
const store = loadGitSecrets();
|
|
131
|
+
const output = [];
|
|
132
|
+
if (store.github) output.push({ key: 'github', ...store.github, token: '[set]' });
|
|
133
|
+
if (store.gitlab) output.push({ key: 'gitlab', ...store.gitlab, token: '[set]' });
|
|
134
|
+
for (const [name, config] of Object.entries(store.custom || {})) {
|
|
135
|
+
output.push({ key: `custom:${name}`, ...config, token: '[set]' });
|
|
136
|
+
}
|
|
137
|
+
return output;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getGitSecret(provider, name = '') {
|
|
141
|
+
const key = normalizeProfileName(provider);
|
|
142
|
+
const store = loadGitSecrets();
|
|
143
|
+
if (key === 'github') return store.github || null;
|
|
144
|
+
if (key === 'gitlab') return store.gitlab || null;
|
|
145
|
+
const customName = String(name || '').trim();
|
|
146
|
+
if (!customName) return null;
|
|
147
|
+
return store.custom[customName] || null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function resolveGitProfile(provider, name = '') {
|
|
151
|
+
const key = normalizeProfileName(provider);
|
|
152
|
+
if (key === 'github' || key === 'gitlab') return getGitSecret(key);
|
|
153
|
+
return getGitSecret('custom', name);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function buildCloneUrl(repoUrl, profile = {}) {
|
|
157
|
+
const raw = String(repoUrl || '').trim();
|
|
158
|
+
if (!raw) throw new Error('Missing repo URL');
|
|
159
|
+
if (!profile?.token) return raw;
|
|
160
|
+
let parsed;
|
|
161
|
+
try {
|
|
162
|
+
parsed = new URL(raw);
|
|
163
|
+
} catch {
|
|
164
|
+
return raw;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const host = parsed.hostname.toLowerCase();
|
|
168
|
+
if (host.includes('github.com')) {
|
|
169
|
+
parsed.username = 'x-access-token';
|
|
170
|
+
parsed.password = profile.token;
|
|
171
|
+
return parsed.toString();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (host.includes('gitlab.com')) {
|
|
175
|
+
parsed.username = profile.username || 'oauth2';
|
|
176
|
+
parsed.password = profile.token;
|
|
177
|
+
return parsed.toString();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
parsed.username = profile.username || 'oauth2';
|
|
181
|
+
parsed.password = profile.token;
|
|
182
|
+
return parsed.toString();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function getApiBaseUrl(provider, profile = {}) {
|
|
186
|
+
const key = normalizeProfileName(provider);
|
|
187
|
+
if (profile?.apiBaseUrl) return normalizeBaseUrl(profile.apiBaseUrl);
|
|
188
|
+
if (key === 'github') return 'https://api.github.com';
|
|
189
|
+
if (key === 'gitlab') return 'https://gitlab.com/api/v4';
|
|
190
|
+
return '';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function buildApiHeaders(provider, profile = {}, extraHeaders = {}) {
|
|
194
|
+
const key = normalizeProfileName(provider);
|
|
195
|
+
const token = profile?.token || '';
|
|
196
|
+
const headers = { ...extraHeaders };
|
|
197
|
+
|
|
198
|
+
if (token) {
|
|
199
|
+
if (key === 'gitlab') {
|
|
200
|
+
headers['PRIVATE-TOKEN'] = token;
|
|
201
|
+
} else if (profile?.authHeader) {
|
|
202
|
+
headers[profile.authHeader] = token;
|
|
203
|
+
} else {
|
|
204
|
+
headers.Authorization = `Bearer ${token}`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return headers;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = {
|
|
212
|
+
buildApiHeaders,
|
|
213
|
+
buildCloneUrl,
|
|
214
|
+
getApiBaseUrl,
|
|
215
|
+
getGitSecret,
|
|
216
|
+
listGitSecrets,
|
|
217
|
+
loadGitSecrets,
|
|
218
|
+
normalizeProfileName,
|
|
219
|
+
removeGitSecret,
|
|
220
|
+
resolveGitProfile,
|
|
221
|
+
saveGitSecrets,
|
|
222
|
+
upsertGitSecret,
|
|
223
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { TASKS_FILE, USER_DATA_ROOT } = require('../config');
|
|
4
|
+
|
|
5
|
+
const fsp = fs.promises;
|
|
6
|
+
|
|
7
|
+
function readJson(filePath) {
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function writeJson(filePath, data) {
|
|
16
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
17
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function loadTaskStore() {
|
|
21
|
+
const raw = readJson(TASKS_FILE);
|
|
22
|
+
if (!raw || typeof raw !== 'object') {
|
|
23
|
+
return { version: 1, tasks: [] };
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(raw)) {
|
|
26
|
+
return { version: 1, tasks: raw };
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
version: Number(raw.version || 1),
|
|
30
|
+
tasks: Array.isArray(raw.tasks) ? raw.tasks : [],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function saveTaskStore(store) {
|
|
35
|
+
writeJson(TASKS_FILE, {
|
|
36
|
+
version: 1,
|
|
37
|
+
tasks: Array.isArray(store.tasks) ? store.tasks : [],
|
|
38
|
+
updatedAt: new Date().toISOString(),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function normalizeTaskText(text) {
|
|
43
|
+
return String(text || '').trim().replace(/\s+/g, ' ');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function makeTaskId() {
|
|
47
|
+
return `task_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function enrichTask(task) {
|
|
51
|
+
return {
|
|
52
|
+
id: task.id,
|
|
53
|
+
title: normalizeTaskText(task.title),
|
|
54
|
+
description: normalizeTaskText(task.description || ''),
|
|
55
|
+
status: task.status || 'todo',
|
|
56
|
+
priority: task.priority || 'medium',
|
|
57
|
+
createdAt: task.createdAt || new Date().toISOString(),
|
|
58
|
+
updatedAt: task.updatedAt || task.createdAt || new Date().toISOString(),
|
|
59
|
+
dueAt: task.dueAt || null,
|
|
60
|
+
tags: Array.isArray(task.tags) ? task.tags : [],
|
|
61
|
+
source: task.source || 'manual',
|
|
62
|
+
sessionId: task.sessionId || null,
|
|
63
|
+
notes: normalizeTaskText(task.notes || ''),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function listTasks(options = {}) {
|
|
68
|
+
const { status, includeDone = true } = options;
|
|
69
|
+
const store = loadTaskStore();
|
|
70
|
+
return store.tasks
|
|
71
|
+
.map(enrichTask)
|
|
72
|
+
.filter(task => (includeDone ? true : task.status !== 'done'))
|
|
73
|
+
.filter(task => (status ? task.status === status : true))
|
|
74
|
+
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function createTask(input = {}) {
|
|
78
|
+
const store = loadTaskStore();
|
|
79
|
+
const title = normalizeTaskText(input.title || input.text || input.description);
|
|
80
|
+
if (!title) {
|
|
81
|
+
throw new Error('task title requerido');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const now = new Date().toISOString();
|
|
85
|
+
const task = enrichTask({
|
|
86
|
+
id: makeTaskId(),
|
|
87
|
+
title,
|
|
88
|
+
description: input.description || '',
|
|
89
|
+
status: input.status || 'todo',
|
|
90
|
+
priority: input.priority || 'medium',
|
|
91
|
+
createdAt: now,
|
|
92
|
+
updatedAt: now,
|
|
93
|
+
dueAt: input.dueAt || null,
|
|
94
|
+
tags: input.tags || [],
|
|
95
|
+
source: input.source || 'manual',
|
|
96
|
+
sessionId: input.sessionId || null,
|
|
97
|
+
notes: input.notes || '',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
store.tasks.push(task);
|
|
101
|
+
saveTaskStore(store);
|
|
102
|
+
return task;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function findTaskIndex(store, idOrTitle) {
|
|
106
|
+
const needle = normalizeTaskText(idOrTitle).toLowerCase();
|
|
107
|
+
if (!needle) return -1;
|
|
108
|
+
return store.tasks.findIndex(task => {
|
|
109
|
+
const idMatch = String(task.id || '').toLowerCase() === needle;
|
|
110
|
+
const titleMatch = String(task.title || '').toLowerCase() === needle;
|
|
111
|
+
return idMatch || titleMatch;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function updateTask(idOrTitle, updates = {}) {
|
|
116
|
+
const store = loadTaskStore();
|
|
117
|
+
const idx = findTaskIndex(store, idOrTitle);
|
|
118
|
+
if (idx === -1) {
|
|
119
|
+
throw new Error(`No existe la tarea: ${idOrTitle}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const current = store.tasks[idx];
|
|
123
|
+
const next = enrichTask({
|
|
124
|
+
...current,
|
|
125
|
+
...updates,
|
|
126
|
+
title: updates.title ?? current.title,
|
|
127
|
+
description: updates.description ?? current.description,
|
|
128
|
+
priority: updates.priority ?? current.priority,
|
|
129
|
+
status: updates.status ?? current.status,
|
|
130
|
+
dueAt: updates.dueAt === undefined ? current.dueAt : updates.dueAt,
|
|
131
|
+
tags: updates.tags ?? current.tags,
|
|
132
|
+
notes: updates.notes ?? current.notes,
|
|
133
|
+
updatedAt: new Date().toISOString(),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
store.tasks[idx] = next;
|
|
137
|
+
saveTaskStore(store);
|
|
138
|
+
return next;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function completeTask(idOrTitle, notes = '') {
|
|
142
|
+
return updateTask(idOrTitle, {
|
|
143
|
+
status: 'done',
|
|
144
|
+
notes,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function deleteTask(idOrTitle) {
|
|
149
|
+
const store = loadTaskStore();
|
|
150
|
+
const idx = findTaskIndex(store, idOrTitle);
|
|
151
|
+
if (idx === -1) {
|
|
152
|
+
throw new Error(`No existe la tarea: ${idOrTitle}`);
|
|
153
|
+
}
|
|
154
|
+
const [removed] = store.tasks.splice(idx, 1);
|
|
155
|
+
saveTaskStore(store);
|
|
156
|
+
return enrichTask(removed);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function clearTasks() {
|
|
160
|
+
saveTaskStore({ tasks: [] });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function formatTask(task) {
|
|
164
|
+
const state = task.status === 'done' ? '✓' : task.status === 'doing' ? '▶' : '·';
|
|
165
|
+
const due = task.dueAt ? ` · due ${task.dueAt}` : '';
|
|
166
|
+
const priority = task.priority ? ` · ${task.priority}` : '';
|
|
167
|
+
const desc = task.description ? `\n ${task.description}` : '';
|
|
168
|
+
const notes = task.notes ? `\n notas: ${task.notes}` : '';
|
|
169
|
+
return `${state} ${task.id} :: ${task.title}${priority}${due}${desc}${notes}`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function summarizeTasks(tasks = listTasks()) {
|
|
173
|
+
const lines = [`Tasks: ${tasks.length}`];
|
|
174
|
+
for (const task of tasks.slice(0, 20)) {
|
|
175
|
+
lines.push(formatTask(task));
|
|
176
|
+
}
|
|
177
|
+
return lines.join('\n');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = {
|
|
181
|
+
clearTasks,
|
|
182
|
+
completeTask,
|
|
183
|
+
createTask,
|
|
184
|
+
deleteTask,
|
|
185
|
+
formatTask,
|
|
186
|
+
listTasks,
|
|
187
|
+
loadTaskStore,
|
|
188
|
+
saveTaskStore,
|
|
189
|
+
summarizeTasks,
|
|
190
|
+
updateTask,
|
|
191
|
+
USER_DATA_ROOT,
|
|
192
|
+
};
|
package/src/web/webAgent.js
CHANGED
|
@@ -599,7 +599,7 @@ async function executeTool(tool, args, ctx) {
|
|
|
599
599
|
}
|
|
600
600
|
}
|
|
601
601
|
|
|
602
|
-
async function runConcuerdo(primaryContent, primaryKey, modelMessages, onEvent, isAborted) {
|
|
602
|
+
async function runConcuerdo(primaryContent, primaryKey, modelMessages, onEvent, isAborted, language = 'en') {
|
|
603
603
|
const otherKeys = Object.keys(MODELS).filter(k => k !== primaryKey);
|
|
604
604
|
if (!otherKeys.length) return null;
|
|
605
605
|
|
|
@@ -1029,7 +1029,7 @@ async function runWebAgent({ chatData, user, onEvent, isAborted }) {
|
|
|
1029
1029
|
// ── Final response ──
|
|
1030
1030
|
if (parsed.type === 'final') {
|
|
1031
1031
|
if (group) {
|
|
1032
|
-
const synthResult = await runConcuerdo(parsed.content, modelKey, modelMessages, onEvent, isAborted);
|
|
1032
|
+
const synthResult = await runConcuerdo(parsed.content, modelKey, modelMessages, onEvent, isAborted, language);
|
|
1033
1033
|
if (synthResult) {
|
|
1034
1034
|
chatData.messages.push({ role: 'assistant', content: synthResult, ts: Date.now() });
|
|
1035
1035
|
store.saveChat(chatData);
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
name: Publish to npm
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types: [published]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
publish:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v4
|
|
13
|
-
|
|
14
|
-
- uses: actions/setup-node@v4
|
|
15
|
-
with:
|
|
16
|
-
node-version: '18'
|
|
17
|
-
registry-url: 'https://registry.npmjs.org/'
|
|
18
|
-
|
|
19
|
-
- run: npm install
|
|
20
|
-
|
|
21
|
-
- run: npm publish --access public
|
|
22
|
-
env:
|
|
23
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/data/models.example.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"models": {
|
|
3
|
-
"local-llama": {
|
|
4
|
-
"label": "Local Llama",
|
|
5
|
-
"provider": "ollama",
|
|
6
|
-
"ollamaModel": "llama3.1:8b"
|
|
7
|
-
},
|
|
8
|
-
"my-openai-model": {
|
|
9
|
-
"label": "Mi modelo OpenAI-compatible",
|
|
10
|
-
"provider": "openai-compatible",
|
|
11
|
-
"openaiModel": "gpt-4o-mini",
|
|
12
|
-
"baseUrl": "https://api.example.com/v1"
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# Estilo de respuestas
|
|
2
|
-
|
|
3
|
-
## Respuestas finales al usuario
|
|
4
|
-
|
|
5
|
-
- Usa markdown: **bold**, `code inline`, bloques de codigo con ```, headers ##, listas -.
|
|
6
|
-
- Se conciso pero completo. No repitas informacion que el usuario ya sabe.
|
|
7
|
-
- Si creaste/editaste archivos, menciona que cambiaste y en que archivo.
|
|
8
|
-
- Si ejecutaste comandos, resume el resultado relevante (no copies todo el stdout).
|
|
9
|
-
- Para preguntas simples, responde en 1-3 lineas.
|
|
10
|
-
- Para tareas completadas, da un resumen estructurado de lo que hiciste.
|
|
11
|
-
- Si algo salio mal, explica que paso y que alternativas hay.
|
|
12
|
-
- Usa listas cuando hay multiples items. Usa code blocks para codigo.
|
|
13
|
-
- NUNCA inventes output de comandos o contenido de archivos. Solo reporta lo real.
|
|
14
|
-
|
|
15
|
-
## Formato de codigo en respuestas
|
|
16
|
-
|
|
17
|
-
Cuando muestres codigo en content, usa triple backtick con el lenguaje:
|
|
18
|
-
```js
|
|
19
|
-
const x = 42;
|
|
20
|
-
```
|
|
21
|
-
Para paths o comandos inline usa backtick simple: `src/index.js`, `npm install`.
|
|
22
|
-
|
|
23
|
-
# Generacion de codigo
|
|
24
|
-
|
|
25
|
-
## Principios generales (cualquier lenguaje)
|
|
26
|
-
|
|
27
|
-
- Codigo limpio y autoexplicativo. Nombres descriptivos reemplazan comentarios.
|
|
28
|
-
- Solo comenta lo que NO es obvio por el codigo (decisiones de diseno, workarounds, gotchas).
|
|
29
|
-
- Funciones pequenas con una sola responsabilidad.
|
|
30
|
-
- Early return / guard clauses para evitar nesting profundo.
|
|
31
|
-
- Manejo de errores robusto: try/catch en operaciones async, validacion de inputs.
|
|
32
|
-
- No hardcodear valores magicos. Usa constantes con nombre descriptivo.
|
|
33
|
-
- DRY (Don't Repeat Yourself) pero sin abstracciones prematuras.
|
|
34
|
-
- Codigo production-ready: maneja edge cases, inputs invalidos, errores de red.
|
|
35
|
-
|
|
36
|
-
## JavaScript / Node.js
|
|
37
|
-
|
|
38
|
-
- const por defecto, let solo si reasigna, NUNCA var.
|
|
39
|
-
- async/await siempre. No callbacks, no .then() chains.
|
|
40
|
-
- Arrow functions para callbacks: arr.map(x => x.id).
|
|
41
|
-
- Template literals para interpolacion: `Hola ${nombre}`.
|
|
42
|
-
- Optional chaining: obj?.prop?.sub.
|
|
43
|
-
- Nullish coalescing: valor ?? 'default'.
|
|
44
|
-
- Destructuring cuando simplifica: const { name, age } = user.
|
|
45
|
-
- Indentacion: 2 espacios.
|
|
46
|
-
- Semicolons: siempre.
|
|
47
|
-
- Strings: comillas simples en JS, dobles en atributos HTML.
|
|
48
|
-
|
|
49
|
-
## Python
|
|
50
|
-
|
|
51
|
-
- Type hints en funciones: def process(data: list[str]) -> dict:
|
|
52
|
-
- f-strings para interpolacion: f"Hola {nombre}".
|
|
53
|
-
- List/dict comprehensions cuando son legibles.
|
|
54
|
-
- Context managers (with) para archivos y recursos.
|
|
55
|
-
- pathlib sobre os.path para manejo de paths.
|
|
56
|
-
- Indentacion: 4 espacios.
|
|
57
|
-
- Docstrings solo en funciones publicas complejas.
|
|
58
|
-
|
|
59
|
-
## Bash / Shell
|
|
60
|
-
|
|
61
|
-
- set -euo pipefail al inicio de scripts.
|
|
62
|
-
- Comillas dobles en variables: "$variable".
|
|
63
|
-
- Usa [[ ]] en lugar de [ ] para condicionales.
|
|
64
|
-
- Funciones para logica reutilizable.
|
|
65
|
-
- Exit codes significativos.
|
|
66
|
-
|
|
67
|
-
## HTML / CSS
|
|
68
|
-
|
|
69
|
-
- HTML semantico: header, main, nav, section, article, footer.
|
|
70
|
-
- Clases descriptivas y consistentes.
|
|
71
|
-
- Mobile-first: disenar para movil, escalar a desktop.
|
|
72
|
-
- Accesibilidad basica: alt en imgs, labels en forms, aria cuando aplique.
|
|
73
|
-
|
|
74
|
-
## Cuando modificas codigo existente
|
|
75
|
-
|
|
76
|
-
- Respeta el estilo del archivo existente (indentacion, naming, patron).
|
|
77
|
-
- No refactorices lo que no te pidieron. Cambios minimos y quirurgicos.
|
|
78
|
-
- No agregues imports, types, o abstracciones que no son necesarias para el cambio.
|
|
79
|
-
- Si el archivo tiene un patron (ej: todos los handlers siguen X estructura), siguelo.
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# Completion discipline
|
|
2
|
-
|
|
3
|
-
## Operating rule
|
|
4
|
-
- When the user asks for an action, complete the action instead of narrating it.
|
|
5
|
-
- Do not ask the user to choose between options when one clear path exists.
|
|
6
|
-
- If a tool can do the work, use the tool.
|
|
7
|
-
- If a response would be only a plan, replace it with execution or a concrete result.
|
|
8
|
-
- If the model stalls, switch to investigation, then act, then verify.
|
|
9
|
-
|
|
10
|
-
## Quality bar
|
|
11
|
-
- No fake progress.
|
|
12
|
-
- No pretending to have executed commands.
|
|
13
|
-
- No claiming success without evidence.
|
|
14
|
-
- No giving a tutorial when a direct change is possible.
|
|
15
|
-
|
|
16
|
-
## Recovery behavior
|
|
17
|
-
- Read the relevant files or state first.
|
|
18
|
-
- Apply the fix.
|
|
19
|
-
- Verify the result.
|
|
20
|
-
- Report the result plainly.
|
package/data/skills/core.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# Identidad y Rol
|
|
2
|
-
Eres Zyn, un Agente de Terminal Senior y Arquitecto de Software desarrolado por Maycol y Ado.
|
|
3
|
-
|
|
4
|
-
Dominio: Programacion polyglot, Arquitectura de Sistemas, DevOps, Bases de Datos, APIs, Web Scraping, Automatizacion, Debugging, Servidores, Ciberseguridad.
|
|
5
|
-
|
|
6
|
-
Nivel: Resolutivo, codigo production-ready. Anticipas edge cases y manejas errores.
|
|
7
|
-
|
|
8
|
-
Adaptate al idioma que te habla el usuario.
|
|
9
|
-
Tono: Tecnico, directo, conciso.
|
|
10
|
-
|
|
11
|
-
# Directrices
|
|
12
|
-
- Eficiente: minimas operaciones necesarias. Lee contexto antes de actuar.
|
|
13
|
-
- Honesto: si algo falla, indicalo sin rodeos.
|
|
14
|
-
- Preciso: cambios que funcionan a la primera.
|
|
15
|
-
- Seguro: alerta vulnerabilidades y riesgos.
|
|
16
|
-
|
|
17
|
-
# Formato de respuesta — CRITICO
|
|
18
|
-
|
|
19
|
-
Cada respuesta DEBE ser EXACTAMENTE un JSON valido. Sin texto antes ni despues.
|
|
20
|
-
Sin markdown wrapping. Sin bloques de codigo. Solo el JSON puro.
|
|
21
|
-
|
|
22
|
-
Para invocar una herramienta:
|
|
23
|
-
{"type":"tool","tool":"nombre_herramienta","args":{...}}
|
|
24
|
-
|
|
25
|
-
Para responder al usuario (soporta markdown dentro de content):
|
|
26
|
-
{"type":"final","content":"tu respuesta aqui"}
|
|
27
|
-
|
|
28
|
-
Reglas estrictas:
|
|
29
|
-
- UNA sola accion por respuesta (una herramienta O una respuesta final).
|
|
30
|
-
- Si necesitas una herramienta, responde SOLO con el JSON de herramienta.
|
|
31
|
-
- El campo "content" en respuesta final SI acepta markdown.
|
|
32
|
-
- Escapa comillas dobles con \" y saltos de linea con \n dentro del JSON.
|
|
33
|
-
- JAMAS pongas texto plano fuera del JSON.
|
|
34
|
-
- JAMAS anides JSON de herramienta dentro de content.
|
|
35
|
-
- Si la pregunta es conversacional, responde directo con type=final.
|