@taskforcehq/taskforce 0.3.303 → 0.3.305

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +140 -247
  2. package/dist/Taskforce.module.css +102 -5
  3. package/dist/TaskforceCore.js +635 -238
  4. package/dist/TaskforceCore.test.js +2243 -495
  5. package/dist/annotatedAttachments/service.d.ts +1 -1
  6. package/dist/annotatedAttachments/types.d.ts +1 -1
  7. package/dist/compat/workspaceSyncCompat.js +6 -0
  8. package/dist/components/features/AgentsModule.js +1 -1
  9. package/dist/components/features/AgentsModule.test.js +1 -1
  10. package/dist/components/features/DocumentIndex.d.ts +1 -1
  11. package/dist/components/features/DocumentIndex.js +1 -1
  12. package/dist/components/features/DocumentViewer.d.ts +1 -1
  13. package/dist/components/features/DocumentViewer.js +32 -9
  14. package/dist/components/features/DocumentWorkspace.d.ts +5 -22
  15. package/dist/components/features/DocumentWorkspace.js +12 -155
  16. package/dist/components/features/DocumentWorkspace.test.js +193 -6
  17. package/dist/components/features/TaskSettings.js +87 -41
  18. package/dist/components/features/TaskSettings.test.js +180 -40
  19. package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
  20. package/dist/components/features/documentWorkspaceModel.js +57 -0
  21. package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
  22. package/dist/components/features/useDocumentWorkspaceController.js +113 -0
  23. package/dist/components/task/TaskActionHeader.d.ts +2 -1
  24. package/dist/components/task/TaskActionHeader.js +3 -3
  25. package/dist/components/task/TaskActionHeader.test.js +8 -0
  26. package/dist/components/task/TaskCard.js +16 -1
  27. package/dist/components/task/TaskCard.test.js +31 -0
  28. package/dist/components/task/TaskForm.d.ts +9 -8
  29. package/dist/components/task/TaskForm.js +374 -219
  30. package/dist/components/task/TaskForm.test.js +482 -361
  31. package/dist/components/task/TaskKanban.d.ts +1 -1
  32. package/dist/components/task/TaskKanban.js +33 -161
  33. package/dist/components/task/TaskStatusActions.d.ts +6 -1
  34. package/dist/components/task/TaskStatusActions.js +8 -3
  35. package/dist/components/task/TaskStatusActions.test.js +9 -0
  36. package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
  37. package/dist/components/task/taskKanbanDropRules.js +187 -0
  38. package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
  39. package/dist/components/task/taskKanbanDropRules.test.js +114 -0
  40. package/dist/components/ui/Modal.d.ts +1 -1
  41. package/dist/components/ui/Modal.js +1 -0
  42. package/dist/components/views/AppShellHeader.js +5 -1
  43. package/dist/components/views/AppShellHeader.test.js +4 -0
  44. package/dist/components/views/PlanComparisonPage.d.ts +3 -2
  45. package/dist/components/views/PlanComparisonPage.js +9 -10
  46. package/dist/components/views/PlanComparisonPage.test.js +55 -41
  47. package/dist/components/views/PlansPage.d.ts +5 -1
  48. package/dist/components/views/PlansPage.js +150 -81
  49. package/dist/components/views/PlansPage.test.js +367 -41
  50. package/dist/components/views/StandaloneLayout.js +135 -54
  51. package/dist/components/views/WidgetView.js +11 -2
  52. package/dist/components/views/panels/FilterToolbar.test.js +6 -4
  53. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +2 -14
  54. package/dist/components/views/standalone/modals/AccountHubModal.js +12 -14
  55. package/dist/components/views/standalone/modals/AccountHubModal.test.js +24 -1
  56. package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
  57. package/dist/config/envSchema.js +1 -1
  58. package/dist/core/AiProfileService.d.ts +6 -1
  59. package/dist/core/AiProfileService.js +161 -16
  60. package/dist/core/AiProfileService.test.js +3 -1
  61. package/dist/core/AiProfiles.test.js +200 -0
  62. package/dist/core/AuthTokenService.test.d.ts +1 -0
  63. package/dist/core/AuthTokenService.test.js +78 -0
  64. package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
  65. package/dist/core/EntitlementsPolicy.test.js +75 -13
  66. package/dist/core/PlanEntitlementService.d.ts +9 -1
  67. package/dist/core/PlanEntitlementService.js +144 -19
  68. package/dist/core/PlanVersionPolicy.test.js +19 -26
  69. package/dist/core/SignupMonetizationSettings.test.js +75 -15
  70. package/dist/core/SystemAdmin.test.js +212 -56
  71. package/dist/core/TaskActivityService.d.ts +4 -1
  72. package/dist/core/TaskActivityService.js +45 -0
  73. package/dist/core/TaskAuditTimeline.test.js +52 -1
  74. package/dist/core/TaskTaxonomyValidation.test.js +53 -0
  75. package/dist/core/Taskforce.d.ts +18 -11
  76. package/dist/core/Taskforce.js +93 -12
  77. package/dist/core/WorkspacePlanMode.test.js +73 -8
  78. package/dist/core/shared.d.ts +2 -0
  79. package/dist/core/shared.js +11 -0
  80. package/dist/documentReviews/service.d.ts +1 -1
  81. package/dist/documentReviews/types.d.ts +1 -1
  82. package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
  83. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
  84. package/dist/hooks/sync/auth.d.ts +37 -0
  85. package/dist/hooks/sync/auth.js +63 -0
  86. package/dist/hooks/sync/bootstrap.d.ts +34 -0
  87. package/dist/hooks/sync/bootstrap.js +52 -0
  88. package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
  89. package/dist/hooks/sync/orchestratorShared.js +209 -0
  90. package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
  91. package/dist/hooks/sync/orchestratorShared.test.js +49 -0
  92. package/dist/hooks/sync/realtime.d.ts +25 -0
  93. package/dist/hooks/sync/realtime.js +60 -0
  94. package/dist/hooks/sync/recovery.d.ts +69 -0
  95. package/dist/hooks/sync/recovery.js +118 -0
  96. package/dist/hooks/sync/transfers.d.ts +123 -0
  97. package/dist/hooks/sync/transfers.js +435 -0
  98. package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
  99. package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
  100. package/dist/hooks/ui/useResourceExport.d.ts +3 -1
  101. package/dist/hooks/ui/useResourceExport.js +5 -2
  102. package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
  103. package/dist/hooks/useRealtimeSync.js +2 -1
  104. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
  105. package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
  106. package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
  107. package/dist/hooks/useSyncOrchestrator.d.ts +12 -8
  108. package/dist/hooks/useSyncOrchestrator.js +388 -879
  109. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +641 -6
  110. package/dist/hooks/useTaskMutations.d.ts +2 -1
  111. package/dist/hooks/useTaskMutations.js +57 -7
  112. package/dist/hooks/useTaskMutations.test.js +176 -0
  113. package/dist/hooks/useTaskforce.d.ts +13 -4
  114. package/dist/hooks/useTaskforce.js +302 -817
  115. package/dist/hooks/useTaskforce.sync-behavior.test.js +78 -0
  116. package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
  117. package/dist/hooks/useWorkspaceSyncController.js +33 -11
  118. package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
  119. package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
  120. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
  121. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
  122. package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
  123. package/dist/hooks/workspace/workspaceLocalState.js +100 -0
  124. package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
  125. package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
  126. package/dist/localization/locales/en-US.js +2 -2
  127. package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
  128. package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
  129. package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
  130. package/dist/mcp/canonicalAssetHelpers.js +259 -0
  131. package/dist/mcp/clientIntegrations.d.ts +12 -1
  132. package/dist/mcp/clientIntegrations.js +161 -6
  133. package/dist/mcp/clientIntegrations.test.js +76 -4
  134. package/dist/mcp/collaborationRegistrar.d.ts +2 -0
  135. package/dist/mcp/collaborationRegistrar.js +71 -0
  136. package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
  137. package/dist/mcp/documentAssetRegistrar.js +346 -0
  138. package/dist/mcp/documentHelpers.d.ts +100 -0
  139. package/dist/mcp/documentHelpers.js +161 -0
  140. package/dist/mcp/runtime.js +2455 -3925
  141. package/dist/mcp/runtime.test.js +240 -8
  142. package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
  143. package/dist/mcp/taskAttachmentHelpers.js +60 -0
  144. package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
  145. package/dist/mcp/taskAttachmentTypes.js +1 -0
  146. package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
  147. package/dist/mcp/taskPlanningRegistrar.js +418 -0
  148. package/dist/mcp/toolCatalog.d.ts +35 -0
  149. package/dist/mcp/toolCatalog.js +3 -0
  150. package/dist/plugins/vite.js +1 -1
  151. package/dist/runtime/appGateDefinitions.d.ts +1 -1
  152. package/dist/runtime/appGateDefinitions.js +1 -1
  153. package/dist/server/index.d.ts +1 -0
  154. package/dist/server/index.js +46 -3
  155. package/dist/server/index.test.js +12 -1
  156. package/dist/server/routes/admin.d.ts +1 -5
  157. package/dist/server/routes/admin.js +151 -80
  158. package/dist/server/routes/annotatedAttachments.d.ts +1 -1
  159. package/dist/server/routes/annotatedAttachments.js +1 -1
  160. package/dist/server/routes/auth.d.ts +1 -4
  161. package/dist/server/routes/auth.js +59 -64
  162. package/dist/server/routes/authSupport.d.ts +30 -0
  163. package/dist/server/routes/authSupport.js +81 -0
  164. package/dist/server/routes/billing.d.ts +1 -1
  165. package/dist/server/routes/billing.js +122 -255
  166. package/dist/server/routes/billing.test.js +257 -86
  167. package/dist/server/routes/documentReviews.d.ts +1 -1
  168. package/dist/server/routes/documentReviews.js +1 -1
  169. package/dist/server/routes/documents.d.ts +4 -2
  170. package/dist/server/routes/documents.js +73 -7
  171. package/dist/server/routes/primitives.d.ts +11 -0
  172. package/dist/server/routes/primitives.js +73 -0
  173. package/dist/server/routes/resources.d.ts +1 -1
  174. package/dist/server/routes/resources.js +1 -1
  175. package/dist/server/routes/shared.d.ts +31 -1
  176. package/dist/server/routes/shared.js +26 -0
  177. package/dist/server/routes/sync.d.ts +1 -1
  178. package/dist/server/routes/sync.integration.test.js +20 -0
  179. package/dist/server/routes/sync.js +111 -238
  180. package/dist/server/routes/tasks.d.ts +1 -1
  181. package/dist/server/routes/tasks.js +50 -8
  182. package/dist/server/routes/workspaces.d.ts +1 -1
  183. package/dist/server/routes/workspaces.js +1 -1
  184. package/dist/server/routes.d.ts +2 -9
  185. package/dist/server/routes.js +84 -181
  186. package/dist/server/routes.test.js +912 -32
  187. package/dist/shared/taskActor.d.ts +10 -0
  188. package/dist/shared/taskActor.js +1 -0
  189. package/dist/sync/collaborationSyncPayload.js +4 -0
  190. package/dist/sync/collaborationSyncPayload.test.js +8 -0
  191. package/dist/sync/collaborationSyncState.d.ts +3 -1
  192. package/dist/sync/collaborationSyncState.js +19 -0
  193. package/dist/sync/contentSyncState.js +28 -17
  194. package/dist/sync/syncApplyHandlers.d.ts +87 -1
  195. package/dist/sync/syncApplyHandlers.js +370 -8
  196. package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
  197. package/dist/sync/syncApplyHandlers.test.js +379 -0
  198. package/dist/sync/syncService.d.ts +9 -1
  199. package/dist/sync/syncService.js +44 -2
  200. package/dist/sync/syncService.test.js +62 -0
  201. package/dist/sync/workspacePullFeed.d.ts +1 -0
  202. package/dist/sync/workspacePullFeed.js +66 -59
  203. package/dist/sync/workspacePullFeed.test.js +72 -4
  204. package/dist/sync/workspaceSyncModel.d.ts +25 -1
  205. package/dist/sync/workspaceSyncModel.js +77 -3
  206. package/dist/sync/workspaceSyncModel.test.js +153 -0
  207. package/dist/sync/workspaceSyncState.d.ts +2 -0
  208. package/dist/sync/workspaceSyncState.js +1 -0
  209. package/dist/types.d.ts +2 -10
  210. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +1 -0
  211. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BG6P_GVW.js} +1 -1
  212. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +1 -0
  213. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +252 -0
  214. package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.js → InitiativesModule-Dhm7M8e7.js} +1 -1
  215. package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
  216. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +1 -0
  217. package/dist/ui/assets/TaskSettings-ln0aF7xc.js +9 -0
  218. package/dist/ui/assets/{WorkflowsModule-CGk9s3NJ.js → WorkflowsModule-BcS4afRn.js} +1 -1
  219. package/dist/ui/assets/index-C2-OXZV7.css +1 -0
  220. package/dist/ui/assets/index-DcSI5F86.js +6 -0
  221. package/dist/ui/assets/{vendor-icons-pWocEipT.js → vendor-icons-BSUaRwF8.js} +1 -1
  222. package/dist/ui/index.html +4 -4
  223. package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
  224. package/dist/utils/commercialLifecyclePresentation.js +196 -0
  225. package/dist/utils/taskActivity.js +4 -0
  226. package/dist/utils/taskActivity.test.js +25 -1
  227. package/package.json +10 -1
  228. package/scripts/check-cloud-mcp.mjs +83 -0
  229. package/scripts/playwright-auth.sh +9 -1
  230. package/scripts/run-billing-performance-smoke.sh +24 -0
  231. package/dist/ui/assets/AgentsModule-CpsTmrIW.js +0 -1
  232. package/dist/ui/assets/DocumentWorkspace-C3GyzrWm.js +0 -250
  233. package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
  234. package/dist/ui/assets/PlansPage-BP7AYOqr.js +0 -1
  235. package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +0 -8
  236. package/dist/ui/assets/index-CLIMgR6g.js +0 -6
  237. package/dist/ui/assets/index-DneETxcu.css +0 -1
@@ -1,14 +1,15 @@
1
1
  import { buildAiProfileBootstrapHeaders } from './aiProfileBootstrap.js';
2
2
  export const MCP_CLIENT_OPTIONS = [
3
- { id: 'cursor', label: 'Cursor' },
4
- { id: 'cline', label: 'Cline' },
5
- { id: 'vscode', label: 'VS Code / Copilot' },
6
- { id: 'windsurf', label: 'Windsurf' },
3
+ { id: 'general', label: 'General' },
7
4
  { id: 'antigravity', label: 'Antigravity' },
8
- { id: 'openclaw', label: 'OpenClaw' },
9
- { id: 'codex', label: 'Codex' },
10
5
  { id: 'claude-code', label: 'Claude Code CLI' },
6
+ { id: 'cline', label: 'Cline' },
7
+ { id: 'codex', label: 'Codex' },
8
+ { id: 'cursor', label: 'Cursor' },
11
9
  { id: 'gemini-cli', label: 'Gemini CLI' },
10
+ { id: 'openclaw', label: 'OpenClaw' },
11
+ { id: 'vscode', label: 'VS Code / Copilot' },
12
+ { id: 'windsurf', label: 'Windsurf' },
12
13
  ];
13
14
  export function sanitizeMcpId(value, fallback) {
14
15
  const normalized = String(value || '')
@@ -43,6 +44,27 @@ function buildConfigurationInstruction(baseInstruction, options) {
43
44
  return baseInstruction;
44
45
  return buildAutomationBootstrapInstruction(`${baseInstruction} Use the generated config exactly as shown.`);
45
46
  }
47
+ function buildLocalConfigurationInstruction(baseInstruction) {
48
+ return `${baseInstruction} Use the generated config exactly as shown. No token is required for local stdio MCP.`;
49
+ }
50
+ function formatTomlString(value) {
51
+ return JSON.stringify(value);
52
+ }
53
+ function formatTomlArray(values) {
54
+ return `[${values.map((value) => formatTomlString(value)).join(', ')}]`;
55
+ }
56
+ function buildTomlEnvAssignment(env) {
57
+ if (!env || Object.keys(env).length === 0)
58
+ return [];
59
+ return [
60
+ `env = { ${Object.entries(env).map(([key, value]) => `${key} = ${formatTomlString(value)}`).join(', ')} }`
61
+ ];
62
+ }
63
+ function shellEscapeArg(value) {
64
+ if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
65
+ return value;
66
+ return `'${value.replace(/'/g, `'\"'\"'`)}'`;
67
+ }
46
68
  export function buildCloudMcpClientIntegration(input) {
47
69
  const { clientId, endpoint, tokenValue, serverId } = input;
48
70
  const headers = buildCloudMcpHeaders(input);
@@ -52,6 +74,18 @@ export function buildCloudMcpClientIntegration(input) {
52
74
  .map(([key, value]) => ` --header "${key}: ${value}"`)
53
75
  .join('');
54
76
  switch (clientId) {
77
+ case 'general':
78
+ return {
79
+ clientId,
80
+ kind: 'json',
81
+ rootKey: 'mcpServers',
82
+ serverConfig: {
83
+ url: endpoint,
84
+ headers
85
+ },
86
+ instructionLabel: 'General MCP Configuration Instructions',
87
+ instructionText: buildConfigurationInstruction('Add this server to your MCP client using its standard remote HTTP configuration format.')
88
+ };
55
89
  case 'cursor':
56
90
  return {
57
91
  clientId,
@@ -178,3 +212,124 @@ export function buildCloudMcpClientIntegration(input) {
178
212
  }
179
213
  }
180
214
  }
215
+ export function buildLocalMcpClientIntegration(input) {
216
+ const { clientId, serverId, serverConfig } = input;
217
+ const baseStdioConfig = {
218
+ command: serverConfig.command,
219
+ args: serverConfig.args,
220
+ ...(serverConfig.env && Object.keys(serverConfig.env).length > 0 ? { env: serverConfig.env } : {})
221
+ };
222
+ const claudeEnvFlags = Object.entries(serverConfig.env || {})
223
+ .map(([key, value]) => ` --env ${shellEscapeArg(`${key}=${value}`)}`)
224
+ .join('');
225
+ const claudeArgs = [serverConfig.command, ...serverConfig.args].map((value) => shellEscapeArg(value)).join(' ');
226
+ switch (clientId) {
227
+ case 'general':
228
+ return {
229
+ clientId,
230
+ kind: 'json',
231
+ rootKey: 'mcpServers',
232
+ serverConfig: baseStdioConfig,
233
+ instructionLabel: 'General MCP Configuration Instructions',
234
+ instructionText: buildLocalConfigurationInstruction('Add this server to your MCP client using its standard local stdio configuration format.')
235
+ };
236
+ case 'cursor':
237
+ return {
238
+ clientId,
239
+ kind: 'json',
240
+ rootKey: 'mcpServers',
241
+ serverConfig: baseStdioConfig,
242
+ instructionLabel: 'Cursor Configuration Instructions',
243
+ instructionText: buildLocalConfigurationInstruction('Open Cursor settings, find MCP servers, and paste this local stdio config.')
244
+ };
245
+ case 'cline':
246
+ return {
247
+ clientId,
248
+ kind: 'json',
249
+ rootKey: 'mcpServers',
250
+ serverConfig: baseStdioConfig,
251
+ instructionLabel: 'Cline Configuration Instructions',
252
+ instructionText: buildLocalConfigurationInstruction('Open Cline MCP Servers and paste this local stdio server config.')
253
+ };
254
+ case 'vscode':
255
+ return {
256
+ clientId,
257
+ kind: 'json',
258
+ rootKey: 'servers',
259
+ serverConfig: {
260
+ type: 'stdio',
261
+ ...baseStdioConfig
262
+ },
263
+ instructionLabel: 'VS Code Configuration Instructions',
264
+ instructionText: buildLocalConfigurationInstruction('Open .vscode/mcp.json or MCP: Open User Configuration and add this server under the servers object.')
265
+ };
266
+ case 'windsurf':
267
+ return {
268
+ clientId,
269
+ kind: 'json',
270
+ rootKey: 'mcpServers',
271
+ serverConfig: baseStdioConfig,
272
+ instructionLabel: 'Windsurf Configuration Instructions',
273
+ instructionText: buildLocalConfigurationInstruction('Open Windsurf MCP settings and paste this local stdio config.')
274
+ };
275
+ case 'antigravity':
276
+ return {
277
+ clientId,
278
+ kind: 'json',
279
+ rootKey: 'mcpServers',
280
+ serverConfig: baseStdioConfig,
281
+ instructionLabel: 'Antigravity Configuration Instructions',
282
+ instructionText: buildLocalConfigurationInstruction('Open Antigravity MCP settings, view the raw config, and paste this local stdio configuration.')
283
+ };
284
+ case 'openclaw':
285
+ return {
286
+ clientId,
287
+ kind: 'json',
288
+ rootKey: 'servers',
289
+ wrapperKeys: ['mcp'],
290
+ serverConfig: {
291
+ transport: 'stdio',
292
+ ...baseStdioConfig
293
+ },
294
+ instructionLabel: 'OpenClaw Configuration Instructions',
295
+ instructionText: buildLocalConfigurationInstruction('Open ~/.openclaw/openclaw.json and add this local stdio server under mcp.servers.')
296
+ };
297
+ case 'codex':
298
+ return {
299
+ clientId,
300
+ kind: 'text',
301
+ renderedText: [
302
+ `[mcp_servers.${serverId}]`,
303
+ `command = ${formatTomlString(serverConfig.command)}`,
304
+ `args = ${formatTomlArray(serverConfig.args)}`,
305
+ ...buildTomlEnvAssignment(serverConfig.env)
306
+ ].join('\n'),
307
+ instructionLabel: 'Codex Configuration Instructions',
308
+ instructionText: buildLocalConfigurationInstruction('Open ~/.codex/config.toml and add this local MCP entry.')
309
+ };
310
+ case 'claude-code':
311
+ return {
312
+ clientId,
313
+ kind: 'text',
314
+ renderedText: `claude mcp add ${serverId}${claudeEnvFlags} -- ${claudeArgs}`,
315
+ instructionLabel: 'Claude Code Configuration Instructions',
316
+ instructionText: buildLocalConfigurationInstruction('Run this Claude Code command in your terminal to add the local Taskforce MCP server.')
317
+ };
318
+ case 'gemini-cli':
319
+ return {
320
+ clientId,
321
+ kind: 'json',
322
+ rootKey: 'mcpServers',
323
+ serverConfig: baseStdioConfig,
324
+ instructionLabel: 'Gemini CLI Configuration Instructions',
325
+ instructionText: buildLocalConfigurationInstruction('Open ~/.gemini/settings.json and add this local MCP server under mcpServers.')
326
+ };
327
+ case 'claude-desktop':
328
+ case 'chatgpt-desktop':
329
+ throw new Error(`Unsupported local MCP client: ${clientId}`);
330
+ default: {
331
+ const exhaustiveCheck = clientId;
332
+ throw new Error(`Unsupported local MCP client: ${exhaustiveCheck}`);
333
+ }
334
+ }
335
+ }
@@ -1,13 +1,40 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { MCP_CLIENT_OPTIONS, buildCloudMcpClientIntegration, buildCloudMcpServerId, } from './clientIntegrations';
2
+ import { MCP_CLIENT_OPTIONS, buildCloudMcpClientIntegration, buildLocalMcpClientIntegration, buildCloudMcpServerId, } from './clientIntegrations';
3
3
  describe('clientIntegrations', () => {
4
4
  it('builds a readable cloud MCP server id from workspace name', () => {
5
5
  expect(buildCloudMcpServerId('Workspace Alpha', null)).toBe('taskforce-workspace-alpha');
6
6
  expect(buildCloudMcpServerId('A Workspace Name That Is Way Too Long For Comfort', null)).toBe('taskforce-a-workspace-name-that-is');
7
7
  });
8
- it('keeps enabled connector clients available in the selector', () => {
9
- expect(MCP_CLIENT_OPTIONS.map((option) => option.id)).toEqual(expect.arrayContaining(['openclaw', 'codex', 'gemini-cli']));
10
- expect(MCP_CLIENT_OPTIONS.map((option) => option.id)).not.toEqual(expect.arrayContaining(['chatgpt-desktop', 'claude-desktop']));
8
+ it('keeps enabled connector clients available in the selector in the intended order', () => {
9
+ expect(MCP_CLIENT_OPTIONS.map((option) => option.id)).toEqual([
10
+ 'general',
11
+ 'antigravity',
12
+ 'claude-code',
13
+ 'cline',
14
+ 'codex',
15
+ 'cursor',
16
+ 'gemini-cli',
17
+ 'openclaw',
18
+ 'vscode',
19
+ 'windsurf'
20
+ ]);
21
+ });
22
+ it('builds generic cloud MCP config for the General option', () => {
23
+ const integration = buildCloudMcpClientIntegration({
24
+ clientId: 'general',
25
+ endpoint: 'https://performance-app.taskforcehq.ai/mcp',
26
+ tokenValue: '<<GENERATE_TOKEN>>',
27
+ serverId: 'taskforce-workspace-alpha',
28
+ });
29
+ expect(integration.kind).toBe('json');
30
+ expect(integration.rootKey).toBe('mcpServers');
31
+ expect(integration.instructionLabel).toBe('General MCP Configuration Instructions');
32
+ expect(integration.serverConfig).toEqual({
33
+ url: 'https://performance-app.taskforcehq.ai/mcp',
34
+ headers: {
35
+ Authorization: 'Bearer <<GENERATE_TOKEN>>'
36
+ }
37
+ });
11
38
  });
12
39
  it('builds JSON config for VS Code using the servers root', () => {
13
40
  const integration = buildCloudMcpClientIntegration({
@@ -133,4 +160,49 @@ describe('clientIntegrations', () => {
133
160
  expect(integration.kind).toBe('text');
134
161
  expect(integration.renderedText).toBe('gemini mcp add taskforce-workspace-alpha https://performance-app.taskforcehq.ai/mcp --transport http --header "Authorization: Bearer <<GENERATE_TOKEN>>"');
135
162
  });
163
+ it('builds local Codex config without token headers', () => {
164
+ const integration = buildLocalMcpClientIntegration({
165
+ clientId: 'codex',
166
+ serverId: 'taskforce-workspace-alpha',
167
+ serverConfig: {
168
+ command: 'taskforce',
169
+ args: ['mcp', '/Users/rossburke/projects/taskforce'],
170
+ env: {
171
+ TASKFORCE_TENANT_ID: 'tenant-alpha',
172
+ TASKFORCE_WORKSPACE_ID: 'workspace-alpha'
173
+ }
174
+ }
175
+ });
176
+ expect(integration.kind).toBe('text');
177
+ expect(integration.renderedText).toContain('[mcp_servers.taskforce-workspace-alpha]');
178
+ expect(integration.renderedText).toContain('command = "taskforce"');
179
+ expect(integration.renderedText).toContain('args = ["mcp", "/Users/rossburke/projects/taskforce"]');
180
+ expect(integration.renderedText).toContain('TASKFORCE_TENANT_ID = "tenant-alpha"');
181
+ expect(integration.renderedText).not.toContain('Authorization');
182
+ });
183
+ it('builds local VS Code config with stdio transport', () => {
184
+ const integration = buildLocalMcpClientIntegration({
185
+ clientId: 'vscode',
186
+ serverId: 'taskforce-workspace-alpha',
187
+ serverConfig: {
188
+ command: 'taskforce',
189
+ args: ['mcp', '/Users/rossburke/projects/taskforce'],
190
+ env: {
191
+ TASKFORCE_TENANT_ID: 'tenant-alpha',
192
+ TASKFORCE_WORKSPACE_ID: 'workspace-alpha'
193
+ }
194
+ }
195
+ });
196
+ expect(integration.kind).toBe('json');
197
+ expect(integration.rootKey).toBe('servers');
198
+ expect(integration.serverConfig).toEqual({
199
+ type: 'stdio',
200
+ command: 'taskforce',
201
+ args: ['mcp', '/Users/rossburke/projects/taskforce'],
202
+ env: {
203
+ TASKFORCE_TENANT_ID: 'tenant-alpha',
204
+ TASKFORCE_WORKSPACE_ID: 'workspace-alpha'
205
+ }
206
+ });
207
+ });
136
208
  });
@@ -0,0 +1,2 @@
1
+ import type { DomainToolExecutor, RegisterMcpTool } from './toolCatalog.js';
2
+ export declare function registerCollaborationTools(registerTool: RegisterMcpTool, executeTool: DomainToolExecutor): void;
@@ -0,0 +1,71 @@
1
+ import { AI_PROFILE_SURFACE_TYPES } from '../shared/aiProfileSurfaceType.js';
2
+ import { bindDomainTool } from './toolCatalog.js';
3
+ function stringProperty(description) {
4
+ return { type: 'string', description };
5
+ }
6
+ export function registerCollaborationTools(registerTool, executeTool) {
7
+ const register = (name, buildDescriptor) => {
8
+ registerTool({
9
+ name,
10
+ buildDescriptor: (context) => {
11
+ const descriptor = buildDescriptor(context);
12
+ return {
13
+ name,
14
+ description: descriptor.description,
15
+ inputSchema: {
16
+ type: 'object',
17
+ properties: descriptor.inputSchema.properties,
18
+ ...(descriptor.inputSchema.required ? { required: descriptor.inputSchema.required } : {}),
19
+ },
20
+ };
21
+ },
22
+ handler: bindDomainTool(executeTool, name),
23
+ });
24
+ };
25
+ register('list_assignees', (context) => ({
26
+ description: context.describeTool('List eligible assignees for the active workspace. Returns human workspace members and AI profiles that can own tasks. Use this before create_task, update_task, assign_task, or bulk_assign_tasks when you need a valid assignee ID.'),
27
+ inputSchema: { properties: {} },
28
+ }));
29
+ register('get_current_profile', (context) => ({
30
+ description: context.describeTool('Inspect the effective AI profile for this session. Use this when an agent needs to know its current profile id before filtering for tasks assigned to itself. If no profile is currently bound, this tool explains how to bootstrap with resolve_profile. You may also pass profileToken to inspect a stateless AI profile without mutating session state. The response includes the profileToken so automations can persist it after first bootstrap.'),
31
+ inputSchema: {
32
+ properties: {
33
+ profileToken: stringProperty('Optional AI profile token to inspect when no profile is currently bound in session state.'),
34
+ },
35
+ },
36
+ }));
37
+ register('resolve_profile', (context) => ({
38
+ description: context.describeTool('Resolve or create the current AI collaborator profile for this workspace. On first bootstrap, omit profileToken and Taskforce will create a named AI profile, issue a stable profileToken, and return the profile id to use in assignee, createdBy, and comment author fields on later calls. When a valid profileToken is provided, Taskforce resolves the existing profile and updates any supplied presentation fields such as name, icon, color, description, role, provider, model, and surfaceType. Update semantics: omitted optional fields leave existing stored values unchanged. For description, role, provider, and model, empty strings are ignored rather than clearing. For surfaceType, null or an empty string clears the stored value; valid enum values replace it.'),
39
+ inputSchema: {
40
+ properties: {
41
+ name: stringProperty('Display name for the AI collaborator.'),
42
+ icon: stringProperty('Optional icon name or emoji saved with the AI profile. Omit to leave unchanged when profileToken is provided.'),
43
+ color: stringProperty('Optional profile color saved with the AI profile. Omit to leave unchanged when profileToken is provided.'),
44
+ description: stringProperty('Optional short description stored on the AI profile. Omit to leave unchanged when profileToken is provided.'),
45
+ role: stringProperty('Optional role label stored on the AI profile. Omit to leave unchanged when profileToken is provided.'),
46
+ provider: stringProperty('Optional provider name stored as secondary diagnostic metadata on the AI profile. Omit to leave unchanged when profileToken is provided.'),
47
+ model: stringProperty('Optional model name stored as secondary diagnostic metadata on the AI profile. Omit to leave unchanged when profileToken is provided.'),
48
+ surfaceType: {
49
+ anyOf: [
50
+ { type: 'string', enum: [...AI_PROFILE_SURFACE_TYPES] },
51
+ { type: 'null' },
52
+ ],
53
+ description: 'Optional self-declared AI surface type. Use agent for a persistent named AI collaborator in Taskforce, coding_tool for the coding surface or terminal tool itself, chat_app for a conversational app identity, ide for an IDE-native copilot identity, and other only when none of those fit. If you are unsure and you are registering a named collaborator such as a teammate-style AI persona, choose agent. Omit to leave unchanged; pass null or an empty string to clear.',
54
+ },
55
+ profileToken: stringProperty('Previously issued Taskforce profile token. When provided, Taskforce resolves the existing profile instead of creating a new one, and applies any supplied presentation field updates to that profile.'),
56
+ },
57
+ required: ['name'],
58
+ },
59
+ }));
60
+ register('add_comment', (context) => ({
61
+ description: context.describeTool('Add a comment to a task. Use this for agent progress notes, handoff summaries, decision logs, and audit trails. Comments are visible in the task timeline.'),
62
+ inputSchema: {
63
+ properties: {
64
+ id: stringProperty('Task ID or task reference such as T-123.'),
65
+ text: stringProperty('Comment text.'),
66
+ author: stringProperty('Comment author identity. Use your AI profile id from resolve_profile, or use user for human-authored comments.'),
67
+ },
68
+ required: ['id', 'text', 'author'],
69
+ },
70
+ }));
71
+ }
@@ -0,0 +1,2 @@
1
+ import type { DomainToolExecutor, RegisterMcpTool } from './toolCatalog.js';
2
+ export declare function registerDocumentAssetTools(registerTool: RegisterMcpTool, executeTool: DomainToolExecutor): void;