trackops 2.0.3 → 2.0.4
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 +238 -0
- package/lib/init.js +2 -2
- package/lib/locale.js +41 -17
- package/lib/opera-bootstrap.js +68 -7
- package/lib/opera.js +10 -2
- package/lib/registry.js +18 -0
- package/lib/server.js +312 -207
- package/locales/en.json +4 -0
- package/locales/es.json +4 -0
- package/package.json +1 -1
- package/skills/trackops/locales/en/references/activation.md +15 -0
- package/skills/trackops/locales/en/references/troubleshooting.md +12 -0
- package/skills/trackops/references/activation.md +15 -0
- package/skills/trackops/references/troubleshooting.md +12 -0
- package/skills/trackops/skill.json +2 -2
- package/ui/css/base.css +19 -1
- package/ui/css/charts.css +106 -8
- package/ui/css/components.css +554 -17
- package/ui/css/onboarding.css +133 -0
- package/ui/css/panels.css +345 -406
- package/ui/css/terminal.css +125 -0
- package/ui/css/timeline.css +58 -0
- package/ui/css/tokens.css +170 -113
- package/ui/index.html +3 -0
- package/ui/js/api.js +49 -13
- package/ui/js/app.js +28 -32
- package/ui/js/charts.js +526 -0
- package/ui/js/filters.js +247 -0
- package/ui/js/icons.js +82 -57
- package/ui/js/keyboard.js +229 -0
- package/ui/js/onboarding.js +33 -42
- package/ui/js/router.js +20 -3
- package/ui/js/views/board.js +84 -114
- package/ui/js/views/dashboard.js +870 -0
- package/ui/js/views/projects.js +745 -0
- package/ui/js/views/scrum.js +476 -0
- package/ui/js/views/settings.js +197 -247
- package/ui/js/views/sidebar.js +37 -31
- package/ui/js/views/tasks.js +218 -101
- package/ui/js/views/timeline.js +265 -0
- package/ui/js/views/topbar.js +94 -107
- package/ui/app.js +0 -950
- package/ui/js/views/insights.js +0 -340
- package/ui/js/views/overview.js +0 -369
- package/ui/styles.css +0 -688
package/README.md
CHANGED
|
@@ -67,6 +67,125 @@ Esta separacion es intencional:
|
|
|
67
67
|
- el runtime se instala con un paso visible y verificable
|
|
68
68
|
- no hay instalacion transitiva oculta desde la propia skill
|
|
69
69
|
|
|
70
|
+
Si `npm install -g trackops` se ejecuta en modo interactivo, TrackOps intenta pedir el idioma global en ese momento. Si tu terminal o npm no muestran ese prompt, puedes fijarlo manualmente despues:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
trackops locale set es
|
|
74
|
+
trackops locale set en
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Flujo completo recomendado
|
|
78
|
+
|
|
79
|
+
1. Instala la skill global:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npx skills add Baxahaun/trackops --skill trackops --agent "*" --global -y
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
2. Instala el runtime:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm install -g trackops@latest
|
|
89
|
+
trackops --version
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
3. Entra en el repo que quieres gestionar:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cd ruta/a/tu/proyecto
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
4. Activa TrackOps y elige el idioma del proyecto cuando el CLI lo pida:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
trackops init
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
5. Instala OPERA:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
trackops opera install
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
6. Responde el intake inicial con estos valores:
|
|
111
|
+
|
|
112
|
+
- nivel tecnico:
|
|
113
|
+
`low|medium|high|senior`
|
|
114
|
+
tambien acepta `bajo|medio|alto`
|
|
115
|
+
- estado del proyecto:
|
|
116
|
+
`idea|draft|existing_repo|advanced`
|
|
117
|
+
- documentacion:
|
|
118
|
+
`none|notes|sos|spec_dossier|repo_docs`
|
|
119
|
+
- propiedad de decision:
|
|
120
|
+
`user|shared|agent`
|
|
121
|
+
tambien acepta `usuario|compartido|agente`
|
|
122
|
+
|
|
123
|
+
7. Si OPERA deriva al agente:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
trackops opera handoff --print
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Pega ese contexto en el agente, deja que genere:
|
|
130
|
+
|
|
131
|
+
- `ops/bootstrap/intake.json`
|
|
132
|
+
- `ops/bootstrap/spec-dossier.md`
|
|
133
|
+
- `ops/bootstrap/open-questions.md` si faltan decisiones
|
|
134
|
+
|
|
135
|
+
Y despues reanuda:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
trackops opera bootstrap --resume
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
8. Si OPERA completa bootstrap directo, revisa estado y continua con:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
trackops opera status
|
|
145
|
+
trackops next
|
|
146
|
+
trackops sync
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Desinstalacion global y local
|
|
150
|
+
|
|
151
|
+
#### Quitar la instalacion global
|
|
152
|
+
|
|
153
|
+
Quita la skill global del agente:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
npx skills remove --global trackops -y
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Quita el runtime global:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm uninstall -g trackops
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Verifica:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
npx skills ls -g
|
|
169
|
+
trackops --version
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Quitar TrackOps de un proyecto
|
|
173
|
+
|
|
174
|
+
Hoy no existe un comando `trackops uninstall` para el repo. La retirada local es manual.
|
|
175
|
+
|
|
176
|
+
En un workspace split, revisa y elimina solo lo que de verdad quieras retirar:
|
|
177
|
+
|
|
178
|
+
- `.trackops-workspace.json`
|
|
179
|
+
- `ops/`
|
|
180
|
+
- `app/.env` si era solo bridge
|
|
181
|
+
|
|
182
|
+
Revisa con cuidado antes de borrar:
|
|
183
|
+
|
|
184
|
+
- `/.env`
|
|
185
|
+
- `/.env.example`
|
|
186
|
+
|
|
187
|
+
Esos archivos pueden seguir siendo utiles para tu proyecto aunque dejes de usar TrackOps.
|
|
188
|
+
|
|
70
189
|
### Activacion local
|
|
71
190
|
|
|
72
191
|
Dentro de un repo:
|
|
@@ -138,6 +257,8 @@ Si el usuario no es tecnico, el proyecto esta en fase idea, o no hay documentaci
|
|
|
138
257
|
trackops opera bootstrap --resume
|
|
139
258
|
```
|
|
140
259
|
|
|
260
|
+
La terminal tambien debe decirte este siguiente paso al terminar el handoff.
|
|
261
|
+
|
|
141
262
|
#### Ya tengo un repo
|
|
142
263
|
|
|
143
264
|
Si el usuario es tecnico y el proyecto ya tiene suficiente contexto, OPERA sigue por bootstrap directo, compila `ops/contract/operating-contract.json` y recompila `ops/genesis.md`.
|
|
@@ -288,6 +409,83 @@ This split is intentional:
|
|
|
288
409
|
- the runtime is installed through a visible and verifiable step
|
|
289
410
|
- there is no hidden transitive install from the skill itself
|
|
290
411
|
|
|
412
|
+
If `npm install -g trackops` runs interactively, TrackOps tries to ask for the global language at that moment. If your terminal or npm do not show that prompt, set it manually afterwards:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
trackops locale set es
|
|
416
|
+
trackops locale set en
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Recommended full flow
|
|
420
|
+
|
|
421
|
+
1. Install the global skill:
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
npx skills add Baxahaun/trackops --skill trackops --agent "*" --global -y
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
2. Install the runtime:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
npm install -g trackops@latest
|
|
431
|
+
trackops --version
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
3. Enter the repository you want to manage:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
cd path/to/your/project
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
4. Activate TrackOps and choose the project language when the CLI asks:
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
trackops init
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
5. Install OPERA:
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
trackops opera install
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
6. Answer the initial intake with these values:
|
|
453
|
+
|
|
454
|
+
- technical level:
|
|
455
|
+
`low|medium|high|senior`
|
|
456
|
+
- project state:
|
|
457
|
+
`idea|draft|existing_repo|advanced`
|
|
458
|
+
- documentation:
|
|
459
|
+
`none|notes|sos|spec_dossier|repo_docs`
|
|
460
|
+
- decision ownership:
|
|
461
|
+
`user|shared|agent`
|
|
462
|
+
|
|
463
|
+
7. If OPERA routes to the agent:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
trackops opera handoff --print
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Paste that context into the agent and let it generate:
|
|
470
|
+
|
|
471
|
+
- `ops/bootstrap/intake.json`
|
|
472
|
+
- `ops/bootstrap/spec-dossier.md`
|
|
473
|
+
- `ops/bootstrap/open-questions.md` when decisions are still missing
|
|
474
|
+
|
|
475
|
+
Then resume with:
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
trackops opera bootstrap --resume
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
8. If OPERA completes direct bootstrap, review status and continue with:
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
trackops opera status
|
|
485
|
+
trackops next
|
|
486
|
+
trackops sync
|
|
487
|
+
```
|
|
488
|
+
|
|
291
489
|
### Local activation
|
|
292
490
|
|
|
293
491
|
Inside a repository:
|
|
@@ -358,6 +556,46 @@ trackops opera install --bootstrap-mode handoff
|
|
|
358
556
|
trackops opera install --bootstrap-mode direct
|
|
359
557
|
```
|
|
360
558
|
|
|
559
|
+
### Global and local removal
|
|
560
|
+
|
|
561
|
+
#### Remove the global install
|
|
562
|
+
|
|
563
|
+
Remove the global skill from the agent:
|
|
564
|
+
|
|
565
|
+
```bash
|
|
566
|
+
npx skills remove --global trackops -y
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Remove the global runtime:
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
npm uninstall -g trackops
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Verify:
|
|
576
|
+
|
|
577
|
+
```bash
|
|
578
|
+
npx skills ls -g
|
|
579
|
+
trackops --version
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
#### Remove TrackOps from a project
|
|
583
|
+
|
|
584
|
+
There is no `trackops uninstall` command for the repository yet. Local removal is manual.
|
|
585
|
+
|
|
586
|
+
In a split workspace, review and remove only what you really want to retire:
|
|
587
|
+
|
|
588
|
+
- `.trackops-workspace.json`
|
|
589
|
+
- `ops/`
|
|
590
|
+
- `app/.env` if it was only the compatibility bridge
|
|
591
|
+
|
|
592
|
+
Review carefully before deleting:
|
|
593
|
+
|
|
594
|
+
- `/.env`
|
|
595
|
+
- `/.env.example`
|
|
596
|
+
|
|
597
|
+
Those files may still be useful to the project even if you stop using TrackOps.
|
|
598
|
+
|
|
361
599
|
### Environment and secrets
|
|
362
600
|
|
|
363
601
|
TrackOps manages:
|
package/lib/init.js
CHANGED
|
@@ -9,7 +9,7 @@ const registry = require("./registry");
|
|
|
9
9
|
const env = require("./env");
|
|
10
10
|
const workspace = require("./workspace");
|
|
11
11
|
const { t, setLocale } = require("./i18n");
|
|
12
|
-
const { detectSystemLocale, promptForLocale, resolveLocale } = require("./locale");
|
|
12
|
+
const { detectSystemLocale, promptForLocale, maybePromptForLocale, resolveLocale } = require("./locale");
|
|
13
13
|
const runtimeState = require("./runtime-state");
|
|
14
14
|
|
|
15
15
|
const GENERATED_SCRIPT_COMMANDS = {
|
|
@@ -299,7 +299,7 @@ async function cmdInit(args) {
|
|
|
299
299
|
} else if (!globalLocale) {
|
|
300
300
|
options.locale = await promptForLocale(detectSystemLocale());
|
|
301
301
|
} else {
|
|
302
|
-
options.locale = globalLocale;
|
|
302
|
+
options.locale = await maybePromptForLocale(globalLocale, { promptMode: "always" });
|
|
303
303
|
}
|
|
304
304
|
if (!globalLocale) {
|
|
305
305
|
await runtimeState.ensureGlobalLocale({ preferredLocale: options.locale, interactive: false });
|
package/lib/locale.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const readline = require("readline/promises");
|
|
4
|
-
|
|
5
|
-
const SUPPORTED_LOCALES = ["es", "en"];
|
|
3
|
+
const readline = require("readline/promises");
|
|
4
|
+
|
|
5
|
+
const SUPPORTED_LOCALES = ["es", "en"];
|
|
6
6
|
|
|
7
7
|
function normalizeLocale(value) {
|
|
8
8
|
const raw = String(value || "").trim().toLowerCase();
|
|
@@ -32,9 +32,9 @@ function isInteractive() {
|
|
|
32
32
|
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async function promptForLocale(defaultLocale) {
|
|
36
|
-
const initial = normalizeLocale(defaultLocale) || detectSystemLocale();
|
|
37
|
-
if (!isInteractive()) return initial;
|
|
35
|
+
async function promptForLocale(defaultLocale) {
|
|
36
|
+
const initial = normalizeLocale(defaultLocale) || detectSystemLocale();
|
|
37
|
+
if (!isInteractive()) return initial;
|
|
38
38
|
|
|
39
39
|
const rl = readline.createInterface({
|
|
40
40
|
input: process.stdin,
|
|
@@ -46,18 +46,42 @@ async function promptForLocale(defaultLocale) {
|
|
|
46
46
|
return normalizeLocale(answer) || initial;
|
|
47
47
|
} finally {
|
|
48
48
|
rl.close();
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function maybePromptForLocale(defaultLocale, options = {}) {
|
|
53
|
+
const initial = normalizeLocale(defaultLocale) || detectSystemLocale();
|
|
54
|
+
if (!isInteractive()) return initial;
|
|
55
|
+
|
|
56
|
+
const promptMode = String(options.promptMode || "always").trim().toLowerCase();
|
|
57
|
+
if (promptMode === "never") return initial;
|
|
58
|
+
|
|
59
|
+
const rl = readline.createInterface({
|
|
60
|
+
input: process.stdin,
|
|
61
|
+
output: process.stdout,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const answer = await rl.question(
|
|
66
|
+
`Choose project language / Elige idioma del proyecto [es/en] (${initial}, Enter = keep): `,
|
|
67
|
+
);
|
|
68
|
+
const normalized = normalizeLocale(answer);
|
|
69
|
+
return normalized || initial;
|
|
70
|
+
} finally {
|
|
71
|
+
rl.close();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveLocale(preferred, fallback) {
|
|
76
|
+
return normalizeLocale(preferred) || normalizeLocale(fallback) || detectSystemLocale();
|
|
77
|
+
}
|
|
55
78
|
|
|
56
79
|
module.exports = {
|
|
57
80
|
SUPPORTED_LOCALES,
|
|
58
81
|
normalizeLocale,
|
|
59
|
-
detectSystemLocale,
|
|
60
|
-
isInteractive,
|
|
61
|
-
promptForLocale,
|
|
62
|
-
|
|
63
|
-
|
|
82
|
+
detectSystemLocale,
|
|
83
|
+
isInteractive,
|
|
84
|
+
promptForLocale,
|
|
85
|
+
maybePromptForLocale,
|
|
86
|
+
resolveLocale,
|
|
87
|
+
};
|
package/lib/opera-bootstrap.js
CHANGED
|
@@ -35,6 +35,49 @@ const BOOTSTRAP_MODES = ["auto", "direct", "handoff"];
|
|
|
35
35
|
const QUALITY_STATUSES = ["ready", "needs_review", "blocked"];
|
|
36
36
|
const CONTRACT_READINESS = ["hypothesis", "provisional", "verified", "locked"];
|
|
37
37
|
const CONTRACT_VERSION = 3;
|
|
38
|
+
const ENUM_ALIASES = {
|
|
39
|
+
low: "low",
|
|
40
|
+
bajo: "low",
|
|
41
|
+
basic: "low",
|
|
42
|
+
beginner: "low",
|
|
43
|
+
medium: "medium",
|
|
44
|
+
medio: "medium",
|
|
45
|
+
mid: "medium",
|
|
46
|
+
high: "high",
|
|
47
|
+
alto: "high",
|
|
48
|
+
advanced: "high",
|
|
49
|
+
senior: "senior",
|
|
50
|
+
idea: "idea",
|
|
51
|
+
ideacion: "idea",
|
|
52
|
+
ideation: "idea",
|
|
53
|
+
draft: "draft",
|
|
54
|
+
borrador: "draft",
|
|
55
|
+
existing_repo: "existing_repo",
|
|
56
|
+
existingrepo: "existing_repo",
|
|
57
|
+
existing: "existing_repo",
|
|
58
|
+
repo_existente: "existing_repo",
|
|
59
|
+
"repo existente": "existing_repo",
|
|
60
|
+
advanced_project: "advanced",
|
|
61
|
+
avanzado: "advanced",
|
|
62
|
+
advancedrepo: "advanced",
|
|
63
|
+
none: "none",
|
|
64
|
+
ninguna: "none",
|
|
65
|
+
notes: "notes",
|
|
66
|
+
notas: "notes",
|
|
67
|
+
sos: "sos",
|
|
68
|
+
spec_dossier: "spec_dossier",
|
|
69
|
+
spec: "spec_dossier",
|
|
70
|
+
dossier: "spec_dossier",
|
|
71
|
+
repo_docs: "repo_docs",
|
|
72
|
+
repodocs: "repo_docs",
|
|
73
|
+
docs_repo: "repo_docs",
|
|
74
|
+
user: "user",
|
|
75
|
+
usuario: "user",
|
|
76
|
+
shared: "shared",
|
|
77
|
+
compartido: "shared",
|
|
78
|
+
agent: "agent",
|
|
79
|
+
agente: "agent",
|
|
80
|
+
};
|
|
38
81
|
|
|
39
82
|
function nowIso() {
|
|
40
83
|
return new Date().toISOString();
|
|
@@ -81,8 +124,17 @@ function unique(items) {
|
|
|
81
124
|
}
|
|
82
125
|
|
|
83
126
|
function normalizeEnum(value, allowed) {
|
|
84
|
-
const normalized = String(value || "").trim().toLowerCase();
|
|
85
|
-
|
|
127
|
+
const normalized = String(value || "").trim().toLowerCase().replace(/\s+/g, "_");
|
|
128
|
+
const alias = ENUM_ALIASES[normalized] || normalized;
|
|
129
|
+
return allowed.includes(alias) ? alias : null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function choiceHint(allowed) {
|
|
133
|
+
if (allowed === TECHNICAL_LEVELS) return "low|medium|high|senior (o bajo|medio|alto)";
|
|
134
|
+
if (allowed === PROJECT_STATES) return "idea|draft|existing_repo|advanced";
|
|
135
|
+
if (allowed === DOC_STATES) return "none|notes|sos|spec_dossier|repo_docs";
|
|
136
|
+
if (allowed === DECISION_OWNERSHIPS) return "user|shared|agent (o usuario|compartido|agente)";
|
|
137
|
+
return Array.isArray(allowed) ? allowed.join("|") : null;
|
|
86
138
|
}
|
|
87
139
|
|
|
88
140
|
function explanationModeFor(technicalLevel) {
|
|
@@ -384,6 +436,14 @@ async function askQuestion(rl, message, defaultValue) {
|
|
|
384
436
|
return String(answer || "").trim() || String(defaultValue || "").trim();
|
|
385
437
|
}
|
|
386
438
|
|
|
439
|
+
async function askEnumQuestion(rl, message, defaultValue, allowed) {
|
|
440
|
+
const hint = choiceHint(allowed);
|
|
441
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
442
|
+
const answer = await rl.question(`${message}${hint ? ` [${hint}]` : ""}${suffix}: `);
|
|
443
|
+
const normalized = normalizeEnum(answer || defaultValue, allowed);
|
|
444
|
+
return normalized || normalizeEnum(defaultValue, allowed);
|
|
445
|
+
}
|
|
446
|
+
|
|
387
447
|
async function collectBootstrapProfile(root, control, options = {}) {
|
|
388
448
|
const context = config.ensureContext(root);
|
|
389
449
|
const locale = config.getLocale(control);
|
|
@@ -427,15 +487,16 @@ async function collectBootstrapProfile(root, control, options = {}) {
|
|
|
427
487
|
const answers = { ...defaults };
|
|
428
488
|
|
|
429
489
|
if (interactive) {
|
|
430
|
-
console.log("");
|
|
490
|
+
console.log("");
|
|
431
491
|
console.log(t("bootstrap.header"));
|
|
432
492
|
console.log(t("bootstrap.subtitle"));
|
|
493
|
+
console.log(t("bootstrap.instructions"));
|
|
433
494
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
434
495
|
try {
|
|
435
|
-
answers.technicalLevel =
|
|
436
|
-
answers.projectState =
|
|
437
|
-
answers.documentationState =
|
|
438
|
-
answers.decisionOwnership =
|
|
496
|
+
answers.technicalLevel = await askEnumQuestion(rl, t("bootstrap.question.technicalLevel"), defaults.technicalLevel || "medium", TECHNICAL_LEVELS) || "medium";
|
|
497
|
+
answers.projectState = await askEnumQuestion(rl, t("bootstrap.question.projectState"), defaults.projectState || "idea", PROJECT_STATES) || "idea";
|
|
498
|
+
answers.documentationState = await askEnumQuestion(rl, t("bootstrap.question.docsState"), defaults.documentationState || "none", DOC_STATES) || "none";
|
|
499
|
+
answers.decisionOwnership = await askEnumQuestion(rl, t("bootstrap.question.decisionOwnership"), defaults.decisionOwnership || "shared", DECISION_OWNERSHIPS) || "shared";
|
|
439
500
|
} finally {
|
|
440
501
|
rl.close();
|
|
441
502
|
}
|
package/lib/opera.js
CHANGED
|
@@ -6,7 +6,7 @@ const path = require("path");
|
|
|
6
6
|
const config = require("./config");
|
|
7
7
|
const env = require("./env");
|
|
8
8
|
const { t, setLocale } = require("./i18n");
|
|
9
|
-
const { promptForLocale, resolveLocale } = require("./locale");
|
|
9
|
+
const { promptForLocale, maybePromptForLocale, resolveLocale } = require("./locale");
|
|
10
10
|
const { resolveLocalizedFile, resolveLocalizedDir, resolveSkillFile } = require("./resources");
|
|
11
11
|
const bootstrap = require("./opera-bootstrap");
|
|
12
12
|
const runtimeState = require("./runtime-state");
|
|
@@ -188,8 +188,10 @@ async function install(root, options = {}) {
|
|
|
188
188
|
if (!runtimeState.getGlobalLocale()) {
|
|
189
189
|
await runtimeState.ensureGlobalLocale({ preferredLocale: locale, interactive: false });
|
|
190
190
|
}
|
|
191
|
+
} else if (!options.locale) {
|
|
192
|
+
locale = await maybePromptForLocale(locale, { promptMode: "always" });
|
|
191
193
|
}
|
|
192
|
-
control.meta.locale = locale;
|
|
194
|
+
control.meta.locale = locale;
|
|
193
195
|
setLocale(locale);
|
|
194
196
|
|
|
195
197
|
const alreadyInstalled = config.isOperaInstalled(control);
|
|
@@ -313,8 +315,14 @@ async function runBootstrap(root, options = {}) {
|
|
|
313
315
|
if (profile.mode === "agent_handoff") {
|
|
314
316
|
console.log(t("bootstrap.awaitingAgent"));
|
|
315
317
|
console.log(`${t("bootstrap.handoffFile")}: ${profile.handoffFiles?.markdown || bootstrap.bootstrapRelativePaths(context).markdown}`);
|
|
318
|
+
console.log(t("bootstrap.next.handoff"));
|
|
316
319
|
} else {
|
|
317
320
|
console.log(profile.status === "completed" ? t("bootstrap.completed") : t("bootstrap.pending"));
|
|
321
|
+
console.log(
|
|
322
|
+
profile.status === "completed"
|
|
323
|
+
? t("bootstrap.next.directCompleted")
|
|
324
|
+
: t("bootstrap.next.directPending"),
|
|
325
|
+
);
|
|
318
326
|
}
|
|
319
327
|
return profile;
|
|
320
328
|
}
|
package/lib/registry.js
CHANGED
|
@@ -122,6 +122,22 @@ function unregisterProject(rootDir) {
|
|
|
122
122
|
return registry;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
function unregisterById(projectId) {
|
|
126
|
+
const registry = loadRegistry();
|
|
127
|
+
registry.projects = registry.projects.filter((p) => p.id !== projectId);
|
|
128
|
+
saveRegistry(registry);
|
|
129
|
+
return registry;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function purgeUnavailable() {
|
|
133
|
+
const registry = loadRegistry();
|
|
134
|
+
const before = registry.projects.length;
|
|
135
|
+
registry.projects = registry.projects.filter((p) => isProjectInstalled(p.workspaceRoot || p.root));
|
|
136
|
+
const removed = before - registry.projects.length;
|
|
137
|
+
saveRegistry(registry);
|
|
138
|
+
return { removed, registry };
|
|
139
|
+
}
|
|
140
|
+
|
|
125
141
|
function listProjects() {
|
|
126
142
|
const registry = loadRegistry();
|
|
127
143
|
return registry.projects.map((project) => {
|
|
@@ -185,10 +201,12 @@ module.exports = {
|
|
|
185
201
|
isProjectInstalled,
|
|
186
202
|
listProjects,
|
|
187
203
|
loadRegistry,
|
|
204
|
+
purgeUnavailable,
|
|
188
205
|
registerProject,
|
|
189
206
|
resolveProject,
|
|
190
207
|
saveRegistry,
|
|
191
208
|
slugify,
|
|
209
|
+
unregisterById,
|
|
192
210
|
unregisterProject,
|
|
193
211
|
cmdRegister,
|
|
194
212
|
cmdList,
|