orbital-command 0.3.0 → 1.0.1
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 +90 -873
- 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 +28 -816
- 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 +31 -896
- 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/server/init.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared init logic — used by
|
|
3
|
-
* programmatic callers (e.g. tests).
|
|
2
|
+
* Shared init logic — used by the wizard and
|
|
3
|
+
* programmatic callers (e.g. tests, API routes).
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
-
import { GLOBAL_PRIMITIVES_DIR, ensureOrbitalHome
|
|
9
|
+
import { GLOBAL_PRIMITIVES_DIR, ensureOrbitalHome } from './global-config.js';
|
|
10
10
|
import {
|
|
11
|
-
loadManifest,
|
|
12
11
|
saveManifest,
|
|
13
12
|
createManifest,
|
|
14
13
|
hashFile,
|
|
@@ -17,18 +16,9 @@ import {
|
|
|
17
16
|
userFileRecord,
|
|
18
17
|
isSelfHosting,
|
|
19
18
|
getSymlinkTarget,
|
|
20
|
-
createBackup,
|
|
21
|
-
refreshFileStatuses,
|
|
22
|
-
reverseRemapPath,
|
|
23
|
-
safeBackupFile,
|
|
24
|
-
safeCopyTemplate,
|
|
25
19
|
} from './manifest.js';
|
|
26
20
|
import type { OrbitalManifest } from './manifest-types.js';
|
|
27
|
-
import {
|
|
28
|
-
import { computeUpdatePlan, loadRenameMap, formatPlan, getFilesToBackup } from './update-planner.js';
|
|
29
|
-
import { syncSettingsHooks, removeAllOrbitalHooks, getTemplateChecksum } from './settings-sync.js';
|
|
30
|
-
import { migrateConfig } from './config-migrator.js';
|
|
31
|
-
import { validate, formatValidationReport } from './validator.js';
|
|
21
|
+
import { getTemplateChecksum } from './settings-sync.js';
|
|
32
22
|
import { getPackageVersion as _getPackageVersionUtil } from './utils/package-info.js';
|
|
33
23
|
|
|
34
24
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -252,7 +242,8 @@ function generateManifest(config: Record<string, unknown>): string {
|
|
|
252
242
|
lines.push('');
|
|
253
243
|
|
|
254
244
|
lines.push('# ─── Entry point status ───');
|
|
255
|
-
|
|
245
|
+
const entryPointId = (lists.find((l) => l.isEntryPoint)?.id as string) || (lists[0]?.id as string) || 'todo';
|
|
246
|
+
lines.push(`WORKFLOW_ENTRY_STATUS="${entryPointId}"`);
|
|
256
247
|
lines.push('');
|
|
257
248
|
|
|
258
249
|
const listMap = new Map(lists.map((l) => [l.id, l]));
|
|
@@ -279,6 +270,25 @@ function generateManifest(config: Record<string, unknown>): string {
|
|
|
279
270
|
lines.push(')');
|
|
280
271
|
lines.push('');
|
|
281
272
|
|
|
273
|
+
lines.push('# ─── Commit session branch patterns (regex) ───');
|
|
274
|
+
lines.push(`WORKFLOW_COMMIT_BRANCHES="${(config.commitBranchPatterns as string) ?? ''}"`);
|
|
275
|
+
lines.push('');
|
|
276
|
+
|
|
277
|
+
lines.push('# ─── Backward-compat direction aliases (alias:from:to:sessionKey) ───');
|
|
278
|
+
lines.push('WORKFLOW_DIRECTION_ALIASES=(');
|
|
279
|
+
for (const edge of (config.edges as Array<Record<string, unknown>>) || []) {
|
|
280
|
+
if (edge.direction !== 'forward' || !edge.dispatchOnly) continue;
|
|
281
|
+
const targetList = listMap.get(edge.to as string);
|
|
282
|
+
if (!targetList) continue;
|
|
283
|
+
const group = targetList.group as string | undefined;
|
|
284
|
+
if (group?.startsWith('deploy')) {
|
|
285
|
+
const sessionKey = (targetList.sessionKey as string) ?? '';
|
|
286
|
+
lines.push(` "to-${edge.to as string}:${edge.from as string}:${edge.to as string}:${sessionKey}"`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
lines.push(')');
|
|
290
|
+
lines.push('');
|
|
291
|
+
|
|
282
292
|
lines.push('# ─── Helper functions ──────────────────────────────');
|
|
283
293
|
lines.push('');
|
|
284
294
|
lines.push('status_to_dir() {');
|
|
@@ -625,12 +635,16 @@ export function seedGlobalPrimitives(): void {
|
|
|
625
635
|
}
|
|
626
636
|
}
|
|
627
637
|
|
|
628
|
-
export { TEMPLATES_DIR, ensureDir };
|
|
638
|
+
export { TEMPLATES_DIR, ensureDir, cleanEmptyDirs, chmodScripts, listTemplateFiles, writeManifest, generateIndexMd, getPackageVersion };
|
|
629
639
|
|
|
630
640
|
// Re-export manifest utilities for CLI access via loadSharedModule()
|
|
631
641
|
export { loadManifest, saveManifest, hashFile, buildTemplateInventory, refreshFileStatuses, summarizeManifest } from './manifest.js';
|
|
632
642
|
export { validate, formatValidationReport } from './validator.js';
|
|
633
643
|
|
|
644
|
+
// Re-export update and uninstall from their new homes (backward compat for loadSharedModule)
|
|
645
|
+
export { runUpdate, type UpdateOptions } from './update.js';
|
|
646
|
+
export { runUninstall, type UninstallOptions } from './uninstall.js';
|
|
647
|
+
|
|
634
648
|
export interface InitOptions {
|
|
635
649
|
force?: boolean;
|
|
636
650
|
quiet?: boolean; // suppress console output (used by wizard)
|
|
@@ -905,430 +919,5 @@ export function runInit(projectRoot: string, options: InitOptions = {}): void {
|
|
|
905
919
|
log(`\nDone. ${totalCreated} files installed, ${totalSkipped} skipped (use --force to overwrite).`);
|
|
906
920
|
}
|
|
907
921
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
force?: boolean;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
export function runUpdate(projectRoot: string, options: UpdateOptions = {}): void {
|
|
914
|
-
const { dryRun = false } = options;
|
|
915
|
-
const claudeDir = path.join(projectRoot, '.claude');
|
|
916
|
-
const newVersion = getPackageVersion();
|
|
917
|
-
|
|
918
|
-
console.log(`\nOrbital Command — update${dryRun ? ' (dry run)' : ''}`);
|
|
919
|
-
console.log(`Project root: ${projectRoot}\n`);
|
|
920
|
-
|
|
921
|
-
// 1. Load or create manifest (auto-migrate legacy installs)
|
|
922
|
-
let manifest = loadManifest(projectRoot);
|
|
923
|
-
if (!manifest) {
|
|
924
|
-
if (needsLegacyMigration(projectRoot)) {
|
|
925
|
-
console.log(' Migrating from legacy install...');
|
|
926
|
-
const result = migrateFromLegacy(projectRoot, TEMPLATES_DIR, newVersion);
|
|
927
|
-
console.log(` Migrated ${result.synced} synced, ${result.modified} modified, ${result.userOwned} user-owned files`);
|
|
928
|
-
if (result.importedPins > 0) console.log(` Imported ${result.importedPins} pinned files from orbital-sync.json`);
|
|
929
|
-
manifest = loadManifest(projectRoot);
|
|
930
|
-
}
|
|
931
|
-
if (!manifest) {
|
|
932
|
-
console.log(' No manifest found. Run `orbital init` first.');
|
|
933
|
-
return;
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
const oldVersion = manifest.packageVersion;
|
|
938
|
-
|
|
939
|
-
// 1b. Refresh file statuses so outdated vs modified is accurate
|
|
940
|
-
refreshFileStatuses(manifest, claudeDir);
|
|
941
|
-
|
|
942
|
-
// 2. Compute update plan
|
|
943
|
-
const renameMap = loadRenameMap(TEMPLATES_DIR, oldVersion, newVersion);
|
|
944
|
-
const plan = computeUpdatePlan({
|
|
945
|
-
templatesDir: TEMPLATES_DIR,
|
|
946
|
-
claudeDir,
|
|
947
|
-
manifest,
|
|
948
|
-
newVersion,
|
|
949
|
-
renameMap,
|
|
950
|
-
});
|
|
951
|
-
|
|
952
|
-
// 3. Dry-run mode — print plan and exit
|
|
953
|
-
if (dryRun) {
|
|
954
|
-
console.log(formatPlan(plan, oldVersion, newVersion));
|
|
955
|
-
return;
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
if (plan.isEmpty && oldVersion === newVersion) {
|
|
959
|
-
console.log(' Everything up to date. No changes needed.');
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
// 4. Create backup of files that will be modified
|
|
963
|
-
const filesToBackup = getFilesToBackup(plan);
|
|
964
|
-
if (filesToBackup.length > 0) {
|
|
965
|
-
const backupDir = createBackup(claudeDir, filesToBackup);
|
|
966
|
-
if (backupDir) {
|
|
967
|
-
console.log(` Backup ${filesToBackup.length} files → ${path.relative(claudeDir, backupDir)}/`);
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
// 5. Execute plan
|
|
972
|
-
const templateInventory = buildTemplateInventory(TEMPLATES_DIR);
|
|
973
|
-
|
|
974
|
-
// 5a. Handle renames
|
|
975
|
-
for (const { from, to } of plan.toRename) {
|
|
976
|
-
const fromPath = path.join(claudeDir, from);
|
|
977
|
-
const toPath = path.join(claudeDir, to);
|
|
978
|
-
const toDir = path.dirname(toPath);
|
|
979
|
-
if (!fs.existsSync(toDir)) fs.mkdirSync(toDir, { recursive: true });
|
|
980
|
-
|
|
981
|
-
if (fs.existsSync(fromPath)) {
|
|
982
|
-
safeBackupFile(fromPath);
|
|
983
|
-
const stat = fs.lstatSync(fromPath);
|
|
984
|
-
if (stat.isSymbolicLink()) {
|
|
985
|
-
// Recreate symlink at new path pointing to remapped template
|
|
986
|
-
const target = fs.readlinkSync(fromPath);
|
|
987
|
-
fs.unlinkSync(fromPath);
|
|
988
|
-
fs.symlinkSync(target, toPath);
|
|
989
|
-
} else {
|
|
990
|
-
fs.renameSync(fromPath, toPath);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
// Transfer manifest record
|
|
995
|
-
const record = manifest.files[from];
|
|
996
|
-
if (record) {
|
|
997
|
-
delete manifest.files[from];
|
|
998
|
-
const newHash = templateInventory.get(to);
|
|
999
|
-
manifest.files[to] = { ...record, templateHash: newHash, installedHash: newHash || record.installedHash };
|
|
1000
|
-
}
|
|
1001
|
-
console.log(` RENAME ${from} → ${to}`);
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
// 5b. Add new files
|
|
1005
|
-
for (const filePath of plan.toAdd) {
|
|
1006
|
-
const templateHash = templateInventory.get(filePath);
|
|
1007
|
-
if (!templateHash) continue;
|
|
1008
|
-
|
|
1009
|
-
const destPath = path.join(claudeDir, filePath);
|
|
1010
|
-
const destDir = path.dirname(destPath);
|
|
1011
|
-
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
1012
|
-
|
|
1013
|
-
copyTemplateFile(TEMPLATES_DIR, filePath, destPath);
|
|
1014
|
-
manifest.files[filePath] = templateFileRecord(templateHash);
|
|
1015
|
-
console.log(` ADD ${filePath}`);
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
// 5c. Update changed synced/outdated files
|
|
1019
|
-
for (const filePath of plan.toUpdate) {
|
|
1020
|
-
const templateHash = templateInventory.get(filePath);
|
|
1021
|
-
if (!templateHash) continue;
|
|
1022
|
-
|
|
1023
|
-
const destPath = path.join(claudeDir, filePath);
|
|
1024
|
-
safeBackupFile(destPath);
|
|
1025
|
-
copyTemplateFile(TEMPLATES_DIR, filePath, destPath);
|
|
1026
|
-
manifest.files[filePath] = templateFileRecord(templateHash);
|
|
1027
|
-
console.log(` UPDATE ${filePath}`);
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
// 5d. Remove deleted files
|
|
1031
|
-
for (const filePath of plan.toRemove) {
|
|
1032
|
-
const absPath = path.join(claudeDir, filePath);
|
|
1033
|
-
if (fs.existsSync(absPath)) {
|
|
1034
|
-
safeBackupFile(absPath);
|
|
1035
|
-
fs.unlinkSync(absPath);
|
|
1036
|
-
}
|
|
1037
|
-
delete manifest.files[filePath];
|
|
1038
|
-
console.log(` REMOVE ${filePath}`);
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
// 5e. Update pinned/modified file records (record new template hash without touching file)
|
|
1042
|
-
for (const { file, reason, newTemplateHash } of plan.toSkip) {
|
|
1043
|
-
if (manifest.files[file]) {
|
|
1044
|
-
manifest.files[file].templateHash = newTemplateHash;
|
|
1045
|
-
}
|
|
1046
|
-
if (reason === 'modified') {
|
|
1047
|
-
console.log(` SKIP ${file} (user modified)`);
|
|
1048
|
-
} else {
|
|
1049
|
-
console.log(` SKIP ${file} (pinned)`);
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
// 5f. Clean up empty directories
|
|
1054
|
-
for (const dir of ['hooks', 'skills', 'agents', 'config/workflows']) {
|
|
1055
|
-
const dirPath = path.join(claudeDir, dir);
|
|
1056
|
-
if (fs.existsSync(dirPath)) cleanEmptyDirs(dirPath);
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
// 6. Bidirectional settings hook sync
|
|
1060
|
-
const settingsTarget = path.join(claudeDir, 'settings.local.json');
|
|
1061
|
-
const settingsSrc = path.join(TEMPLATES_DIR, 'settings-hooks.json');
|
|
1062
|
-
const syncResult = syncSettingsHooks(settingsTarget, settingsSrc, manifest.settingsHooksChecksum, renameMap);
|
|
1063
|
-
if (!syncResult.skipped) {
|
|
1064
|
-
console.log(` Settings +${syncResult.added} -${syncResult.removed} hooks (${syncResult.updated} renamed)`);
|
|
1065
|
-
manifest.settingsHooksChecksum = getTemplateChecksum(settingsSrc);
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
// 7. Config migrations
|
|
1069
|
-
const configPath = path.join(claudeDir, 'orbital.config.json');
|
|
1070
|
-
const migrationResult = migrateConfig(configPath, manifest.appliedMigrations);
|
|
1071
|
-
if (migrationResult.applied.length > 0) {
|
|
1072
|
-
manifest.appliedMigrations.push(...migrationResult.applied);
|
|
1073
|
-
console.log(` Config ${migrationResult.applied.length} migration(s) applied`);
|
|
1074
|
-
}
|
|
1075
|
-
if (migrationResult.defaultsFilled.length > 0) {
|
|
1076
|
-
console.log(` Config ${migrationResult.defaultsFilled.length} default(s) filled`);
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
// 8. Regenerate derived artifacts (always)
|
|
1080
|
-
const workflowManifestOk = writeManifest(claudeDir);
|
|
1081
|
-
console.log(` ${workflowManifestOk ? 'Updated' : 'Skipped'} .claude/config/workflow-manifest.sh`);
|
|
1082
|
-
|
|
1083
|
-
const indexContent = generateIndexMd(projectRoot, claudeDir);
|
|
1084
|
-
fs.writeFileSync(path.join(claudeDir, 'INDEX.md'), indexContent, 'utf8');
|
|
1085
|
-
console.log(` Updated .claude/INDEX.md`);
|
|
1086
|
-
|
|
1087
|
-
// 9. Update agent-triggers.json (template-managed)
|
|
1088
|
-
const triggersSrc = path.join(TEMPLATES_DIR, 'config', 'agent-triggers.json');
|
|
1089
|
-
const triggersDest = path.join(claudeDir, 'config', 'agent-triggers.json');
|
|
1090
|
-
if (fs.existsSync(triggersSrc)) {
|
|
1091
|
-
fs.copyFileSync(triggersSrc, triggersDest);
|
|
1092
|
-
console.log(` Updated .claude/config/agent-triggers.json`);
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// 10. Update scope template
|
|
1096
|
-
const scopeTemplateSrc = path.join(TEMPLATES_DIR, 'scopes', '_template.md');
|
|
1097
|
-
const scopeTemplateDest = path.join(projectRoot, 'scopes', '_template.md');
|
|
1098
|
-
if (fs.existsSync(scopeTemplateSrc)) {
|
|
1099
|
-
ensureDir(path.join(projectRoot, 'scopes'));
|
|
1100
|
-
fs.copyFileSync(scopeTemplateSrc, scopeTemplateDest);
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
// 11. Make hook scripts executable
|
|
1104
|
-
chmodScripts(path.join(claudeDir, 'hooks'));
|
|
1105
|
-
|
|
1106
|
-
// 12. Refresh global primitives
|
|
1107
|
-
seedGlobalPrimitives();
|
|
1108
|
-
|
|
1109
|
-
// 13. Update manifest metadata
|
|
1110
|
-
manifest.previousPackageVersion = oldVersion;
|
|
1111
|
-
manifest.packageVersion = newVersion;
|
|
1112
|
-
manifest.updatedAt = new Date().toISOString();
|
|
1113
|
-
saveManifest(projectRoot, manifest);
|
|
1114
|
-
|
|
1115
|
-
// 14. Validate
|
|
1116
|
-
const report = validate(projectRoot, newVersion);
|
|
1117
|
-
if (report.errors > 0) {
|
|
1118
|
-
console.log(`\n Validation: ${report.errors} errors found`);
|
|
1119
|
-
console.log(formatValidationReport(report));
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
const totalChanges = plan.toAdd.length + plan.toUpdate.length + plan.toRemove.length + plan.toRename.length;
|
|
1123
|
-
console.log(`\nUpdate complete. ${totalChanges} file changes, ${plan.toSkip.length} skipped.\n`);
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
export interface UninstallOptions {
|
|
1127
|
-
dryRun?: boolean;
|
|
1128
|
-
keepConfig?: boolean;
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
export function runUninstall(projectRoot: string, options: UninstallOptions = {}): void {
|
|
1132
|
-
const { dryRun = false, keepConfig = false } = options;
|
|
1133
|
-
const claudeDir = path.join(projectRoot, '.claude');
|
|
1134
|
-
|
|
1135
|
-
console.log(`\nOrbital Command — uninstall${dryRun ? ' (dry run)' : ''}`);
|
|
1136
|
-
console.log(`Project root: ${projectRoot}\n`);
|
|
1137
|
-
|
|
1138
|
-
const manifest = loadManifest(projectRoot);
|
|
1139
|
-
|
|
1140
|
-
// Fall back to legacy uninstall if no manifest
|
|
1141
|
-
if (!manifest) {
|
|
1142
|
-
console.log(' No manifest found — falling back to legacy uninstall.');
|
|
1143
|
-
runLegacyUninstall(projectRoot);
|
|
1144
|
-
return;
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
// Compute what to remove vs preserve
|
|
1148
|
-
const toRemove: string[] = [];
|
|
1149
|
-
const toPreserve: string[] = [];
|
|
1150
|
-
|
|
1151
|
-
for (const [filePath, record] of Object.entries(manifest.files)) {
|
|
1152
|
-
if (record.origin === 'user') {
|
|
1153
|
-
toPreserve.push(filePath);
|
|
1154
|
-
} else if (record.status === 'modified' || record.status === 'outdated') {
|
|
1155
|
-
toPreserve.push(filePath);
|
|
1156
|
-
} else {
|
|
1157
|
-
toRemove.push(filePath);
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
if (dryRun) {
|
|
1162
|
-
console.log(' Files to REMOVE:');
|
|
1163
|
-
for (const f of toRemove) console.log(` ${f}`);
|
|
1164
|
-
if (toPreserve.length > 0) {
|
|
1165
|
-
console.log(' Files to PRESERVE:');
|
|
1166
|
-
for (const f of toPreserve) console.log(` ${f} (${manifest.files[f].origin}/${manifest.files[f].status})`);
|
|
1167
|
-
}
|
|
1168
|
-
console.log(`\n Would also remove: settings hooks, generated artifacts, config files, gitignore entries`);
|
|
1169
|
-
console.log(` No changes made. Run without --dry-run to apply.`);
|
|
1170
|
-
return;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
// 1. Remove _orbital hooks from settings.local.json
|
|
1174
|
-
const settingsPath = path.join(claudeDir, 'settings.local.json');
|
|
1175
|
-
const removedHooks = removeAllOrbitalHooks(settingsPath);
|
|
1176
|
-
console.log(` Removed ${removedHooks} orbital hook registrations`);
|
|
1177
|
-
|
|
1178
|
-
// 2. Delete template files (synced + pinned, not modified or user-owned)
|
|
1179
|
-
let filesRemoved = 0;
|
|
1180
|
-
for (const filePath of toRemove) {
|
|
1181
|
-
const absPath = path.join(claudeDir, filePath);
|
|
1182
|
-
if (fs.existsSync(absPath)) {
|
|
1183
|
-
fs.unlinkSync(absPath);
|
|
1184
|
-
filesRemoved++;
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
console.log(` Removed ${filesRemoved} template files`);
|
|
1188
|
-
if (toPreserve.length > 0) {
|
|
1189
|
-
console.log(` Preserved ${toPreserve.length} user/modified files`);
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
// 3. Clean up empty directories
|
|
1193
|
-
for (const dir of ['hooks', 'skills', 'agents', 'config/workflows', 'quick', 'anti-patterns']) {
|
|
1194
|
-
const dirPath = path.join(claudeDir, dir);
|
|
1195
|
-
if (fs.existsSync(dirPath)) cleanEmptyDirs(dirPath);
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
// 4. Remove generated artifacts
|
|
1199
|
-
for (const artifact of manifest.generatedArtifacts) {
|
|
1200
|
-
const artifactPath = path.join(claudeDir, artifact);
|
|
1201
|
-
if (fs.existsSync(artifactPath)) {
|
|
1202
|
-
fs.unlinkSync(artifactPath);
|
|
1203
|
-
console.log(` Removed .claude/${artifact}`);
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// 5. Remove template-sourced config files
|
|
1208
|
-
const configFiles = [
|
|
1209
|
-
'config/agent-triggers.json',
|
|
1210
|
-
'config/workflow.json',
|
|
1211
|
-
'lessons-learned.md',
|
|
1212
|
-
];
|
|
1213
|
-
for (const file of configFiles) {
|
|
1214
|
-
const filePath = path.join(claudeDir, file);
|
|
1215
|
-
if (fs.existsSync(filePath)) {
|
|
1216
|
-
fs.unlinkSync(filePath);
|
|
1217
|
-
console.log(` Removed .claude/${file}`);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
// Remove config/workflows/ directory entirely
|
|
1222
|
-
const workflowsDir = path.join(claudeDir, 'config', 'workflows');
|
|
1223
|
-
if (fs.existsSync(workflowsDir)) {
|
|
1224
|
-
fs.rmSync(workflowsDir, { recursive: true, force: true });
|
|
1225
|
-
console.log(` Removed .claude/config/workflows/`);
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
// 6. Remove gitignore entries
|
|
1229
|
-
removeGitignoreEntries(projectRoot, manifest.gitignoreEntries);
|
|
1230
|
-
console.log(` Cleaned .gitignore`);
|
|
1231
|
-
|
|
1232
|
-
// 7. Deregister from global registry
|
|
1233
|
-
if (unregisterProject(projectRoot)) {
|
|
1234
|
-
console.log(` Removed project from ~/.orbital/config.json`);
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
// 8. Remove orbital config and manifest (unless --keep-config)
|
|
1238
|
-
if (!keepConfig) {
|
|
1239
|
-
const toClean = ['orbital.config.json', 'orbital-manifest.json', 'orbital-sync.json'];
|
|
1240
|
-
for (const file of toClean) {
|
|
1241
|
-
const filePath = path.join(claudeDir, file);
|
|
1242
|
-
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
// Remove backups directory
|
|
1246
|
-
const backupsDir = path.join(claudeDir, '.orbital-backups');
|
|
1247
|
-
if (fs.existsSync(backupsDir)) fs.rmSync(backupsDir, { recursive: true, force: true });
|
|
1248
|
-
|
|
1249
|
-
console.log(` Removed orbital config and manifest`);
|
|
1250
|
-
} else {
|
|
1251
|
-
// Still remove the manifest — it's invalid after uninstall
|
|
1252
|
-
const manifestPath = path.join(claudeDir, 'orbital-manifest.json');
|
|
1253
|
-
if (fs.existsSync(manifestPath)) fs.unlinkSync(manifestPath);
|
|
1254
|
-
console.log(` Kept orbital.config.json (--keep-config)`);
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
// Clean up remaining empty directories
|
|
1258
|
-
for (const dir of ['config', 'quick', 'anti-patterns', 'review-verdicts']) {
|
|
1259
|
-
const dirPath = path.join(claudeDir, dir);
|
|
1260
|
-
if (fs.existsSync(dirPath)) cleanEmptyDirs(dirPath);
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
const total = removedHooks + filesRemoved;
|
|
1264
|
-
console.log(`\nUninstall complete. ${total} items removed.`);
|
|
1265
|
-
if (toPreserve.length > 0) {
|
|
1266
|
-
console.log(`Note: ${toPreserve.length} user/modified files were preserved.`);
|
|
1267
|
-
}
|
|
1268
|
-
console.log(`Note: scopes/ and .claude/orbital-events/ were preserved.\n`);
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
/** Legacy uninstall for projects without a manifest (backward compat). */
|
|
1272
|
-
function runLegacyUninstall(projectRoot: string): void {
|
|
1273
|
-
const claudeDir = path.join(projectRoot, '.claude');
|
|
1274
|
-
|
|
1275
|
-
// Remove orbital hooks from settings.local.json
|
|
1276
|
-
const settingsPath = path.join(claudeDir, 'settings.local.json');
|
|
1277
|
-
const removedHooks = removeAllOrbitalHooks(settingsPath);
|
|
1278
|
-
console.log(` Removed ${removedHooks} orbital hook registrations`);
|
|
1279
|
-
|
|
1280
|
-
// Delete hooks/skills/agents that match template files
|
|
1281
|
-
for (const dir of ['hooks', 'skills', 'agents']) {
|
|
1282
|
-
const templateFiles = listTemplateFiles(path.join(TEMPLATES_DIR, dir), path.join(claudeDir, dir));
|
|
1283
|
-
let removed = 0;
|
|
1284
|
-
for (const f of templateFiles) {
|
|
1285
|
-
if (fs.existsSync(f)) { fs.unlinkSync(f); removed++; }
|
|
1286
|
-
}
|
|
1287
|
-
const dirPath = path.join(claudeDir, dir);
|
|
1288
|
-
if (fs.existsSync(dirPath)) cleanEmptyDirs(dirPath);
|
|
1289
|
-
console.log(` Removed ${removed} ${dir} files`);
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
console.log(`\nLegacy uninstall complete.`);
|
|
1293
|
-
console.log(`Note: scopes/ and .claude/orbital-events/ were preserved.\n`);
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
/** Remove Orbital-added entries from .gitignore. */
|
|
1297
|
-
function removeGitignoreEntries(projectRoot: string, entries: string[]): void {
|
|
1298
|
-
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
1299
|
-
if (!fs.existsSync(gitignorePath)) return;
|
|
1300
|
-
|
|
1301
|
-
let content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
1302
|
-
const marker = '# Orbital Command';
|
|
1303
|
-
|
|
1304
|
-
// Try to remove the entire block
|
|
1305
|
-
const markerIdx = content.indexOf(marker);
|
|
1306
|
-
if (markerIdx !== -1) {
|
|
1307
|
-
// Find the block boundaries: from the marker to the next non-empty/non-orbital line
|
|
1308
|
-
const before = content.slice(0, markerIdx).replace(/\n+$/, '');
|
|
1309
|
-
const after = content.slice(markerIdx);
|
|
1310
|
-
const lines = after.split('\n');
|
|
1311
|
-
let endIdx = 0;
|
|
1312
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1313
|
-
const line = lines[i].trim();
|
|
1314
|
-
if (i === 0) { endIdx = i + 1; continue; } // skip the marker line
|
|
1315
|
-
if (line === '' || entries.includes(line)) { endIdx = i + 1; continue; }
|
|
1316
|
-
break;
|
|
1317
|
-
}
|
|
1318
|
-
const remaining = lines.slice(endIdx).join('\n');
|
|
1319
|
-
content = before + (remaining ? '\n' + remaining : '') + '\n';
|
|
1320
|
-
fs.writeFileSync(gitignorePath, content, 'utf-8');
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
/** Copy a single template file to a destination, resolving the template path. */
|
|
1325
|
-
function copyTemplateFile(templatesDir: string, claudeRelPath: string, destPath: string): void {
|
|
1326
|
-
const templateRelPath = reverseRemapPath(claudeRelPath);
|
|
1327
|
-
const srcPath = path.join(templatesDir, templateRelPath);
|
|
1328
|
-
if (!fs.existsSync(srcPath)) {
|
|
1329
|
-
throw new Error(`Template file not found: ${templateRelPath}`);
|
|
1330
|
-
}
|
|
1331
|
-
const destDir = path.dirname(destPath);
|
|
1332
|
-
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
1333
|
-
safeCopyTemplate(srcPath, destPath);
|
|
1334
|
-
}
|
|
922
|
+
// runUpdate and runUninstall have been extracted to update.ts and uninstall.ts
|
|
923
|
+
// and are re-exported above for backward compatibility.
|
package/server/launch.ts
CHANGED
|
@@ -46,7 +46,10 @@ export function parseEventFile(filePath: string): RawEvent | null {
|
|
|
46
46
|
timestamp: String(parsed.timestamp),
|
|
47
47
|
};
|
|
48
48
|
} catch (err) {
|
|
49
|
-
|
|
49
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
50
|
+
if (code !== 'ENOENT') {
|
|
51
|
+
log.warn('Failed to parse event file', { file: filePath, error: (err as Error).message });
|
|
52
|
+
}
|
|
50
53
|
return null;
|
|
51
54
|
}
|
|
52
55
|
}
|
|
@@ -26,7 +26,6 @@ import { startScopeWatcher } from './watchers/scope-watcher.js';
|
|
|
26
26
|
import { startEventWatcher } from './watchers/event-watcher.js';
|
|
27
27
|
import { resolveStaleDispatches, resolveActiveDispatchesForScope, resolveDispatchesByPid, resolveDispatchesByDispatchId, linkPidToDispatch, tryAutoRevertAndClear } from './utils/dispatch-utils.js';
|
|
28
28
|
import { syncClaudeSessionsToDB } from './services/claude-session-service.js';
|
|
29
|
-
import { TelemetryService } from './services/telemetry-service.js';
|
|
30
29
|
import { ensureDynamicProfiles } from './utils/terminal-launcher.js';
|
|
31
30
|
import { createLogger } from './utils/logger.js';
|
|
32
31
|
|
|
@@ -36,6 +35,11 @@ const log = createLogger('project-context');
|
|
|
36
35
|
|
|
37
36
|
export type ProjectStatus = 'active' | 'error' | 'offline';
|
|
38
37
|
|
|
38
|
+
interface TelemetryEnabled {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
uploadChangedSessions(): Promise<unknown>;
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
export interface ProjectContext {
|
|
40
44
|
/** Project slug ID (derived from directory name) */
|
|
41
45
|
id: string;
|
|
@@ -61,7 +65,8 @@ export interface ProjectContext {
|
|
|
61
65
|
workflowService: WorkflowService;
|
|
62
66
|
gitService: GitService;
|
|
63
67
|
githubService: GitHubService;
|
|
64
|
-
telemetryService:
|
|
68
|
+
telemetryService: TelemetryEnabled | null;
|
|
69
|
+
telemetryRouter: import('express').Router | null;
|
|
65
70
|
|
|
66
71
|
// Watchers
|
|
67
72
|
scopeWatcher: FSWatcher;
|
|
@@ -122,8 +127,8 @@ export async function createProjectContext(
|
|
|
122
127
|
const gateService = new GateService(db, emitter);
|
|
123
128
|
const deployService = new DeployService(db, emitter);
|
|
124
129
|
const sprintService = new SprintService(db, emitter, scopeService);
|
|
125
|
-
const sprintOrchestrator = new SprintOrchestrator(db, emitter, sprintService, scopeService, workflowEngine, config.projectRoot);
|
|
126
|
-
const batchOrchestrator = new BatchOrchestrator(db, emitter, sprintService, scopeService, workflowEngine, config.projectRoot);
|
|
130
|
+
const sprintOrchestrator = new SprintOrchestrator(db, emitter, sprintService, scopeService, workflowEngine, config.projectRoot, config);
|
|
131
|
+
const batchOrchestrator = new BatchOrchestrator(db, emitter, sprintService, scopeService, workflowEngine, config.projectRoot, config);
|
|
127
132
|
const readinessService = new ReadinessService(scopeService, gateService, workflowEngine, config.projectRoot);
|
|
128
133
|
const workflowService = new WorkflowService(config.configDir, workflowEngine, config.scopesDir, getDefaultConfigPath());
|
|
129
134
|
workflowService.setSocketServer(emitter);
|
|
@@ -132,7 +137,17 @@ export async function createProjectContext(
|
|
|
132
137
|
workflowEngine.reload(workflowService.getActive());
|
|
133
138
|
const gitService = new GitService(config.projectRoot, scopeCache);
|
|
134
139
|
const githubService = new GitHubService(config.projectRoot);
|
|
135
|
-
|
|
140
|
+
|
|
141
|
+
let telemetryService: TelemetryEnabled | null = null;
|
|
142
|
+
let telemetryRouter: import('express').Router | null = null;
|
|
143
|
+
const telemetryMod = './services/telemetry-service.js';
|
|
144
|
+
try {
|
|
145
|
+
const mod = await import(telemetryMod);
|
|
146
|
+
telemetryService = new mod.TelemetryService(db, config.telemetry, config.projectName, config.projectRoot);
|
|
147
|
+
if (telemetryService?.enabled) {
|
|
148
|
+
telemetryRouter = mod.createTelemetryRoutes({ telemetryService });
|
|
149
|
+
}
|
|
150
|
+
} catch { /* telemetry service not installed */ }
|
|
136
151
|
|
|
137
152
|
// Wire active-group guard
|
|
138
153
|
scopeService.setActiveGroupCheck((scopeId) => sprintService.getActiveGroupForScope(scopeId));
|
|
@@ -190,7 +205,7 @@ export async function createProjectContext(
|
|
|
190
205
|
|
|
191
206
|
// Wire status change callbacks
|
|
192
207
|
scopeService.onStatusChange((scopeId, newStatus) => {
|
|
193
|
-
if (newStatus
|
|
208
|
+
if (workflowEngine.isTerminalStatus(newStatus)) sprintOrchestrator.onScopeReachedDev(scopeId);
|
|
194
209
|
batchOrchestrator.onScopeStatusChanged(scopeId, newStatus);
|
|
195
210
|
});
|
|
196
211
|
scopeService.onStatusChange((scopeId, newStatus) => {
|
|
@@ -220,14 +235,14 @@ export async function createProjectContext(
|
|
|
220
235
|
if (staleBatchesResolved > 0) log.info('Resolved stale batches', { count: staleBatchesResolved });
|
|
221
236
|
|
|
222
237
|
// Resolve stale dispatches
|
|
223
|
-
resolveStaleDispatches(db, emitter, scopeService, workflowEngine);
|
|
238
|
+
resolveStaleDispatches(db, emitter, scopeService, workflowEngine, config.dispatch.staleTimeoutMinutes);
|
|
224
239
|
|
|
225
240
|
// Initial session sync + legacy purge (Fix 7)
|
|
226
241
|
syncClaudeSessionsToDB(db, scopeService, config.projectRoot).then((count) => {
|
|
227
242
|
if (count > 0) log.info('Synced sessions', { id: projectId, count });
|
|
228
243
|
const purged = db.prepare("DELETE FROM sessions WHERE action IS NULL AND id LIKE 'claude-%'").run();
|
|
229
244
|
if (purged.changes > 0) log.info('Purged legacy session rows', { count: purged.changes });
|
|
230
|
-
if (telemetryService
|
|
245
|
+
if (telemetryService?.enabled) {
|
|
231
246
|
telemetryService.uploadChangedSessions().catch(() => {});
|
|
232
247
|
}
|
|
233
248
|
}).catch(err => log.error('Session sync failed', { error: err.message }));
|
|
@@ -245,13 +260,13 @@ export async function createProjectContext(
|
|
|
245
260
|
}, 30_000));
|
|
246
261
|
|
|
247
262
|
intervals.push(setInterval(() => {
|
|
248
|
-
resolveStaleDispatches(db, emitter, scopeService, workflowEngine);
|
|
263
|
+
resolveStaleDispatches(db, emitter, scopeService, workflowEngine, config.dispatch.staleTimeoutMinutes);
|
|
249
264
|
}, 30_000));
|
|
250
265
|
|
|
251
266
|
intervals.push(setInterval(async () => {
|
|
252
267
|
const count = await syncClaudeSessionsToDB(db, scopeService, config.projectRoot);
|
|
253
268
|
if (count > 0) emitter.emit('session:updated', { type: 'resync', count });
|
|
254
|
-
if (telemetryService
|
|
269
|
+
if (telemetryService?.enabled) {
|
|
255
270
|
telemetryService.uploadChangedSessions().catch(() => {});
|
|
256
271
|
}
|
|
257
272
|
}, 5 * 60_000));
|
|
@@ -289,6 +304,7 @@ export async function createProjectContext(
|
|
|
289
304
|
gitService,
|
|
290
305
|
githubService,
|
|
291
306
|
telemetryService,
|
|
307
|
+
telemetryRouter,
|
|
292
308
|
scopeWatcher,
|
|
293
309
|
eventWatcher,
|
|
294
310
|
intervals,
|