orbital-command 0.3.0 → 1.0.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.
- package/README.md +67 -42
- package/bin/commands/config.js +19 -0
- package/bin/commands/events.js +40 -0
- package/bin/commands/launch.js +126 -0
- package/bin/commands/manifest.js +283 -0
- package/bin/commands/registry.js +104 -0
- package/bin/commands/update.js +24 -0
- package/bin/lib/helpers.js +229 -0
- package/bin/orbital.js +95 -870
- package/dist/assets/Landing-CfQdHR0N.js +11 -0
- package/dist/assets/PrimitivesConfig-DThSipFy.js +32 -0
- package/dist/assets/QualityGates-B4kxM5UU.js +26 -0
- package/dist/assets/SessionTimeline-Bz1iZnmg.js +1 -0
- package/dist/assets/Settings-DLcZwbCT.js +12 -0
- package/dist/assets/SourceControl-BMNIz7Lt.js +36 -0
- package/dist/assets/WorkflowVisualizer-CxuSBOYu.js +69 -0
- package/dist/assets/{arrow-down-CPy85_J6.js → arrow-down-DVPp6_qp.js} +1 -1
- package/dist/assets/bot-NFaJBDn_.js +6 -0
- package/dist/assets/{charts-DbDg0Psc.js → charts-LGLb8hyU.js} +1 -1
- package/dist/assets/{circle-x-Cwz6ZQDV.js → circle-x-IsFCkBZu.js} +1 -1
- package/dist/assets/{file-text-C46Xr65c.js → file-text-J1cebZXF.js} +1 -1
- package/dist/assets/{globe-Cn2yNZUD.js → globe-WzeyHsUc.js} +1 -1
- package/dist/assets/index-BdJ57EhC.css +1 -0
- package/dist/assets/index-o4ScMAuR.js +349 -0
- package/dist/assets/{key-OPaNTWJ5.js → key-CKR8JJSj.js} +1 -1
- package/dist/assets/{minus-GMsbpKym.js → minus-CHBsJyjp.js} +1 -1
- package/dist/assets/radio-xqZaR-Uk.js +6 -0
- package/dist/assets/rocket-D_xvvNG6.js +6 -0
- package/dist/assets/{shield-DwAFkDYI.js → shield-TdB1yv_a.js} +1 -1
- package/dist/assets/useSocketListener-0L5yiN5i.js +1 -0
- package/dist/assets/useWorkflowEditor-CqeRWVQX.js +11 -0
- package/dist/assets/workflow-constants-Rw-GmgHZ.js +6 -0
- package/dist/assets/zap-C9wqYMpl.js +6 -0
- package/dist/index.html +3 -3
- package/dist/server/server/__tests__/data-routes.test.js +2 -0
- package/dist/server/server/__tests__/scope-routes.test.js +1 -0
- package/dist/server/server/config-migrator.js +0 -3
- package/dist/server/server/config.js +35 -6
- package/dist/server/server/database.js +0 -22
- package/dist/server/server/index.js +26 -814
- package/dist/server/server/init.js +32 -399
- package/dist/server/server/launch.js +1 -1
- package/dist/server/server/parsers/event-parser.js +4 -1
- package/dist/server/server/project-context.js +19 -9
- package/dist/server/server/project-manager.js +6 -6
- package/dist/server/server/routes/aggregate-routes.js +871 -0
- package/dist/server/server/routes/config-routes.js +41 -88
- package/dist/server/server/routes/data-routes.js +5 -15
- package/dist/server/server/routes/dispatch-routes.js +24 -8
- package/dist/server/server/routes/manifest-routes.js +1 -1
- package/dist/server/server/routes/scope-routes.js +10 -7
- package/dist/server/server/schema.js +1 -0
- package/dist/server/server/services/batch-orchestrator.js +17 -3
- package/dist/server/server/services/config-service.js +10 -1
- package/dist/server/server/services/scope-service.js +7 -7
- package/dist/server/server/services/sprint-orchestrator.js +24 -11
- package/dist/server/server/services/sprint-service.js +2 -2
- package/dist/server/server/uninstall.js +195 -0
- package/dist/server/server/update.js +212 -0
- package/dist/server/server/utils/dispatch-utils.js +8 -6
- package/dist/server/server/utils/flag-builder.js +54 -0
- package/dist/server/server/utils/json-fields.js +14 -0
- package/dist/server/server/utils/json-fields.test.js +73 -0
- package/dist/server/server/utils/route-helpers.js +37 -0
- package/dist/server/server/utils/route-helpers.test.js +115 -0
- package/dist/server/server/watchers/event-watcher.js +28 -13
- package/dist/server/server/wizard/config-editor.js +4 -4
- package/dist/server/server/wizard/doctor.js +2 -2
- package/dist/server/server/wizard/index.js +224 -39
- package/dist/server/server/wizard/phases/welcome.js +1 -4
- package/dist/server/server/wizard/ui.js +6 -7
- package/dist/server/shared/api-types.js +80 -1
- package/dist/server/shared/workflow-engine.js +1 -1
- package/package.json +20 -20
- package/schemas/orbital.config.schema.json +1 -19
- package/scripts/postinstall.js +6 -42
- package/scripts/release.sh +53 -0
- package/server/__tests__/data-routes.test.ts +2 -0
- package/server/__tests__/scope-routes.test.ts +1 -0
- package/server/config-migrator.ts +0 -3
- package/server/config.ts +39 -11
- package/server/database.ts +0 -26
- package/server/global-config.ts +4 -0
- package/server/index.ts +29 -894
- package/server/init.ts +32 -443
- package/server/launch.ts +1 -1
- package/server/parsers/event-parser.ts +4 -1
- package/server/project-context.ts +26 -10
- package/server/project-manager.ts +5 -6
- package/server/routes/aggregate-routes.ts +968 -0
- package/server/routes/config-routes.ts +41 -81
- package/server/routes/data-routes.ts +7 -16
- package/server/routes/dispatch-routes.ts +29 -8
- package/server/routes/manifest-routes.ts +1 -1
- package/server/routes/scope-routes.ts +12 -7
- package/server/schema.ts +1 -0
- package/server/services/batch-orchestrator.ts +18 -2
- package/server/services/config-service.ts +10 -1
- package/server/services/scope-service.ts +6 -6
- package/server/services/sprint-orchestrator.ts +24 -9
- package/server/services/sprint-service.ts +2 -2
- package/server/uninstall.ts +214 -0
- package/server/update.ts +263 -0
- package/server/utils/dispatch-utils.ts +8 -6
- package/server/utils/flag-builder.ts +56 -0
- package/server/utils/json-fields.test.ts +83 -0
- package/server/utils/json-fields.ts +14 -0
- package/server/utils/route-helpers.test.ts +144 -0
- package/server/utils/route-helpers.ts +38 -0
- package/server/watchers/event-watcher.ts +24 -12
- package/server/wizard/config-editor.ts +4 -4
- package/server/wizard/doctor.ts +2 -2
- package/server/wizard/index.ts +291 -40
- package/server/wizard/phases/welcome.ts +1 -5
- package/server/wizard/ui.ts +6 -7
- package/shared/api-types.ts +106 -0
- package/shared/workflow-engine.ts +1 -1
- package/templates/agents/QUICK-REFERENCE.md +1 -0
- package/templates/agents/README.md +1 -0
- package/templates/agents/SKILL-TRIGGERS.md +11 -0
- package/templates/agents/green-team/deep-dive.md +361 -0
- package/templates/hooks/end-session.sh +1 -0
- package/templates/hooks/init-session.sh +1 -0
- package/templates/hooks/scope-commit-logger.sh +2 -2
- package/templates/hooks/scope-create-gate.sh +2 -4
- package/templates/hooks/scope-gate.sh +4 -6
- package/templates/hooks/scope-helpers.sh +10 -1
- package/templates/hooks/scope-lifecycle-gate.sh +14 -5
- package/templates/hooks/scope-prepare.sh +1 -1
- package/templates/hooks/scope-transition.sh +14 -6
- package/templates/hooks/time-tracker.sh +2 -5
- package/templates/orbital.config.json +1 -4
- package/templates/presets/development.json +4 -4
- package/templates/presets/gitflow.json +7 -0
- package/templates/prompts/README.md +23 -0
- package/templates/prompts/deep-dive-audit.md +94 -0
- package/templates/quick/rules.md +56 -5
- package/templates/skills/git-commit/SKILL.md +21 -6
- package/templates/skills/git-dev/SKILL.md +8 -4
- package/templates/skills/git-main/SKILL.md +8 -4
- package/templates/skills/git-production/SKILL.md +6 -3
- package/templates/skills/git-staging/SKILL.md +6 -3
- package/templates/skills/scope-fix-review/SKILL.md +8 -4
- package/templates/skills/scope-implement/SKILL.md +13 -5
- package/templates/skills/scope-post-review/SKILL.md +16 -4
- package/templates/skills/scope-pre-review/SKILL.md +6 -2
- package/dist/assets/PrimitivesConfig-CrmQXYh4.js +0 -32
- package/dist/assets/QualityGates-BbasOsF3.js +0 -21
- package/dist/assets/SessionTimeline-CGeJsVvy.js +0 -1
- package/dist/assets/Settings-oiM496mc.js +0 -12
- package/dist/assets/SourceControl-B1fP2nJL.js +0 -41
- package/dist/assets/WorkflowVisualizer-CWLYf-f0.js +0 -74
- package/dist/assets/formatDistanceToNow-BMqsSP44.js +0 -1
- package/dist/assets/index-Aj4sV8Al.css +0 -1
- package/dist/assets/index-Bc9dK3MW.js +0 -354
- package/dist/assets/useWorkflowEditor-BJkTX_NR.js +0 -16
- package/dist/assets/zap-DfbUoOty.js +0 -11
- package/dist/server/server/services/telemetry-service.js +0 -143
- package/server/services/telemetry-service.ts +0 -195
- /package/{shared/default-workflow.json → templates/presets/default.json} +0 -0
package/bin/orbital.js
CHANGED
|
@@ -2,876 +2,118 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import {
|
|
6
|
+
detectProjectRoot,
|
|
7
|
+
getPackageVersion,
|
|
8
|
+
isGitRepo,
|
|
9
|
+
requireGitRepo,
|
|
10
|
+
loadRegistry,
|
|
11
|
+
writeRegistryAtomic,
|
|
12
|
+
loadSharedModule,
|
|
13
|
+
loadWizardModule,
|
|
14
|
+
orbitalSetupDone,
|
|
15
|
+
stampTemplateVersion,
|
|
16
|
+
printHelp,
|
|
17
|
+
} from './lib/helpers.js';
|
|
18
|
+
|
|
19
|
+
import { cmdLaunchOrDev, cmdBuild } from './commands/launch.js';
|
|
20
|
+
import { cmdStatus, cmdValidate, cmdPin, cmdUnpin, cmdPins, cmdDiff, cmdReset } from './commands/manifest.js';
|
|
21
|
+
import { cmdRegister, cmdUnregister, cmdProjects } from './commands/registry.js';
|
|
22
|
+
import { cmdConfig, cmdDoctor } from './commands/config.js';
|
|
23
|
+
import { cmdEmit } from './commands/events.js';
|
|
24
|
+
import { cmdUpdate, cmdUninstall } from './commands/update.js';
|
|
8
25
|
|
|
9
26
|
// ---------------------------------------------------------------------------
|
|
10
|
-
//
|
|
27
|
+
// Hub Flow — the primary entry point
|
|
11
28
|
// ---------------------------------------------------------------------------
|
|
12
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
-
const __dirname = path.dirname(__filename);
|
|
14
|
-
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
15
29
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* then the parent node_modules/.bin (hoisted local installs where deps are
|
|
20
|
-
* lifted to <project>/node_modules/.bin/). Returns null to fall back to npx.
|
|
21
|
-
*/
|
|
22
|
-
function resolveBin(name) {
|
|
23
|
-
const local = path.join(PACKAGE_ROOT, 'node_modules', '.bin', name);
|
|
24
|
-
if (fs.existsSync(local)) return local;
|
|
25
|
-
const hoisted = path.join(PACKAGE_ROOT, '..', '.bin', name);
|
|
26
|
-
if (fs.existsSync(hoisted)) return path.resolve(hoisted);
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
// CLI Helpers
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
|
|
34
|
-
function getFlagValue(args, flag) {
|
|
35
|
-
const idx = args.indexOf(flag);
|
|
36
|
-
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function detectProjectRoot() {
|
|
40
|
-
try {
|
|
41
|
-
return execFileSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim();
|
|
42
|
-
} catch {
|
|
43
|
-
return process.cwd();
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function loadConfig(projectRoot) {
|
|
48
|
-
const configPath = path.join(projectRoot, '.claude', 'orbital.config.json');
|
|
49
|
-
if (fs.existsSync(configPath)) {
|
|
50
|
-
try {
|
|
51
|
-
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
52
|
-
} catch (err) {
|
|
53
|
-
console.warn(`Warning: could not parse ${configPath}: ${err.message}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return { serverPort: 4444, clientPort: 4445 };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function getPackageVersion() {
|
|
60
|
-
try {
|
|
61
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'));
|
|
62
|
-
return pkg.version || '0.0.0';
|
|
63
|
-
} catch {
|
|
64
|
-
return '0.0.0';
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function stampTemplateVersion(projectRoot) {
|
|
69
|
-
const configPath = path.join(projectRoot, '.claude', 'orbital.config.json');
|
|
70
|
-
if (!fs.existsSync(configPath)) return;
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
74
|
-
const version = getPackageVersion();
|
|
75
|
-
if (config.templateVersion !== version) {
|
|
76
|
-
config.templateVersion = version;
|
|
77
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
78
|
-
console.log(` Stamped templateVersion: ${version}`);
|
|
79
|
-
}
|
|
80
|
-
} catch { /* ignore malformed config */ }
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function checkTemplatesStaleness(projectRoot) {
|
|
84
|
-
const configPath = path.join(projectRoot, '.claude', 'orbital.config.json');
|
|
85
|
-
if (!fs.existsSync(configPath)) return;
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
89
|
-
const projectVersion = config.templateVersion || null;
|
|
90
|
-
const packageVersion = getPackageVersion();
|
|
91
|
-
|
|
92
|
-
if (projectVersion && projectVersion === packageVersion) return;
|
|
93
|
-
|
|
94
|
-
if (projectVersion) {
|
|
95
|
-
console.log(`\n ⚠ Templates outdated (project: v${projectVersion}, package: v${packageVersion})`);
|
|
96
|
-
} else {
|
|
97
|
-
console.log(`\n ⚠ Templates have no version stamp`);
|
|
98
|
-
}
|
|
99
|
-
console.log(` Run \`orbital update\` to refresh templates.\n`);
|
|
100
|
-
} catch { /* ignore malformed config */ }
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function openBrowser(url) {
|
|
104
|
-
const platform = process.platform;
|
|
105
|
-
if (platform === 'darwin') {
|
|
106
|
-
spawn('open', [url], { detached: true, stdio: 'ignore' }).unref();
|
|
107
|
-
} else if (platform === 'win32') {
|
|
108
|
-
spawn('cmd', ['/c', 'start', url], { detached: true, stdio: 'ignore' }).unref();
|
|
109
|
-
} else {
|
|
110
|
-
spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }).unref();
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ---------------------------------------------------------------------------
|
|
115
|
-
// Shared Module Loader
|
|
116
|
-
// ---------------------------------------------------------------------------
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Load the shared init module. Tries compiled JS first (production/global
|
|
120
|
-
* installs via npm publish), then TypeScript source (dev via tsx).
|
|
121
|
-
*/
|
|
122
|
-
async function loadSharedModule() {
|
|
123
|
-
try {
|
|
124
|
-
// Production: compiled JS (tsconfig.server.json outputs to dist/server/server/)
|
|
125
|
-
return await import('../dist/server/server/init.js');
|
|
126
|
-
} catch {
|
|
127
|
-
try {
|
|
128
|
-
// Dev: TypeScript source loaded via tsx
|
|
129
|
-
return await import('../server/init.js');
|
|
130
|
-
} catch {
|
|
131
|
-
console.error('Error: Orbital Command server module not found.');
|
|
132
|
-
console.error('Try reinstalling: npm install -g orbital-command');
|
|
133
|
-
console.error('For local development: npm run build:server');
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Load the interactive wizard module.
|
|
141
|
-
*/
|
|
142
|
-
async function loadWizardModule() {
|
|
143
|
-
try {
|
|
144
|
-
return await import('../dist/server/server/wizard/index.js');
|
|
145
|
-
} catch {
|
|
146
|
-
try {
|
|
147
|
-
return await import('../server/wizard/index.js');
|
|
148
|
-
} catch {
|
|
149
|
-
console.error('Error: Wizard module not found.');
|
|
150
|
-
console.error('Try reinstalling: npm install -g orbital-command');
|
|
151
|
-
process.exit(1);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
|
-
// Commands
|
|
158
|
-
// ---------------------------------------------------------------------------
|
|
159
|
-
|
|
160
|
-
function orbitalSetupDone() {
|
|
161
|
-
return fs.existsSync(path.join(ORBITAL_HOME, 'config.json'));
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function autoRegisterProject(projectRoot) {
|
|
165
|
-
if (!fs.existsSync(ORBITAL_HOME)) fs.mkdirSync(ORBITAL_HOME, { recursive: true });
|
|
166
|
-
const registry = loadRegistry();
|
|
167
|
-
if (!(registry.projects || []).some(p => p.path === projectRoot)) {
|
|
168
|
-
cmdRegister([projectRoot]);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async function cmdInit(args) {
|
|
173
|
-
const isYes = args.includes('--yes') || args.includes('-y');
|
|
174
|
-
const isInteractive = process.stdout.isTTY && !process.env.CI && !isYes;
|
|
175
|
-
const projectRoot = detectProjectRoot();
|
|
176
|
-
|
|
177
|
-
if (isInteractive) {
|
|
178
|
-
const wiz = await loadWizardModule();
|
|
179
|
-
const version = getPackageVersion();
|
|
180
|
-
|
|
181
|
-
// If Orbital hasn't been set up yet, run Phase 1 first
|
|
182
|
-
if (!orbitalSetupDone()) {
|
|
183
|
-
await wiz.runSetupWizard(version);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Phase 2: project setup for current directory
|
|
187
|
-
await wiz.runProjectSetup(projectRoot, version, args);
|
|
188
|
-
stampTemplateVersion(projectRoot);
|
|
30
|
+
async function runHubFlow() {
|
|
31
|
+
if (!process.stdout.isTTY || process.env.CI) {
|
|
32
|
+
printHelp();
|
|
189
33
|
return;
|
|
190
34
|
}
|
|
191
35
|
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
const globalPrivate = loadRegistry().privateMode === true;
|
|
195
|
-
const isPrivate = args.includes('--private') || globalPrivate;
|
|
196
|
-
const preset = getFlagValue(args, '--preset');
|
|
197
|
-
const projectName = getFlagValue(args, '--project-name');
|
|
198
|
-
const serverPort = getFlagValue(args, '--server-port');
|
|
199
|
-
const clientPort = getFlagValue(args, '--client-port');
|
|
200
|
-
|
|
201
|
-
const { runInit } = await loadSharedModule();
|
|
202
|
-
runInit(projectRoot, {
|
|
203
|
-
force,
|
|
204
|
-
preset: preset || undefined,
|
|
205
|
-
projectName: projectName || undefined,
|
|
206
|
-
serverPort: serverPort ? Number(serverPort) : undefined,
|
|
207
|
-
clientPort: clientPort ? Number(clientPort) : undefined,
|
|
208
|
-
});
|
|
209
|
-
stampTemplateVersion(projectRoot);
|
|
210
|
-
|
|
211
|
-
if (isPrivate) {
|
|
212
|
-
const configPath = path.join(projectRoot, '.claude', 'orbital.config.json');
|
|
213
|
-
if (fs.existsSync(configPath)) {
|
|
214
|
-
try {
|
|
215
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
216
|
-
if (!config.telemetry) config.telemetry = {};
|
|
217
|
-
config.telemetry.enabled = false;
|
|
218
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
219
|
-
|
|
220
|
-
} catch { /* leave config as-is */ }
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
autoRegisterProject(projectRoot);
|
|
225
|
-
console.log(`Run \`orbital launch\` to start the dashboard.\n`);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function cmdLaunchOrDev(forceViteFlag) {
|
|
229
|
-
const shouldOpen = process.argv.includes('--open');
|
|
230
|
-
const forceVite = forceViteFlag || process.argv.includes('--vite');
|
|
231
|
-
const projectRoot = detectProjectRoot();
|
|
232
|
-
const config = loadConfig(projectRoot);
|
|
233
|
-
const serverPort = config.serverPort || 4444;
|
|
234
|
-
const clientPort = config.clientPort || 4445;
|
|
235
|
-
|
|
236
|
-
autoRegisterProject(projectRoot);
|
|
237
|
-
|
|
238
|
-
// Detect packaged mode: dist/index.html exists → serve pre-built frontend
|
|
239
|
-
const hasPrebuiltFrontend = fs.existsSync(path.join(PACKAGE_ROOT, 'dist', 'index.html'));
|
|
240
|
-
const useVite = forceVite || !hasPrebuiltFrontend;
|
|
241
|
-
|
|
242
|
-
console.log(`\nOrbital Command — ${useVite ? 'dev' : 'launch'}`);
|
|
243
|
-
console.log(`Project root: ${projectRoot}`);
|
|
244
|
-
if (useVite) {
|
|
245
|
-
console.log(`Server: http://localhost:${serverPort}`);
|
|
246
|
-
console.log(`Client: http://localhost:${clientPort} (Vite dev server)\n`);
|
|
247
|
-
} else {
|
|
248
|
-
console.log(`Dashboard: http://localhost:${serverPort}\n`);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
checkTemplatesStaleness(projectRoot);
|
|
252
|
-
|
|
253
|
-
const env = {
|
|
254
|
-
...process.env,
|
|
255
|
-
ORBITAL_LAUNCH_MODE: 'central',
|
|
256
|
-
ORBITAL_AUTO_REGISTER: projectRoot,
|
|
257
|
-
ORBITAL_SERVER_PORT: String(serverPort),
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const serverScript = path.join(PACKAGE_ROOT, 'server', 'launch.ts');
|
|
261
|
-
const tsxBin = resolveBin('tsx');
|
|
262
|
-
const serverProcess = tsxBin
|
|
263
|
-
? spawn(tsxBin, ['watch', serverScript],
|
|
264
|
-
{ stdio: 'inherit', env, cwd: PACKAGE_ROOT })
|
|
265
|
-
: spawn('npx', ['tsx', 'watch', serverScript],
|
|
266
|
-
{ stdio: 'inherit', env, cwd: PACKAGE_ROOT });
|
|
267
|
-
|
|
268
|
-
let viteProcess = null;
|
|
269
|
-
|
|
270
|
-
if (useVite) {
|
|
271
|
-
const viteBin = resolveBin('vite');
|
|
272
|
-
viteProcess = viteBin
|
|
273
|
-
? spawn(viteBin, ['--config', path.join(PACKAGE_ROOT, 'vite.config.ts'), '--port', String(clientPort)],
|
|
274
|
-
{ stdio: 'inherit', env, cwd: PACKAGE_ROOT })
|
|
275
|
-
: spawn('npx', ['vite', '--config', path.join(PACKAGE_ROOT, 'vite.config.ts'), '--port', String(clientPort)],
|
|
276
|
-
{ stdio: 'inherit', env, cwd: PACKAGE_ROOT });
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const dashboardUrl = useVite
|
|
280
|
-
? `http://localhost:${clientPort}`
|
|
281
|
-
: `http://localhost:${serverPort}`;
|
|
282
|
-
|
|
283
|
-
if (shouldOpen) {
|
|
284
|
-
setTimeout(() => openBrowser(dashboardUrl), 2000);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
let exiting = false;
|
|
288
|
-
|
|
289
|
-
function cleanup() {
|
|
290
|
-
if (exiting) return;
|
|
291
|
-
exiting = true;
|
|
292
|
-
serverProcess.kill();
|
|
293
|
-
if (viteProcess) viteProcess.kill();
|
|
294
|
-
process.exit(0);
|
|
295
|
-
}
|
|
296
|
-
process.on('SIGINT', cleanup);
|
|
297
|
-
process.on('SIGTERM', cleanup);
|
|
298
|
-
|
|
299
|
-
serverProcess.on('exit', (code) => {
|
|
300
|
-
if (exiting) return;
|
|
301
|
-
exiting = true;
|
|
302
|
-
console.log(`Server exited with code ${code}`);
|
|
303
|
-
if (viteProcess) viteProcess.kill();
|
|
304
|
-
process.exit(code || 0);
|
|
305
|
-
});
|
|
306
|
-
if (viteProcess) {
|
|
307
|
-
viteProcess.on('exit', (code) => {
|
|
308
|
-
if (exiting) return;
|
|
309
|
-
exiting = true;
|
|
310
|
-
console.log(`Vite exited with code ${code}`);
|
|
311
|
-
serverProcess.kill();
|
|
312
|
-
process.exit(code || 0);
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function cmdBuild() {
|
|
318
|
-
console.log(`\nOrbital Command — build\n`);
|
|
319
|
-
|
|
320
|
-
const viteBin = resolveBin('vite');
|
|
321
|
-
const buildProcess = viteBin
|
|
322
|
-
? spawn(viteBin, ['build', '--config', path.join(PACKAGE_ROOT, 'vite.config.ts')],
|
|
323
|
-
{ stdio: 'inherit', cwd: PACKAGE_ROOT })
|
|
324
|
-
: spawn('npx', ['vite', 'build', '--config', path.join(PACKAGE_ROOT, 'vite.config.ts')],
|
|
325
|
-
{ stdio: 'inherit', cwd: PACKAGE_ROOT });
|
|
326
|
-
|
|
327
|
-
buildProcess.on('exit', (code) => {
|
|
328
|
-
process.exit(code || 0);
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function cmdEmit(args) {
|
|
333
|
-
const type = args[0];
|
|
334
|
-
const jsonStr = args.slice(1).join(' ');
|
|
335
|
-
|
|
336
|
-
if (!type) {
|
|
337
|
-
console.error('Usage: orbital emit <TYPE> <JSON>');
|
|
338
|
-
process.exit(1);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const projectRoot = detectProjectRoot();
|
|
342
|
-
const eventsDir = path.join(projectRoot, '.claude', 'orbital-events');
|
|
343
|
-
if (!fs.existsSync(eventsDir)) fs.mkdirSync(eventsDir, { recursive: true });
|
|
344
|
-
|
|
345
|
-
let payload;
|
|
346
|
-
try {
|
|
347
|
-
payload = jsonStr ? JSON.parse(jsonStr) : {};
|
|
348
|
-
} catch (err) {
|
|
349
|
-
console.error(`Invalid JSON: ${err.message}`);
|
|
350
|
-
process.exit(1);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const eventId = crypto.randomUUID();
|
|
354
|
-
const event = {
|
|
355
|
-
...payload,
|
|
356
|
-
id: eventId,
|
|
357
|
-
type,
|
|
358
|
-
timestamp: new Date().toISOString(),
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
const filePath = path.join(eventsDir, `${eventId}.json`);
|
|
362
|
-
fs.writeFileSync(filePath, JSON.stringify(event, null, 2) + '\n', 'utf8');
|
|
363
|
-
|
|
364
|
-
console.log(`Event emitted: ${type} (${eventId})`);
|
|
365
|
-
console.log(` File: ${path.relative(projectRoot, filePath)}`);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
async function cmdUpdate(args) {
|
|
369
|
-
const projectRoot = detectProjectRoot();
|
|
370
|
-
const dryRun = args.includes('--dry-run');
|
|
371
|
-
|
|
372
|
-
const { runUpdate } = await loadSharedModule();
|
|
373
|
-
runUpdate(projectRoot, { dryRun });
|
|
374
|
-
|
|
375
|
-
if (!dryRun) stampTemplateVersion(projectRoot);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
async function cmdUninstall(args) {
|
|
379
|
-
const projectRoot = detectProjectRoot();
|
|
380
|
-
const dryRun = args.includes('--dry-run');
|
|
381
|
-
const keepConfig = args.includes('--keep-config');
|
|
382
|
-
|
|
383
|
-
const { runUninstall } = await loadSharedModule();
|
|
384
|
-
runUninstall(projectRoot, { dryRun, keepConfig });
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// ---------------------------------------------------------------------------
|
|
388
|
-
// Manifest management commands
|
|
389
|
-
// ---------------------------------------------------------------------------
|
|
390
|
-
|
|
391
|
-
async function cmdValidate() {
|
|
392
|
-
const projectRoot = detectProjectRoot();
|
|
393
|
-
|
|
394
|
-
const mod = await loadSharedModule();
|
|
395
|
-
const report = mod.validate(projectRoot, getPackageVersion());
|
|
396
|
-
console.log(mod.formatValidationReport(report));
|
|
397
|
-
process.exit(report.errors > 0 ? 1 : 0);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
async function cmdStatus() {
|
|
401
|
-
const projectRoot = detectProjectRoot();
|
|
402
|
-
|
|
403
|
-
const mod = await loadSharedModule();
|
|
404
|
-
const manifest = mod.loadManifest(projectRoot);
|
|
405
|
-
|
|
406
|
-
if (!manifest) {
|
|
407
|
-
console.log('\nNo manifest found. Run `orbital init` or `orbital update` to create one.\n');
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const claudeDir = path.join(projectRoot, '.claude');
|
|
412
|
-
mod.refreshFileStatuses(manifest, claudeDir);
|
|
413
|
-
|
|
414
|
-
const summary = mod.summarizeManifest(manifest);
|
|
415
|
-
const packageVersion = getPackageVersion();
|
|
416
|
-
const needsUpdate = manifest.packageVersion !== packageVersion;
|
|
417
|
-
|
|
418
|
-
console.log(`\nOrbital Command v${packageVersion}${needsUpdate ? ` (installed: ${manifest.packageVersion} → needs update)` : ''}\n`);
|
|
419
|
-
|
|
420
|
-
for (const [type, counts] of Object.entries(summary.byType)) {
|
|
421
|
-
const parts = [];
|
|
422
|
-
if (counts.synced) parts.push(`${counts.synced} synced`);
|
|
423
|
-
if (counts.outdated) parts.push(`${counts.outdated} outdated`);
|
|
424
|
-
if (counts.modified) parts.push(`${counts.modified} modified`);
|
|
425
|
-
if (counts.pinned) parts.push(`${counts.pinned} pinned`);
|
|
426
|
-
if (counts.userOwned) parts.push(`${counts.userOwned} user-owned`);
|
|
427
|
-
console.log(` ${type.padEnd(16)} ${parts.join(', ')}`);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Show outdated files (template moved ahead, user hasn't touched)
|
|
431
|
-
const outdated = Object.entries(manifest.files)
|
|
432
|
-
.filter(([, r]) => r.status === 'outdated');
|
|
433
|
-
if (outdated.length > 0) {
|
|
434
|
-
console.log('\n Outdated files (safe to update):');
|
|
435
|
-
for (const [file] of outdated) {
|
|
436
|
-
console.log(` ${file}`);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Show modified files (user edited)
|
|
441
|
-
const modified = Object.entries(manifest.files)
|
|
442
|
-
.filter(([, r]) => r.status === 'modified');
|
|
443
|
-
if (modified.length > 0) {
|
|
444
|
-
console.log('\n Modified files (user edited):');
|
|
445
|
-
for (const [file] of modified) {
|
|
446
|
-
console.log(` ${file} (run 'orbital diff ${file}')`);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Show pinned files
|
|
451
|
-
const pinned = Object.entries(manifest.files)
|
|
452
|
-
.filter(([, r]) => r.status === 'pinned');
|
|
453
|
-
if (pinned.length > 0) {
|
|
454
|
-
console.log('\n Pinned files:');
|
|
455
|
-
for (const [file, record] of pinned) {
|
|
456
|
-
const reason = record.pinnedReason ? `"${record.pinnedReason}"` : '';
|
|
457
|
-
console.log(` ${file} ${reason}`);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
console.log();
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
async function cmdPin(args) {
|
|
465
|
-
const projectRoot = detectProjectRoot();
|
|
466
|
-
const filePath = args.find(a => !a.startsWith('--'));
|
|
467
|
-
const reasonIdx = args.indexOf('--reason');
|
|
468
|
-
const reason = reasonIdx !== -1 ? args[reasonIdx + 1] : undefined;
|
|
469
|
-
|
|
470
|
-
if (!filePath) {
|
|
471
|
-
console.error('Usage: orbital pin <relative-path> [--reason "..."]');
|
|
472
|
-
process.exit(1);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const mod = await loadSharedModule();
|
|
476
|
-
const manifest = mod.loadManifest(projectRoot);
|
|
477
|
-
if (!manifest) {
|
|
478
|
-
console.error('No manifest found. Run `orbital init` first.');
|
|
479
|
-
process.exit(1);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
const record = manifest.files[filePath];
|
|
483
|
-
if (!record) {
|
|
484
|
-
console.error(`File not tracked: ${filePath}`);
|
|
485
|
-
process.exit(1);
|
|
486
|
-
}
|
|
487
|
-
if (record.origin === 'user') {
|
|
488
|
-
console.error(`Cannot pin user-owned file: ${filePath}`);
|
|
489
|
-
process.exit(1);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
record.status = 'pinned';
|
|
493
|
-
record.pinnedAt = new Date().toISOString();
|
|
494
|
-
if (reason) record.pinnedReason = reason;
|
|
495
|
-
|
|
496
|
-
mod.saveManifest(projectRoot, manifest);
|
|
497
|
-
console.log(`Pinned: ${filePath}${reason ? ` (${reason})` : ''}`);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
async function cmdUnpin(args) {
|
|
501
|
-
const projectRoot = detectProjectRoot();
|
|
502
|
-
const filePath = args[0];
|
|
503
|
-
|
|
504
|
-
if (!filePath) {
|
|
505
|
-
console.error('Usage: orbital unpin <relative-path>');
|
|
506
|
-
process.exit(1);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
const mod = await loadSharedModule();
|
|
510
|
-
const manifest = mod.loadManifest(projectRoot);
|
|
511
|
-
if (!manifest) {
|
|
512
|
-
console.error('No manifest found. Run `orbital init` first.');
|
|
513
|
-
process.exit(1);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
const record = manifest.files[filePath];
|
|
517
|
-
if (!record || record.status !== 'pinned') {
|
|
518
|
-
console.error(`File is not pinned: ${filePath}`);
|
|
519
|
-
process.exit(1);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Clear pinned state, then recompute
|
|
523
|
-
record.status = 'synced';
|
|
524
|
-
delete record.pinnedAt;
|
|
525
|
-
delete record.pinnedReason;
|
|
526
|
-
|
|
527
|
-
const absPath = path.join(projectRoot, '.claude', filePath);
|
|
528
|
-
if (fs.existsSync(absPath)) {
|
|
529
|
-
const currentHash = mod.hashFile(absPath);
|
|
530
|
-
record.status = mod.computeFileStatus(record, currentHash);
|
|
531
|
-
} else {
|
|
532
|
-
record.status = 'synced';
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
mod.saveManifest(projectRoot, manifest);
|
|
536
|
-
console.log(`Unpinned: ${filePath} (now ${record.status})`);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
async function cmdPins() {
|
|
540
|
-
const projectRoot = detectProjectRoot();
|
|
36
|
+
const wiz = await loadWizardModule();
|
|
37
|
+
const hubVersion = getPackageVersion();
|
|
541
38
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
console.error('No manifest found. Run `orbital init` first.');
|
|
546
|
-
process.exit(1);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
const pinned = Object.entries(manifest.files)
|
|
550
|
-
.filter(([, r]) => r.status === 'pinned');
|
|
551
|
-
|
|
552
|
-
if (pinned.length === 0) {
|
|
553
|
-
console.log('No pinned files.');
|
|
39
|
+
// First-time global setup — no menu, just run the wizard
|
|
40
|
+
if (!orbitalSetupDone()) {
|
|
41
|
+
await wiz.runSetupWizard(hubVersion);
|
|
554
42
|
return;
|
|
555
43
|
}
|
|
556
44
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
const date = record.pinnedAt ? new Date(record.pinnedAt).toLocaleDateString() : '';
|
|
561
|
-
console.log(` ${file}`);
|
|
562
|
-
console.log(` Reason: ${reason} Pinned: ${date}`);
|
|
563
|
-
if (record.templateHash !== record.installedHash) {
|
|
564
|
-
console.log(` Template has changed since pin — run 'orbital diff ${file}' to compare`);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
console.log();
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
async function cmdDiff(args) {
|
|
571
|
-
const projectRoot = detectProjectRoot();
|
|
572
|
-
const filePath = args[0];
|
|
573
|
-
|
|
574
|
-
if (!filePath) {
|
|
575
|
-
console.error('Usage: orbital diff <relative-path>');
|
|
576
|
-
process.exit(1);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
const mod = await loadSharedModule();
|
|
580
|
-
const manifest = mod.loadManifest(projectRoot);
|
|
581
|
-
if (!manifest) {
|
|
582
|
-
console.error('No manifest found. Run `orbital init` first.');
|
|
583
|
-
process.exit(1);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
const record = manifest.files[filePath];
|
|
587
|
-
if (!record || record.origin !== 'template') {
|
|
588
|
-
console.error(`Not a template file: ${filePath}`);
|
|
589
|
-
process.exit(1);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// Resolve template path
|
|
593
|
-
let templateRelPath = filePath;
|
|
594
|
-
if (filePath.startsWith('config/workflows/')) {
|
|
595
|
-
templateRelPath = filePath.replace('config/workflows/', 'presets/');
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
const templatePath = path.join(PACKAGE_ROOT, 'templates', templateRelPath);
|
|
599
|
-
const localPath = path.join(projectRoot, '.claude', filePath);
|
|
600
|
-
|
|
601
|
-
if (!fs.existsSync(templatePath)) {
|
|
602
|
-
console.error(`Template file not found: ${templateRelPath}`);
|
|
603
|
-
process.exit(1);
|
|
604
|
-
}
|
|
605
|
-
if (!fs.existsSync(localPath)) {
|
|
606
|
-
console.log('Local file does not exist. Template content:');
|
|
607
|
-
console.log(fs.readFileSync(templatePath, 'utf-8'));
|
|
45
|
+
// Need a git repo for everything else
|
|
46
|
+
if (!isGitRepo()) {
|
|
47
|
+
requireGitRepo(); // exits with error
|
|
608
48
|
return;
|
|
609
49
|
}
|
|
610
50
|
|
|
611
|
-
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
);
|
|
618
|
-
console.log(output);
|
|
619
|
-
} catch (e) {
|
|
620
|
-
// git diff exits 1 when files differ — that's expected
|
|
621
|
-
if (e.stdout) console.log(e.stdout);
|
|
622
|
-
else console.log('Files differ but git diff is unavailable.');
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
async function cmdReset(args) {
|
|
627
|
-
const projectRoot = detectProjectRoot();
|
|
628
|
-
const filePath = args[0];
|
|
629
|
-
|
|
630
|
-
if (!filePath) {
|
|
631
|
-
console.error('Usage: orbital reset <relative-path>');
|
|
632
|
-
process.exit(1);
|
|
633
|
-
}
|
|
51
|
+
const hubRoot = detectProjectRoot();
|
|
52
|
+
const isInitialized = fs.existsSync(
|
|
53
|
+
path.join(hubRoot, '.claude', 'orbital.config.json')
|
|
54
|
+
);
|
|
55
|
+
const hubRegistry = loadRegistry();
|
|
56
|
+
const projectNames = (hubRegistry.projects || []).map(p => p.name);
|
|
634
57
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
process.exit(1);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
const record = manifest.files[filePath];
|
|
643
|
-
if (!record || record.origin !== 'template') {
|
|
644
|
-
console.error(`Not a template file: ${filePath}`);
|
|
645
|
-
process.exit(1);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
// Resolve and copy template file
|
|
649
|
-
let templateRelPath = filePath;
|
|
650
|
-
if (filePath.startsWith('config/workflows/')) {
|
|
651
|
-
templateRelPath = filePath.replace('config/workflows/', 'presets/');
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
const templatePath = path.join(PACKAGE_ROOT, 'templates', templateRelPath);
|
|
655
|
-
const localPath = path.join(projectRoot, '.claude', filePath);
|
|
656
|
-
|
|
657
|
-
if (!fs.existsSync(templatePath)) {
|
|
658
|
-
console.error(`Template file not found: ${templateRelPath}`);
|
|
659
|
-
process.exit(1);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
fs.copyFileSync(templatePath, localPath);
|
|
663
|
-
const newHash = mod.hashFile(localPath);
|
|
664
|
-
record.status = 'synced';
|
|
665
|
-
record.templateHash = newHash;
|
|
666
|
-
record.installedHash = newHash;
|
|
667
|
-
delete record.pinnedAt;
|
|
668
|
-
delete record.pinnedReason;
|
|
669
|
-
|
|
670
|
-
mod.saveManifest(projectRoot, manifest);
|
|
671
|
-
console.log(`Reset: ${filePath} → synced with template`);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
// ---------------------------------------------------------------------------
|
|
675
|
-
// Multi-project commands
|
|
676
|
-
// ---------------------------------------------------------------------------
|
|
677
|
-
|
|
678
|
-
const ORBITAL_HOME = path.join(process.env.HOME || process.env.USERPROFILE || '~', '.orbital');
|
|
679
|
-
const REGISTRY_PATH = path.join(ORBITAL_HOME, 'config.json');
|
|
680
|
-
|
|
681
|
-
function loadRegistry() {
|
|
682
|
-
if (!fs.existsSync(REGISTRY_PATH)) return { version: 1, projects: [] };
|
|
683
|
-
try {
|
|
684
|
-
return JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf8'));
|
|
685
|
-
} catch {
|
|
686
|
-
return { version: 1, projects: [] };
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// cmdLaunch removed — merged into cmdLaunchOrDev() above
|
|
691
|
-
|
|
692
|
-
function cmdRegister(args) {
|
|
693
|
-
const targetPath = args[0] ? path.resolve(args[0]) : detectProjectRoot();
|
|
694
|
-
const nameFlag = args.indexOf('--alias');
|
|
695
|
-
const name = nameFlag >= 0 ? args[nameFlag + 1] : path.basename(targetPath);
|
|
696
|
-
|
|
697
|
-
// Ensure ~/.orbital/ exists
|
|
698
|
-
if (!fs.existsSync(ORBITAL_HOME)) fs.mkdirSync(ORBITAL_HOME, { recursive: true });
|
|
699
|
-
|
|
700
|
-
// Check the project has been initialized
|
|
701
|
-
if (!fs.existsSync(path.join(targetPath, '.claude'))) {
|
|
702
|
-
console.error(`Error: ${targetPath} has not been initialized with Orbital Command.`);
|
|
703
|
-
console.error(`Run \`orbital init\` in that directory first.`);
|
|
704
|
-
process.exit(1);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const registry = loadRegistry();
|
|
708
|
-
|
|
709
|
-
// Check if already registered
|
|
710
|
-
if (registry.projects?.some(p => p.path === targetPath)) {
|
|
711
|
-
console.log(`Project already registered: ${targetPath}`);
|
|
58
|
+
// Not initialized and no registered projects — just run setup wizard
|
|
59
|
+
if (!isInitialized && projectNames.length === 0) {
|
|
60
|
+
await wiz.runProjectSetup(hubRoot, hubVersion, []);
|
|
61
|
+
stampTemplateVersion(hubRoot);
|
|
712
62
|
return;
|
|
713
63
|
}
|
|
714
64
|
|
|
715
|
-
//
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const project = {
|
|
732
|
-
id: slug,
|
|
733
|
-
path: targetPath,
|
|
734
|
-
name,
|
|
735
|
-
color,
|
|
736
|
-
registeredAt: new Date().toISOString(),
|
|
737
|
-
enabled: true,
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
if (!registry.projects) registry.projects = [];
|
|
741
|
-
registry.projects.push(project);
|
|
742
|
-
fs.writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2), 'utf8');
|
|
743
|
-
|
|
744
|
-
console.log(`Registered project: ${name}`);
|
|
745
|
-
console.log(` ID: ${slug}`);
|
|
746
|
-
console.log(` Path: ${targetPath}`);
|
|
747
|
-
console.log(` Color: ${color}`);
|
|
748
|
-
}
|
|
65
|
+
// Show hub menu (initialized OR has registered projects)
|
|
66
|
+
const projects = (hubRegistry.projects || [])
|
|
67
|
+
.filter(p => p.enabled !== false)
|
|
68
|
+
.map(p => ({ name: p.name, path: p.path }));
|
|
69
|
+
|
|
70
|
+
const hubResult = await wiz.runHub({
|
|
71
|
+
packageVersion: hubVersion,
|
|
72
|
+
isProjectInitialized: isInitialized,
|
|
73
|
+
projectNames,
|
|
74
|
+
itermPromptShown: hubRegistry.itermPromptShown === true,
|
|
75
|
+
isMac: process.platform === 'darwin',
|
|
76
|
+
lastUpdateCheck: hubRegistry.lastUpdateCheck,
|
|
77
|
+
latestVersion: hubRegistry.latestVersion,
|
|
78
|
+
projectPaths: projects,
|
|
79
|
+
});
|
|
749
80
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
if (
|
|
753
|
-
|
|
754
|
-
|
|
81
|
+
// Persist registry changes in one write
|
|
82
|
+
let registryChanged = false;
|
|
83
|
+
if (hubResult.setItermPromptShown) {
|
|
84
|
+
hubRegistry.itermPromptShown = true;
|
|
85
|
+
registryChanged = true;
|
|
755
86
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
if (idx === -1) {
|
|
762
|
-
console.error(`Project not found: ${idOrPath}`);
|
|
763
|
-
process.exit(1);
|
|
87
|
+
if (hubResult.updateCache) {
|
|
88
|
+
hubRegistry.lastUpdateCheck = hubResult.updateCache.lastUpdateCheck;
|
|
89
|
+
hubRegistry.latestVersion = hubResult.updateCache.latestVersion;
|
|
90
|
+
registryChanged = true;
|
|
764
91
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
fs.writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2), 'utf8');
|
|
768
|
-
|
|
769
|
-
console.log(`Unregistered project: ${removed.name} (${removed.id})`);
|
|
770
|
-
console.log(` Project files in ${removed.path} are preserved.`);
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
function cmdProjects() {
|
|
774
|
-
const registry = loadRegistry();
|
|
775
|
-
const projects = registry.projects || [];
|
|
776
|
-
|
|
777
|
-
if (projects.length === 0) {
|
|
778
|
-
console.log('\nNo projects registered.');
|
|
779
|
-
console.log('Use `orbital register` or `orbital init` to add a project.\n');
|
|
780
|
-
return;
|
|
92
|
+
if (registryChanged) {
|
|
93
|
+
writeRegistryAtomic(hubRegistry);
|
|
781
94
|
}
|
|
782
95
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
96
|
+
// Route the chosen action
|
|
97
|
+
switch (hubResult.action) {
|
|
98
|
+
case 'launch': cmdLaunchOrDev(false); break;
|
|
99
|
+
case 'init':
|
|
100
|
+
await wiz.runProjectSetup(hubRoot, hubVersion, []);
|
|
101
|
+
stampTemplateVersion(hubRoot);
|
|
102
|
+
break;
|
|
103
|
+
case 'config': await cmdConfig([]); break;
|
|
104
|
+
case 'doctor': await cmdDoctor(); break;
|
|
105
|
+
case 'update': await cmdUpdate([]); break;
|
|
106
|
+
case 'status': await cmdStatus(); break;
|
|
107
|
+
case 'reset': {
|
|
108
|
+
const { runInit } = await loadSharedModule();
|
|
109
|
+
runInit(hubRoot, { force: true });
|
|
110
|
+
stampTemplateVersion(hubRoot);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
default:
|
|
114
|
+
console.error(`Unknown action: ${hubResult.action}`);
|
|
115
|
+
process.exit(1);
|
|
788
116
|
}
|
|
789
|
-
console.log();
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
async function cmdConfig(args) {
|
|
793
|
-
const { runConfigEditor } = await loadWizardModule();
|
|
794
|
-
const projectRoot = detectProjectRoot();
|
|
795
|
-
const version = getPackageVersion();
|
|
796
|
-
await runConfigEditor(projectRoot, version, args);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
async function cmdDoctor() {
|
|
800
|
-
const { runDoctor } = await loadWizardModule();
|
|
801
|
-
const projectRoot = detectProjectRoot();
|
|
802
|
-
const version = getPackageVersion();
|
|
803
|
-
await runDoctor(projectRoot, version);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
function printHelp() {
|
|
807
|
-
console.log(`
|
|
808
|
-
Orbital Command — CLI for the agentic project management system
|
|
809
|
-
|
|
810
|
-
Usage:
|
|
811
|
-
orbital <command> [options]
|
|
812
|
-
|
|
813
|
-
Commands:
|
|
814
|
-
init Set up Orbital Command (interactive wizard)
|
|
815
|
-
launch Start the dashboard
|
|
816
|
-
config Modify project settings interactively
|
|
817
|
-
doctor Health check and version diagnostics
|
|
818
|
-
update Sync templates and apply migrations
|
|
819
|
-
status Show template sync status
|
|
820
|
-
|
|
821
|
-
Aliases:
|
|
822
|
-
setup Same as init
|
|
823
|
-
dev Same as launch --vite (development with HMR)
|
|
824
|
-
|
|
825
|
-
Config Subcommands:
|
|
826
|
-
config show Print current config as JSON
|
|
827
|
-
config set <k> <v> Set a config value non-interactively
|
|
828
|
-
|
|
829
|
-
Template Management:
|
|
830
|
-
validate Check cross-references and consistency
|
|
831
|
-
pin <path> Lock a file from updates
|
|
832
|
-
unpin <path> Unlock a pinned file
|
|
833
|
-
pins List all pinned files
|
|
834
|
-
diff <path> Show diff between template and local file
|
|
835
|
-
reset <path> Restore a file from the current template
|
|
836
|
-
|
|
837
|
-
Project Management:
|
|
838
|
-
register [path] Register a project with the dashboard
|
|
839
|
-
unregister <id> Remove a project from the dashboard
|
|
840
|
-
projects List all registered projects
|
|
841
|
-
|
|
842
|
-
Other:
|
|
843
|
-
build Production build of the dashboard frontend
|
|
844
|
-
emit <TYPE> <JSON> Emit an orbital event
|
|
845
|
-
uninstall Remove Orbital artifacts from the project
|
|
846
|
-
|
|
847
|
-
Init Options:
|
|
848
|
-
--force Overwrite existing hooks, skills, and agents
|
|
849
|
-
--yes, -y Skip the wizard, use auto-detected defaults
|
|
850
|
-
--private Disable telemetry for this project
|
|
851
|
-
--preset <name> Workflow preset (default/minimal/development/gitflow)
|
|
852
|
-
--project-name <n> Override auto-detected project name
|
|
853
|
-
--server-port <n> Override default server port (4444)
|
|
854
|
-
--client-port <n> Override default client port (4445)
|
|
855
|
-
|
|
856
|
-
Launch Options:
|
|
857
|
-
--open Open the dashboard in the browser
|
|
858
|
-
--vite Force Vite dev server for HMR
|
|
859
|
-
|
|
860
|
-
Update Options:
|
|
861
|
-
--dry-run Preview changes without applying them
|
|
862
|
-
|
|
863
|
-
Uninstall Options:
|
|
864
|
-
--dry-run Preview removal without applying
|
|
865
|
-
--keep-config Keep orbital.config.json for re-initialization
|
|
866
|
-
|
|
867
|
-
Examples:
|
|
868
|
-
orbital init
|
|
869
|
-
orbital launch --open
|
|
870
|
-
orbital config
|
|
871
|
-
orbital doctor
|
|
872
|
-
orbital update --dry-run
|
|
873
|
-
orbital status
|
|
874
|
-
`);
|
|
875
117
|
}
|
|
876
118
|
|
|
877
119
|
// ---------------------------------------------------------------------------
|
|
@@ -882,13 +124,15 @@ const [command, ...args] = process.argv.slice(2);
|
|
|
882
124
|
|
|
883
125
|
async function main() {
|
|
884
126
|
switch (command) {
|
|
885
|
-
|
|
886
|
-
cmdLaunchOrDev(false);
|
|
887
|
-
break;
|
|
127
|
+
// Deprecated commands — silently redirect to hub
|
|
888
128
|
case 'init':
|
|
889
129
|
case 'setup':
|
|
890
|
-
|
|
130
|
+
case 'launch':
|
|
131
|
+
case undefined:
|
|
132
|
+
await runHubFlow();
|
|
891
133
|
break;
|
|
134
|
+
|
|
135
|
+
// Active commands
|
|
892
136
|
case 'config':
|
|
893
137
|
await cmdConfig(args);
|
|
894
138
|
break;
|
|
@@ -944,10 +188,8 @@ async function main() {
|
|
|
944
188
|
const registry = loadRegistry();
|
|
945
189
|
const enable = args[0] !== 'off';
|
|
946
190
|
registry.privateMode = enable;
|
|
947
|
-
|
|
948
|
-
fs.writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2), 'utf8');
|
|
191
|
+
writeRegistryAtomic(registry);
|
|
949
192
|
console.log(`Private mode ${enable ? 'enabled' : 'disabled'} globally.`);
|
|
950
|
-
|
|
951
193
|
break;
|
|
952
194
|
}
|
|
953
195
|
case 'help':
|
|
@@ -955,23 +197,6 @@ async function main() {
|
|
|
955
197
|
case '-h':
|
|
956
198
|
printHelp();
|
|
957
199
|
break;
|
|
958
|
-
case undefined:
|
|
959
|
-
if (process.stdout.isTTY && !process.env.CI) {
|
|
960
|
-
const wiz = await loadWizardModule();
|
|
961
|
-
const version = getPackageVersion();
|
|
962
|
-
if (!orbitalSetupDone()) {
|
|
963
|
-
// First time — run Phase 1 setup
|
|
964
|
-
await wiz.runSetupWizard(version);
|
|
965
|
-
} else {
|
|
966
|
-
// Already set up — run Phase 2 for current directory
|
|
967
|
-
const projectRoot = detectProjectRoot();
|
|
968
|
-
await wiz.runProjectSetup(projectRoot, version, []);
|
|
969
|
-
stampTemplateVersion(projectRoot);
|
|
970
|
-
}
|
|
971
|
-
} else {
|
|
972
|
-
printHelp();
|
|
973
|
-
}
|
|
974
|
-
break;
|
|
975
200
|
default:
|
|
976
201
|
console.error(`Unknown command: ${command}`);
|
|
977
202
|
printHelp();
|