trackops 2.0.4 → 2.0.6

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 (92) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +660 -575
  3. package/bin/trackops.js +127 -106
  4. package/lib/cli-format.js +118 -0
  5. package/lib/config.js +352 -326
  6. package/lib/control.js +408 -246
  7. package/lib/env.js +234 -222
  8. package/lib/i18n.js +5 -4
  9. package/lib/init.js +390 -282
  10. package/lib/locale.js +41 -41
  11. package/lib/opera-bootstrap.js +1066 -880
  12. package/lib/opera.js +615 -444
  13. package/lib/preferences.js +74 -74
  14. package/lib/registry.js +214 -214
  15. package/lib/release.js +56 -56
  16. package/lib/runtime-state.js +144 -144
  17. package/lib/skills.js +114 -89
  18. package/lib/workspace.js +259 -248
  19. package/locales/en.json +311 -167
  20. package/locales/es.json +314 -170
  21. package/package.json +61 -58
  22. package/scripts/postinstall-locale.js +21 -21
  23. package/scripts/skills-marketplace-smoke.js +124 -124
  24. package/scripts/smoke-tests.js +563 -517
  25. package/scripts/sync-skill-version.js +21 -21
  26. package/scripts/validate-skill.js +103 -103
  27. package/skills/trackops/SKILL.md +126 -122
  28. package/skills/trackops/agents/openai.yaml +7 -7
  29. package/skills/trackops/locales/en/SKILL.md +126 -122
  30. package/skills/trackops/locales/en/references/activation.md +94 -90
  31. package/skills/trackops/locales/en/references/troubleshooting.md +73 -67
  32. package/skills/trackops/locales/en/references/workflow.md +55 -32
  33. package/skills/trackops/references/activation.md +94 -90
  34. package/skills/trackops/references/troubleshooting.md +73 -67
  35. package/skills/trackops/references/workflow.md +55 -32
  36. package/skills/trackops/skill.json +29 -29
  37. package/templates/hooks/post-checkout +2 -2
  38. package/templates/hooks/post-commit +2 -2
  39. package/templates/hooks/post-merge +2 -2
  40. package/templates/opera/agent.md +28 -27
  41. package/templates/opera/architecture/dependency-graph.md +24 -24
  42. package/templates/opera/architecture/runtime-automation.md +24 -24
  43. package/templates/opera/architecture/runtime-operations.md +34 -34
  44. package/templates/opera/en/agent.md +22 -21
  45. package/templates/opera/en/architecture/dependency-graph.md +24 -24
  46. package/templates/opera/en/architecture/runtime-automation.md +24 -24
  47. package/templates/opera/en/architecture/runtime-operations.md +34 -34
  48. package/templates/opera/en/reviews/delivery-audit.md +18 -18
  49. package/templates/opera/en/reviews/integration-audit.md +18 -18
  50. package/templates/opera/en/router.md +24 -19
  51. package/templates/opera/references/autonomy-and-recovery.md +117 -117
  52. package/templates/opera/references/opera-cycle.md +193 -193
  53. package/templates/opera/registry.md +28 -28
  54. package/templates/opera/reviews/delivery-audit.md +18 -18
  55. package/templates/opera/reviews/integration-audit.md +18 -18
  56. package/templates/opera/router.md +54 -49
  57. package/templates/skills/changelog-updater/SKILL.md +69 -69
  58. package/templates/skills/commiter/SKILL.md +99 -99
  59. package/templates/skills/opera-contract-auditor/SKILL.md +38 -38
  60. package/templates/skills/opera-contract-auditor/locales/en/SKILL.md +38 -38
  61. package/templates/skills/opera-policy-guard/SKILL.md +26 -26
  62. package/templates/skills/opera-policy-guard/locales/en/SKILL.md +26 -26
  63. package/templates/skills/opera-skill/SKILL.md +279 -0
  64. package/templates/skills/opera-skill/locales/en/SKILL.md +279 -0
  65. package/templates/skills/opera-skill/locales/en/references/phase-dod.md +138 -0
  66. package/templates/skills/opera-skill/references/phase-dod.md +138 -0
  67. package/templates/skills/project-starter-skill/SKILL.md +150 -131
  68. package/templates/skills/project-starter-skill/locales/en/SKILL.md +143 -105
  69. package/templates/skills/project-starter-skill/references/opera-cycle.md +195 -193
  70. package/ui/css/base.css +284 -284
  71. package/ui/css/charts.css +425 -425
  72. package/ui/css/components.css +1107 -1107
  73. package/ui/css/onboarding.css +133 -133
  74. package/ui/css/terminal.css +125 -125
  75. package/ui/css/timeline.css +58 -58
  76. package/ui/css/tokens.css +284 -284
  77. package/ui/favicon.svg +5 -5
  78. package/ui/index.html +99 -99
  79. package/ui/js/charts.js +526 -526
  80. package/ui/js/console-logger.js +172 -172
  81. package/ui/js/filters.js +247 -247
  82. package/ui/js/icons.js +129 -129
  83. package/ui/js/keyboard.js +229 -229
  84. package/ui/js/router.js +142 -142
  85. package/ui/js/theme.js +100 -100
  86. package/ui/js/time-tracker.js +248 -248
  87. package/ui/js/views/dashboard.js +870 -870
  88. package/ui/js/views/flash.js +47 -47
  89. package/ui/js/views/projects.js +745 -745
  90. package/ui/js/views/scrum.js +476 -476
  91. package/ui/js/views/settings.js +331 -331
  92. package/ui/js/views/timeline.js +265 -265
package/ui/js/keyboard.js CHANGED
@@ -1,229 +1,229 @@
1
- /**
2
- * keyboard.js — Sistema de atajos de teclado global para TrackOps Dashboard
3
- * Secuencias de 2 teclas estilo Vim/GitHub (g d = go dashboard).
4
- * No activo cuando un input/textarea/select tiene foco.
5
- */
6
-
7
- import * as router from './router.js';
8
- import * as state from './state.js';
9
- import { icon } from './icons.js';
10
-
11
- /** @type {Map<string, {handler: Function, label: string, group: string}>} */
12
- const _shortcuts = new Map();
13
-
14
- /** Buffer para secuencias de 2 teclas */
15
- let _pendingKey = '';
16
- let _pendingTimer = null;
17
- const SEQUENCE_TIMEOUT = 600; // ms para completar secuencia
18
-
19
- /** Elemento del panel de ayuda */
20
- let _helpVisible = false;
21
-
22
- // ─────────────────────────────── REGISTRO ────────────────────────────────────
23
-
24
- /**
25
- * Registrar un atajo de teclado
26
- * @param {string} keys — Tecla o secuencia ('g o', '/', '?', 'Escape')
27
- * @param {Function} handler — Callback
28
- * @param {string} label — Descripcion para el panel de ayuda
29
- * @param {string} [group='General'] — Grupo en el panel de ayuda
30
- */
31
- export function register(keys, handler, label, group = 'General') {
32
- _shortcuts.set(keys, { handler, label, group });
33
- }
34
-
35
- // ─────────────────────────────── DEFAULTS ────────────────────────────────────
36
-
37
- function _registerDefaults() {
38
- // Navegacion
39
- register('g d', () => router.navigate('dashboard'), 'Go to Dashboard', 'Navigation');
40
- register('g t', () => router.navigate('tasks'), 'Go to Tasks', 'Navigation');
41
- register('g l', () => router.navigate('timeline'), 'Go to Timeline', 'Navigation');
42
- register('g e', () => router.navigate('terminal'), 'Go to Terminal', 'Navigation');
43
- register('g p', () => router.navigate('projects'), 'Go to Projects', 'Navigation');
44
- register('g s', () => router.navigate('settings'), 'Go to Settings', 'Navigation');
45
-
46
- // Acciones
47
- register('/', () => _focusSearch(), 'Focus search', 'Actions');
48
- register('r', () => _refreshState(), 'Refresh data', 'Actions');
49
- register('Escape', () => _closeActiveOverlay(), 'Close overlay', 'Actions');
50
-
51
- // Ayuda
52
- register('?', () => toggleHelp(), 'Show shortcuts', 'Help');
53
- }
54
-
55
- // ─────────────────────────────── HANDLER ─────────────────────────────────────
56
-
57
- function _onKeyDown(e) {
58
- // No interceptar si un input tiene foco
59
- const tag = document.activeElement?.tagName?.toLowerCase();
60
- if (tag === 'input' || tag === 'textarea' || tag === 'select') {
61
- // Solo Escape escapa de inputs
62
- if (e.key === 'Escape') {
63
- document.activeElement.blur();
64
- e.preventDefault();
65
- }
66
- return;
67
- }
68
-
69
- // No interceptar si hay modificadores (excepto Shift para ?)
70
- if (e.ctrlKey || e.altKey || e.metaKey) return;
71
-
72
- const key = e.key;
73
-
74
- // Buscar atajo directo (1 tecla)
75
- if (!_pendingKey) {
76
- const direct = _shortcuts.get(key);
77
- if (direct) {
78
- e.preventDefault();
79
- direct.handler();
80
- return;
81
- }
82
-
83
- // Iniciar secuencia de 2 teclas
84
- // Solo letras minusculas inician secuencias
85
- if (/^[a-z]$/.test(key)) {
86
- _pendingKey = key;
87
- _pendingTimer = setTimeout(() => {
88
- _pendingKey = '';
89
- _pendingTimer = null;
90
- }, SEQUENCE_TIMEOUT);
91
- return;
92
- }
93
- return;
94
- }
95
-
96
- // Completar secuencia de 2 teclas
97
- clearTimeout(_pendingTimer);
98
- const sequence = `${_pendingKey} ${key}`;
99
- _pendingKey = '';
100
- _pendingTimer = null;
101
-
102
- const seq = _shortcuts.get(sequence);
103
- if (seq) {
104
- e.preventDefault();
105
- seq.handler();
106
- }
107
- }
108
-
109
- // ─────────────────────────────── ACCIONES ────────────────────────────────────
110
-
111
- function _focusSearch() {
112
- const input = document.querySelector('.topbar-search input, .topbar-search-input');
113
- if (input) {
114
- input.focus();
115
- input.select();
116
- }
117
- }
118
-
119
- function _refreshState() {
120
- window.dispatchEvent(new CustomEvent('ops:refresh'));
121
- }
122
-
123
- function _closeActiveOverlay() {
124
- // Cerrar help panel
125
- if (_helpVisible) {
126
- toggleHelp();
127
- return;
128
- }
129
- // Cerrar modal
130
- const modal = document.querySelector('.modal-overlay:not(.is-hidden)');
131
- if (modal) {
132
- modal.classList.add('is-hidden');
133
- return;
134
- }
135
- // Cerrar console panel
136
- const consolePanel = document.getElementById('console-panel');
137
- if (consolePanel?.classList.contains('is-open')) {
138
- consolePanel.classList.remove('is-open');
139
- }
140
- }
141
-
142
- // ─────────────────────────────── HELP PANEL ──────────────────────────────────
143
-
144
- export function toggleHelp() {
145
- _helpVisible = !_helpVisible;
146
- let panel = document.getElementById('keyboard-help-panel');
147
-
148
- if (!_helpVisible && panel) {
149
- panel.remove();
150
- return;
151
- }
152
-
153
- if (_helpVisible) {
154
- panel = document.createElement('div');
155
- panel.id = 'keyboard-help-panel';
156
- panel.className = 'modal-overlay';
157
- panel.setAttribute('role', 'dialog');
158
- panel.setAttribute('aria-modal', 'true');
159
- panel.setAttribute('aria-label', 'Keyboard shortcuts');
160
- panel.addEventListener('click', (e) => {
161
- if (e.target === panel) toggleHelp();
162
- });
163
-
164
- // Agrupar shortcuts
165
- const groups = new Map();
166
- for (const [keys, { label, group }] of _shortcuts) {
167
- if (!groups.has(group)) groups.set(group, []);
168
- groups.get(group).push({ keys, label });
169
- }
170
-
171
- let groupsHtml = '';
172
- for (const [group, items] of groups) {
173
- groupsHtml += `
174
- <div class="kb-help-group">
175
- <h3 class="kb-help-group-title">${group}</h3>
176
- <div class="kb-help-items">
177
- ${items.map(({ keys, label }) => `
178
- <div class="kb-help-item">
179
- <kbd class="kb-help-key">${_formatKey(keys)}</kbd>
180
- <span class="kb-help-label">${label}</span>
181
- </div>
182
- `).join('')}
183
- </div>
184
- </div>
185
- `;
186
- }
187
-
188
- panel.innerHTML = `
189
- <div class="modal" style="max-width:520px">
190
- <div class="modal-header">
191
- <h2 class="modal-title">${icon('keyboard', 18)} Keyboard shortcuts</h2>
192
- <button class="modal-close" type="button" aria-label="Close" id="kb-help-close">&times;</button>
193
- </div>
194
- <div class="modal-body" style="padding:var(--space-4) var(--space-6)">
195
- ${groupsHtml}
196
- </div>
197
- <div class="modal-footer" style="justify-content:center">
198
- <p style="font-size:var(--text-xs);color:var(--text-muted)">Press <kbd class="kb-help-key">?</kbd> to toggle this panel</p>
199
- </div>
200
- </div>
201
- `;
202
-
203
- document.body.appendChild(panel);
204
- panel.querySelector('#kb-help-close')?.addEventListener('click', () => toggleHelp());
205
- }
206
- }
207
-
208
- function _formatKey(keys) {
209
- return keys.split(' ').map(k => {
210
- if (k === 'Escape') return 'Esc';
211
- if (k === '/') return '/';
212
- if (k === '?') return '?';
213
- return k.toUpperCase();
214
- }).join(' <span style="opacity:0.4">then</span> ');
215
- }
216
-
217
- // ─────────────────────────────── INIT ────────────────────────────────────────
218
-
219
- export function init() {
220
- _registerDefaults();
221
- document.addEventListener('keydown', _onKeyDown);
222
- }
223
-
224
- export function destroy() {
225
- document.removeEventListener('keydown', _onKeyDown);
226
- _shortcuts.clear();
227
- }
228
-
229
- export default { init, destroy, register, toggleHelp };
1
+ /**
2
+ * keyboard.js — Sistema de atajos de teclado global para TrackOps Dashboard
3
+ * Secuencias de 2 teclas estilo Vim/GitHub (g d = go dashboard).
4
+ * No activo cuando un input/textarea/select tiene foco.
5
+ */
6
+
7
+ import * as router from './router.js';
8
+ import * as state from './state.js';
9
+ import { icon } from './icons.js';
10
+
11
+ /** @type {Map<string, {handler: Function, label: string, group: string}>} */
12
+ const _shortcuts = new Map();
13
+
14
+ /** Buffer para secuencias de 2 teclas */
15
+ let _pendingKey = '';
16
+ let _pendingTimer = null;
17
+ const SEQUENCE_TIMEOUT = 600; // ms para completar secuencia
18
+
19
+ /** Elemento del panel de ayuda */
20
+ let _helpVisible = false;
21
+
22
+ // ─────────────────────────────── REGISTRO ────────────────────────────────────
23
+
24
+ /**
25
+ * Registrar un atajo de teclado
26
+ * @param {string} keys — Tecla o secuencia ('g o', '/', '?', 'Escape')
27
+ * @param {Function} handler — Callback
28
+ * @param {string} label — Descripcion para el panel de ayuda
29
+ * @param {string} [group='General'] — Grupo en el panel de ayuda
30
+ */
31
+ export function register(keys, handler, label, group = 'General') {
32
+ _shortcuts.set(keys, { handler, label, group });
33
+ }
34
+
35
+ // ─────────────────────────────── DEFAULTS ────────────────────────────────────
36
+
37
+ function _registerDefaults() {
38
+ // Navegacion
39
+ register('g d', () => router.navigate('dashboard'), 'Go to Dashboard', 'Navigation');
40
+ register('g t', () => router.navigate('tasks'), 'Go to Tasks', 'Navigation');
41
+ register('g l', () => router.navigate('timeline'), 'Go to Timeline', 'Navigation');
42
+ register('g e', () => router.navigate('terminal'), 'Go to Terminal', 'Navigation');
43
+ register('g p', () => router.navigate('projects'), 'Go to Projects', 'Navigation');
44
+ register('g s', () => router.navigate('settings'), 'Go to Settings', 'Navigation');
45
+
46
+ // Acciones
47
+ register('/', () => _focusSearch(), 'Focus search', 'Actions');
48
+ register('r', () => _refreshState(), 'Refresh data', 'Actions');
49
+ register('Escape', () => _closeActiveOverlay(), 'Close overlay', 'Actions');
50
+
51
+ // Ayuda
52
+ register('?', () => toggleHelp(), 'Show shortcuts', 'Help');
53
+ }
54
+
55
+ // ─────────────────────────────── HANDLER ─────────────────────────────────────
56
+
57
+ function _onKeyDown(e) {
58
+ // No interceptar si un input tiene foco
59
+ const tag = document.activeElement?.tagName?.toLowerCase();
60
+ if (tag === 'input' || tag === 'textarea' || tag === 'select') {
61
+ // Solo Escape escapa de inputs
62
+ if (e.key === 'Escape') {
63
+ document.activeElement.blur();
64
+ e.preventDefault();
65
+ }
66
+ return;
67
+ }
68
+
69
+ // No interceptar si hay modificadores (excepto Shift para ?)
70
+ if (e.ctrlKey || e.altKey || e.metaKey) return;
71
+
72
+ const key = e.key;
73
+
74
+ // Buscar atajo directo (1 tecla)
75
+ if (!_pendingKey) {
76
+ const direct = _shortcuts.get(key);
77
+ if (direct) {
78
+ e.preventDefault();
79
+ direct.handler();
80
+ return;
81
+ }
82
+
83
+ // Iniciar secuencia de 2 teclas
84
+ // Solo letras minusculas inician secuencias
85
+ if (/^[a-z]$/.test(key)) {
86
+ _pendingKey = key;
87
+ _pendingTimer = setTimeout(() => {
88
+ _pendingKey = '';
89
+ _pendingTimer = null;
90
+ }, SEQUENCE_TIMEOUT);
91
+ return;
92
+ }
93
+ return;
94
+ }
95
+
96
+ // Completar secuencia de 2 teclas
97
+ clearTimeout(_pendingTimer);
98
+ const sequence = `${_pendingKey} ${key}`;
99
+ _pendingKey = '';
100
+ _pendingTimer = null;
101
+
102
+ const seq = _shortcuts.get(sequence);
103
+ if (seq) {
104
+ e.preventDefault();
105
+ seq.handler();
106
+ }
107
+ }
108
+
109
+ // ─────────────────────────────── ACCIONES ────────────────────────────────────
110
+
111
+ function _focusSearch() {
112
+ const input = document.querySelector('.topbar-search input, .topbar-search-input');
113
+ if (input) {
114
+ input.focus();
115
+ input.select();
116
+ }
117
+ }
118
+
119
+ function _refreshState() {
120
+ window.dispatchEvent(new CustomEvent('ops:refresh'));
121
+ }
122
+
123
+ function _closeActiveOverlay() {
124
+ // Cerrar help panel
125
+ if (_helpVisible) {
126
+ toggleHelp();
127
+ return;
128
+ }
129
+ // Cerrar modal
130
+ const modal = document.querySelector('.modal-overlay:not(.is-hidden)');
131
+ if (modal) {
132
+ modal.classList.add('is-hidden');
133
+ return;
134
+ }
135
+ // Cerrar console panel
136
+ const consolePanel = document.getElementById('console-panel');
137
+ if (consolePanel?.classList.contains('is-open')) {
138
+ consolePanel.classList.remove('is-open');
139
+ }
140
+ }
141
+
142
+ // ─────────────────────────────── HELP PANEL ──────────────────────────────────
143
+
144
+ export function toggleHelp() {
145
+ _helpVisible = !_helpVisible;
146
+ let panel = document.getElementById('keyboard-help-panel');
147
+
148
+ if (!_helpVisible && panel) {
149
+ panel.remove();
150
+ return;
151
+ }
152
+
153
+ if (_helpVisible) {
154
+ panel = document.createElement('div');
155
+ panel.id = 'keyboard-help-panel';
156
+ panel.className = 'modal-overlay';
157
+ panel.setAttribute('role', 'dialog');
158
+ panel.setAttribute('aria-modal', 'true');
159
+ panel.setAttribute('aria-label', 'Keyboard shortcuts');
160
+ panel.addEventListener('click', (e) => {
161
+ if (e.target === panel) toggleHelp();
162
+ });
163
+
164
+ // Agrupar shortcuts
165
+ const groups = new Map();
166
+ for (const [keys, { label, group }] of _shortcuts) {
167
+ if (!groups.has(group)) groups.set(group, []);
168
+ groups.get(group).push({ keys, label });
169
+ }
170
+
171
+ let groupsHtml = '';
172
+ for (const [group, items] of groups) {
173
+ groupsHtml += `
174
+ <div class="kb-help-group">
175
+ <h3 class="kb-help-group-title">${group}</h3>
176
+ <div class="kb-help-items">
177
+ ${items.map(({ keys, label }) => `
178
+ <div class="kb-help-item">
179
+ <kbd class="kb-help-key">${_formatKey(keys)}</kbd>
180
+ <span class="kb-help-label">${label}</span>
181
+ </div>
182
+ `).join('')}
183
+ </div>
184
+ </div>
185
+ `;
186
+ }
187
+
188
+ panel.innerHTML = `
189
+ <div class="modal" style="max-width:520px">
190
+ <div class="modal-header">
191
+ <h2 class="modal-title">${icon('keyboard', 18)} Keyboard shortcuts</h2>
192
+ <button class="modal-close" type="button" aria-label="Close" id="kb-help-close">&times;</button>
193
+ </div>
194
+ <div class="modal-body" style="padding:var(--space-4) var(--space-6)">
195
+ ${groupsHtml}
196
+ </div>
197
+ <div class="modal-footer" style="justify-content:center">
198
+ <p style="font-size:var(--text-xs);color:var(--text-muted)">Press <kbd class="kb-help-key">?</kbd> to toggle this panel</p>
199
+ </div>
200
+ </div>
201
+ `;
202
+
203
+ document.body.appendChild(panel);
204
+ panel.querySelector('#kb-help-close')?.addEventListener('click', () => toggleHelp());
205
+ }
206
+ }
207
+
208
+ function _formatKey(keys) {
209
+ return keys.split(' ').map(k => {
210
+ if (k === 'Escape') return 'Esc';
211
+ if (k === '/') return '/';
212
+ if (k === '?') return '?';
213
+ return k.toUpperCase();
214
+ }).join(' <span style="opacity:0.4">then</span> ');
215
+ }
216
+
217
+ // ─────────────────────────────── INIT ────────────────────────────────────────
218
+
219
+ export function init() {
220
+ _registerDefaults();
221
+ document.addEventListener('keydown', _onKeyDown);
222
+ }
223
+
224
+ export function destroy() {
225
+ document.removeEventListener('keydown', _onKeyDown);
226
+ _shortcuts.clear();
227
+ }
228
+
229
+ export default { init, destroy, register, toggleHelp };