trackops 1.0.0 → 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 (72) hide show
  1. package/README.md +341 -232
  2. package/bin/trackops.js +102 -70
  3. package/lib/config.js +260 -35
  4. package/lib/control.js +518 -475
  5. package/lib/env.js +227 -0
  6. package/lib/i18n.js +61 -53
  7. package/lib/init.js +146 -55
  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 +912 -418
  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 +14 -3
  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/etapa/agent.md +2 -2
  32. package/templates/etapa/references/etapa-cycle.md +1 -1
  33. package/templates/opera/agent.md +1 -1
  34. package/templates/opera/en/agent.md +26 -0
  35. package/templates/opera/en/genesis.md +79 -0
  36. package/templates/opera/en/references/autonomy-and-recovery.md +23 -0
  37. package/templates/opera/en/references/opera-cycle.md +62 -0
  38. package/templates/opera/en/registry.md +28 -0
  39. package/templates/opera/en/router.md +39 -0
  40. package/templates/opera/genesis.md +79 -94
  41. package/templates/skills/changelog-updater/locales/en/SKILL.md +11 -0
  42. package/templates/skills/commiter/locales/en/SKILL.md +11 -0
  43. package/templates/skills/project-starter-skill/SKILL.md +5 -3
  44. package/templates/skills/project-starter-skill/locales/en/SKILL.md +24 -0
  45. package/ui/css/base.css +266 -0
  46. package/ui/css/charts.css +327 -0
  47. package/ui/css/components.css +570 -0
  48. package/ui/css/panels.css +956 -0
  49. package/ui/css/tokens.css +227 -0
  50. package/ui/favicon.svg +5 -0
  51. package/ui/index.html +91 -351
  52. package/ui/js/api.js +220 -0
  53. package/ui/js/app.js +200 -0
  54. package/ui/js/console-logger.js +172 -0
  55. package/ui/js/i18n.js +14 -0
  56. package/ui/js/icons.js +104 -0
  57. package/ui/js/onboarding.js +439 -0
  58. package/ui/js/router.js +125 -0
  59. package/ui/js/state.js +130 -0
  60. package/ui/js/theme.js +100 -0
  61. package/ui/js/time-tracker.js +248 -0
  62. package/ui/js/utils.js +175 -0
  63. package/ui/js/views/board.js +255 -0
  64. package/ui/js/views/execution.js +256 -0
  65. package/ui/js/views/flash.js +47 -0
  66. package/ui/js/views/insights.js +340 -0
  67. package/ui/js/views/overview.js +365 -0
  68. package/ui/js/views/settings.js +381 -0
  69. package/ui/js/views/sidebar.js +131 -0
  70. package/ui/js/views/skills.js +163 -0
  71. package/ui/js/views/tasks.js +406 -0
  72. package/ui/js/views/topbar.js +239 -0
package/bin/trackops.js CHANGED
@@ -1,75 +1,101 @@
1
1
  #!/usr/bin/env node
2
-
2
+
3
3
  const path = require("path");
4
4
  const config = require("../lib/config");
5
-
6
- const command = process.argv[2];
7
- const args = process.argv.slice(3);
8
-
5
+ const pkg = require("../package.json");
6
+
7
+ const command = process.argv[2];
8
+ const args = process.argv.slice(3);
9
+
9
10
  function resolveRoot() {
10
- const root = config.resolveProjectRoot();
11
- if (!root) {
12
- console.error("No project_control.json found in this directory or any parent.");
11
+ const context = config.resolveWorkspaceContext();
12
+ if (!context) {
13
+ console.error("No TrackOps workspace found in this directory or any parent.");
13
14
  process.exit(1);
14
15
  }
15
- return root;
16
+ return context.workspaceRoot;
16
17
  }
17
-
18
- function run() {
19
- try {
20
- switch (command) {
21
- case "init":
22
- require("../lib/init").cmdInit(args);
23
- break;
24
-
25
- case "status":
26
- require("../lib/control").cmdStatus(resolveRoot());
27
- break;
28
-
29
- case "next":
30
- require("../lib/control").cmdNext(resolveRoot());
31
- break;
32
-
33
- case "sync":
34
- require("../lib/control").cmdSync(resolveRoot());
35
- break;
36
-
37
- case "dashboard":
38
- require("../lib/server").run();
39
- break;
40
-
41
- case "refresh-repo":
42
- require("../lib/control").cmdRefreshRepo(resolveRoot(), args);
43
- break;
44
-
45
- case "install-hooks":
46
- require("../lib/control").cmdInstallHooks(resolveRoot());
47
- break;
48
-
49
- case "task":
50
- require("../lib/control").cmdTask(resolveRoot(), args);
51
- break;
52
-
18
+
19
+ async function run() {
20
+ try {
21
+ switch (command) {
22
+ case "init":
23
+ await require("../lib/init").cmdInit(args);
24
+ break;
25
+
26
+ case "status":
27
+ require("../lib/control").cmdStatus(resolveRoot());
28
+ break;
29
+
30
+ case "next":
31
+ require("../lib/control").cmdNext(resolveRoot());
32
+ break;
33
+
34
+ case "sync":
35
+ require("../lib/control").cmdSync(resolveRoot());
36
+ break;
37
+
38
+ case "dashboard":
39
+ await require("../lib/server").run(args);
40
+ break;
41
+
42
+ case "refresh-repo":
43
+ require("../lib/control").cmdRefreshRepo(resolveRoot(), args);
44
+ break;
45
+
46
+ case "install-hooks":
47
+ require("../lib/control").cmdInstallHooks(resolveRoot());
48
+ break;
49
+
50
+ case "task":
51
+ require("../lib/control").cmdTask(resolveRoot(), args);
52
+ break;
53
+
53
54
  case "register":
54
55
  require("../lib/registry").cmdRegister(config.resolveProjectRoot() || process.cwd());
55
56
  break;
56
-
57
- case "projects":
58
- require("../lib/registry").cmdList();
57
+
58
+ case "projects":
59
+ require("../lib/registry").cmdList();
60
+ break;
61
+
62
+ case "workspace": {
63
+ const workspace = require("../lib/workspace");
64
+ const sub = args[0];
65
+ const root = config.resolveProjectRoot() || process.cwd();
66
+ if (sub === "status") workspace.cmdStatus(root);
67
+ else if (sub === "migrate") workspace.cmdMigrate(root, args.slice(1));
68
+ else console.log("Usage: trackops workspace <status|migrate>");
59
69
  break;
70
+ }
60
71
 
61
- case "opera": {
62
- const opera = require("../lib/opera");
72
+ case "env": {
73
+ const env = require("../lib/env");
63
74
  const sub = args[0];
64
75
  const root = config.resolveProjectRoot() || process.cwd();
65
- if (sub === "install") opera.cmdInstall(root, args.slice(1));
66
- else if (sub === "status") opera.cmdStatus(root);
67
- else if (sub === "configure") opera.cmdConfigure(root, args.slice(1));
68
- else if (sub === "upgrade") opera.cmdUpgrade(root);
69
- else { console.log("Usage: trackops opera <install|status|configure|upgrade>"); }
76
+ if (sub === "status") env.cmdStatus(root);
77
+ else if (sub === "sync") env.cmdSync(root);
78
+ else console.log("Usage: trackops env <status|sync>");
70
79
  break;
71
80
  }
72
81
 
82
+ case "release":
83
+ require("../lib/release").cmdRelease(config.resolveProjectRoot() || process.cwd(), args);
84
+ break;
85
+
86
+ case "opera": {
87
+ const opera = require("../lib/opera");
88
+ const sub = args[0];
89
+ const root = config.resolveProjectRoot() || process.cwd();
90
+ if (sub === "install") await opera.cmdInstall(root, args.slice(1));
91
+ else if (sub === "bootstrap") await opera.cmdBootstrap(root, args.slice(1));
92
+ else if (sub === "status") opera.cmdStatus(root);
93
+ else if (sub === "configure") opera.cmdConfigure(root, args.slice(1));
94
+ else if (sub === "upgrade") opera.cmdUpgrade(root);
95
+ else { console.log("Usage: trackops opera <install|bootstrap|status|configure|upgrade>"); }
96
+ break;
97
+ }
98
+
73
99
  case "skill": {
74
100
  const skills = require("../lib/skills");
75
101
  const sub = args[0];
@@ -82,22 +108,28 @@ function run() {
82
108
  break;
83
109
  }
84
110
 
111
+ case "version":
112
+ case "--version":
113
+ case "-v":
114
+ console.log(pkg.version);
115
+ break;
116
+
85
117
  case "help":
86
118
  case "--help":
87
119
  case "-h":
88
120
  case undefined:
89
- require("../lib/control").cmdHelp();
90
- break;
91
-
92
- default:
93
- console.error(`Unknown command: ${command}`);
94
- console.error("Run 'trackops help' for usage.");
95
- process.exit(1);
96
- }
97
- } catch (error) {
98
- console.error(error.message);
99
- process.exit(1);
100
- }
101
- }
102
-
103
- run();
121
+ require("../lib/control").cmdHelp();
122
+ break;
123
+
124
+ default:
125
+ console.error(`Unknown command: ${command}`);
126
+ console.error("Run 'trackops help' for usage.");
127
+ process.exit(1);
128
+ }
129
+ } catch (error) {
130
+ console.error(error.message);
131
+ process.exit(1);
132
+ }
133
+ }
134
+
135
+ run();
package/lib/config.js CHANGED
@@ -2,57 +2,249 @@
2
2
 
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
+ const { normalizeLocale } = require("./locale");
5
6
 
6
- const DEFAULT_PHASES = [
7
- { id: "O", label: "Orquestar", index: 1 },
8
- { id: "P", label: "Probar", index: 2 },
9
- { id: "E", label: "Estructurar", index: 3 },
10
- { id: "R", label: "Refinar", index: 4 },
11
- { id: "A", label: "Automatizar", index: 5 },
12
- ];
7
+ const DEFAULT_PHASE_IDS = ["O", "P", "E", "R", "A"];
8
+ const DEFAULT_PHASE_LABELS = {
9
+ es: {
10
+ O: "Orquestar",
11
+ P: "Probar",
12
+ E: "Estructurar",
13
+ R: "Refinar",
14
+ A: "Automatizar",
15
+ },
16
+ en: {
17
+ O: "Orchestrate",
18
+ P: "Prove",
19
+ E: "Establish",
20
+ R: "Refine",
21
+ A: "Automate",
22
+ },
23
+ };
13
24
 
14
25
  const DEFAULT_LOCALE = "es";
26
+ const WORKSPACE_MANIFEST = ".trackops-workspace.json";
27
+ const DEFAULT_APP_DIR = "app";
28
+ const DEFAULT_OPS_DIR = "ops";
29
+ const DEFAULT_DEV_BRANCH = "develop";
30
+ const DEFAULT_PUBLISH_BRANCH = "master";
15
31
 
16
- function resolveProjectRoot(startDir) {
32
+ function buildDefaultPhases(locale) {
33
+ const normalized = normalizeLocale(locale) || DEFAULT_LOCALE;
34
+ const labels = DEFAULT_PHASE_LABELS[normalized] || DEFAULT_PHASE_LABELS[DEFAULT_LOCALE];
35
+ return DEFAULT_PHASE_IDS.map((id, index) => ({
36
+ id,
37
+ label: labels[id],
38
+ index: index + 1,
39
+ }));
40
+ }
41
+
42
+ const DEFAULT_PHASES = buildDefaultPhases(DEFAULT_LOCALE);
43
+
44
+ function fileExists(filePath) {
45
+ try {
46
+ return fs.existsSync(filePath);
47
+ } catch (_error) {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ function readJson(filePath, fallback = null) {
53
+ if (!fileExists(filePath)) return fallback;
54
+ try {
55
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
56
+ } catch (_error) {
57
+ return fallback;
58
+ }
59
+ }
60
+
61
+ function createSplitContext(workspaceRoot, manifest = {}) {
62
+ const appDir = manifest.appDir || DEFAULT_APP_DIR;
63
+ const opsDir = manifest.opsDir || DEFAULT_OPS_DIR;
64
+ const workspace = path.resolve(workspaceRoot);
65
+ const appRoot = path.join(workspace, appDir);
66
+ const opsRoot = path.join(workspace, opsDir);
67
+ const env = manifest.env || {};
68
+
69
+ return {
70
+ layout: "split",
71
+ workspaceRoot: workspace,
72
+ root: workspace,
73
+ projectRoot: workspace,
74
+ appRoot,
75
+ opsRoot,
76
+ manifestFile: path.join(workspace, WORKSPACE_MANIFEST),
77
+ packageFile: path.join(appRoot, "package.json"),
78
+ controlFile: path.join(opsRoot, "project_control.json"),
79
+ runtimeFile: path.join(opsRoot, ".tmp", "project-control-runtime.json"),
80
+ docs: {
81
+ taskPlan: path.join(opsRoot, "task_plan.md"),
82
+ progress: path.join(opsRoot, "progress.md"),
83
+ findings: path.join(opsRoot, "findings.md"),
84
+ },
85
+ paths: {
86
+ taskPlan: path.join(opsRoot, "task_plan.md"),
87
+ progress: path.join(opsRoot, "progress.md"),
88
+ findings: path.join(opsRoot, "findings.md"),
89
+ hooksDir: path.join(opsRoot, ".githooks"),
90
+ tmpDir: path.join(opsRoot, ".tmp"),
91
+ skillsDir: path.join(opsRoot, ".agents", "skills"),
92
+ registryPath: path.join(opsRoot, ".agents", "skills", "_registry.md"),
93
+ agentHubDir: path.join(opsRoot, ".agent", "hub"),
94
+ genesisFile: path.join(opsRoot, "genesis.md"),
95
+ },
96
+ env: {
97
+ rootFile: path.join(workspace, env.rootFile || ".env"),
98
+ exampleFile: path.join(workspace, env.exampleFile || ".env.example"),
99
+ appBridgeFile: path.join(workspace, env.appBridgeFile || path.join(appDir, ".env")),
100
+ appExampleBridgeFile: path.join(appRoot, ".env.example"),
101
+ bridgeMode: env.bridgeMode || "symlink-or-copy",
102
+ },
103
+ branches: {
104
+ development: manifest.branches?.development || DEFAULT_DEV_BRANCH,
105
+ publish: manifest.branches?.publish || DEFAULT_PUBLISH_BRANCH,
106
+ },
107
+ publish: {
108
+ mode: manifest.publish?.mode || "subtree-flatten",
109
+ sourceDir: manifest.publish?.sourceDir || appDir,
110
+ includeRootFiles: Array.isArray(manifest.publish?.includeRootFiles)
111
+ ? manifest.publish.includeRootFiles
112
+ : [".env.example"],
113
+ requireCleanWorktree: manifest.publish?.requireCleanWorktree !== false,
114
+ },
115
+ };
116
+ }
117
+
118
+ function createLegacyContext(rootDir) {
119
+ const root = path.resolve(rootDir);
120
+ return {
121
+ layout: "legacy",
122
+ workspaceRoot: root,
123
+ root,
124
+ projectRoot: root,
125
+ appRoot: root,
126
+ opsRoot: root,
127
+ manifestFile: null,
128
+ packageFile: path.join(root, "package.json"),
129
+ controlFile: path.join(root, "project_control.json"),
130
+ runtimeFile: path.join(root, ".tmp", "project-control-runtime.json"),
131
+ docs: {
132
+ taskPlan: path.join(root, "task_plan.md"),
133
+ progress: path.join(root, "progress.md"),
134
+ findings: path.join(root, "findings.md"),
135
+ },
136
+ paths: {
137
+ taskPlan: path.join(root, "task_plan.md"),
138
+ progress: path.join(root, "progress.md"),
139
+ findings: path.join(root, "findings.md"),
140
+ hooksDir: path.join(root, ".githooks"),
141
+ tmpDir: path.join(root, ".tmp"),
142
+ skillsDir: path.join(root, ".agents", "skills"),
143
+ registryPath: path.join(root, ".agents", "skills", "_registry.md"),
144
+ agentHubDir: path.join(root, ".agent", "hub"),
145
+ genesisFile: path.join(root, "genesis.md"),
146
+ },
147
+ env: {
148
+ rootFile: path.join(root, ".env"),
149
+ exampleFile: path.join(root, ".env.example"),
150
+ appBridgeFile: path.join(root, ".env"),
151
+ appExampleBridgeFile: path.join(root, ".env.example"),
152
+ bridgeMode: "none",
153
+ },
154
+ branches: {
155
+ development: DEFAULT_DEV_BRANCH,
156
+ publish: DEFAULT_PUBLISH_BRANCH,
157
+ },
158
+ publish: {
159
+ mode: "legacy",
160
+ sourceDir: ".",
161
+ includeRootFiles: [".env.example"],
162
+ requireCleanWorktree: true,
163
+ },
164
+ };
165
+ }
166
+
167
+ function resolveWorkspaceContext(startDir) {
17
168
  let dir = path.resolve(startDir || process.cwd());
18
169
  const root = path.parse(dir).root;
19
- while (dir !== root) {
20
- if (fs.existsSync(path.join(dir, "project_control.json"))) {
21
- return dir;
170
+ let legacyCandidate = null;
171
+
172
+ while (true) {
173
+ const manifestFile = path.join(dir, WORKSPACE_MANIFEST);
174
+ if (fileExists(manifestFile)) {
175
+ return createSplitContext(dir, readJson(manifestFile, {}) || {});
176
+ }
177
+
178
+ if (!legacyCandidate && fileExists(path.join(dir, "project_control.json"))) {
179
+ legacyCandidate = dir;
22
180
  }
181
+
182
+ if (dir === root) break;
23
183
  dir = path.dirname(dir);
24
184
  }
185
+
186
+ if (legacyCandidate) {
187
+ return createLegacyContext(legacyCandidate);
188
+ }
189
+
25
190
  return null;
26
191
  }
27
192
 
28
- function controlFilePath(root) {
29
- return path.join(root, "project_control.json");
193
+ function ensureContext(contextOrRoot) {
194
+ if (!contextOrRoot) return resolveWorkspaceContext(process.cwd());
195
+ if (typeof contextOrRoot === "object" && contextOrRoot.workspaceRoot) return contextOrRoot;
196
+ const resolved = resolveWorkspaceContext(contextOrRoot);
197
+ if (resolved) return resolved;
198
+ return createLegacyContext(contextOrRoot);
30
199
  }
31
200
 
32
- function runtimeFilePath(root) {
33
- return path.join(root, ".tmp", "project-control-runtime.json");
201
+ function resolveProjectRoot(startDir) {
202
+ const context = resolveWorkspaceContext(startDir);
203
+ return context ? context.workspaceRoot : null;
34
204
  }
35
205
 
36
- function docFilePaths(root) {
37
- return {
38
- taskPlan: path.join(root, "task_plan.md"),
39
- progress: path.join(root, "progress.md"),
40
- findings: path.join(root, "findings.md"),
41
- };
206
+ function controlFilePath(contextOrRoot) {
207
+ return ensureContext(contextOrRoot).controlFile;
208
+ }
209
+
210
+ function runtimeFilePath(contextOrRoot) {
211
+ return ensureContext(contextOrRoot).runtimeFile;
212
+ }
213
+
214
+ function docFilePaths(contextOrRoot) {
215
+ return ensureContext(contextOrRoot).docs;
216
+ }
217
+
218
+ function envFilePaths(contextOrRoot) {
219
+ return ensureContext(contextOrRoot).env;
220
+ }
221
+
222
+ function packageFilePath(contextOrRoot) {
223
+ return ensureContext(contextOrRoot).packageFile;
42
224
  }
43
225
 
44
- function getPhases(control) {
45
- if (
46
- Array.isArray(control.meta?.phases) &&
47
- control.meta.phases.length > 0
48
- ) {
226
+ function workspaceManifestPath(contextOrRoot) {
227
+ return ensureContext(contextOrRoot).manifestFile;
228
+ }
229
+
230
+ function isDefaultPhaseShape(phases) {
231
+ if (!Array.isArray(phases) || phases.length !== DEFAULT_PHASE_IDS.length) return false;
232
+ return phases.every((phase, index) => phase?.id === DEFAULT_PHASE_IDS[index]);
233
+ }
234
+
235
+ function getPhases(control, localeOverride) {
236
+ const locale = normalizeLocale(localeOverride || control.meta?.locale) || DEFAULT_LOCALE;
237
+ if (Array.isArray(control.meta?.phases) && control.meta.phases.length > 0) {
238
+ if (isDefaultPhaseShape(control.meta.phases)) {
239
+ return buildDefaultPhases(locale);
240
+ }
49
241
  return control.meta.phases;
50
242
  }
51
- return DEFAULT_PHASES;
243
+ return buildDefaultPhases(locale);
52
244
  }
53
245
 
54
246
  function getLocale(control) {
55
- return control.meta?.locale || DEFAULT_LOCALE;
247
+ return normalizeLocale(control.meta?.locale) || DEFAULT_LOCALE;
56
248
  }
57
249
 
58
250
  function isOperaInstalled(control) {
@@ -63,29 +255,62 @@ function getOperaVersion(control) {
63
255
  return control.meta?.opera?.version || null;
64
256
  }
65
257
 
66
- // Backwards-compat aliases
67
- function isEtapaInstalled(control) { return isOperaInstalled(control); }
68
- function getEtapaVersion(control) { return getOperaVersion(control); }
258
+ function isEtapaInstalled(control) {
259
+ return isOperaInstalled(control);
260
+ }
261
+
262
+ function getEtapaVersion(control) {
263
+ return getOperaVersion(control);
264
+ }
69
265
 
70
- function loadControl(root) {
71
- const filePath = controlFilePath(root);
266
+ function loadControl(contextOrRoot) {
267
+ const filePath = controlFilePath(contextOrRoot);
72
268
  return JSON.parse(fs.readFileSync(filePath, "utf8"));
73
269
  }
74
270
 
75
- function saveControl(root, control) {
271
+ function saveControl(contextOrRoot, control) {
76
272
  control.meta = control.meta || {};
77
273
  control.meta.updatedAt = new Date().toISOString();
78
- const filePath = controlFilePath(root);
274
+ const filePath = controlFilePath(contextOrRoot);
275
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
79
276
  fs.writeFileSync(filePath, JSON.stringify(control, null, 2) + "\n", "utf8");
80
277
  }
81
278
 
279
+ function loadWorkspaceManifest(contextOrRoot) {
280
+ const context = ensureContext(contextOrRoot);
281
+ if (!context.manifestFile || !fileExists(context.manifestFile)) return null;
282
+ return readJson(context.manifestFile, null);
283
+ }
284
+
285
+ function saveWorkspaceManifest(contextOrRoot, manifest) {
286
+ const context = ensureContext(contextOrRoot);
287
+ const manifestFile = context.manifestFile || path.join(context.workspaceRoot, WORKSPACE_MANIFEST);
288
+ fs.writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
289
+ }
290
+
82
291
  module.exports = {
83
292
  DEFAULT_PHASES,
293
+ DEFAULT_PHASE_IDS,
84
294
  DEFAULT_LOCALE,
295
+ DEFAULT_APP_DIR,
296
+ DEFAULT_OPS_DIR,
297
+ DEFAULT_DEV_BRANCH,
298
+ DEFAULT_PUBLISH_BRANCH,
299
+ WORKSPACE_MANIFEST,
300
+ buildDefaultPhases,
301
+ createSplitContext,
302
+ createLegacyContext,
303
+ resolveWorkspaceContext,
85
304
  resolveProjectRoot,
305
+ ensureContext,
86
306
  controlFilePath,
87
307
  runtimeFilePath,
88
308
  docFilePaths,
309
+ envFilePaths,
310
+ packageFilePath,
311
+ workspaceManifestPath,
312
+ loadWorkspaceManifest,
313
+ saveWorkspaceManifest,
89
314
  getPhases,
90
315
  getLocale,
91
316
  isOperaInstalled,