runwork 0.15.1 → 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 -105
- 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 -59
- package/dist/agents/claude-code.js +0 -753
- 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 -35
- package/dist/agents/cursor.js +0 -294
- 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 -87
- package/dist/agents/defaults-merge.js +0 -150
- 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
|
@@ -1,753 +0,0 @@
|
|
|
1
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { homedir, platform } from 'os';
|
|
4
|
-
import { execFileSync } from '../utils/subprocess.js';
|
|
5
|
-
import { whichBinary } from '../utils/which.js';
|
|
6
|
-
import { buildSkillMd } from './types.js';
|
|
7
|
-
import { mergeJsonMcpServers, readJsonConfig, writeJsonConfig, removeRunworkMcpServers } from './utils/json-config.js';
|
|
8
|
-
import { writeHintToFile, writeTeamInstructionsToFile, removeHintFromFile, removeTeamInstructionsFromFile } from './utils/instruction-hint.js';
|
|
9
|
-
import { SESSION_START_HOOK_SCRIPT } from './session-start-hook.js';
|
|
10
|
-
import { mergeMarkerless } from './defaults-merge.js';
|
|
11
|
-
const PLUGIN_NAME = 'runwork';
|
|
12
|
-
const PLUGIN_VERSION = '1.0.0';
|
|
13
|
-
/**
|
|
14
|
-
* Legacy in-array sentinel that older CLI versions injected at the head of the
|
|
15
|
-
* managed permission block. Claude Code >= 2.1.x validates every allow/deny
|
|
16
|
-
* entry as a tool rule and rejects this with "matches no known tool" on
|
|
17
|
-
* startup, so the adapter is now markerless and actively strips this marker
|
|
18
|
-
* from any existing user's settings on their next sync.
|
|
19
|
-
*/
|
|
20
|
-
const LEGACY_MANAGED_MARKER = '# runwork-managed';
|
|
21
|
-
function getPluginJson() {
|
|
22
|
-
return {
|
|
23
|
-
name: PLUGIN_NAME,
|
|
24
|
-
version: PLUGIN_VERSION,
|
|
25
|
-
description: 'Skills and tools from your Runwork workspace',
|
|
26
|
-
author: { name: 'Runwork' },
|
|
27
|
-
homepage: 'https://www.runwork.ai',
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
function getMarketplaceJson() {
|
|
31
|
-
return {
|
|
32
|
-
name: 'runwork',
|
|
33
|
-
owner: { name: 'Runwork' },
|
|
34
|
-
plugins: [
|
|
35
|
-
{
|
|
36
|
-
name: PLUGIN_NAME,
|
|
37
|
-
source: `./plugins/${PLUGIN_NAME}`,
|
|
38
|
-
description: 'Skills and tools from your Runwork workspace',
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export class ClaudeCodeAdapter {
|
|
44
|
-
name = 'Claude Code';
|
|
45
|
-
slug = 'claude-code';
|
|
46
|
-
mcpProvidesSkills = true;
|
|
47
|
-
async detect() {
|
|
48
|
-
if (whichBinary('claude'))
|
|
49
|
-
return true;
|
|
50
|
-
// Fallback: check app bundle on macOS
|
|
51
|
-
if (platform() === 'darwin')
|
|
52
|
-
return existsSync('/Applications/Claude.app');
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
supportsMcpScope(_scope) {
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
supportsSkills() {
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
async writeMcpServers(servers, scope) {
|
|
62
|
-
if (scope === 'project') {
|
|
63
|
-
// Project scope: write to .mcp.json (standard Claude Code location)
|
|
64
|
-
const filePath = join(process.cwd(), '.mcp.json');
|
|
65
|
-
const entries = {};
|
|
66
|
-
for (const s of servers) {
|
|
67
|
-
entries[s.name] = {
|
|
68
|
-
type: 'http',
|
|
69
|
-
url: s.url,
|
|
70
|
-
...(s.headers ? { headers: s.headers } : {}),
|
|
71
|
-
...(s.description ? { description: s.description } : {}),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
mergeJsonMcpServers(filePath, entries, 'mcpServers');
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
// User scope: write MCP config both in global settings and inside the plugin
|
|
78
|
-
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
79
|
-
const entries = {};
|
|
80
|
-
for (const s of servers) {
|
|
81
|
-
entries[s.name] = {
|
|
82
|
-
type: 'http',
|
|
83
|
-
url: s.url,
|
|
84
|
-
...(s.headers ? { headers: s.headers } : {}),
|
|
85
|
-
...(s.description ? { description: s.description } : {}),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
mergeJsonMcpServers(settingsPath, entries, 'mcpServers');
|
|
89
|
-
// Also write .mcp.json in the plugin directory
|
|
90
|
-
const pluginDir = this.getPluginDir();
|
|
91
|
-
mkdirSync(pluginDir, { recursive: true });
|
|
92
|
-
const pluginMcpPath = join(pluginDir, '.mcp.json');
|
|
93
|
-
const marketDir = this.getMarketplaceDir();
|
|
94
|
-
mkdirSync(marketDir, { recursive: true });
|
|
95
|
-
const marketplaceMcpPath = join(marketDir, '.mcp.json');
|
|
96
|
-
const pluginEntries = {};
|
|
97
|
-
for (const s of servers) {
|
|
98
|
-
pluginEntries[s.name] = {
|
|
99
|
-
type: 'http',
|
|
100
|
-
url: s.url,
|
|
101
|
-
...(s.headers ? { headers: s.headers } : {}),
|
|
102
|
-
...(s.description ? { description: s.description } : {}),
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
writeJsonConfig(pluginMcpPath, pluginEntries);
|
|
106
|
-
writeJsonConfig(marketplaceMcpPath, pluginEntries);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Install the Runwork SessionStart hook into the plugin tree (user scope).
|
|
111
|
-
* The hook writes ~/.runwork/sessions/<sessionId>.json with the active
|
|
112
|
-
* conversation's transcript path + metadata, used by `runwork share-convo`
|
|
113
|
-
* for high-fidelity capture and by `runwork resume` for placement.
|
|
114
|
-
*
|
|
115
|
-
* Called as part of user-scope writeSkills() to keep sync flow consistent.
|
|
116
|
-
* Emits a one-line confirmation matching the format other adapter steps
|
|
117
|
-
* use (` [Claude Code] Installed ... (user)`) so `runwork sync` output
|
|
118
|
-
* shows whether the hook actually landed.
|
|
119
|
-
*/
|
|
120
|
-
installSessionStartHook(pluginDir, label) {
|
|
121
|
-
const hooksDir = join(pluginDir, 'hooks');
|
|
122
|
-
mkdirSync(hooksDir, { recursive: true });
|
|
123
|
-
const scriptPath = join(hooksDir, 'on-session-start.sh');
|
|
124
|
-
writeFileSync(scriptPath, SESSION_START_HOOK_SCRIPT);
|
|
125
|
-
try {
|
|
126
|
-
// Best-effort executable bit; chmod is a no-op on Windows.
|
|
127
|
-
chmodSync(scriptPath, 0o755);
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
/* not fatal */
|
|
131
|
-
}
|
|
132
|
-
const hooksManifest = {
|
|
133
|
-
SessionStart: [
|
|
134
|
-
{ command: '${CLAUDE_PLUGIN_ROOT}/hooks/on-session-start.sh' },
|
|
135
|
-
],
|
|
136
|
-
};
|
|
137
|
-
writeFileSync(join(hooksDir, 'hooks.json'), JSON.stringify(hooksManifest, null, 2));
|
|
138
|
-
console.log(` [Claude Code] Installed SessionStart hook (${label}) -> ${scriptPath}`);
|
|
139
|
-
}
|
|
140
|
-
async writeSkills(skills, scope) {
|
|
141
|
-
// Always write to the standard skills directory (guaranteed to work)
|
|
142
|
-
const baseDir = scope === 'project'
|
|
143
|
-
? join(process.cwd(), '.claude', 'skills')
|
|
144
|
-
: join(homedir(), '.claude', 'skills');
|
|
145
|
-
for (const skill of skills) {
|
|
146
|
-
const skillDir = join(baseDir, skill.filename);
|
|
147
|
-
cleanupOldSkillDir(baseDir, skill);
|
|
148
|
-
mkdirSync(skillDir, { recursive: true });
|
|
149
|
-
writeFileSync(join(skillDir, 'SKILL.md'), buildSkillMd(skill));
|
|
150
|
-
}
|
|
151
|
-
// User scope: also write as a plugin for native plugin UI support
|
|
152
|
-
if (scope === 'user') {
|
|
153
|
-
const pluginDir = this.getPluginDir();
|
|
154
|
-
// Create plugin.json
|
|
155
|
-
const pluginJsonDir = join(pluginDir, '.claude-plugin');
|
|
156
|
-
mkdirSync(pluginJsonDir, { recursive: true });
|
|
157
|
-
writeFileSync(join(pluginJsonDir, 'plugin.json'), JSON.stringify(getPluginJson(), null, 2));
|
|
158
|
-
// Write skill files inside the plugin
|
|
159
|
-
for (const skill of skills) {
|
|
160
|
-
const skillDir = join(pluginDir, 'skills', skill.filename);
|
|
161
|
-
cleanupOldSkillDir(join(pluginDir, 'skills'), skill);
|
|
162
|
-
mkdirSync(skillDir, { recursive: true });
|
|
163
|
-
writeFileSync(join(skillDir, 'SKILL.md'), buildSkillMd(skill));
|
|
164
|
-
}
|
|
165
|
-
// Also write to the marketplace directory
|
|
166
|
-
const marketDir = this.getMarketplaceDir();
|
|
167
|
-
const marketPluginJsonDir = join(marketDir, '.claude-plugin');
|
|
168
|
-
mkdirSync(marketPluginJsonDir, { recursive: true });
|
|
169
|
-
writeFileSync(join(marketPluginJsonDir, 'plugin.json'), JSON.stringify(getPluginJson(), null, 2));
|
|
170
|
-
for (const skill of skills) {
|
|
171
|
-
const skillDir = join(marketDir, 'skills', skill.filename);
|
|
172
|
-
cleanupOldSkillDir(join(marketDir, 'skills'), skill);
|
|
173
|
-
mkdirSync(skillDir, { recursive: true });
|
|
174
|
-
writeFileSync(join(skillDir, 'SKILL.md'), buildSkillMd(skill));
|
|
175
|
-
}
|
|
176
|
-
// Register in installed_plugins.json
|
|
177
|
-
this.registerPlugin(pluginDir);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Install the runwork SessionStart hook for Claude Code (user scope only).
|
|
182
|
-
* Called once per sync by the main sync loop AFTER writeSkills has
|
|
183
|
-
* populated the plugin directories. Kept separate from writeSkills because
|
|
184
|
-
* writeSkills is also invoked per-skill by the diff executor during
|
|
185
|
-
* push/pull, and the hook install + log line must not repeat for every
|
|
186
|
-
* skill change.
|
|
187
|
-
*/
|
|
188
|
-
async writeBuiltInHooks(scope) {
|
|
189
|
-
if (scope !== 'user')
|
|
190
|
-
return;
|
|
191
|
-
const pluginDir = this.getPluginDir();
|
|
192
|
-
const marketDir = this.getMarketplaceDir();
|
|
193
|
-
if (existsSync(pluginDir)) {
|
|
194
|
-
this.installSessionStartHook(pluginDir, 'plugin cache');
|
|
195
|
-
}
|
|
196
|
-
if (existsSync(marketDir)) {
|
|
197
|
-
this.installSessionStartHook(marketDir, 'marketplace');
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
async writeInstructionHint(hint, scope) {
|
|
201
|
-
const filePath = scope === 'project'
|
|
202
|
-
? join(process.cwd(), '.claude', 'CLAUDE.md')
|
|
203
|
-
: join(homedir(), '.claude', 'CLAUDE.md');
|
|
204
|
-
writeHintToFile(filePath, hint);
|
|
205
|
-
}
|
|
206
|
-
async writeTeamInstructions(instructions, scope) {
|
|
207
|
-
// Write to CLAUDE.md via markers (direct instruction file approach)
|
|
208
|
-
const filePath = scope === 'project'
|
|
209
|
-
? join(process.cwd(), '.claude', 'CLAUDE.md')
|
|
210
|
-
: join(homedir(), '.claude', 'CLAUDE.md');
|
|
211
|
-
writeTeamInstructionsToFile(filePath, instructions);
|
|
212
|
-
// Also write as a plugin skill (same mechanism as Claude Desktop)
|
|
213
|
-
if (scope === 'user') {
|
|
214
|
-
const skillContent = `---\nname: runwork-team-instructions\ndescription: Team instructions from your Runwork workspace. Always follow these guidelines.\n---\n\n${instructions}`;
|
|
215
|
-
const pluginDir = this.getPluginDir();
|
|
216
|
-
const marketDir = this.getMarketplaceDir();
|
|
217
|
-
for (const dir of [
|
|
218
|
-
join(pluginDir, 'skills', 'runwork-team-instructions'),
|
|
219
|
-
join(marketDir, 'skills', 'runwork-team-instructions'),
|
|
220
|
-
]) {
|
|
221
|
-
mkdirSync(dir, { recursive: true });
|
|
222
|
-
writeFileSync(join(dir, 'SKILL.md'), skillContent);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
// ── Agent config (model, permissions) ──────────────────────────────
|
|
227
|
-
async writeAgentConfig(config, scope, baseline) {
|
|
228
|
-
const settingsPath = scope === 'project'
|
|
229
|
-
? join(process.cwd(), '.claude', 'settings.json')
|
|
230
|
-
: join(homedir(), '.claude', 'settings.json');
|
|
231
|
-
const hadFile = existsSync(settingsPath);
|
|
232
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
233
|
-
let settings = {};
|
|
234
|
-
if (hadFile) {
|
|
235
|
-
try {
|
|
236
|
-
settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
237
|
-
}
|
|
238
|
-
catch { /* start fresh */ }
|
|
239
|
-
}
|
|
240
|
-
// Retro-fix migration: strip the legacy in-array sentinel from any existing
|
|
241
|
-
// settings, regardless of whether we inject new rules this sync. This is
|
|
242
|
-
// what removes the broken "# runwork-managed" entry that makes Claude Code
|
|
243
|
-
// >= 2.1.x print "matches no known tool" on startup.
|
|
244
|
-
if (settings.permissions && typeof settings.permissions === 'object') {
|
|
245
|
-
for (const key of ['allow', 'deny']) {
|
|
246
|
-
const arr = settings.permissions[key];
|
|
247
|
-
if (Array.isArray(arr)) {
|
|
248
|
-
settings.permissions[key] = arr.filter((x) => x !== LEGACY_MANAGED_MARKER);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if (config.modelPreference) {
|
|
253
|
-
settings.model = config.modelPreference;
|
|
254
|
-
}
|
|
255
|
-
if (config.permissionRules) {
|
|
256
|
-
if (!settings.permissions)
|
|
257
|
-
settings.permissions = {};
|
|
258
|
-
const perms = settings.permissions;
|
|
259
|
-
// Markerless merge: subtract our previous contribution (baseline) to leave
|
|
260
|
-
// the user's own entries intact, then append the freshly resolved set.
|
|
261
|
-
if (config.permissionRules.allow) {
|
|
262
|
-
perms.allow = mergeMarkerless(Array.isArray(perms.allow) ? perms.allow : [], baseline?.allow ?? [], config.permissionRules.allow);
|
|
263
|
-
}
|
|
264
|
-
if (config.permissionRules.deny) {
|
|
265
|
-
perms.deny = mergeMarkerless(Array.isArray(perms.deny) ? perms.deny : [], baseline?.deny ?? [], config.permissionRules.deny);
|
|
266
|
-
}
|
|
267
|
-
if (config.permissionRules.defaultMode) {
|
|
268
|
-
perms.defaultMode = config.permissionRules.defaultMode;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
// Nothing to persist and no file to clean up: avoid creating an empty file.
|
|
272
|
-
if (!hadFile && !config.modelPreference && !config.permissionRules)
|
|
273
|
-
return;
|
|
274
|
-
mkdirSync(join(settingsPath, '..'), { recursive: true });
|
|
275
|
-
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Markerless adapter: removal detection relies on the `baseline`
|
|
279
|
-
* (state.lastInjected) passed to writeAgentConfig, exactly like Cursor, since
|
|
280
|
-
* the adapter no longer writes an on-disk sentinel to delimit its entries.
|
|
281
|
-
* Returning undefined tells the sync loop to skip on-disk removal detection.
|
|
282
|
-
*/
|
|
283
|
-
async readManagedBlock(_scope) {
|
|
284
|
-
return undefined;
|
|
285
|
-
}
|
|
286
|
-
// ── Cleanup (uninstall) ────────────────────────────────────────────
|
|
287
|
-
async cleanup(scope, manifest) {
|
|
288
|
-
// 1. Remove MCP server entries
|
|
289
|
-
if (scope === 'project') {
|
|
290
|
-
removeRunworkMcpServers(join(process.cwd(), '.mcp.json'), 'mcpServers');
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
removeRunworkMcpServers(join(homedir(), '.claude', 'settings.json'), 'mcpServers');
|
|
294
|
-
}
|
|
295
|
-
// 2. Remove only skill directories that we synced (listed in manifest)
|
|
296
|
-
const skillsDir = scope === 'project'
|
|
297
|
-
? join(process.cwd(), '.claude', 'skills')
|
|
298
|
-
: join(homedir(), '.claude', 'skills');
|
|
299
|
-
if (existsSync(skillsDir) && manifest?.skillFilenames.length) {
|
|
300
|
-
const allowed = new Set(manifest.skillFilenames);
|
|
301
|
-
for (const entry of readdirSync(skillsDir)) {
|
|
302
|
-
if (!allowed.has(entry))
|
|
303
|
-
continue;
|
|
304
|
-
try {
|
|
305
|
-
rmSync(join(skillsDir, entry), { recursive: true, force: true });
|
|
306
|
-
}
|
|
307
|
-
catch { /* best-effort */ }
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
// 3. Remove instruction hints and team instructions
|
|
311
|
-
const instructionFile = scope === 'project'
|
|
312
|
-
? join(process.cwd(), '.claude', 'CLAUDE.md')
|
|
313
|
-
: join(homedir(), '.claude', 'CLAUDE.md');
|
|
314
|
-
removeHintFromFile(instructionFile);
|
|
315
|
-
removeTeamInstructionsFromFile(instructionFile);
|
|
316
|
-
// 4. Remove agent config overrides (managed permission entries, model, enabledPlugins)
|
|
317
|
-
if (scope === 'user') {
|
|
318
|
-
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
319
|
-
if (existsSync(settingsPath)) {
|
|
320
|
-
try {
|
|
321
|
-
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
322
|
-
// Remove managed permission entries. Legacy installs carry the
|
|
323
|
-
// in-array marker, so everything from the marker onward is ours.
|
|
324
|
-
// Always also drop any stray marker so uninstall never leaves the
|
|
325
|
-
// entry that breaks Claude Code startup.
|
|
326
|
-
if (settings.permissions) {
|
|
327
|
-
for (const key of ['allow', 'deny']) {
|
|
328
|
-
const arr = settings.permissions[key];
|
|
329
|
-
if (!Array.isArray(arr))
|
|
330
|
-
continue;
|
|
331
|
-
const idx = arr.indexOf(LEGACY_MANAGED_MARKER);
|
|
332
|
-
settings.permissions[key] = (idx >= 0 ? arr.slice(0, idx) : arr)
|
|
333
|
-
.filter((x) => x !== LEGACY_MANAGED_MARKER);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
// Remove plugin enablement
|
|
337
|
-
if (settings.enabledPlugins) {
|
|
338
|
-
delete settings.enabledPlugins[`${PLUGIN_NAME}@runwork`];
|
|
339
|
-
}
|
|
340
|
-
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
341
|
-
}
|
|
342
|
-
catch { /* best-effort */ }
|
|
343
|
-
}
|
|
344
|
-
// 5. Remove plugin directories
|
|
345
|
-
const pluginDir = this.getPluginDir();
|
|
346
|
-
const marketRoot = this.getMarketplaceRoot();
|
|
347
|
-
for (const dir of [pluginDir, marketRoot]) {
|
|
348
|
-
if (existsSync(dir)) {
|
|
349
|
-
try {
|
|
350
|
-
rmSync(dir, { recursive: true, force: true });
|
|
351
|
-
}
|
|
352
|
-
catch { /* best-effort */ }
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
// 6. Remove from installed_plugins.json
|
|
356
|
-
const pluginsBase = this.getPluginsBaseDir();
|
|
357
|
-
const installedPath = join(pluginsBase, 'installed_plugins.json');
|
|
358
|
-
if (existsSync(installedPath)) {
|
|
359
|
-
try {
|
|
360
|
-
const installed = readJsonConfig(installedPath);
|
|
361
|
-
if (installed.plugins) {
|
|
362
|
-
delete installed.plugins[`${PLUGIN_NAME}@runwork`];
|
|
363
|
-
writeJsonConfig(installedPath, installed);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
catch { /* best-effort */ }
|
|
367
|
-
}
|
|
368
|
-
// 7. Remove from known_marketplaces.json
|
|
369
|
-
const marketplacesPath = join(pluginsBase, 'known_marketplaces.json');
|
|
370
|
-
if (existsSync(marketplacesPath)) {
|
|
371
|
-
try {
|
|
372
|
-
const marketplaces = readJsonConfig(marketplacesPath);
|
|
373
|
-
delete marketplaces['runwork'];
|
|
374
|
-
writeJsonConfig(marketplacesPath, marketplaces);
|
|
375
|
-
}
|
|
376
|
-
catch { /* best-effort */ }
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
// ── Usage stats (passive, best-effort) ─────────────────────────────
|
|
381
|
-
async readUsageStats(lastSyncAt) {
|
|
382
|
-
// Source of truth: ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl
|
|
383
|
-
// Each line is one user/assistant/system entry. The file grows continuously
|
|
384
|
-
// during a live session, so mtime + per-entry timestamp give an accurate
|
|
385
|
-
// post-sync activity picture. (stats-cache.json was abandoned upstream and
|
|
386
|
-
// session-env/ only records session-start hooks.)
|
|
387
|
-
try {
|
|
388
|
-
const claudeDir = join(homedir(), '.claude');
|
|
389
|
-
if (!existsSync(claudeDir))
|
|
390
|
-
return null;
|
|
391
|
-
const projectsDir = join(claudeDir, 'projects');
|
|
392
|
-
if (!existsSync(projectsDir))
|
|
393
|
-
return null;
|
|
394
|
-
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
395
|
-
let sessionCount = 0;
|
|
396
|
-
let messageCount = 0;
|
|
397
|
-
let toolCallCount = 0;
|
|
398
|
-
let inputTokens = 0;
|
|
399
|
-
let outputTokens = 0;
|
|
400
|
-
let cacheReadTokens = 0;
|
|
401
|
-
let latestActivityMs = 0;
|
|
402
|
-
// Dedup: ccusage uses ${message.id}:${requestId} to drop retransmissions.
|
|
403
|
-
// Entries without both fields pass through undeduped (same behavior).
|
|
404
|
-
const seenEntries = new Set();
|
|
405
|
-
let cwdEntries;
|
|
406
|
-
try {
|
|
407
|
-
cwdEntries = readdirSync(projectsDir);
|
|
408
|
-
}
|
|
409
|
-
catch {
|
|
410
|
-
return null;
|
|
411
|
-
}
|
|
412
|
-
for (const cwd of cwdEntries) {
|
|
413
|
-
const cwdPath = join(projectsDir, cwd);
|
|
414
|
-
let files;
|
|
415
|
-
try {
|
|
416
|
-
files = readdirSync(cwdPath);
|
|
417
|
-
}
|
|
418
|
-
catch {
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
421
|
-
for (const file of files) {
|
|
422
|
-
if (!file.endsWith('.jsonl'))
|
|
423
|
-
continue;
|
|
424
|
-
const filePath = join(cwdPath, file);
|
|
425
|
-
let stat;
|
|
426
|
-
try {
|
|
427
|
-
stat = statSync(filePath);
|
|
428
|
-
}
|
|
429
|
-
catch {
|
|
430
|
-
continue;
|
|
431
|
-
}
|
|
432
|
-
if (stat.mtimeMs <= sinceMs)
|
|
433
|
-
continue;
|
|
434
|
-
let content;
|
|
435
|
-
try {
|
|
436
|
-
content = readFileSync(filePath, 'utf-8');
|
|
437
|
-
}
|
|
438
|
-
catch {
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
let sessionHadActivity = false;
|
|
442
|
-
for (const line of content.split('\n')) {
|
|
443
|
-
if (!line)
|
|
444
|
-
continue;
|
|
445
|
-
let entry;
|
|
446
|
-
try {
|
|
447
|
-
entry = JSON.parse(line);
|
|
448
|
-
}
|
|
449
|
-
catch {
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
const tsRaw = entry.timestamp;
|
|
453
|
-
if (typeof tsRaw !== 'string')
|
|
454
|
-
continue;
|
|
455
|
-
const ts = Date.parse(tsRaw);
|
|
456
|
-
if (!Number.isFinite(ts) || ts <= sinceMs)
|
|
457
|
-
continue;
|
|
458
|
-
sessionHadActivity = true;
|
|
459
|
-
if (ts > latestActivityMs)
|
|
460
|
-
latestActivityMs = ts;
|
|
461
|
-
const type = entry.type;
|
|
462
|
-
if (type === 'user' || type === 'assistant') {
|
|
463
|
-
// Dedup by message.id + requestId (same approach as ccusage)
|
|
464
|
-
const msg = entry.message;
|
|
465
|
-
const msgId = msg && typeof msg === 'object' ? msg.id : undefined;
|
|
466
|
-
const reqId = entry.requestId;
|
|
467
|
-
if (typeof msgId === 'string' && typeof reqId === 'string') {
|
|
468
|
-
const dedupKey = `${msgId}:${reqId}`;
|
|
469
|
-
if (seenEntries.has(dedupKey))
|
|
470
|
-
continue;
|
|
471
|
-
seenEntries.add(dedupKey);
|
|
472
|
-
}
|
|
473
|
-
messageCount++;
|
|
474
|
-
}
|
|
475
|
-
if (type === 'assistant') {
|
|
476
|
-
const msg = entry.message;
|
|
477
|
-
if (msg && typeof msg === 'object') {
|
|
478
|
-
const msgRecord = msg;
|
|
479
|
-
const blocks = Array.isArray(msgRecord.content) ? msgRecord.content : [];
|
|
480
|
-
for (const block of blocks) {
|
|
481
|
-
if (block && typeof block === 'object'
|
|
482
|
-
&& block.type === 'tool_use') {
|
|
483
|
-
toolCallCount++;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
const usage = msgRecord.usage;
|
|
487
|
-
if (usage && typeof usage === 'object') {
|
|
488
|
-
const u = usage;
|
|
489
|
-
if (typeof u.input_tokens === 'number')
|
|
490
|
-
inputTokens += u.input_tokens;
|
|
491
|
-
if (typeof u.output_tokens === 'number')
|
|
492
|
-
outputTokens += u.output_tokens;
|
|
493
|
-
if (typeof u.cache_read_input_tokens === 'number') {
|
|
494
|
-
cacheReadTokens += u.cache_read_input_tokens;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
if (sessionHadActivity)
|
|
501
|
-
sessionCount++;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
if (sessionCount === 0)
|
|
505
|
-
return null;
|
|
506
|
-
return {
|
|
507
|
-
hasNewActivity: true,
|
|
508
|
-
sessionCount,
|
|
509
|
-
messageCount,
|
|
510
|
-
toolCallCount,
|
|
511
|
-
tokensUsed: inputTokens + outputTokens,
|
|
512
|
-
aiLinesAdded: 0,
|
|
513
|
-
aiLinesRemoved: 0,
|
|
514
|
-
lastActiveAt: latestActivityMs > 0 ? new Date(latestActivityMs).toISOString() : null,
|
|
515
|
-
extra: { inputTokens, outputTokens, cacheReadTokens },
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
catch {
|
|
519
|
-
return null;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
async readSkillUsage(lastSyncAt) {
|
|
523
|
-
// Walk the same JSONL files as readUsageStats and detect actual skill
|
|
524
|
-
// invocations. Two verified paths exist in the data:
|
|
525
|
-
//
|
|
526
|
-
// Path A (model-initiated): assistant message contains a tool_use block
|
|
527
|
-
// with name === "Skill" and input.skill === "<skill-name>".
|
|
528
|
-
//
|
|
529
|
-
// Path B (user-initiated slash command): user message contains
|
|
530
|
-
// <command-name>/xxx</command-name>, and the NEXT user message starts
|
|
531
|
-
// with "Base directory for this skill:" (confirming the harness treated
|
|
532
|
-
// it as a skill, not a built-in command).
|
|
533
|
-
//
|
|
534
|
-
// MCP skill tools (mcp__*__skill_*) are listed in deferred-tool blocks
|
|
535
|
-
// but are never actually invoked in any observed JSONL data.
|
|
536
|
-
try {
|
|
537
|
-
const projectsDir = join(homedir(), '.claude', 'projects');
|
|
538
|
-
if (!existsSync(projectsDir))
|
|
539
|
-
return null;
|
|
540
|
-
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
541
|
-
const skillCounts = new Map();
|
|
542
|
-
const recordSkill = (name, ts) => {
|
|
543
|
-
const existing = skillCounts.get(name);
|
|
544
|
-
if (existing) {
|
|
545
|
-
existing.count++;
|
|
546
|
-
if (ts > existing.lastTs)
|
|
547
|
-
existing.lastTs = ts;
|
|
548
|
-
}
|
|
549
|
-
else {
|
|
550
|
-
skillCounts.set(name, { count: 1, lastTs: ts });
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
let cwdEntries;
|
|
554
|
-
try {
|
|
555
|
-
cwdEntries = readdirSync(projectsDir);
|
|
556
|
-
}
|
|
557
|
-
catch {
|
|
558
|
-
return null;
|
|
559
|
-
}
|
|
560
|
-
for (const cwd of cwdEntries) {
|
|
561
|
-
const cwdPath = join(projectsDir, cwd);
|
|
562
|
-
let files;
|
|
563
|
-
try {
|
|
564
|
-
files = readdirSync(cwdPath);
|
|
565
|
-
}
|
|
566
|
-
catch {
|
|
567
|
-
continue;
|
|
568
|
-
}
|
|
569
|
-
for (const file of files) {
|
|
570
|
-
if (!file.endsWith('.jsonl'))
|
|
571
|
-
continue;
|
|
572
|
-
const filePath = join(cwdPath, file);
|
|
573
|
-
let fileStat;
|
|
574
|
-
try {
|
|
575
|
-
fileStat = statSync(filePath);
|
|
576
|
-
}
|
|
577
|
-
catch {
|
|
578
|
-
continue;
|
|
579
|
-
}
|
|
580
|
-
if (fileStat.mtimeMs <= sinceMs)
|
|
581
|
-
continue;
|
|
582
|
-
let content;
|
|
583
|
-
try {
|
|
584
|
-
content = readFileSync(filePath, 'utf-8');
|
|
585
|
-
}
|
|
586
|
-
catch {
|
|
587
|
-
continue;
|
|
588
|
-
}
|
|
589
|
-
// Track pending slash command for Path B lookahead
|
|
590
|
-
let pendingCommand = null;
|
|
591
|
-
for (const line of content.split('\n')) {
|
|
592
|
-
if (!line)
|
|
593
|
-
continue;
|
|
594
|
-
let entry;
|
|
595
|
-
try {
|
|
596
|
-
entry = JSON.parse(line);
|
|
597
|
-
}
|
|
598
|
-
catch {
|
|
599
|
-
continue;
|
|
600
|
-
}
|
|
601
|
-
const tsRaw = entry.timestamp;
|
|
602
|
-
if (typeof tsRaw !== 'string')
|
|
603
|
-
continue;
|
|
604
|
-
const ts = Date.parse(tsRaw);
|
|
605
|
-
if (!Number.isFinite(ts) || ts <= sinceMs)
|
|
606
|
-
continue;
|
|
607
|
-
const type = entry.type;
|
|
608
|
-
// Path A: Skill tool calls in assistant messages
|
|
609
|
-
if (type === 'assistant') {
|
|
610
|
-
const msg = entry.message;
|
|
611
|
-
if (msg && typeof msg === 'object') {
|
|
612
|
-
const blocks = Array.isArray(msg.content)
|
|
613
|
-
? msg.content
|
|
614
|
-
: [];
|
|
615
|
-
for (const block of blocks) {
|
|
616
|
-
if (block && block.type === 'tool_use' && block.name === 'Skill') {
|
|
617
|
-
const input = block.input;
|
|
618
|
-
if (input && typeof input.skill === 'string') {
|
|
619
|
-
recordSkill(input.skill, ts);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
// Path B: slash command -> skill content injection
|
|
626
|
-
if (type === 'user') {
|
|
627
|
-
const msg = entry.message;
|
|
628
|
-
const msgContent = msg?.content;
|
|
629
|
-
const text = typeof msgContent === 'string' ? msgContent : '';
|
|
630
|
-
// Check if this is a skill content injection following a slash command
|
|
631
|
-
if (pendingCommand && text.startsWith('Base directory for this skill:')) {
|
|
632
|
-
recordSkill(pendingCommand.name, pendingCommand.ts);
|
|
633
|
-
pendingCommand = null;
|
|
634
|
-
continue;
|
|
635
|
-
}
|
|
636
|
-
// Reset pending on any user message that isn't skill content
|
|
637
|
-
if (pendingCommand && type === 'user') {
|
|
638
|
-
pendingCommand = null;
|
|
639
|
-
}
|
|
640
|
-
// Check for slash command pattern
|
|
641
|
-
const match = text.match(/<command-name>\/?([^<]+)<\/command-name>/);
|
|
642
|
-
if (match) {
|
|
643
|
-
const cmdName = match[1].replace(/^\//, '');
|
|
644
|
-
pendingCommand = { name: cmdName, ts };
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
if (skillCounts.size === 0)
|
|
651
|
-
return null;
|
|
652
|
-
const results = [];
|
|
653
|
-
for (const [skillName, { count, lastTs }] of skillCounts) {
|
|
654
|
-
results.push({
|
|
655
|
-
skillName,
|
|
656
|
-
count,
|
|
657
|
-
lastUsedAt: new Date(lastTs).toISOString(),
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
return results;
|
|
661
|
-
}
|
|
662
|
-
catch {
|
|
663
|
-
return null;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
async readVersion() {
|
|
667
|
-
try {
|
|
668
|
-
const output = execFileSync('claude', ['--version'], { stdio: 'pipe' }).toString().trim();
|
|
669
|
-
return output;
|
|
670
|
-
}
|
|
671
|
-
catch {
|
|
672
|
-
return null;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
// ── Plugin helpers ─────────────────────────────────────────────────
|
|
676
|
-
getPluginsBaseDir() {
|
|
677
|
-
return join(homedir(), '.claude', 'plugins');
|
|
678
|
-
}
|
|
679
|
-
getPluginDir() {
|
|
680
|
-
return join(this.getPluginsBaseDir(), 'cache', 'runwork', PLUGIN_NAME, PLUGIN_VERSION);
|
|
681
|
-
}
|
|
682
|
-
/** The marketplace root (contains marketplace.json and plugins/) */
|
|
683
|
-
getMarketplaceRoot() {
|
|
684
|
-
return join(this.getPluginsBaseDir(), 'marketplaces', 'runwork');
|
|
685
|
-
}
|
|
686
|
-
/** The plugin directory within the marketplace */
|
|
687
|
-
getMarketplaceDir() {
|
|
688
|
-
return join(this.getMarketplaceRoot(), 'plugins', PLUGIN_NAME);
|
|
689
|
-
}
|
|
690
|
-
registerPlugin(pluginDir) {
|
|
691
|
-
const pluginsBase = this.getPluginsBaseDir();
|
|
692
|
-
mkdirSync(pluginsBase, { recursive: true });
|
|
693
|
-
// Register in installed_plugins.json
|
|
694
|
-
const installedPath = join(pluginsBase, 'installed_plugins.json');
|
|
695
|
-
const installed = readJsonConfig(installedPath);
|
|
696
|
-
if (!installed.version)
|
|
697
|
-
installed.version = 2;
|
|
698
|
-
if (!installed.plugins)
|
|
699
|
-
installed.plugins = {};
|
|
700
|
-
const pluginKey = `${PLUGIN_NAME}@runwork`;
|
|
701
|
-
const wasRegistered = !!installed.plugins[pluginKey];
|
|
702
|
-
installed.plugins[pluginKey] = [{
|
|
703
|
-
scope: 'user',
|
|
704
|
-
installPath: pluginDir,
|
|
705
|
-
version: PLUGIN_VERSION,
|
|
706
|
-
installedAt: new Date().toISOString(),
|
|
707
|
-
lastUpdated: new Date().toISOString(),
|
|
708
|
-
}];
|
|
709
|
-
writeJsonConfig(installedPath, installed);
|
|
710
|
-
// Write marketplace.json catalog
|
|
711
|
-
const marketRoot = this.getMarketplaceRoot();
|
|
712
|
-
const marketCatalogDir = join(marketRoot, '.claude-plugin');
|
|
713
|
-
mkdirSync(marketCatalogDir, { recursive: true });
|
|
714
|
-
writeFileSync(join(marketCatalogDir, 'marketplace.json'), JSON.stringify(getMarketplaceJson(), null, 2));
|
|
715
|
-
// Register in known_marketplaces.json
|
|
716
|
-
const marketplacesPath = join(pluginsBase, 'known_marketplaces.json');
|
|
717
|
-
const marketplaces = readJsonConfig(marketplacesPath);
|
|
718
|
-
marketplaces['runwork'] = {
|
|
719
|
-
source: { source: 'directory', path: marketRoot },
|
|
720
|
-
installLocation: marketRoot,
|
|
721
|
-
lastUpdated: new Date().toISOString(),
|
|
722
|
-
};
|
|
723
|
-
writeJsonConfig(marketplacesPath, marketplaces);
|
|
724
|
-
console.log(` [Claude Code] ${wasRegistered ? 'Refreshed' : 'Registered'} plugin ` +
|
|
725
|
-
`${pluginKey} v${PLUGIN_VERSION} -> ${installedPath}`);
|
|
726
|
-
// Enable the plugin in settings.json
|
|
727
|
-
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
728
|
-
const settings = readJsonConfig(settingsPath);
|
|
729
|
-
if (!settings['enabledPlugins']) {
|
|
730
|
-
settings['enabledPlugins'] = {};
|
|
731
|
-
}
|
|
732
|
-
const enabled = settings['enabledPlugins'];
|
|
733
|
-
if (!enabled[`${PLUGIN_NAME}@runwork`]) {
|
|
734
|
-
enabled[`${PLUGIN_NAME}@runwork`] = true;
|
|
735
|
-
writeJsonConfig(settingsPath, settings);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
/**
|
|
740
|
-
* Remove old skill directory that used the display name (with spaces) if it
|
|
741
|
-
* differs from the new slug-based directory name.
|
|
742
|
-
*/
|
|
743
|
-
function cleanupOldSkillDir(baseDir, skill) {
|
|
744
|
-
if (skill.name === skill.filename)
|
|
745
|
-
return;
|
|
746
|
-
const oldDir = join(baseDir, skill.name);
|
|
747
|
-
if (existsSync(oldDir)) {
|
|
748
|
-
try {
|
|
749
|
-
rmSync(oldDir, { recursive: true, force: true });
|
|
750
|
-
}
|
|
751
|
-
catch { /* best-effort */ }
|
|
752
|
-
}
|
|
753
|
-
}
|