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/lib/workspace.js CHANGED
@@ -1,260 +1,271 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require("fs");
4
- const path = require("path");
5
- const { spawnSync } = require("child_process");
6
-
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { spawnSync } = require("child_process");
6
+
7
7
  const config = require("./config");
8
8
  const registry = require("./registry");
9
9
  const env = require("./env");
10
-
11
- const OPS_ARTIFACTS = [
12
- "project_control.json",
13
- "task_plan.md",
14
- "progress.md",
15
- "findings.md",
16
- "genesis.md",
17
- ".agent",
18
- ".agents",
19
- ".githooks",
20
- ".tmp",
21
- ];
22
-
23
- function nowStamp() {
24
- return new Date().toISOString().replace(/[:.]/g, "-");
25
- }
26
-
27
- function git(args, cwd) {
28
- return spawnSync("git", args, { cwd, encoding: "utf8" });
29
- }
30
-
31
- function isIgnorableDirtyLine(line) {
32
- const normalized = String(line || "").replace(/\\/g, "/");
33
- return normalized.endsWith(".tmp/project-control-runtime.json");
34
- }
35
-
36
- function isRetryableMoveError(error) {
37
- return ["EPERM", "EXDEV", "EBUSY", "ENOTEMPTY"].includes(error?.code);
38
- }
39
-
40
- function assertCleanGit(root, allowDirty = false) {
41
- const status = git(["status", "--porcelain"], root);
42
- const dirtyLines = String(status.stdout || "")
43
- .split(/\r?\n/)
44
- .filter(Boolean)
45
- .filter((line) => !isIgnorableDirtyLine(line));
46
- if (!allowDirty && dirtyLines.length > 0) {
47
- throw new Error("Workspace migration requires a clean git worktree.");
48
- }
49
- }
50
-
51
- function createBackupBranch(root) {
52
- const name = `backup/trackops-workspace-${nowStamp()}`;
53
- const result = git(["branch", name], root);
54
- if (result.status !== 0) {
55
- throw new Error(result.stderr || `Could not create backup branch ${name}`);
56
- }
57
- return name;
58
- }
59
-
60
- function buildManifest() {
61
- return {
62
- version: 1,
63
- layout: "split",
64
- appDir: config.DEFAULT_APP_DIR,
65
- opsDir: config.DEFAULT_OPS_DIR,
66
- env: {
67
- rootFile: ".env",
68
- exampleFile: ".env.example",
69
- appBridgeFile: "app/.env",
70
- bridgeMode: "symlink-or-copy",
71
- },
72
- branches: {
73
- development: config.DEFAULT_DEV_BRANCH,
74
- publish: config.DEFAULT_PUBLISH_BRANCH,
75
- },
76
- publish: {
77
- mode: "subtree-flatten",
78
- sourceDir: config.DEFAULT_APP_DIR,
79
- includeRootFiles: [".env.example"],
80
- requireCleanWorktree: true,
81
- },
82
- };
83
- }
84
-
85
- function ensureRootGitignore(workspaceRoot) {
86
- const gitignorePath = path.join(workspaceRoot, ".gitignore");
87
- const required = ["/.env", "/app/.env", "/ops/.tmp/"];
88
- let lines = [];
89
- if (fs.existsSync(gitignorePath)) {
90
- lines = fs.readFileSync(gitignorePath, "utf8").split(/\r?\n/);
91
- }
92
- for (const entry of required) {
93
- if (!lines.includes(entry)) lines.push(entry);
94
- }
95
- fs.writeFileSync(gitignorePath, `${lines.filter(Boolean).join("\n")}\n`, "utf8");
96
- }
97
-
98
- function stripOpsScripts(packageFile) {
99
- if (!fs.existsSync(packageFile)) return;
100
- const pkg = JSON.parse(fs.readFileSync(packageFile, "utf8"));
101
- if (!pkg.scripts) return;
102
- ["ops", "ops:help", "ops:dashboard", "ops:status", "ops:next", "ops:sync", "ops:repo"].forEach((key) => {
103
- delete pkg.scripts[key];
104
- });
105
- fs.writeFileSync(packageFile, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
106
- }
107
-
108
- function moveEntry(fromPath, toPath) {
109
- if (!fs.existsSync(fromPath)) return;
110
- fs.mkdirSync(path.dirname(toPath), { recursive: true });
111
- try {
112
- fs.renameSync(fromPath, toPath);
113
- } catch (error) {
114
- if (!isRetryableMoveError(error)) throw error;
115
- const stat = fs.statSync(fromPath);
116
- if (stat.isDirectory()) {
117
- fs.cpSync(fromPath, toPath, { recursive: true, force: true });
118
- fs.rmSync(fromPath, { recursive: true, force: true });
119
- return;
120
- }
121
- fs.copyFileSync(fromPath, toPath);
122
- fs.rmSync(fromPath, { force: true });
123
- }
124
- }
125
-
126
- function shouldResumePartialMigration(root, manifest) {
127
- if (!fs.existsSync(path.join(root, config.WORKSPACE_MANIFEST))) return false;
128
- const excluded = new Set([
129
- ".git",
130
- config.WORKSPACE_MANIFEST,
131
- manifest.appDir,
132
- manifest.opsDir,
133
- ".env",
134
- ".env.example",
135
- ]);
136
- const hasPendingOps = OPS_ARTIFACTS.some((entry) => fs.existsSync(path.join(root, entry)));
137
- const hasPendingAppEntries = fs.readdirSync(root, { withFileTypes: true })
138
- .some((entry) => !excluded.has(entry.name));
139
- return hasPendingOps || hasPendingAppEntries;
140
- }
141
-
142
- function migrateWorkspace(rootDir, options = {}) {
143
- const resolved = config.resolveWorkspaceContext(rootDir);
144
- if (!resolved) {
145
- throw new Error("workspace migrate only applies to legacy TrackOps projects.");
146
- }
147
-
148
- const root = resolved.workspaceRoot;
149
- let manifest;
150
- if (resolved.layout === "legacy") {
151
- manifest = buildManifest();
152
- } else {
153
- manifest = config.loadWorkspaceManifest(resolved) || buildManifest();
154
- if (!shouldResumePartialMigration(root, manifest)) {
155
- throw new Error("workspace migrate only applies to legacy TrackOps projects.");
156
- }
157
- }
158
-
159
- assertCleanGit(root, options.allowDirty === true);
160
- const backupBranch = createBackupBranch(root);
161
-
162
- const appDir = path.join(root, manifest.appDir);
163
- const opsDir = path.join(root, manifest.opsDir);
164
- fs.mkdirSync(appDir, { recursive: true });
165
- fs.mkdirSync(opsDir, { recursive: true });
166
- config.saveWorkspaceManifest(config.createSplitContext(root, manifest), manifest);
167
-
168
- for (const entry of OPS_ARTIFACTS) {
169
- moveEntry(path.join(root, entry), path.join(opsDir, entry));
170
- }
171
-
172
- const excluded = new Set([
173
- ".git",
174
- config.WORKSPACE_MANIFEST,
175
- manifest.appDir,
176
- manifest.opsDir,
177
- ".env",
178
- ".env.example",
179
- ]);
180
-
181
- const topLevel = fs.readdirSync(root, { withFileTypes: true });
182
- for (const entry of topLevel) {
183
- if (excluded.has(entry.name)) continue;
184
- moveEntry(path.join(root, entry.name), path.join(appDir, entry.name));
185
- }
186
-
187
- ensureRootGitignore(root);
188
- stripOpsScripts(path.join(appDir, "package.json"));
189
-
190
- const context = config.createSplitContext(root, manifest);
191
- const control = config.loadControl(context);
192
- control.meta = control.meta || {};
193
- control.meta.workspace = {
194
- layout: "split",
195
- workspaceRoot: ".",
196
- appDir: manifest.appDir,
197
- opsDir: manifest.opsDir,
198
- developmentBranch: manifest.branches.development,
199
- publishBranch: manifest.branches.publish,
200
- publishMode: manifest.publish.mode,
201
- };
202
- control.meta.environment = {
203
- ...(control.meta.environment || {}),
204
- rootEnvFile: ".env",
205
- exampleFile: ".env.example",
206
- appBridgeFile: "app/.env",
207
- bridgeMode: "symlink-or-copy",
208
- requiredKeys: control.meta.environment?.requiredKeys || [],
209
- optionalKeys: control.meta.environment?.optionalKeys || [],
210
- lastAuditAt: control.meta.environment?.lastAuditAt || null,
211
- };
212
- config.saveControl(context, control);
213
- env.syncEnvironment(context, control);
214
- const controlApi = require("./control");
215
- controlApi.syncDocs(context, config.loadControl(context));
216
- controlApi.refreshRepoRuntime(context, { quiet: true });
217
-
218
- const hookConfig = git(["config", "core.hooksPath", "ops/.githooks"], root);
219
- if (hookConfig.status !== 0) {
220
- throw new Error(hookConfig.stderr || "Could not update git hooks path.");
221
- }
222
-
223
- registry.registerProject(root);
224
- return { root, backupBranch, context };
225
- }
226
-
10
+ const { t, setLocale } = require("./i18n");
11
+
12
+ const OPS_ARTIFACTS = [
13
+ "project_control.json",
14
+ "task_plan.md",
15
+ "progress.md",
16
+ "findings.md",
17
+ "genesis.md",
18
+ ".agent",
19
+ ".agents",
20
+ ".githooks",
21
+ ".tmp",
22
+ ];
23
+
24
+ function nowStamp() {
25
+ return new Date().toISOString().replace(/[:.]/g, "-");
26
+ }
27
+
28
+ function git(args, cwd) {
29
+ return spawnSync("git", args, { cwd, encoding: "utf8" });
30
+ }
31
+
32
+ function isIgnorableDirtyLine(line) {
33
+ const normalized = String(line || "").replace(/\\/g, "/");
34
+ return normalized.endsWith(".tmp/project-control-runtime.json");
35
+ }
36
+
37
+ function isRetryableMoveError(error) {
38
+ return ["EPERM", "EXDEV", "EBUSY", "ENOTEMPTY"].includes(error?.code);
39
+ }
40
+
41
+ function assertCleanGit(root, allowDirty = false) {
42
+ const status = git(["status", "--porcelain"], root);
43
+ const dirtyLines = String(status.stdout || "")
44
+ .split(/\r?\n/)
45
+ .filter(Boolean)
46
+ .filter((line) => !isIgnorableDirtyLine(line));
47
+ if (!allowDirty && dirtyLines.length > 0) {
48
+ throw new Error("Workspace migration requires a clean git worktree.");
49
+ }
50
+ }
51
+
52
+ function createBackupBranch(root) {
53
+ const name = `backup/trackops-workspace-${nowStamp()}`;
54
+ const result = git(["branch", name], root);
55
+ if (result.status !== 0) {
56
+ throw new Error(result.stderr || `Could not create backup branch ${name}`);
57
+ }
58
+ return name;
59
+ }
60
+
61
+ function buildManifest() {
62
+ return {
63
+ version: 1,
64
+ layout: "split",
65
+ appDir: config.DEFAULT_APP_DIR,
66
+ opsDir: config.DEFAULT_OPS_DIR,
67
+ env: {
68
+ rootFile: ".env",
69
+ exampleFile: ".env.example",
70
+ appBridgeFile: "app/.env",
71
+ bridgeMode: "symlink-or-copy",
72
+ },
73
+ branches: {
74
+ development: config.DEFAULT_DEV_BRANCH,
75
+ publish: config.DEFAULT_PUBLISH_BRANCH,
76
+ },
77
+ publish: {
78
+ mode: "subtree-flatten",
79
+ sourceDir: config.DEFAULT_APP_DIR,
80
+ includeRootFiles: [".env.example"],
81
+ requireCleanWorktree: true,
82
+ },
83
+ };
84
+ }
85
+
86
+ function ensureRootGitignore(workspaceRoot) {
87
+ const gitignorePath = path.join(workspaceRoot, ".gitignore");
88
+ const required = ["/.env", "/app/.env", "/ops/.tmp/"];
89
+ let lines = [];
90
+ if (fs.existsSync(gitignorePath)) {
91
+ lines = fs.readFileSync(gitignorePath, "utf8").split(/\r?\n/);
92
+ }
93
+ for (const entry of required) {
94
+ if (!lines.includes(entry)) lines.push(entry);
95
+ }
96
+ fs.writeFileSync(gitignorePath, `${lines.filter(Boolean).join("\n")}\n`, "utf8");
97
+ }
98
+
99
+ function stripOpsScripts(packageFile) {
100
+ if (!fs.existsSync(packageFile)) return;
101
+ const pkg = JSON.parse(fs.readFileSync(packageFile, "utf8"));
102
+ if (!pkg.scripts) return;
103
+ ["ops", "ops:help", "ops:dashboard", "ops:status", "ops:next", "ops:sync", "ops:repo"].forEach((key) => {
104
+ delete pkg.scripts[key];
105
+ });
106
+ fs.writeFileSync(packageFile, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
107
+ }
108
+
109
+ function moveEntry(fromPath, toPath) {
110
+ if (!fs.existsSync(fromPath)) return;
111
+ fs.mkdirSync(path.dirname(toPath), { recursive: true });
112
+ try {
113
+ fs.renameSync(fromPath, toPath);
114
+ } catch (error) {
115
+ if (!isRetryableMoveError(error)) throw error;
116
+ const stat = fs.statSync(fromPath);
117
+ if (stat.isDirectory()) {
118
+ fs.cpSync(fromPath, toPath, { recursive: true, force: true });
119
+ fs.rmSync(fromPath, { recursive: true, force: true });
120
+ return;
121
+ }
122
+ fs.copyFileSync(fromPath, toPath);
123
+ fs.rmSync(fromPath, { force: true });
124
+ }
125
+ }
126
+
127
+ function shouldResumePartialMigration(root, manifest) {
128
+ if (!fs.existsSync(path.join(root, config.WORKSPACE_MANIFEST))) return false;
129
+ const excluded = new Set([
130
+ ".git",
131
+ config.WORKSPACE_MANIFEST,
132
+ manifest.appDir,
133
+ manifest.opsDir,
134
+ ".env",
135
+ ".env.example",
136
+ ]);
137
+ const hasPendingOps = OPS_ARTIFACTS.some((entry) => fs.existsSync(path.join(root, entry)));
138
+ const hasPendingAppEntries = fs.readdirSync(root, { withFileTypes: true })
139
+ .some((entry) => !excluded.has(entry.name));
140
+ return hasPendingOps || hasPendingAppEntries;
141
+ }
142
+
143
+ function migrateWorkspace(rootDir, options = {}) {
144
+ const resolved = config.resolveWorkspaceContext(rootDir);
145
+ if (!resolved) {
146
+ throw new Error("workspace migrate only applies to legacy TrackOps projects.");
147
+ }
148
+
149
+ const root = resolved.workspaceRoot;
150
+ let manifest;
151
+ if (resolved.layout === "legacy") {
152
+ manifest = buildManifest();
153
+ } else {
154
+ manifest = config.loadWorkspaceManifest(resolved) || buildManifest();
155
+ if (!shouldResumePartialMigration(root, manifest)) {
156
+ throw new Error("workspace migrate only applies to legacy TrackOps projects.");
157
+ }
158
+ }
159
+
160
+ assertCleanGit(root, options.allowDirty === true);
161
+ const backupBranch = createBackupBranch(root);
162
+
163
+ const appDir = path.join(root, manifest.appDir);
164
+ const opsDir = path.join(root, manifest.opsDir);
165
+ fs.mkdirSync(appDir, { recursive: true });
166
+ fs.mkdirSync(opsDir, { recursive: true });
167
+ config.saveWorkspaceManifest(config.createSplitContext(root, manifest), manifest);
168
+
169
+ for (const entry of OPS_ARTIFACTS) {
170
+ moveEntry(path.join(root, entry), path.join(opsDir, entry));
171
+ }
172
+
173
+ const excluded = new Set([
174
+ ".git",
175
+ config.WORKSPACE_MANIFEST,
176
+ manifest.appDir,
177
+ manifest.opsDir,
178
+ ".env",
179
+ ".env.example",
180
+ ]);
181
+
182
+ const topLevel = fs.readdirSync(root, { withFileTypes: true });
183
+ for (const entry of topLevel) {
184
+ if (excluded.has(entry.name)) continue;
185
+ moveEntry(path.join(root, entry.name), path.join(appDir, entry.name));
186
+ }
187
+
188
+ ensureRootGitignore(root);
189
+ stripOpsScripts(path.join(appDir, "package.json"));
190
+
191
+ const context = config.createSplitContext(root, manifest);
192
+ const control = config.loadControl(context);
193
+ control.meta = control.meta || {};
194
+ control.meta.workspace = {
195
+ layout: "split",
196
+ workspaceRoot: ".",
197
+ appDir: manifest.appDir,
198
+ opsDir: manifest.opsDir,
199
+ developmentBranch: manifest.branches.development,
200
+ publishBranch: manifest.branches.publish,
201
+ publishMode: manifest.publish.mode,
202
+ };
203
+ control.meta.environment = {
204
+ ...(control.meta.environment || {}),
205
+ rootEnvFile: ".env",
206
+ exampleFile: ".env.example",
207
+ appBridgeFile: "app/.env",
208
+ bridgeMode: "symlink-or-copy",
209
+ requiredKeys: control.meta.environment?.requiredKeys || [],
210
+ optionalKeys: control.meta.environment?.optionalKeys || [],
211
+ lastAuditAt: control.meta.environment?.lastAuditAt || null,
212
+ };
213
+ config.saveControl(context, control);
214
+ env.syncEnvironment(context, control);
215
+ const controlApi = require("./control");
216
+ controlApi.syncDocs(context, config.loadControl(context));
217
+ controlApi.refreshRepoRuntime(context, { quiet: true });
218
+
219
+ const hookConfig = git(["config", "core.hooksPath", "ops/.githooks"], root);
220
+ if (hookConfig.status !== 0) {
221
+ throw new Error(hookConfig.stderr || "Could not update git hooks path.");
222
+ }
223
+
224
+ registry.registerProject(root);
225
+ return { root, backupBranch, context };
226
+ }
227
+
227
228
  function status(contextOrRoot) {
228
229
  const context = config.ensureContext(contextOrRoot);
229
- console.log("Workspace:");
230
- console.log(` Layout: ${context.layout}`);
231
- console.log(` Root: ${context.workspaceRoot}`);
232
- console.log(` App: ${context.appRoot}`);
233
- console.log(` Ops: ${context.opsRoot}`);
230
+ try {
231
+ setLocale(config.getLocale(config.loadControl(context)));
232
+ } catch (_error) {
233
+ setLocale("es");
234
+ }
235
+ console.log(t("workspace.status.title"));
236
+ console.log(t("workspace.status.layout", { value: context.layout }));
237
+ console.log(t("workspace.status.root", { path: context.workspaceRoot }));
238
+ console.log(t("workspace.status.app", { path: context.appRoot }));
239
+ console.log(t("workspace.status.ops", { path: context.opsRoot }));
234
240
  if (context.manifestFile) {
235
- console.log(` Manifest: ${context.manifestFile}`);
241
+ console.log(t("workspace.status.manifest", { path: context.manifestFile }));
236
242
  }
237
- console.log(` Control: ${context.controlFile}`);
243
+ console.log(t("workspace.status.control", { path: context.controlFile }));
238
244
  }
239
-
240
- function cmdStatus(root) {
241
- status(root);
242
- }
243
-
245
+
246
+ function cmdStatus(root) {
247
+ status(root);
248
+ }
249
+
244
250
  function cmdMigrate(root, args = []) {
245
251
  const allowDirty = args.includes("--allow-dirty");
246
252
  const result = migrateWorkspace(root, { allowDirty });
247
- console.log(`Workspace migrated: ${result.root}`);
248
- console.log(`Backup branch: ${result.backupBranch}`);
253
+ try {
254
+ setLocale(config.getLocale(config.loadControl(result.context)));
255
+ } catch (_error) {
256
+ setLocale("es");
257
+ }
258
+ console.log(t("workspace.migrate.updated", { path: result.root }));
259
+ console.log(t("workspace.migrate.backup", { branch: result.backupBranch }));
249
260
  }
250
-
251
- module.exports = {
252
- OPS_ARTIFACTS,
253
- buildManifest,
254
- ensureRootGitignore,
255
- stripOpsScripts,
256
- migrateWorkspace,
257
- status,
258
- cmdStatus,
259
- cmdMigrate,
260
- };
261
+
262
+ module.exports = {
263
+ OPS_ARTIFACTS,
264
+ buildManifest,
265
+ ensureRootGitignore,
266
+ stripOpsScripts,
267
+ migrateWorkspace,
268
+ status,
269
+ cmdStatus,
270
+ cmdMigrate,
271
+ };