trackops 1.0.1 → 1.1.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 +326 -270
- package/bin/trackops.js +102 -70
- package/lib/config.js +260 -35
- package/lib/control.js +517 -475
- package/lib/env.js +227 -0
- package/lib/i18n.js +61 -53
- package/lib/init.js +135 -46
- package/lib/locale.js +63 -0
- package/lib/opera-bootstrap.js +523 -0
- package/lib/opera.js +319 -170
- package/lib/registry.js +27 -13
- package/lib/release.js +56 -0
- package/lib/resources.js +42 -0
- package/lib/server.js +907 -554
- package/lib/skills.js +148 -124
- package/lib/workspace.js +260 -0
- package/locales/en.json +331 -139
- package/locales/es.json +331 -139
- package/package.json +7 -9
- package/scripts/skills-marketplace-smoke.js +124 -0
- package/scripts/smoke-tests.js +445 -0
- package/scripts/sync-skill-version.js +21 -0
- package/scripts/validate-skill.js +88 -0
- package/skills/trackops/SKILL.md +64 -0
- package/skills/trackops/agents/openai.yaml +3 -0
- package/skills/trackops/references/activation.md +39 -0
- package/skills/trackops/references/troubleshooting.md +34 -0
- package/skills/trackops/references/workflow.md +20 -0
- package/skills/trackops/scripts/bootstrap-trackops.js +201 -0
- package/skills/trackops/skill.json +29 -0
- package/templates/opera/en/agent.md +26 -0
- package/templates/opera/en/genesis.md +79 -0
- package/templates/opera/en/references/autonomy-and-recovery.md +23 -0
- package/templates/opera/en/references/opera-cycle.md +62 -0
- package/templates/opera/en/registry.md +28 -0
- package/templates/opera/en/router.md +39 -0
- package/templates/opera/genesis.md +79 -94
- package/templates/skills/changelog-updater/locales/en/SKILL.md +11 -0
- package/templates/skills/commiter/locales/en/SKILL.md +11 -0
- package/templates/skills/project-starter-skill/locales/en/SKILL.md +24 -0
- package/ui/css/panels.css +956 -953
- package/ui/index.html +1 -1
- package/ui/js/api.js +211 -194
- package/ui/js/app.js +200 -199
- package/ui/js/i18n.js +14 -0
- package/ui/js/onboarding.js +439 -437
- package/ui/js/state.js +130 -129
- package/ui/js/utils.js +175 -172
- package/ui/js/views/board.js +255 -254
- package/ui/js/views/execution.js +256 -256
- package/ui/js/views/insights.js +340 -339
- package/ui/js/views/overview.js +365 -364
- package/ui/js/views/settings.js +340 -202
- package/ui/js/views/sidebar.js +131 -132
- package/ui/js/views/skills.js +163 -162
- package/ui/js/views/tasks.js +406 -405
- package/ui/js/views/topbar.js +239 -183
package/ui/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>TrackOps Beta — Centro de control</title>
|
|
7
7
|
<meta name="description" content="Panel local beta de TrackOps: gestión de proyectos, tareas, analíticas y seguimiento de tiempo para desarrolladores." />
|
|
8
|
-
<link rel="icon" type="image/svg+xml" href="
|
|
8
|
+
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
|
9
9
|
|
|
10
10
|
<!-- Google Fonts (coherente con web corporativa) -->
|
|
11
11
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
package/ui/js/api.js
CHANGED
|
@@ -1,203 +1,220 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* api.js — Capa de comunicación con el backend TrackOps
|
|
3
|
-
* Wrapper sobre fetch con gestión de errores, project-awareness
|
|
4
|
-
* y tipado de los endpoints disponibles en lib/server.js
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import * as state from './state.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Llamada base a la API
|
|
11
|
-
* @param {string} url
|
|
12
|
-
* @param {RequestInit & { projectAware?: boolean }} options
|
|
13
|
-
* @returns {Promise<Object>}
|
|
14
|
-
*/
|
|
15
|
-
async function call(url, options = {}) {
|
|
16
|
-
const target = new URL(url, window.location.origin);
|
|
17
|
-
const currentId = state.get('currentProjectId');
|
|
18
|
-
if (options.projectAware !== false && currentId && !target.searchParams.has('project')) {
|
|
19
|
-
target.searchParams.set('project', currentId);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const response = await fetch(target, {
|
|
23
|
-
...options,
|
|
24
|
-
headers: {
|
|
25
|
-
'Content-Type': 'application/json',
|
|
26
|
-
...(options.headers || {}),
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const text = await response.text();
|
|
31
|
-
const json = text ? JSON.parse(text) : {};
|
|
32
|
-
|
|
33
|
-
if (!response.ok || json.ok === false) {
|
|
34
|
-
const err = new Error(json.error || `HTTP ${response.status}: ${response.statusText}`);
|
|
35
|
-
err.status = response.status;
|
|
36
|
-
throw err;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return json;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ─────────────────────────────── PROYECTOS ──────────────────────────────────
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Lista todos los proyectos del portfolio
|
|
46
|
-
*/
|
|
47
|
-
export async function getProjects() {
|
|
48
|
-
return call('/api/projects', { projectAware: false });
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Registra un proyecto existente en el portfolio
|
|
53
|
-
* @param {string} root - ruta del directorio del proyecto
|
|
54
|
-
*/
|
|
55
|
-
export async function registerProject(root) {
|
|
56
|
-
return call('/api/projects/register', {
|
|
57
|
-
method: 'POST',
|
|
58
|
-
projectAware: false,
|
|
59
|
-
body: JSON.stringify({ root }),
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Instala trackops en un nuevo proyecto
|
|
65
|
-
* @param {string} root
|
|
66
|
-
*/
|
|
67
|
-
export async function installProject(root) {
|
|
68
|
-
return call('/api/projects/install', {
|
|
69
|
-
method: 'POST',
|
|
70
|
-
projectAware: false,
|
|
71
|
-
body: JSON.stringify({ root }),
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
1
|
+
/**
|
|
2
|
+
* api.js — Capa de comunicación con el backend TrackOps
|
|
3
|
+
* Wrapper sobre fetch con gestión de errores, project-awareness
|
|
4
|
+
* y tipado de los endpoints disponibles en lib/server.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as state from './state.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Llamada base a la API
|
|
11
|
+
* @param {string} url
|
|
12
|
+
* @param {RequestInit & { projectAware?: boolean }} options
|
|
13
|
+
* @returns {Promise<Object>}
|
|
14
|
+
*/
|
|
15
|
+
async function call(url, options = {}) {
|
|
16
|
+
const target = new URL(url, window.location.origin);
|
|
17
|
+
const currentId = state.get('currentProjectId');
|
|
18
|
+
if (options.projectAware !== false && currentId && !target.searchParams.has('project')) {
|
|
19
|
+
target.searchParams.set('project', currentId);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const response = await fetch(target, {
|
|
23
|
+
...options,
|
|
24
|
+
headers: {
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
...(options.headers || {}),
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const text = await response.text();
|
|
31
|
+
const json = text ? JSON.parse(text) : {};
|
|
32
|
+
|
|
33
|
+
if (!response.ok || json.ok === false) {
|
|
34
|
+
const err = new Error(json.error || `HTTP ${response.status}: ${response.statusText}`);
|
|
35
|
+
err.status = response.status;
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return json;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ─────────────────────────────── PROYECTOS ──────────────────────────────────
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Lista todos los proyectos del portfolio
|
|
46
|
+
*/
|
|
47
|
+
export async function getProjects() {
|
|
48
|
+
return call('/api/projects', { projectAware: false });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Registra un proyecto existente en el portfolio
|
|
53
|
+
* @param {string} root - ruta del directorio del proyecto
|
|
54
|
+
*/
|
|
55
|
+
export async function registerProject(root) {
|
|
56
|
+
return call('/api/projects/register', {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
projectAware: false,
|
|
59
|
+
body: JSON.stringify({ root }),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Instala trackops en un nuevo proyecto
|
|
65
|
+
* @param {string} root
|
|
66
|
+
*/
|
|
67
|
+
export async function installProject(root, options = {}) {
|
|
68
|
+
return call('/api/projects/install', {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
projectAware: false,
|
|
71
|
+
body: JSON.stringify({ root, ...options }),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function updateProjectLocale(locale) {
|
|
76
|
+
return call('/api/projects/locale', {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
body: JSON.stringify({ projectId: state.get('currentProjectId'), locale }),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─────────────────────────────── ESTADO ─────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Obtiene el estado completo del proyecto activo
|
|
86
|
+
*/
|
|
80
87
|
export async function getState() {
|
|
81
88
|
return call('/api/state');
|
|
82
89
|
}
|
|
83
90
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Crea una nueva tarea
|
|
88
|
-
* @param {Object} payload
|
|
89
|
-
*/
|
|
90
|
-
export async function createTask(payload) {
|
|
91
|
-
return call('/api/tasks', {
|
|
92
|
-
method: 'POST',
|
|
93
|
-
body: JSON.stringify({ projectId: state.get('currentProjectId'), ...payload }),
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Actualiza una tarea existente (edición completa)
|
|
99
|
-
* @param {string} taskId
|
|
100
|
-
* @param {Object} payload
|
|
101
|
-
*/
|
|
102
|
-
export async function updateTask(taskId, payload) {
|
|
103
|
-
return call(`/api/tasks/${encodeURIComponent(taskId)}`, {
|
|
104
|
-
method: 'PUT',
|
|
105
|
-
body: JSON.stringify({ projectId: state.get('currentProjectId'), ...payload }),
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Ejecuta una acción sobre una tarea (start, review, complete, block, pending, cancel)
|
|
111
|
-
* @param {string} taskId
|
|
112
|
-
* @param {string} action
|
|
113
|
-
* @param {string} [note]
|
|
114
|
-
*/
|
|
115
|
-
export async function taskAction(taskId, action, note = '') {
|
|
116
|
-
return call(`/api/tasks/${encodeURIComponent(taskId)}/action`, {
|
|
117
|
-
method: 'POST',
|
|
118
|
-
body: JSON.stringify({ projectId: state.get('currentProjectId'), action, note }),
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// ─────────────────────────────── SYNC ───────────────────────────────────────
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Sincroniza los docs del proyecto (task_plan.md, progress.md, findings.md)
|
|
126
|
-
*/
|
|
127
|
-
export async function syncDocs() {
|
|
128
|
-
return call('/api/sync', {
|
|
129
|
-
method: 'POST',
|
|
130
|
-
body: JSON.stringify({ projectId: state.get('currentProjectId') }),
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// ─────────────────────────────── COMANDOS ───────────────────────────────────
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Ejecuta un comando en el shell del proyecto
|
|
138
|
-
* @param {string} command
|
|
139
|
-
*/
|
|
140
|
-
export async function runCommand(command) {
|
|
141
|
-
return call('/api/commands', {
|
|
142
|
-
method: 'POST',
|
|
143
|
-
body: JSON.stringify({ projectId: state.get('currentProjectId'), command }),
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Crea un EventSource para hacer streaming de salida de una sesión
|
|
149
|
-
* @param {string} sessionId
|
|
150
|
-
* @returns {EventSource}
|
|
151
|
-
*/
|
|
152
|
-
export function streamSession(sessionId) {
|
|
153
|
-
return new EventSource(`/api/commands/${encodeURIComponent(sessionId)}/stream`);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// ─────────────────────────────── TIME TRACKING ──────────────────────────────
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Inicia un time entry para una tarea
|
|
160
|
-
* @param {string} taskId
|
|
161
|
-
* @param {string} taskTitle
|
|
162
|
-
*/
|
|
163
|
-
export async function startTimeEntry(taskId, taskTitle) {
|
|
164
|
-
return call('/api/time/start', {
|
|
165
|
-
method: 'POST',
|
|
166
|
-
body: JSON.stringify({ projectId: state.get('currentProjectId'), taskId, taskTitle }),
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Detiene el time entry activo
|
|
172
|
-
* @param {string} entryId
|
|
173
|
-
*/
|
|
174
|
-
export async function stopTimeEntry(entryId) {
|
|
175
|
-
return call('/api/time/stop', {
|
|
176
|
-
method: 'POST',
|
|
177
|
-
body: JSON.stringify({ projectId: state.get('currentProjectId'), entryId }),
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Obtiene los time entries del proyecto activo
|
|
183
|
-
*/
|
|
184
|
-
export async function getTimeEntries() {
|
|
185
|
-
return call('/api/time');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// ─────────────────────────────── SKILLS HUB ────────────────────────────────
|
|
189
|
-
|
|
190
|
-
export async function fetchSkillsLocal() {
|
|
191
|
-
return call('/api/skills/local');
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export async function fetchSkillsDiscover() {
|
|
195
|
-
return call('/api/skills/discover');
|
|
91
|
+
export async function getEnvStatus() {
|
|
92
|
+
return call('/api/env');
|
|
196
93
|
}
|
|
197
94
|
|
|
198
|
-
export async function
|
|
199
|
-
return call('/api/
|
|
95
|
+
export async function syncEnv() {
|
|
96
|
+
return call('/api/env/sync', {
|
|
200
97
|
method: 'POST',
|
|
201
|
-
body: JSON.stringify({ skillId })
|
|
202
98
|
});
|
|
203
99
|
}
|
|
100
|
+
|
|
101
|
+
// ─────────────────────────────── TAREAS ─────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Crea una nueva tarea
|
|
105
|
+
* @param {Object} payload
|
|
106
|
+
*/
|
|
107
|
+
export async function createTask(payload) {
|
|
108
|
+
return call('/api/tasks', {
|
|
109
|
+
method: 'POST',
|
|
110
|
+
body: JSON.stringify({ projectId: state.get('currentProjectId'), ...payload }),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Actualiza una tarea existente (edición completa)
|
|
116
|
+
* @param {string} taskId
|
|
117
|
+
* @param {Object} payload
|
|
118
|
+
*/
|
|
119
|
+
export async function updateTask(taskId, payload) {
|
|
120
|
+
return call(`/api/tasks/${encodeURIComponent(taskId)}`, {
|
|
121
|
+
method: 'PUT',
|
|
122
|
+
body: JSON.stringify({ projectId: state.get('currentProjectId'), ...payload }),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Ejecuta una acción sobre una tarea (start, review, complete, block, pending, cancel)
|
|
128
|
+
* @param {string} taskId
|
|
129
|
+
* @param {string} action
|
|
130
|
+
* @param {string} [note]
|
|
131
|
+
*/
|
|
132
|
+
export async function taskAction(taskId, action, note = '') {
|
|
133
|
+
return call(`/api/tasks/${encodeURIComponent(taskId)}/action`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
body: JSON.stringify({ projectId: state.get('currentProjectId'), action, note }),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ─────────────────────────────── SYNC ───────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Sincroniza los docs del proyecto (task_plan.md, progress.md, findings.md)
|
|
143
|
+
*/
|
|
144
|
+
export async function syncDocs() {
|
|
145
|
+
return call('/api/sync', {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
body: JSON.stringify({ projectId: state.get('currentProjectId') }),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ─────────────────────────────── COMANDOS ───────────────────────────────────
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Ejecuta un comando en el shell del proyecto
|
|
155
|
+
* @param {string} command
|
|
156
|
+
*/
|
|
157
|
+
export async function runCommand(command) {
|
|
158
|
+
return call('/api/commands', {
|
|
159
|
+
method: 'POST',
|
|
160
|
+
body: JSON.stringify({ projectId: state.get('currentProjectId'), command }),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Crea un EventSource para hacer streaming de salida de una sesión
|
|
166
|
+
* @param {string} sessionId
|
|
167
|
+
* @returns {EventSource}
|
|
168
|
+
*/
|
|
169
|
+
export function streamSession(sessionId) {
|
|
170
|
+
return new EventSource(`/api/commands/${encodeURIComponent(sessionId)}/stream`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ─────────────────────────────── TIME TRACKING ──────────────────────────────
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Inicia un time entry para una tarea
|
|
177
|
+
* @param {string} taskId
|
|
178
|
+
* @param {string} taskTitle
|
|
179
|
+
*/
|
|
180
|
+
export async function startTimeEntry(taskId, taskTitle) {
|
|
181
|
+
return call('/api/time/start', {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
body: JSON.stringify({ projectId: state.get('currentProjectId'), taskId, taskTitle }),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Detiene el time entry activo
|
|
189
|
+
* @param {string} entryId
|
|
190
|
+
*/
|
|
191
|
+
export async function stopTimeEntry(entryId) {
|
|
192
|
+
return call('/api/time/stop', {
|
|
193
|
+
method: 'POST',
|
|
194
|
+
body: JSON.stringify({ projectId: state.get('currentProjectId'), entryId }),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Obtiene los time entries del proyecto activo
|
|
200
|
+
*/
|
|
201
|
+
export async function getTimeEntries() {
|
|
202
|
+
return call('/api/time');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ─────────────────────────────── SKILLS HUB ────────────────────────────────
|
|
206
|
+
|
|
207
|
+
export async function fetchSkillsLocal() {
|
|
208
|
+
return call('/api/skills/local');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function fetchSkillsDiscover() {
|
|
212
|
+
return call('/api/skills/discover');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export async function installSkill(skillId) {
|
|
216
|
+
return call('/api/skills/install', {
|
|
217
|
+
method: 'POST',
|
|
218
|
+
body: JSON.stringify({ skillId })
|
|
219
|
+
});
|
|
220
|
+
}
|