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.
Files changed (57) hide show
  1. package/README.md +326 -270
  2. package/bin/trackops.js +102 -70
  3. package/lib/config.js +260 -35
  4. package/lib/control.js +517 -475
  5. package/lib/env.js +227 -0
  6. package/lib/i18n.js +61 -53
  7. package/lib/init.js +135 -46
  8. package/lib/locale.js +63 -0
  9. package/lib/opera-bootstrap.js +523 -0
  10. package/lib/opera.js +319 -170
  11. package/lib/registry.js +27 -13
  12. package/lib/release.js +56 -0
  13. package/lib/resources.js +42 -0
  14. package/lib/server.js +907 -554
  15. package/lib/skills.js +148 -124
  16. package/lib/workspace.js +260 -0
  17. package/locales/en.json +331 -139
  18. package/locales/es.json +331 -139
  19. package/package.json +7 -9
  20. package/scripts/skills-marketplace-smoke.js +124 -0
  21. package/scripts/smoke-tests.js +445 -0
  22. package/scripts/sync-skill-version.js +21 -0
  23. package/scripts/validate-skill.js +88 -0
  24. package/skills/trackops/SKILL.md +64 -0
  25. package/skills/trackops/agents/openai.yaml +3 -0
  26. package/skills/trackops/references/activation.md +39 -0
  27. package/skills/trackops/references/troubleshooting.md +34 -0
  28. package/skills/trackops/references/workflow.md +20 -0
  29. package/skills/trackops/scripts/bootstrap-trackops.js +201 -0
  30. package/skills/trackops/skill.json +29 -0
  31. package/templates/opera/en/agent.md +26 -0
  32. package/templates/opera/en/genesis.md +79 -0
  33. package/templates/opera/en/references/autonomy-and-recovery.md +23 -0
  34. package/templates/opera/en/references/opera-cycle.md +62 -0
  35. package/templates/opera/en/registry.md +28 -0
  36. package/templates/opera/en/router.md +39 -0
  37. package/templates/opera/genesis.md +79 -94
  38. package/templates/skills/changelog-updater/locales/en/SKILL.md +11 -0
  39. package/templates/skills/commiter/locales/en/SKILL.md +11 -0
  40. package/templates/skills/project-starter-skill/locales/en/SKILL.md +24 -0
  41. package/ui/css/panels.css +956 -953
  42. package/ui/index.html +1 -1
  43. package/ui/js/api.js +211 -194
  44. package/ui/js/app.js +200 -199
  45. package/ui/js/i18n.js +14 -0
  46. package/ui/js/onboarding.js +439 -437
  47. package/ui/js/state.js +130 -129
  48. package/ui/js/utils.js +175 -172
  49. package/ui/js/views/board.js +255 -254
  50. package/ui/js/views/execution.js +256 -256
  51. package/ui/js/views/insights.js +340 -339
  52. package/ui/js/views/overview.js +365 -364
  53. package/ui/js/views/settings.js +340 -202
  54. package/ui/js/views/sidebar.js +131 -132
  55. package/ui/js/views/skills.js +163 -162
  56. package/ui/js/views/tasks.js +406 -405
  57. 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="/favicon.svg" />
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
- // ─────────────────────────────── ESTADO ─────────────────────────────────────
76
-
77
- /**
78
- * Obtiene el estado completo del proyecto activo
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
- // ─────────────────────────────── TAREAS ─────────────────────────────────────
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 installSkill(skillId) {
199
- return call('/api/skills/install', {
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
+ }