runwork 0.15.0 → 0.15.2
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/dist/index.js +19272 -116
- package/package.json +4 -4
- package/dist/__tests__/curl-parser.test.d.ts +0 -1
- package/dist/__tests__/curl-parser.test.js +0 -250
- package/dist/__tests__/install-scripts.test.d.ts +0 -1
- package/dist/__tests__/install-scripts.test.js +0 -280
- package/dist/__tests__/types-manager.test.d.ts +0 -1
- package/dist/__tests__/types-manager.test.js +0 -89
- package/dist/agents/__tests__/chatgpt-registry.test.d.ts +0 -1
- package/dist/agents/__tests__/chatgpt-registry.test.js +0 -61
- package/dist/agents/__tests__/claude-code-managed-block.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-code-managed-block.test.js +0 -97
- package/dist/agents/__tests__/claude-code-plugin.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-code-plugin.test.js +0 -47
- package/dist/agents/__tests__/claude-code-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-code-stats.test.js +0 -390
- package/dist/agents/__tests__/claude-desktop-memory.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-memory.test.js +0 -154
- package/dist/agents/__tests__/claude-desktop-plugin-tree.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-plugin-tree.test.js +0 -161
- package/dist/agents/__tests__/claude-desktop-plugin.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-plugin.test.js +0 -117
- package/dist/agents/__tests__/claude-desktop-rpm.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-rpm.test.js +0 -126
- package/dist/agents/__tests__/claude-desktop-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/claude-desktop-stats.test.js +0 -280
- package/dist/agents/__tests__/codex-instructions.test.d.ts +0 -1
- package/dist/agents/__tests__/codex-instructions.test.js +0 -57
- package/dist/agents/__tests__/codex-minimum-permissions.test.d.ts +0 -1
- package/dist/agents/__tests__/codex-minimum-permissions.test.js +0 -82
- package/dist/agents/__tests__/codex-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/codex-stats.test.js +0 -370
- package/dist/agents/__tests__/cursor-merge.test.d.ts +0 -1
- package/dist/agents/__tests__/cursor-merge.test.js +0 -66
- package/dist/agents/__tests__/cursor-stats.test.d.ts +0 -1
- package/dist/agents/__tests__/cursor-stats.test.js +0 -183
- package/dist/agents/__tests__/defaults-merge.test.d.ts +0 -1
- package/dist/agents/__tests__/defaults-merge.test.js +0 -268
- package/dist/agents/__tests__/detection.test.d.ts +0 -1
- package/dist/agents/__tests__/detection.test.js +0 -89
- package/dist/agents/__tests__/graceful-degradation.test.d.ts +0 -1
- package/dist/agents/__tests__/graceful-degradation.test.js +0 -129
- package/dist/agents/__tests__/instruction-hint.test.d.ts +0 -1
- package/dist/agents/__tests__/instruction-hint.test.js +0 -69
- package/dist/agents/__tests__/intro-skill.test.d.ts +0 -1
- package/dist/agents/__tests__/intro-skill.test.js +0 -193
- package/dist/agents/__tests__/schema-validation.test.d.ts +0 -1
- package/dist/agents/__tests__/schema-validation.test.js +0 -212
- package/dist/agents/__tests__/skill-slug.test.d.ts +0 -1
- package/dist/agents/__tests__/skill-slug.test.js +0 -77
- package/dist/agents/claude-code.d.ts +0 -53
- package/dist/agents/claude-code.js +0 -754
- package/dist/agents/claude-desktop-plugin-tree.d.ts +0 -59
- package/dist/agents/claude-desktop-plugin-tree.js +0 -143
- package/dist/agents/claude-desktop.d.ts +0 -47
- package/dist/agents/claude-desktop.js +0 -684
- package/dist/agents/cline.d.ts +0 -15
- package/dist/agents/cline.js +0 -130
- package/dist/agents/codex.d.ts +0 -44
- package/dist/agents/codex.js +0 -453
- package/dist/agents/conversation-skills.d.ts +0 -18
- package/dist/agents/conversation-skills.js +0 -229
- package/dist/agents/cursor.d.ts +0 -48
- package/dist/agents/cursor.js +0 -324
- package/dist/agents/default-config.d.ts +0 -30
- package/dist/agents/default-config.js +0 -67
- package/dist/agents/defaults-merge.d.ts +0 -72
- package/dist/agents/defaults-merge.js +0 -131
- package/dist/agents/detect.d.ts +0 -4
- package/dist/agents/detect.js +0 -67
- package/dist/agents/detection.d.ts +0 -18
- package/dist/agents/detection.js +0 -94
- package/dist/agents/gemini.d.ts +0 -15
- package/dist/agents/gemini.js +0 -122
- package/dist/agents/generic-adapter.d.ts +0 -25
- package/dist/agents/generic-adapter.js +0 -151
- package/dist/agents/intro-skill.d.ts +0 -45
- package/dist/agents/intro-skill.js +0 -447
- package/dist/agents/registry-data.d.ts +0 -192
- package/dist/agents/registry-data.js +0 -669
- package/dist/agents/registry.d.ts +0 -13
- package/dist/agents/registry.js +0 -36
- package/dist/agents/runtime-detection.d.ts +0 -82
- package/dist/agents/runtime-detection.js +0 -271
- package/dist/agents/session-start-hook.d.ts +0 -37
- package/dist/agents/session-start-hook.js +0 -159
- package/dist/agents/types.d.ts +0 -171
- package/dist/agents/types.js +0 -75
- package/dist/agents/utils/instruction-hint.d.ts +0 -21
- package/dist/agents/utils/instruction-hint.js +0 -108
- package/dist/agents/utils/json-config.d.ts +0 -19
- package/dist/agents/utils/json-config.js +0 -69
- package/dist/agents/utils/toml-config.d.ts +0 -8
- package/dist/agents/utils/toml-config.js +0 -27
- package/dist/agents/windsurf.d.ts +0 -14
- package/dist/agents/windsurf.js +0 -102
- package/dist/api/__tests__/client.test.d.ts +0 -1
- package/dist/api/__tests__/client.test.js +0 -272
- package/dist/api/client.d.ts +0 -326
- package/dist/api/client.js +0 -427
- package/dist/auth/__tests__/login-flow.test.d.ts +0 -1
- package/dist/auth/__tests__/login-flow.test.js +0 -101
- package/dist/auth/__tests__/store.test.d.ts +0 -1
- package/dist/auth/__tests__/store.test.js +0 -132
- package/dist/auth/login-flow.d.ts +0 -26
- package/dist/auth/login-flow.js +0 -137
- package/dist/auth/store.d.ts +0 -5
- package/dist/auth/store.js +0 -62
- package/dist/commands/__tests__/clone-args.test.d.ts +0 -1
- package/dist/commands/__tests__/clone-args.test.js +0 -51
- package/dist/commands/__tests__/clone-source-result.test.d.ts +0 -1
- package/dist/commands/__tests__/clone-source-result.test.js +0 -45
- package/dist/commands/__tests__/info-merge.test.d.ts +0 -1
- package/dist/commands/__tests__/info-merge.test.js +0 -55
- package/dist/commands/__tests__/mcp-entries.test.d.ts +0 -1
- package/dist/commands/__tests__/mcp-entries.test.js +0 -48
- package/dist/commands/__tests__/setup-persona.test.d.ts +0 -1
- package/dist/commands/__tests__/setup-persona.test.js +0 -31
- package/dist/commands/__tests__/setup.test.d.ts +0 -1
- package/dist/commands/__tests__/setup.test.js +0 -81
- package/dist/commands/__tests__/sync-redetect.test.d.ts +0 -1
- package/dist/commands/__tests__/sync-redetect.test.js +0 -96
- package/dist/commands/__tests__/sync-telemetry.test.d.ts +0 -1
- package/dist/commands/__tests__/sync-telemetry.test.js +0 -281
- package/dist/commands/__tests__/upgrade.test.d.ts +0 -1
- package/dist/commands/__tests__/upgrade.test.js +0 -157
- package/dist/commands/agents.d.ts +0 -2
- package/dist/commands/agents.js +0 -160
- package/dist/commands/api-keys.d.ts +0 -2
- package/dist/commands/api-keys.js +0 -95
- package/dist/commands/apps.d.ts +0 -2
- package/dist/commands/apps.js +0 -82
- package/dist/commands/build-plugin.d.ts +0 -2
- package/dist/commands/build-plugin.js +0 -135
- package/dist/commands/clone.d.ts +0 -60
- package/dist/commands/clone.js +0 -271
- package/dist/commands/components.d.ts +0 -2
- package/dist/commands/components.js +0 -43
- package/dist/commands/deploy.d.ts +0 -2
- package/dist/commands/deploy.js +0 -279
- package/dist/commands/dev.d.ts +0 -11
- package/dist/commands/dev.js +0 -1011
- package/dist/commands/doctor.d.ts +0 -2
- package/dist/commands/doctor.js +0 -230
- package/dist/commands/endpoints.d.ts +0 -2
- package/dist/commands/endpoints.js +0 -189
- package/dist/commands/entities.d.ts +0 -2
- package/dist/commands/entities.js +0 -198
- package/dist/commands/files.d.ts +0 -2
- package/dist/commands/files.js +0 -193
- package/dist/commands/inbox.d.ts +0 -11
- package/dist/commands/inbox.js +0 -60
- package/dist/commands/info.d.ts +0 -178
- package/dist/commands/info.js +0 -455
- package/dist/commands/init.d.ts +0 -21
- package/dist/commands/init.js +0 -185
- package/dist/commands/integrations.d.ts +0 -2
- package/dist/commands/integrations.js +0 -233
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +0 -44
- package/dist/commands/logout.d.ts +0 -2
- package/dist/commands/logout.js +0 -13
- package/dist/commands/logs.d.ts +0 -2
- package/dist/commands/logs.js +0 -232
- package/dist/commands/mcp-entries.d.ts +0 -13
- package/dist/commands/mcp-entries.js +0 -26
- package/dist/commands/mcp.d.ts +0 -2
- package/dist/commands/mcp.js +0 -245
- package/dist/commands/open.d.ts +0 -2
- package/dist/commands/open.js +0 -53
- package/dist/commands/resume.d.ts +0 -2
- package/dist/commands/resume.js +0 -289
- package/dist/commands/save-convo.d.ts +0 -8
- package/dist/commands/save-convo.js +0 -20
- package/dist/commands/schedules.d.ts +0 -2
- package/dist/commands/schedules.js +0 -221
- package/dist/commands/setup.d.ts +0 -27
- package/dist/commands/setup.js +0 -177
- package/dist/commands/share-convo.d.ts +0 -24
- package/dist/commands/share-convo.js +0 -167
- package/dist/commands/skills.d.ts +0 -2
- package/dist/commands/skills.js +0 -352
- package/dist/commands/sync-telemetry.d.ts +0 -33
- package/dist/commands/sync-telemetry.js +0 -186
- package/dist/commands/sync.d.ts +0 -37
- package/dist/commands/sync.js +0 -702
- package/dist/commands/uninstall.d.ts +0 -2
- package/dist/commands/uninstall.js +0 -145
- package/dist/commands/upgrade.d.ts +0 -14
- package/dist/commands/upgrade.js +0 -217
- package/dist/commands/validate.d.ts +0 -2
- package/dist/commands/validate.js +0 -89
- package/dist/commands/welcome.d.ts +0 -1
- package/dist/commands/welcome.js +0 -86
- package/dist/commands/workflows.d.ts +0 -2
- package/dist/commands/workflows.js +0 -187
- package/dist/deploy/__tests__/deploy-state.test.d.ts +0 -1
- package/dist/deploy/__tests__/deploy-state.test.js +0 -80
- package/dist/deploy/__tests__/deploy-status.test.d.ts +0 -1
- package/dist/deploy/__tests__/deploy-status.test.js +0 -41
- package/dist/deploy/__tests__/detach-args.test.d.ts +0 -1
- package/dist/deploy/__tests__/detach-args.test.js +0 -30
- package/dist/deploy/deploy-state.d.ts +0 -36
- package/dist/deploy/deploy-state.js +0 -63
- package/dist/deploy/deploy-status.d.ts +0 -21
- package/dist/deploy/deploy-status.js +0 -36
- package/dist/deploy/detach.d.ts +0 -32
- package/dist/deploy/detach.js +0 -71
- package/dist/dev/__tests__/attach.test.d.ts +0 -1
- package/dist/dev/__tests__/attach.test.js +0 -296
- package/dist/dev/__tests__/detach.test.d.ts +0 -1
- package/dist/dev/__tests__/detach.test.js +0 -404
- package/dist/dev/__tests__/preview-url-poller.test.d.ts +0 -1
- package/dist/dev/__tests__/preview-url-poller.test.js +0 -149
- package/dist/dev/__tests__/session.test.d.ts +0 -1
- package/dist/dev/__tests__/session.test.js +0 -446
- package/dist/dev/__tests__/startup-sync.test.d.ts +0 -1
- package/dist/dev/__tests__/startup-sync.test.js +0 -32
- package/dist/dev/__tests__/stop.test.d.ts +0 -1
- package/dist/dev/__tests__/stop.test.js +0 -172
- package/dist/dev/attach.d.ts +0 -120
- package/dist/dev/attach.js +0 -269
- package/dist/dev/detach.d.ts +0 -187
- package/dist/dev/detach.js +0 -292
- package/dist/dev/preview-url-poller.d.ts +0 -35
- package/dist/dev/preview-url-poller.js +0 -50
- package/dist/dev/session.d.ts +0 -175
- package/dist/dev/session.js +0 -258
- package/dist/dev/startup-sync.d.ts +0 -23
- package/dist/dev/startup-sync.js +0 -14
- package/dist/dev/stop.d.ts +0 -52
- package/dist/dev/stop.js +0 -101
- package/dist/devtools/registry-data.d.ts +0 -46
- package/dist/devtools/registry-data.js +0 -40
- package/dist/generated/bundled-types.d.ts +0 -1
- package/dist/generated/bundled-types.js +0 -46
- package/dist/generated/version.d.ts +0 -1
- package/dist/generated/version.js +0 -2
- package/dist/git/__tests__/auto-commit.test.d.ts +0 -1
- package/dist/git/__tests__/auto-commit.test.js +0 -431
- package/dist/git/__tests__/classify-sync-error.test.d.ts +0 -1
- package/dist/git/__tests__/classify-sync-error.test.js +0 -57
- package/dist/git/__tests__/credential-helper-e2e.test.d.ts +0 -21
- package/dist/git/__tests__/credential-helper-e2e.test.js +0 -195
- package/dist/git/__tests__/credentials.test.d.ts +0 -1
- package/dist/git/__tests__/credentials.test.js +0 -141
- package/dist/git/__tests__/deploy-guard.test.d.ts +0 -1
- package/dist/git/__tests__/deploy-guard.test.js +0 -61
- package/dist/git/__tests__/ensure-git-credential-helper.test.d.ts +0 -1
- package/dist/git/__tests__/ensure-git-credential-helper.test.js +0 -133
- package/dist/git/__tests__/ensure-runwork-remote.test.d.ts +0 -1
- package/dist/git/__tests__/ensure-runwork-remote.test.js +0 -109
- package/dist/git/__tests__/identity.test.d.ts +0 -1
- package/dist/git/__tests__/identity.test.js +0 -146
- package/dist/git/__tests__/manifest.test.d.ts +0 -1
- package/dist/git/__tests__/manifest.test.js +0 -446
- package/dist/git/__tests__/preflight-resolution.test.d.ts +0 -1
- package/dist/git/__tests__/preflight-resolution.test.js +0 -366
- package/dist/git/__tests__/preflight.test.d.ts +0 -1
- package/dist/git/__tests__/preflight.test.js +0 -36
- package/dist/git/__tests__/repo-config.test.d.ts +0 -1
- package/dist/git/__tests__/repo-config.test.js +0 -26
- package/dist/git/__tests__/sync.test.d.ts +0 -1
- package/dist/git/__tests__/sync.test.js +0 -405
- package/dist/git/auto-commit.d.ts +0 -35
- package/dist/git/auto-commit.js +0 -293
- package/dist/git/classify-sync-error.d.ts +0 -21
- package/dist/git/classify-sync-error.js +0 -111
- package/dist/git/credentials.d.ts +0 -75
- package/dist/git/credentials.js +0 -164
- package/dist/git/critical-files.d.ts +0 -20
- package/dist/git/critical-files.js +0 -68
- package/dist/git/deploy-guard.d.ts +0 -53
- package/dist/git/deploy-guard.js +0 -78
- package/dist/git/identity.d.ts +0 -44
- package/dist/git/identity.js +0 -133
- package/dist/git/preflight.d.ts +0 -57
- package/dist/git/preflight.js +0 -276
- package/dist/git/remote.d.ts +0 -37
- package/dist/git/remote.js +0 -75
- package/dist/git/repo-config.d.ts +0 -8
- package/dist/git/repo-config.js +0 -29
- package/dist/git/sync.d.ts +0 -15
- package/dist/git/sync.js +0 -159
- package/dist/health/__tests__/checks.test.d.ts +0 -1
- package/dist/health/__tests__/checks.test.js +0 -357
- package/dist/health/__tests__/cli-distribution-checks.test.d.ts +0 -1
- package/dist/health/__tests__/cli-distribution-checks.test.js +0 -196
- package/dist/health/__tests__/fix.test.d.ts +0 -1
- package/dist/health/__tests__/fix.test.js +0 -72
- package/dist/health/__tests__/runner-filter.test.d.ts +0 -1
- package/dist/health/__tests__/runner-filter.test.js +0 -61
- package/dist/health/checks.d.ts +0 -42
- package/dist/health/checks.js +0 -636
- package/dist/health/fix.d.ts +0 -22
- package/dist/health/fix.js +0 -62
- package/dist/health/runner.d.ts +0 -8
- package/dist/health/runner.js +0 -47
- package/dist/health/types.d.ts +0 -12
- package/dist/health/types.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/logs/__tests__/tailer-format.test.d.ts +0 -1
- package/dist/logs/__tests__/tailer-format.test.js +0 -43
- package/dist/logs/tailer.d.ts +0 -17
- package/dist/logs/tailer.js +0 -217
- package/dist/sync/__tests__/change-detect.test.d.ts +0 -1
- package/dist/sync/__tests__/change-detect.test.js +0 -123
- package/dist/sync/__tests__/hash.test.d.ts +0 -1
- package/dist/sync/__tests__/hash.test.js +0 -18
- package/dist/sync/change-detect.d.ts +0 -19
- package/dist/sync/change-detect.js +0 -136
- package/dist/sync/conflict-ui.d.ts +0 -5
- package/dist/sync/conflict-ui.js +0 -78
- package/dist/sync/executor.d.ts +0 -23
- package/dist/sync/executor.js +0 -102
- package/dist/sync/hash.d.ts +0 -1
- package/dist/sync/hash.js +0 -4
- package/dist/sync/types.d.ts +0 -24
- package/dist/sync/types.js +0 -1
- package/dist/template/manifest.d.ts +0 -17
- package/dist/template/manifest.js +0 -145
- package/dist/tools/types.d.ts +0 -52
- package/dist/tools/types.js +0 -8
- package/dist/types-manager.d.ts +0 -1
- package/dist/types-manager.js +0 -35
- package/dist/types.d.ts +0 -349
- package/dist/types.js +0 -1
- package/dist/ui/__tests__/banner.test.d.ts +0 -1
- package/dist/ui/__tests__/banner.test.js +0 -98
- package/dist/ui/__tests__/colors.test.d.ts +0 -1
- package/dist/ui/__tests__/colors.test.js +0 -22
- package/dist/ui/__tests__/keyboard.test.d.ts +0 -1
- package/dist/ui/__tests__/keyboard.test.js +0 -34
- package/dist/ui/__tests__/status-line.test.d.ts +0 -1
- package/dist/ui/__tests__/status-line.test.js +0 -54
- package/dist/ui/banner.d.ts +0 -29
- package/dist/ui/banner.js +0 -153
- package/dist/ui/colors.d.ts +0 -4
- package/dist/ui/colors.js +0 -7
- package/dist/ui/keyboard.d.ts +0 -12
- package/dist/ui/keyboard.js +0 -61
- package/dist/ui/status-line.d.ts +0 -6
- package/dist/ui/status-line.js +0 -53
- package/dist/utils/__tests__/format-error.test.d.ts +0 -1
- package/dist/utils/__tests__/format-error.test.js +0 -43
- package/dist/utils/__tests__/help-json.test.d.ts +0 -1
- package/dist/utils/__tests__/help-json.test.js +0 -39
- package/dist/utils/__tests__/http.test.d.ts +0 -1
- package/dist/utils/__tests__/http.test.js +0 -381
- package/dist/utils/__tests__/ignore-matcher.test.d.ts +0 -1
- package/dist/utils/__tests__/ignore-matcher.test.js +0 -205
- package/dist/utils/__tests__/output.test.d.ts +0 -1
- package/dist/utils/__tests__/output.test.js +0 -38
- package/dist/utils/__tests__/prompt.test.d.ts +0 -1
- package/dist/utils/__tests__/prompt.test.js +0 -27
- package/dist/utils/__tests__/resolve.test.d.ts +0 -1
- package/dist/utils/__tests__/resolve.test.js +0 -95
- package/dist/utils/__tests__/sqlite.test.d.ts +0 -1
- package/dist/utils/__tests__/sqlite.test.js +0 -89
- package/dist/utils/agent-guidance.d.ts +0 -47
- package/dist/utils/agent-guidance.js +0 -128
- package/dist/utils/app-info.d.ts +0 -70
- package/dist/utils/app-info.js +0 -167
- package/dist/utils/curl-parser.d.ts +0 -12
- package/dist/utils/curl-parser.js +0 -162
- package/dist/utils/data-input.d.ts +0 -1
- package/dist/utils/data-input.js +0 -38
- package/dist/utils/format-error.d.ts +0 -10
- package/dist/utils/format-error.js +0 -38
- package/dist/utils/fs.d.ts +0 -5
- package/dist/utils/fs.js +0 -23
- package/dist/utils/help-json.d.ts +0 -70
- package/dist/utils/help-json.js +0 -88
- package/dist/utils/http.d.ts +0 -46
- package/dist/utils/http.js +0 -421
- package/dist/utils/ignore-matcher.d.ts +0 -47
- package/dist/utils/ignore-matcher.js +0 -132
- package/dist/utils/output.d.ts +0 -17
- package/dist/utils/output.js +0 -27
- package/dist/utils/prompt.d.ts +0 -5
- package/dist/utils/prompt.js +0 -42
- package/dist/utils/resolve.d.ts +0 -12
- package/dist/utils/resolve.js +0 -52
- package/dist/utils/setup-state.d.ts +0 -3
- package/dist/utils/setup-state.js +0 -19
- package/dist/utils/sqlite-adapter.d.ts +0 -48
- package/dist/utils/sqlite-adapter.js +0 -147
- package/dist/utils/sqlite.d.ts +0 -14
- package/dist/utils/sqlite.js +0 -14
- package/dist/utils/subprocess.d.ts +0 -19
- package/dist/utils/subprocess.js +0 -27
- package/dist/utils/which.d.ts +0 -14
- package/dist/utils/which.js +0 -47
- package/dist/utils/zip.d.ts +0 -7
- package/dist/utils/zip.js +0 -42
- package/dist/validate/__tests__/validate.test.d.ts +0 -1
- package/dist/validate/__tests__/validate.test.js +0 -42
- package/dist/validate/freshness.d.ts +0 -28
- package/dist/validate/freshness.js +0 -48
- package/dist/validate/preview.d.ts +0 -21
- package/dist/validate/preview.js +0 -39
- package/dist/workspace/resolve.d.ts +0 -19
- package/dist/workspace/resolve.js +0 -100
package/dist/commands/sync.js
DELETED
|
@@ -1,702 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
|
-
import { requireAuth } from '../auth/store.js';
|
|
6
|
-
import { ApiClient } from '../api/client.js';
|
|
7
|
-
import { getAdapterBySlug, detectAgents } from '../agents/detect.js';
|
|
8
|
-
import { CodexAdapter } from '../agents/codex.js';
|
|
9
|
-
import { RUNWORK_MCP_PREFIX, RUNWORK_WORKSPACE_MCP_NAME } from '../agents/types.js';
|
|
10
|
-
import { buildExternalMcpEntries } from './mcp-entries.js';
|
|
11
|
-
import { RUNWORK_AGENT_DEFAULTS, AGENT_DEFAULTS_SCHEMA_VERSION } from '../agents/default-config.js';
|
|
12
|
-
import { resolveAgentDefaults } from '../agents/defaults-merge.js';
|
|
13
|
-
import { collectTelemetryEvents, printTelemetryVerbose, summarizeTelemetryForDryRun, } from './sync-telemetry.js';
|
|
14
|
-
import { generateIntroSkill, generateInstructionHint, buildAppSkillDescription } from '../agents/intro-skill.js';
|
|
15
|
-
import { buildShareConversationSkill, buildSaveConversationSkill, BUILT_IN_SKILL_NAMES } from '../agents/conversation-skills.js';
|
|
16
|
-
import { computeSyncPlan } from '../sync/change-detect.js';
|
|
17
|
-
import { printSyncSummary, resolveConflict, resolveConflictNonInteractive } from '../sync/conflict-ui.js';
|
|
18
|
-
import { executeSyncPlan } from '../sync/executor.js';
|
|
19
|
-
import { getRegistryAgent, resolveToAbsolute, isConnectOnlyAgent } from '../agents/registry.js';
|
|
20
|
-
function loadSetupState(filePath) {
|
|
21
|
-
if (!existsSync(filePath))
|
|
22
|
-
return null;
|
|
23
|
-
try {
|
|
24
|
-
return JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Read local skill content from the first configured agent's skill directory.
|
|
32
|
-
*/
|
|
33
|
-
function readLocalSkills(state) {
|
|
34
|
-
const results = [];
|
|
35
|
-
if (!state.skills || state.skills.length === 0)
|
|
36
|
-
return results;
|
|
37
|
-
for (const slug of state.configuredAgents) {
|
|
38
|
-
const def = getRegistryAgent(slug);
|
|
39
|
-
if (!def?.skillsPaths)
|
|
40
|
-
continue;
|
|
41
|
-
const useScope = state.scope === 'both' ? 'user' : state.scope;
|
|
42
|
-
const pathTemplate = useScope === 'project' ? def.skillsPaths.project : def.skillsPaths.global;
|
|
43
|
-
if (!pathTemplate)
|
|
44
|
-
continue;
|
|
45
|
-
const baseDir = resolveToAbsolute(pathTemplate, useScope === 'project' ? 'project' : 'global');
|
|
46
|
-
if (!baseDir)
|
|
47
|
-
continue;
|
|
48
|
-
for (const skillName of state.skills) {
|
|
49
|
-
// Try SKILL.md in subdirectory
|
|
50
|
-
const skillMdPath = join(baseDir, skillName, 'SKILL.md');
|
|
51
|
-
if (existsSync(skillMdPath)) {
|
|
52
|
-
results.push({ name: skillName, content: readFileSync(skillMdPath, 'utf-8') });
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
// Try flat file
|
|
56
|
-
const filename = skillName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
57
|
-
const flatPath = join(baseDir, `${filename}.md`);
|
|
58
|
-
if (existsSync(flatPath)) {
|
|
59
|
-
results.push({ name: skillName, content: readFileSync(flatPath, 'utf-8') });
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (results.length > 0)
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
return results;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* How long a detection result stays trusted before sync re-runs detection
|
|
69
|
-
* on its own. Detection is cheap on macOS/Linux but pays a PowerShell
|
|
70
|
-
* waterfall on Windows, so we cache for a day. Users who just installed
|
|
71
|
-
* a new agent can shortcut the wait via `--redetect` (or the desktop's
|
|
72
|
-
* "Rescan" button, which forwards the flag).
|
|
73
|
-
*/
|
|
74
|
-
export const REDETECT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
75
|
-
/**
|
|
76
|
-
* Decide whether to re-run installation detection for this sync. We always
|
|
77
|
-
* re-detect on explicit user request, on first sync (no timestamp yet), when
|
|
78
|
-
* setup ended up with zero agents (recovery), or after the TTL has lapsed.
|
|
79
|
-
*/
|
|
80
|
-
export function shouldRedetect(state, opts) {
|
|
81
|
-
if (opts.redetect)
|
|
82
|
-
return true;
|
|
83
|
-
if (state.configuredAgents.length === 0)
|
|
84
|
-
return true;
|
|
85
|
-
if (!state.lastDetectedAt)
|
|
86
|
-
return true;
|
|
87
|
-
const last = Date.parse(state.lastDetectedAt);
|
|
88
|
-
if (Number.isNaN(last))
|
|
89
|
-
return true;
|
|
90
|
-
return Date.now() - last > REDETECT_TTL_MS;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Detect installed agents and merge any new slugs into `state.configuredAgents`.
|
|
94
|
-
* Never removes agents — a transient PATH glitch shouldn't wipe the setup.
|
|
95
|
-
* Returns the slugs that were newly added (for logging).
|
|
96
|
-
*/
|
|
97
|
-
export async function refreshConfiguredAgents(state) {
|
|
98
|
-
const detected = await detectAgents();
|
|
99
|
-
const before = new Set(state.configuredAgents);
|
|
100
|
-
const added = [];
|
|
101
|
-
for (const adapter of detected) {
|
|
102
|
-
if (!before.has(adapter.slug)) {
|
|
103
|
-
state.configuredAgents.push(adapter.slug);
|
|
104
|
-
added.push(adapter.slug);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
state.lastDetectedAt = new Date().toISOString();
|
|
108
|
-
return added;
|
|
109
|
-
}
|
|
110
|
-
export async function syncFromState(state, statePath, credentials, opts) {
|
|
111
|
-
const client = new ApiClient(credentials);
|
|
112
|
-
// Refresh workspace name/slug if missing or stale (e.g. setup ran before name was set)
|
|
113
|
-
if (!state.workspaceName || state.workspaceName === state.workspaceId || !state.workspaceSlug) {
|
|
114
|
-
try {
|
|
115
|
-
const workspaces = await client.listWorkspaces();
|
|
116
|
-
const ws = workspaces.find(w => w.id === state.workspaceId);
|
|
117
|
-
if (ws?.name)
|
|
118
|
-
state.workspaceName = ws.name;
|
|
119
|
-
if (ws?.slug)
|
|
120
|
-
state.workspaceSlug = ws.slug;
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
// Best-effort, keep existing values
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
console.log(`Syncing workspace: ${state.workspaceName || state.workspaceId}`);
|
|
127
|
-
// Refresh the configured-agents list when the cached detection is stale,
|
|
128
|
-
// empty, or the user explicitly asked. This is what catches "I just installed
|
|
129
|
-
// Claude Code on this machine" without needing the user to re-run setup.
|
|
130
|
-
if (shouldRedetect(state, opts)) {
|
|
131
|
-
console.log(' Detecting installed agents...');
|
|
132
|
-
const added = await refreshConfiguredAgents(state);
|
|
133
|
-
if (added.length > 0) {
|
|
134
|
-
console.log(` Detected new agent${added.length > 1 ? 's' : ''}: ${added.join(', ')}`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
console.log(' Fetching workspace data...');
|
|
138
|
-
// Fetch latest data (includeContent=true gets all skill content in one request)
|
|
139
|
-
const [allSkills, mcpServers, externalSkills, registries, connectedIntegrations, resolvedMcpCredentials] = await Promise.all([
|
|
140
|
-
client.listWorkspaceSkills(state.workspaceId, true),
|
|
141
|
-
client.listMcpServers(state.workspaceId),
|
|
142
|
-
client.listExternalSkills(state.workspaceId),
|
|
143
|
-
client.getWorkspaceAll(state.workspaceId).catch(() => null),
|
|
144
|
-
client.listConnectedIntegrations(state.workspaceId)
|
|
145
|
-
.then(list => list.map(i => i.canonicalId ?? i.integrationId))
|
|
146
|
-
.catch(() => []),
|
|
147
|
-
// Decrypted auth headers for header-auth servers, scoped to this user.
|
|
148
|
-
// Tolerate older backends that lack the endpoint.
|
|
149
|
-
client.resolveMcpCredentials(state.workspaceId).catch(() => ({})),
|
|
150
|
-
]);
|
|
151
|
-
const appCount = allSkills.filter(s => s.type === 'app').length;
|
|
152
|
-
const parts = [];
|
|
153
|
-
if (appCount > 0)
|
|
154
|
-
parts.push(`${appCount} app${appCount > 1 ? 's' : ''}`);
|
|
155
|
-
if (externalSkills.length > 0)
|
|
156
|
-
parts.push(`${externalSkills.length} skill${externalSkills.length > 1 ? 's' : ''}`);
|
|
157
|
-
if (mcpServers.length > 0)
|
|
158
|
-
parts.push(`${mcpServers.filter(s => s.enabled).length} MCP server${mcpServers.filter(s => s.enabled).length > 1 ? 's' : ''}`);
|
|
159
|
-
if (connectedIntegrations.length > 0)
|
|
160
|
-
parts.push(`${connectedIntegrations.length} integration${connectedIntegrations.length > 1 ? 's' : ''}`);
|
|
161
|
-
if (registries) {
|
|
162
|
-
if (registries.entities.length > 0)
|
|
163
|
-
parts.push(`${registries.entities.length} entit${registries.entities.length > 1 ? 'ies' : 'y'}`);
|
|
164
|
-
if (registries.workflows.length > 0)
|
|
165
|
-
parts.push(`${registries.workflows.length} workflow${registries.workflows.length > 1 ? 's' : ''}`);
|
|
166
|
-
if (registries.agents.length > 0)
|
|
167
|
-
parts.push(`${registries.agents.length} agent${registries.agents.length > 1 ? 's' : ''}`);
|
|
168
|
-
}
|
|
169
|
-
if (parts.length > 0) {
|
|
170
|
-
console.log(` Found: ${parts.join(', ')}`);
|
|
171
|
-
}
|
|
172
|
-
// Build MCP entries (pull-only)
|
|
173
|
-
const baseUrl = credentials.baseUrl || 'https://runwork.ai';
|
|
174
|
-
const mcpEntries = buildExternalMcpEntries(mcpServers, resolvedMcpCredentials);
|
|
175
|
-
mcpEntries.push({
|
|
176
|
-
name: RUNWORK_WORKSPACE_MCP_NAME,
|
|
177
|
-
url: `${baseUrl}/api/workspaces/${state.workspaceId}/mcp`,
|
|
178
|
-
transport: 'streamable-http',
|
|
179
|
-
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
180
|
-
description: 'Runwork workspace tools: query entities, trigger workflows, manage integrations, run agents, and access shared data across your team\'s apps.',
|
|
181
|
-
});
|
|
182
|
-
// Build remote skills list
|
|
183
|
-
const remoteSkills = [];
|
|
184
|
-
for (const s of externalSkills) {
|
|
185
|
-
remoteSkills.push({ name: s.name, content: s.content, source: 'external', remoteId: s.id });
|
|
186
|
-
}
|
|
187
|
-
for (const s of allSkills) {
|
|
188
|
-
if (s.type === 'app' && s.appId && s.content?.trim()) {
|
|
189
|
-
remoteSkills.push({ name: s.name, content: s.content, source: 'app', appId: s.appId });
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
// Read local skills for change detection
|
|
193
|
-
const localSkills = readLocalSkills(state);
|
|
194
|
-
// Built-in skills (shipped by the CLI from code) are CANONICAL: they
|
|
195
|
-
// are never pushed to the workspace and never pulled from it. The diff
|
|
196
|
-
// engine must not see them at all -- if it did, a workspace skill that
|
|
197
|
-
// happens to share a name (e.g. someone uploaded `share-conversation`
|
|
198
|
-
// manually) would either push the built-in upstream or pull the stale
|
|
199
|
-
// workspace copy on top, both wrong. Filter both sides before the diff.
|
|
200
|
-
const builtInNameSet = new Set(BUILT_IN_SKILL_NAMES);
|
|
201
|
-
const collidingRemote = remoteSkills.filter(s => builtInNameSet.has(s.name));
|
|
202
|
-
if (collidingRemote.length > 0) {
|
|
203
|
-
console.log(`\n Warning: ${collidingRemote.length} workspace skill${collidingRemote.length === 1 ? '' : 's'} ` +
|
|
204
|
-
`shadow${collidingRemote.length === 1 ? 's' : ''} built-in CLI skill${collidingRemote.length === 1 ? '' : 's'}: ` +
|
|
205
|
-
`${collidingRemote.map(s => s.name).join(', ')}. The CLI built-in versions will be used locally; ` +
|
|
206
|
-
`the workspace copies are stale duplicates and can be safely deleted from the dashboard.`);
|
|
207
|
-
}
|
|
208
|
-
const localSkillsForDiff = localSkills.filter(s => !builtInNameSet.has(s.name));
|
|
209
|
-
const remoteSkillsForDiff = remoteSkills.filter(s => !builtInNameSet.has(s.name));
|
|
210
|
-
// Compute sync plan
|
|
211
|
-
const plan = computeSyncPlan({
|
|
212
|
-
storedHashes: state.skillHashes || {},
|
|
213
|
-
localSkills: localSkillsForDiff,
|
|
214
|
-
remoteSkills: remoteSkillsForDiff,
|
|
215
|
-
});
|
|
216
|
-
// In pull-only mode, move pushes and conflicts to skips
|
|
217
|
-
if (opts.pullOnly) {
|
|
218
|
-
plan.skips.push(...plan.pushes.map(p => ({ ...p, action: 'skip' })));
|
|
219
|
-
plan.pushes = [];
|
|
220
|
-
plan.skips.push(...plan.conflicts.map(c => ({ ...c, action: 'skip' })));
|
|
221
|
-
plan.conflicts = [];
|
|
222
|
-
}
|
|
223
|
-
// Display summary
|
|
224
|
-
const hasChanges = plan.pulls.length > 0 || plan.pushes.length > 0 || plan.conflicts.length > 0 || plan.deletions.length > 0;
|
|
225
|
-
if (hasChanges) {
|
|
226
|
-
printSyncSummary(plan);
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
console.log(' All skills up to date.');
|
|
230
|
-
}
|
|
231
|
-
// Build adapters early so telemetry collection works in both dry-run and real paths
|
|
232
|
-
const adapters = [];
|
|
233
|
-
for (const slug of state.configuredAgents) {
|
|
234
|
-
const adapter = getAdapterBySlug(slug);
|
|
235
|
-
if (adapter)
|
|
236
|
-
adapters.push(adapter);
|
|
237
|
-
}
|
|
238
|
-
if (opts.dryRun) {
|
|
239
|
-
try {
|
|
240
|
-
const telemetry = await collectTelemetryEvents({
|
|
241
|
-
adapters,
|
|
242
|
-
state,
|
|
243
|
-
syncedSkillsCount: remoteSkills.length,
|
|
244
|
-
syncedMcpServersCount: mcpEntries.length,
|
|
245
|
-
teamInstructionsApplied: false,
|
|
246
|
-
agentConfigsApplied: 0,
|
|
247
|
-
});
|
|
248
|
-
if (opts.verbose) {
|
|
249
|
-
printTelemetryVerbose(telemetry);
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
console.log(`\n ${summarizeTelemetryForDryRun(telemetry)}`);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
// Telemetry preview is best-effort
|
|
257
|
-
}
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
// Resolve conflicts
|
|
261
|
-
const resolvedConflicts = new Map();
|
|
262
|
-
for (const conflict of plan.conflicts) {
|
|
263
|
-
if (opts.yes || !process.stdout.isTTY) {
|
|
264
|
-
const nonInteractiveResolution = resolveConflictNonInteractive(opts.prefer || 'remote');
|
|
265
|
-
if (nonInteractiveResolution !== 'skip') {
|
|
266
|
-
resolvedConflicts.set(conflict.name, nonInteractiveResolution);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
const resolution = await resolveConflict(conflict);
|
|
271
|
-
if (resolution !== 'skip') {
|
|
272
|
-
resolvedConflicts.set(conflict.name, resolution);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
const scopes = state.scope === 'both' ? ['project', 'user'] : [state.scope];
|
|
277
|
-
if (adapters.length > 0) {
|
|
278
|
-
console.log(` Syncing to: ${adapters.map(a => a.name).join(', ')}`);
|
|
279
|
-
}
|
|
280
|
-
// Execute skill sync plan
|
|
281
|
-
const newHashes = await executeSyncPlan(plan, resolvedConflicts, {
|
|
282
|
-
client,
|
|
283
|
-
workspaceId: state.workspaceId,
|
|
284
|
-
adapters,
|
|
285
|
-
scopes,
|
|
286
|
-
hasMcp: mcpEntries.length > 0,
|
|
287
|
-
});
|
|
288
|
-
// Write intro skill, MCP configs, instruction hints (pull-only)
|
|
289
|
-
const introSkill = generateIntroSkill({
|
|
290
|
-
workspaceName: state.workspaceName || state.workspaceId,
|
|
291
|
-
workspaceId: state.workspaceId,
|
|
292
|
-
workspaceSlug: state.workspaceSlug,
|
|
293
|
-
registries,
|
|
294
|
-
connectedIntegrations,
|
|
295
|
-
mcpServerCount: mcpEntries.length,
|
|
296
|
-
skillCount: remoteSkills.length,
|
|
297
|
-
mcpServerNames: mcpEntries.map(e => e.name.replace(RUNWORK_MCP_PREFIX, '')),
|
|
298
|
-
skillNames: remoteSkills.map(s => s.name),
|
|
299
|
-
});
|
|
300
|
-
const instructionHint = generateInstructionHint({
|
|
301
|
-
workspaceName: state.workspaceName || state.workspaceId,
|
|
302
|
-
workspaceId: state.workspaceId,
|
|
303
|
-
workspaceSlug: state.workspaceSlug,
|
|
304
|
-
registries,
|
|
305
|
-
connectedIntegrations,
|
|
306
|
-
appCount: allSkills.filter(s => s.type === 'app').length,
|
|
307
|
-
skillCount: remoteSkills.length,
|
|
308
|
-
mcpServerCount: mcpEntries.length,
|
|
309
|
-
persona: state.persona,
|
|
310
|
-
});
|
|
311
|
-
// For project scope: determine which app skill to include (only current app's skill)
|
|
312
|
-
let projectAppSkillFilter = null;
|
|
313
|
-
if (existsSync('.runwork.json')) {
|
|
314
|
-
try {
|
|
315
|
-
const config = JSON.parse(readFileSync('.runwork.json', 'utf-8'));
|
|
316
|
-
if (config.appName) {
|
|
317
|
-
projectAppSkillFilter = config.appName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
catch { /* ignore */ }
|
|
321
|
-
}
|
|
322
|
-
// Built-in skills shipped by the CLI on every sync. Named upfront so we can
|
|
323
|
-
// surface them by name in the per-adapter sync log -- helps users (and
|
|
324
|
-
// their agents) confirm that /runwork, /share-conversation, etc. landed.
|
|
325
|
-
const builtInSkills = [
|
|
326
|
-
introSkill,
|
|
327
|
-
buildShareConversationSkill(),
|
|
328
|
-
buildSaveConversationSkill(),
|
|
329
|
-
];
|
|
330
|
-
const builtInNames = builtInSkills.map(s => s.name);
|
|
331
|
-
// Counters surfaced at the end of the sync as a one-line summary so the
|
|
332
|
-
// user can see at a glance how much actually got written this run.
|
|
333
|
-
const summary = {
|
|
334
|
-
adaptersProcessed: 0,
|
|
335
|
-
adaptersFailed: 0,
|
|
336
|
-
skillWrites: 0,
|
|
337
|
-
skillFilesWritten: 0,
|
|
338
|
-
mcpServerWrites: 0,
|
|
339
|
-
instructionHintWrites: 0,
|
|
340
|
-
hookInstallCalls: 0,
|
|
341
|
-
};
|
|
342
|
-
for (const adapter of adapters) {
|
|
343
|
-
// Connect-only agents (e.g. ChatGPT) have no local files to write; the
|
|
344
|
-
// connector lives in the user's account. Skip all local-write phases.
|
|
345
|
-
if (isConnectOnlyAgent(getRegistryAgent(adapter.slug))) {
|
|
346
|
-
console.log(` [${adapter.name}] Connect-only agent: no local files to sync`);
|
|
347
|
-
summary.adaptersProcessed++;
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
let adapterFailedAnyScope = false;
|
|
351
|
-
for (const scope of scopes) {
|
|
352
|
-
try {
|
|
353
|
-
// Write skills: for project scope, filter to only the current app's skill.
|
|
354
|
-
// When MCP servers are configured for this adapter, skip app-sourced skills
|
|
355
|
-
// since the MCP server already exposes them as skill_* tools. Writing both
|
|
356
|
-
// causes duplicate entries in the agent's skill listing.
|
|
357
|
-
if (adapter.supportsSkills()) {
|
|
358
|
-
const skipAppSkills = adapter.mcpProvidesSkills && mcpEntries.length > 0;
|
|
359
|
-
const scopeSkills = remoteSkills.filter(s => {
|
|
360
|
-
// Built-in CLI skills are written from the canonical built-in array
|
|
361
|
-
// below; a workspace skill with the same name is a shadow that
|
|
362
|
-
// would overwrite the built-in if we let it through. Skip it.
|
|
363
|
-
if (builtInNameSet.has(s.name))
|
|
364
|
-
return false;
|
|
365
|
-
// App-sourced skills are already available via MCP skill_* tools
|
|
366
|
-
if (skipAppSkills && s.source === 'app')
|
|
367
|
-
return false;
|
|
368
|
-
if (scope === 'project' && s.source === 'app' && projectAppSkillFilter) {
|
|
369
|
-
const filename = s.name.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
370
|
-
return filename === projectAppSkillFilter;
|
|
371
|
-
}
|
|
372
|
-
return true;
|
|
373
|
-
});
|
|
374
|
-
const workspaceSkillFiles = scopeSkills.map(s => ({
|
|
375
|
-
name: s.name,
|
|
376
|
-
filename: s.name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
|
|
377
|
-
content: s.content,
|
|
378
|
-
description: s.source === 'app'
|
|
379
|
-
? (buildAppSkillDescription(s.name, registries) || `${s.name} - Runwork workspace application`)
|
|
380
|
-
: `${s.name} - Runwork workspace skill`,
|
|
381
|
-
}));
|
|
382
|
-
const allSkillFiles = [...builtInSkills, ...workspaceSkillFiles];
|
|
383
|
-
await adapter.writeSkills(allSkillFiles, scope);
|
|
384
|
-
summary.skillWrites++;
|
|
385
|
-
summary.skillFilesWritten += allSkillFiles.length;
|
|
386
|
-
console.log(` [${adapter.name}] Wrote ${allSkillFiles.length} skills (${scope}): ` +
|
|
387
|
-
`${builtInSkills.length} built-in [${builtInNames.join(', ')}], ` +
|
|
388
|
-
`${workspaceSkillFiles.length} workspace`);
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
console.log(` [${adapter.name}] Skipped skills (${scope}): adapter does not support skills`);
|
|
392
|
-
}
|
|
393
|
-
// Write MCP configs
|
|
394
|
-
if (adapter.supportsMcpScope(scope) && mcpEntries.length > 0) {
|
|
395
|
-
await adapter.writeMcpServers(mcpEntries, scope);
|
|
396
|
-
summary.mcpServerWrites++;
|
|
397
|
-
console.log(` [${adapter.name}] Updated ${mcpEntries.length} MCP server${mcpEntries.length > 1 ? 's' : ''} (${scope})`);
|
|
398
|
-
}
|
|
399
|
-
else if (mcpEntries.length === 0) {
|
|
400
|
-
console.log(` [${adapter.name}] Skipped MCP servers (${scope}): no workspace MCP servers configured`);
|
|
401
|
-
}
|
|
402
|
-
else if (!adapter.supportsMcpScope(scope)) {
|
|
403
|
-
console.log(` [${adapter.name}] Skipped MCP servers (${scope}): adapter does not support MCP at this scope`);
|
|
404
|
-
}
|
|
405
|
-
// Write instruction hint
|
|
406
|
-
await adapter.writeInstructionHint(instructionHint, scope);
|
|
407
|
-
summary.instructionHintWrites++;
|
|
408
|
-
console.log(` [${adapter.name}] Updated instruction hints (${scope})`);
|
|
409
|
-
// Install built-in hooks (Claude Code's SessionStart, etc.). Called
|
|
410
|
-
// after writeSkills so the plugin tree already exists. Skipped by
|
|
411
|
-
// adapters that don't implement it.
|
|
412
|
-
if (adapter.writeBuiltInHooks) {
|
|
413
|
-
await adapter.writeBuiltInHooks(scope);
|
|
414
|
-
summary.hookInstallCalls++;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
catch (err) {
|
|
418
|
-
adapterFailedAnyScope = true;
|
|
419
|
-
console.warn(` [${adapter.name}] Failed (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
summary.adaptersProcessed++;
|
|
423
|
-
if (adapterFailedAnyScope)
|
|
424
|
-
summary.adaptersFailed++;
|
|
425
|
-
}
|
|
426
|
-
// Pull team config from server. A failure here must not stop the
|
|
427
|
-
// unified user-scope write below, which applies network and minimum-permission
|
|
428
|
-
// floors regardless of whether team config was fetched.
|
|
429
|
-
let teamInstructionsApplied = false;
|
|
430
|
-
let agentConfigsApplied = 0;
|
|
431
|
-
let teamInstructions;
|
|
432
|
-
let agentConfigs;
|
|
433
|
-
try {
|
|
434
|
-
const onboardingConfig = await client.getOnboardingConfig(state.workspaceId);
|
|
435
|
-
teamInstructions = onboardingConfig?.config?.teamInstructions ?? undefined;
|
|
436
|
-
agentConfigs = onboardingConfig?.config?.agentConfigs ?? undefined;
|
|
437
|
-
}
|
|
438
|
-
catch {
|
|
439
|
-
// Team config pull is non-fatal — proceed with no team-managed overrides.
|
|
440
|
-
}
|
|
441
|
-
// Write team instructions and/or per-agent extra instructions.
|
|
442
|
-
for (const adapter of adapters) {
|
|
443
|
-
if (isConnectOnlyAgent(getRegistryAgent(adapter.slug)))
|
|
444
|
-
continue;
|
|
445
|
-
if (!adapter.writeTeamInstructions)
|
|
446
|
-
continue;
|
|
447
|
-
const agentExtra = agentConfigs?.[adapter.slug]?.extraInstructions;
|
|
448
|
-
const parts = [teamInstructions, agentExtra].filter(Boolean);
|
|
449
|
-
if (parts.length === 0)
|
|
450
|
-
continue;
|
|
451
|
-
const fullInstructions = parts.join('\n\n');
|
|
452
|
-
for (const scope of scopes) {
|
|
453
|
-
try {
|
|
454
|
-
await adapter.writeTeamInstructions(fullInstructions, scope);
|
|
455
|
-
teamInstructionsApplied = true;
|
|
456
|
-
console.log(` [${adapter.name}] Updated team instructions (${scope})`);
|
|
457
|
-
}
|
|
458
|
-
catch {
|
|
459
|
-
// Best-effort per adapter/scope
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
// Apply per-agent project-scope config (team overrides only).
|
|
464
|
-
// User scope is handled below in the unified defaults+network+minimum block.
|
|
465
|
-
if (agentConfigs) {
|
|
466
|
-
for (const adapter of adapters) {
|
|
467
|
-
if (isConnectOnlyAgent(getRegistryAgent(adapter.slug)))
|
|
468
|
-
continue;
|
|
469
|
-
const agentConfig = agentConfigs[adapter.slug];
|
|
470
|
-
if (!agentConfig || !adapter.writeAgentConfig)
|
|
471
|
-
continue;
|
|
472
|
-
const { extraInstructions: _, ...configWithoutInstructions } = agentConfig;
|
|
473
|
-
if (Object.keys(configWithoutInstructions).length === 0)
|
|
474
|
-
continue;
|
|
475
|
-
for (const scope of scopes) {
|
|
476
|
-
if (scope === 'user')
|
|
477
|
-
continue; // handled in unified block below
|
|
478
|
-
try {
|
|
479
|
-
await adapter.writeAgentConfig(configWithoutInstructions, scope);
|
|
480
|
-
agentConfigsApplied++;
|
|
481
|
-
const configKeys = Object.keys(configWithoutInstructions).join(', ');
|
|
482
|
-
console.log(` [${adapter.name}] Updated agent config: ${configKeys} (${scope})`);
|
|
483
|
-
}
|
|
484
|
-
catch {
|
|
485
|
-
// Best-effort per adapter/scope
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
// Unified user-scope write: merge baked defaults with team config, network
|
|
491
|
-
// allowlist, and minimum permissions into a single writeAgentConfig call
|
|
492
|
-
// per adapter. The merge layer tracks injected defaults in state.agentDefaults
|
|
493
|
-
// so user-removed entries become sticky opt-outs.
|
|
494
|
-
if (scopes.includes('user')) {
|
|
495
|
-
const networkDomains = ['runwork.ai', '*.runwork.ai'];
|
|
496
|
-
try {
|
|
497
|
-
const baseHost = new URL(baseUrl).hostname;
|
|
498
|
-
if (baseHost !== 'runwork.ai' && !baseHost.endsWith('.runwork.ai')) {
|
|
499
|
-
networkDomains.push(baseHost, `*.${baseHost}`);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
catch { /* use defaults */ }
|
|
503
|
-
// Order arrays list each agent's valid values from most restrictive to most
|
|
504
|
-
// permissive. The floor logic upgrades anything strictly below `minimum`,
|
|
505
|
-
// including unknown values (-1 < minimumIdx). That makes it critical to
|
|
506
|
-
// include the agent's most-permissive option in `order` — otherwise a user
|
|
507
|
-
// who explicitly set, say, `sandbox_mode = "danger-full-access"` gets
|
|
508
|
-
// silently downgraded to `workspace-write`.
|
|
509
|
-
const minimumPermissions = [
|
|
510
|
-
{ field: 'approval_policy', order: ['untrusted', 'on-request', 'on-failure', 'never'], minimum: 'on-request' },
|
|
511
|
-
{ field: 'sandbox_mode', order: ['read-only', 'workspace-write', 'danger-full-access'], minimum: 'workspace-write' },
|
|
512
|
-
];
|
|
513
|
-
// agentDefaults lifecycle: full `runwork uninstall` deletes ~/.runwork
|
|
514
|
-
// (state and all per-agent defaults state with it). Re-running `runwork
|
|
515
|
-
// setup` overwrites setup.json without agentDefaults so every remaining
|
|
516
|
-
// tool bootstraps fresh on the next sync. refreshConfiguredAgents only
|
|
517
|
-
// adds slugs (never removes), so we don't need orphan-entry pruning here.
|
|
518
|
-
if (!state.agentDefaults)
|
|
519
|
-
state.agentDefaults = {};
|
|
520
|
-
for (const adapter of adapters) {
|
|
521
|
-
if (isConnectOnlyAgent(getRegistryAgent(adapter.slug)))
|
|
522
|
-
continue;
|
|
523
|
-
if (!adapter.writeAgentConfig)
|
|
524
|
-
continue;
|
|
525
|
-
const slug = adapter.slug;
|
|
526
|
-
const baked = RUNWORK_AGENT_DEFAULTS[slug];
|
|
527
|
-
const agentState = state.agentDefaults[slug];
|
|
528
|
-
const team = agentConfigs?.[slug];
|
|
529
|
-
// onDisk = undefined signals "removal detection is not possible" — used for
|
|
530
|
-
// markerless adapters (Cursor) and for any adapter without readManagedBlock.
|
|
531
|
-
// The resolver treats undefined differently from an empty array: it skips
|
|
532
|
-
// opt-out fabrication entirely and trusts the baseline mechanism in the
|
|
533
|
-
// adapter to preserve user edits via subtraction.
|
|
534
|
-
let onDisk;
|
|
535
|
-
if (baked && adapter.readManagedBlock) {
|
|
536
|
-
try {
|
|
537
|
-
onDisk = await adapter.readManagedBlock('user');
|
|
538
|
-
}
|
|
539
|
-
catch {
|
|
540
|
-
onDisk = undefined;
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
const resolved = resolveAgentDefaults({
|
|
544
|
-
baked,
|
|
545
|
-
state: agentState,
|
|
546
|
-
onDisk,
|
|
547
|
-
team: team?.permissionRules,
|
|
548
|
-
});
|
|
549
|
-
const mergedConfig = {
|
|
550
|
-
modelPreference: team?.modelPreference,
|
|
551
|
-
permissionRules: (resolved.managedAllow.length || resolved.managedDeny.length || team?.permissionRules?.defaultMode)
|
|
552
|
-
? {
|
|
553
|
-
...(resolved.managedAllow.length ? { allow: resolved.managedAllow } : {}),
|
|
554
|
-
...(resolved.managedDeny.length ? { deny: resolved.managedDeny } : {}),
|
|
555
|
-
...(team?.permissionRules?.defaultMode ? { defaultMode: team.permissionRules.defaultMode } : {}),
|
|
556
|
-
}
|
|
557
|
-
: undefined,
|
|
558
|
-
networkAllowlist: networkDomains,
|
|
559
|
-
minimumPermissions,
|
|
560
|
-
};
|
|
561
|
-
try {
|
|
562
|
-
const baseline = agentState?.lastInjected;
|
|
563
|
-
await adapter.writeAgentConfig(mergedConfig, 'user', baseline);
|
|
564
|
-
if (baked) {
|
|
565
|
-
const nextState = {
|
|
566
|
-
lastInjected: {
|
|
567
|
-
allow: resolved.applicableAllow,
|
|
568
|
-
deny: resolved.applicableDeny,
|
|
569
|
-
},
|
|
570
|
-
userOptOuts: {
|
|
571
|
-
allow: resolved.newOptOutsAllow,
|
|
572
|
-
deny: resolved.newOptOutsDeny,
|
|
573
|
-
},
|
|
574
|
-
};
|
|
575
|
-
state.agentDefaults[slug] = nextState;
|
|
576
|
-
// Diagnostic log line (per "invisible / sync log only" policy)
|
|
577
|
-
if (resolved.bootstrapped) {
|
|
578
|
-
console.log(` [${adapter.name}] Bootstrapped default-rule tracking (defaults apply on next sync)`);
|
|
579
|
-
}
|
|
580
|
-
else if (resolved.applicableAllow.length || resolved.applicableDeny.length || resolved.removalsThisSync.allow || resolved.removalsThisSync.deny) {
|
|
581
|
-
const totalRemovals = resolved.removalsThisSync.allow + resolved.removalsThisSync.deny;
|
|
582
|
-
const removalNote = totalRemovals > 0
|
|
583
|
-
? ` (${totalRemovals} new opt-out${totalRemovals > 1 ? 's' : ''} honored)`
|
|
584
|
-
: '';
|
|
585
|
-
console.log(` [${adapter.name}] Applied defaults: ${resolved.applicableAllow.length} allow, ${resolved.applicableDeny.length} deny${removalNote}`);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
if (team)
|
|
589
|
-
agentConfigsApplied++;
|
|
590
|
-
}
|
|
591
|
-
catch {
|
|
592
|
-
// Best-effort per adapter
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
state.agentDefaultsVersion = AGENT_DEFAULTS_SCHEMA_VERSION;
|
|
596
|
-
}
|
|
597
|
-
// Register ~/.runwork as a project in the Codex desktop app (best-effort).
|
|
598
|
-
// Only attempts when Codex adapter is configured and the desktop app is closed.
|
|
599
|
-
for (const adapter of adapters) {
|
|
600
|
-
if (adapter instanceof CodexAdapter) {
|
|
601
|
-
const runworkDir = join(homedir(), '.runwork');
|
|
602
|
-
const result = adapter.registerDesktopWorkspace(runworkDir, 'Runwork');
|
|
603
|
-
if (result === 'written') {
|
|
604
|
-
console.log(` [${adapter.name}] Registered workspace in Codex desktop app`);
|
|
605
|
-
}
|
|
606
|
-
// 'app_running' and 'already_registered' are silently skipped
|
|
607
|
-
break;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
// Update state
|
|
611
|
-
state.lastSyncAt = new Date().toISOString();
|
|
612
|
-
state.mcpServers = mcpEntries.map(e => e.name);
|
|
613
|
-
state.skills = remoteSkills.map(s => s.name);
|
|
614
|
-
state.skillFilenames = [
|
|
615
|
-
'runwork', // intro skill is always synced
|
|
616
|
-
...remoteSkills.map(s => s.name.toLowerCase().replace(/[^a-z0-9]+/g, '-')),
|
|
617
|
-
];
|
|
618
|
-
// Merge hashes: keep existing for skips, update for everything else
|
|
619
|
-
const mergedHashes = { ...(state.skillHashes || {}) };
|
|
620
|
-
for (const [name, hash] of Object.entries(newHashes)) {
|
|
621
|
-
mergedHashes[name] = hash;
|
|
622
|
-
}
|
|
623
|
-
// Remove deleted skills
|
|
624
|
-
for (const del of plan.deletions) {
|
|
625
|
-
delete mergedHashes[del.name];
|
|
626
|
-
}
|
|
627
|
-
state.skillHashes = mergedHashes;
|
|
628
|
-
writeFileSync(statePath, JSON.stringify(state, null, 2));
|
|
629
|
-
// Report telemetry (non-fatal)
|
|
630
|
-
try {
|
|
631
|
-
const telemetry = await collectTelemetryEvents({
|
|
632
|
-
adapters,
|
|
633
|
-
state,
|
|
634
|
-
syncedSkillsCount: remoteSkills.length,
|
|
635
|
-
syncedMcpServersCount: mcpEntries.length,
|
|
636
|
-
teamInstructionsApplied,
|
|
637
|
-
agentConfigsApplied,
|
|
638
|
-
});
|
|
639
|
-
if (opts.verbose)
|
|
640
|
-
printTelemetryVerbose(telemetry);
|
|
641
|
-
await client.reportTelemetry(state.workspaceId, telemetry.events);
|
|
642
|
-
if (telemetry.healthReported)
|
|
643
|
-
state.lastHealthReportAt = new Date().toISOString();
|
|
644
|
-
state.reportedAgentSlugs = [
|
|
645
|
-
...new Set([...(state.reportedAgentSlugs ?? []), ...telemetry.newlyReportedAgents]),
|
|
646
|
-
];
|
|
647
|
-
writeFileSync(statePath, JSON.stringify(state, null, 2));
|
|
648
|
-
}
|
|
649
|
-
catch {
|
|
650
|
-
// Telemetry failures are non-fatal
|
|
651
|
-
}
|
|
652
|
-
// One-line summary so the user (and any agent reading sync output) can
|
|
653
|
-
// see at a glance what got written this run, without scrolling the whole
|
|
654
|
-
// per-adapter log.
|
|
655
|
-
const summaryParts = [];
|
|
656
|
-
summaryParts.push(`${summary.adaptersProcessed} adapter${summary.adaptersProcessed === 1 ? '' : 's'}`);
|
|
657
|
-
if (summary.adaptersFailed > 0)
|
|
658
|
-
summaryParts.push(`${summary.adaptersFailed} failed`);
|
|
659
|
-
summaryParts.push(`${summary.skillWrites} skill write${summary.skillWrites === 1 ? '' : 's'} (${summary.skillFilesWritten} files)`);
|
|
660
|
-
if (summary.mcpServerWrites > 0)
|
|
661
|
-
summaryParts.push(`${summary.mcpServerWrites} MCP config write${summary.mcpServerWrites === 1 ? '' : 's'}`);
|
|
662
|
-
if (summary.hookInstallCalls > 0)
|
|
663
|
-
summaryParts.push(`${summary.hookInstallCalls} hook install${summary.hookInstallCalls === 1 ? '' : 's'}`);
|
|
664
|
-
summaryParts.push(`${summary.instructionHintWrites} instruction hint write${summary.instructionHintWrites === 1 ? '' : 's'}`);
|
|
665
|
-
console.log(`\n Summary: ${summaryParts.join(', ')}.`);
|
|
666
|
-
}
|
|
667
|
-
export const syncCommand = new Command('sync')
|
|
668
|
-
.description('Sync skills bidirectionally and refresh MCP configs from workspace')
|
|
669
|
-
.option('--dry-run', 'Show what would change without doing anything (includes telemetry preview)')
|
|
670
|
-
.option('-v, --verbose', 'Print per-adapter stats and the telemetry envelope')
|
|
671
|
-
.option('--pull-only', 'Only pull remote changes, never push local edits')
|
|
672
|
-
.option('--prefer <side>', 'Auto-resolve conflicts: "local" or "remote" (implies --yes)')
|
|
673
|
-
.option('-y, --yes', 'Non-interactive mode (default: prefer remote for conflicts)')
|
|
674
|
-
.option('--redetect', 'Force agent-installation re-detection (picks up agents installed since last setup)')
|
|
675
|
-
.action(async (opts) => {
|
|
676
|
-
const credentials = requireAuth();
|
|
677
|
-
const syncOpts = {
|
|
678
|
-
dryRun: !!opts.dryRun,
|
|
679
|
-
pullOnly: !!opts.pullOnly,
|
|
680
|
-
yes: !!opts.yes || !!opts.prefer,
|
|
681
|
-
prefer: opts.prefer,
|
|
682
|
-
verbose: !!opts.verbose,
|
|
683
|
-
redetect: !!opts.redetect,
|
|
684
|
-
};
|
|
685
|
-
const projectStatePath = join(process.cwd(), '.runwork', 'setup.json');
|
|
686
|
-
const userStatePath = join(homedir(), '.runwork', 'setup.json');
|
|
687
|
-
const projectState = loadSetupState(projectStatePath);
|
|
688
|
-
const userState = loadSetupState(userStatePath);
|
|
689
|
-
if (!projectState && !userState) {
|
|
690
|
-
console.error('No setup state found. Run `runwork setup` first.');
|
|
691
|
-
process.exit(1);
|
|
692
|
-
}
|
|
693
|
-
if (projectState) {
|
|
694
|
-
await syncFromState(projectState, projectStatePath, credentials, syncOpts);
|
|
695
|
-
}
|
|
696
|
-
if (userState) {
|
|
697
|
-
await syncFromState(userState, userStatePath, credentials, syncOpts);
|
|
698
|
-
}
|
|
699
|
-
if (!syncOpts.dryRun) {
|
|
700
|
-
console.log('\nSync complete.');
|
|
701
|
-
}
|
|
702
|
-
});
|