@task-mcp/shared 1.0.14 → 1.0.16

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 (70) hide show
  1. package/dist/algorithms/critical-path.test.js +1 -1
  2. package/dist/algorithms/critical-path.test.js.map +1 -1
  3. package/dist/algorithms/dependency-integrity.test.js +1 -1
  4. package/dist/algorithms/dependency-integrity.test.js.map +1 -1
  5. package/dist/algorithms/tech-analysis.test.js +1 -1
  6. package/dist/algorithms/tech-analysis.test.js.map +1 -1
  7. package/dist/algorithms/topological-sort.test.js +1 -1
  8. package/dist/algorithms/topological-sort.test.js.map +1 -1
  9. package/dist/schemas/index.d.ts +1 -2
  10. package/dist/schemas/index.d.ts.map +1 -1
  11. package/dist/schemas/index.js +0 -2
  12. package/dist/schemas/index.js.map +1 -1
  13. package/dist/schemas/response-format.d.ts +8 -13
  14. package/dist/schemas/response-format.d.ts.map +1 -1
  15. package/dist/schemas/response-format.js.map +1 -1
  16. package/dist/schemas/task.d.ts +3 -9
  17. package/dist/schemas/task.d.ts.map +1 -1
  18. package/dist/schemas/task.js +3 -3
  19. package/dist/schemas/task.js.map +1 -1
  20. package/dist/schemas/view.d.ts +16 -8
  21. package/dist/schemas/view.d.ts.map +1 -1
  22. package/dist/schemas/view.js +2 -1
  23. package/dist/schemas/view.js.map +1 -1
  24. package/dist/utils/dashboard-renderer.d.ts +20 -13
  25. package/dist/utils/dashboard-renderer.d.ts.map +1 -1
  26. package/dist/utils/dashboard-renderer.js +46 -37
  27. package/dist/utils/dashboard-renderer.js.map +1 -1
  28. package/dist/utils/dashboard-renderer.test.js +84 -87
  29. package/dist/utils/dashboard-renderer.test.js.map +1 -1
  30. package/dist/utils/hierarchy.test.js +1 -1
  31. package/dist/utils/hierarchy.test.js.map +1 -1
  32. package/dist/utils/id.d.ts +2 -19
  33. package/dist/utils/id.d.ts.map +1 -1
  34. package/dist/utils/id.js +0 -43
  35. package/dist/utils/id.js.map +1 -1
  36. package/dist/utils/id.test.js +3 -38
  37. package/dist/utils/id.test.js.map +1 -1
  38. package/dist/utils/index.d.ts +6 -3
  39. package/dist/utils/index.d.ts.map +1 -1
  40. package/dist/utils/index.js +8 -4
  41. package/dist/utils/index.js.map +1 -1
  42. package/dist/utils/projection.d.ts +1 -10
  43. package/dist/utils/projection.d.ts.map +1 -1
  44. package/dist/utils/projection.js +0 -48
  45. package/dist/utils/projection.js.map +1 -1
  46. package/dist/utils/projection.test.js +2 -66
  47. package/dist/utils/projection.test.js.map +1 -1
  48. package/dist/utils/workspace.d.ts +100 -0
  49. package/dist/utils/workspace.d.ts.map +1 -0
  50. package/dist/utils/workspace.js +173 -0
  51. package/dist/utils/workspace.js.map +1 -0
  52. package/package.json +1 -1
  53. package/src/algorithms/critical-path.test.ts +1 -1
  54. package/src/algorithms/dependency-integrity.test.ts +1 -1
  55. package/src/algorithms/tech-analysis.test.ts +1 -1
  56. package/src/algorithms/topological-sort.test.ts +1 -1
  57. package/src/schemas/index.ts +2 -11
  58. package/src/schemas/response-format.ts +10 -15
  59. package/src/schemas/task.ts +3 -3
  60. package/src/schemas/view.ts +2 -1
  61. package/src/utils/dashboard-renderer.test.ts +92 -90
  62. package/src/utils/dashboard-renderer.ts +63 -48
  63. package/src/utils/hierarchy.test.ts +1 -1
  64. package/src/utils/id.test.ts +2 -48
  65. package/src/utils/id.ts +1 -48
  66. package/src/utils/index.ts +17 -8
  67. package/src/utils/projection.test.ts +1 -81
  68. package/src/utils/projection.ts +0 -62
  69. package/src/utils/workspace.ts +182 -0
  70. package/src/schemas/project.ts +0 -68
@@ -15,16 +15,21 @@ import {
15
15
  } from "./dashboard-renderer.js";
16
16
  import { stripAnsi } from "./terminal-ui.js";
17
17
  import type { Task } from "../schemas/task.js";
18
- import type { Project } from "../schemas/project.js";
19
18
  import type { InboxItem } from "../schemas/inbox.js";
20
19
 
20
+ interface WorkspaceInfo {
21
+ name: string;
22
+ taskCount: number;
23
+ completedCount: number;
24
+ }
25
+
21
26
  // =============================================================================
22
27
  // Test Fixtures
23
28
  // =============================================================================
24
29
 
25
30
  const createTask = (overrides: Partial<Task> = {}): Task => ({
26
31
  id: "task-1",
27
- projectId: "project-1",
32
+ workspace: "test-workspace",
28
33
  title: "Test Task",
29
34
  status: "pending",
30
35
  priority: "medium",
@@ -33,12 +38,10 @@ const createTask = (overrides: Partial<Task> = {}): Task => ({
33
38
  ...overrides,
34
39
  });
35
40
 
36
- const createProject = (overrides: Partial<Project> = {}): Project => ({
37
- id: "proj-1",
38
- name: "Test Project",
39
- status: "active",
40
- createdAt: "2025-01-01T00:00:00.000Z",
41
- updatedAt: "2025-01-01T00:00:00.000Z",
41
+ const createWorkspaceInfo = (overrides: Partial<WorkspaceInfo> = {}): WorkspaceInfo => ({
42
+ name: "test-workspace",
43
+ taskCount: 10,
44
+ completedCount: 5,
42
45
  ...overrides,
43
46
  });
44
47
 
@@ -251,9 +254,8 @@ describe("renderStatusWidget", () => {
251
254
  createTask({ id: "t2", status: "pending" }),
252
255
  createTask({ id: "t3", status: "in_progress" }),
253
256
  ];
254
- const projects = [createProject()];
255
257
 
256
- const widget = renderStatusWidget(tasks, projects);
258
+ const widget = renderStatusWidget(tasks);
257
259
  const plain = stripAnsi(widget);
258
260
 
259
261
  expect(plain).toContain("Status");
@@ -270,7 +272,7 @@ describe("renderStatusWidget", () => {
270
272
  createTask({ id: "t4", status: "pending" }),
271
273
  ];
272
274
 
273
- const widget = renderStatusWidget(tasks, []);
275
+ const widget = renderStatusWidget(tasks);
274
276
  const plain = stripAnsi(widget);
275
277
 
276
278
  expect(plain).toContain("50%");
@@ -283,7 +285,7 @@ describe("renderStatusWidget", () => {
283
285
  createTask({ id: "t2", priority: "high" }),
284
286
  ];
285
287
 
286
- const widget = renderStatusWidget(tasks, []);
288
+ const widget = renderStatusWidget(tasks);
287
289
  const plain = stripAnsi(widget);
288
290
 
289
291
  expect(plain).toContain("Critical");
@@ -302,7 +304,7 @@ describe("renderStatusWidget", () => {
302
304
  }),
303
305
  ];
304
306
 
305
- const widget = renderStatusWidget(tasks, []);
307
+ const widget = renderStatusWidget(tasks);
306
308
  const plain = stripAnsi(widget);
307
309
 
308
310
  expect(plain).toContain("Ready");
@@ -316,7 +318,7 @@ describe("renderStatusWidget", () => {
316
318
  createTask({ id: "t3", status: "pending" }),
317
319
  ];
318
320
 
319
- const widget = renderStatusWidget(tasks, []);
321
+ const widget = renderStatusWidget(tasks);
320
322
  const plain = stripAnsi(widget);
321
323
 
322
324
  // 1 completed out of 2 active = 50%
@@ -510,73 +512,73 @@ describe("renderInboxWidget", () => {
510
512
  // =============================================================================
511
513
 
512
514
  describe("renderProjectsTable", () => {
513
- test("returns message for empty projects", () => {
515
+ test("returns message for empty workspaces", () => {
514
516
  const result = renderProjectsTable([], () => []);
515
517
  const plain = stripAnsi(result);
516
518
 
517
- expect(plain).toContain("No projects found");
519
+ expect(plain).toContain("No workspaces found");
518
520
  });
519
521
 
520
- test("renders project names", () => {
521
- const projects = [
522
- createProject({ id: "p1", name: "Project Alpha" }),
523
- createProject({ id: "p2", name: "Project Beta" }),
522
+ test("renders workspace names", () => {
523
+ const workspaces = [
524
+ createWorkspaceInfo({ name: "workspace-alpha" }),
525
+ createWorkspaceInfo({ name: "workspace-beta" }),
524
526
  ];
525
527
 
526
- const result = renderProjectsTable(projects, () => []);
528
+ const result = renderProjectsTable(workspaces, () => []);
527
529
  const plain = stripAnsi(result);
528
530
 
529
- expect(plain).toContain("Project Alpha");
530
- expect(plain).toContain("Project Beta");
531
+ expect(plain).toContain("workspace-alpha");
532
+ expect(plain).toContain("workspace-beta");
531
533
  });
532
534
 
533
535
  test("renders progress percentage", () => {
534
- const projects = [createProject({ id: "p1", name: "Half Done" })];
536
+ const workspaces = [createWorkspaceInfo({ name: "half-done", taskCount: 2, completedCount: 1 })];
535
537
  const tasks = [
536
- createTask({ id: "t1", projectId: "p1", status: "completed" }),
537
- createTask({ id: "t2", projectId: "p1", status: "pending" }),
538
+ createTask({ id: "t1", workspace: "half-done", status: "completed" }),
539
+ createTask({ id: "t2", workspace: "half-done", status: "pending" }),
538
540
  ];
539
541
 
540
- const result = renderProjectsTable(projects, () => tasks);
542
+ const result = renderProjectsTable(workspaces, () => tasks);
541
543
  const plain = stripAnsi(result);
542
544
 
543
545
  expect(plain).toContain("50%");
544
546
  });
545
547
 
546
548
  test("renders ready and blocked counts", () => {
547
- const projects = [createProject({ id: "p1", name: "Test" })];
549
+ const workspaces = [createWorkspaceInfo({ name: "test-workspace" })];
548
550
  const tasks = [
549
- createTask({ id: "t1", projectId: "p1", status: "pending" }),
551
+ createTask({ id: "t1", workspace: "test-workspace", status: "pending" }),
550
552
  createTask({
551
553
  id: "t2",
552
- projectId: "p1",
554
+ workspace: "test-workspace",
553
555
  status: "pending",
554
556
  dependencies: [{ taskId: "t1", type: "blocked_by" }],
555
557
  }),
556
558
  ];
557
559
 
558
- const result = renderProjectsTable(projects, () => tasks);
560
+ const result = renderProjectsTable(workspaces, () => tasks);
559
561
  const plain = stripAnsi(result);
560
562
 
561
563
  // Table headers should be present
562
- expect(plain).toContain("Project");
564
+ expect(plain).toContain("Workspace");
563
565
  expect(plain).toContain("Progress");
564
566
  expect(plain).toContain("Tasks");
565
567
  expect(plain).toContain("Ready");
566
568
  expect(plain).toContain("Blocked");
567
569
  });
568
570
 
569
- test("limits to 10 projects", () => {
570
- const projects = Array.from({ length: 15 }, (_, i) =>
571
- createProject({ id: `p${i}`, name: `Project ${i}` })
571
+ test("limits to 10 workspaces", () => {
572
+ const workspaces = Array.from({ length: 15 }, (_, i) =>
573
+ createWorkspaceInfo({ name: `workspace-${i}` })
572
574
  );
573
575
 
574
- const result = renderProjectsTable(projects, () => []);
576
+ const result = renderProjectsTable(workspaces, () => []);
575
577
  const plain = stripAnsi(result);
576
578
 
577
- expect(plain).toContain("Project 0");
578
- expect(plain).toContain("Project 9");
579
- expect(plain).not.toContain("Project 10");
579
+ expect(plain).toContain("workspace-0");
580
+ expect(plain).toContain("workspace-9");
581
+ expect(plain).not.toContain("workspace-10");
580
582
  });
581
583
  });
582
584
 
@@ -674,7 +676,7 @@ describe("renderTasksTable", () => {
674
676
  describe("renderDashboard", () => {
675
677
  const defaultData = {
676
678
  tasks: [createTask({ status: "pending" })],
677
- projects: [createProject()],
679
+ workspaces: [createWorkspaceInfo()],
678
680
  };
679
681
 
680
682
  test("renders banner by default", () => {
@@ -701,28 +703,28 @@ describe("renderDashboard", () => {
701
703
  expect(plain).toContain("v1.2.3");
702
704
  });
703
705
 
704
- test("shows current project name when provided", () => {
706
+ test("shows current workspace name when provided", () => {
705
707
  const data = {
706
708
  ...defaultData,
707
- currentProject: createProject({ name: "My Project" }),
709
+ currentWorkspace: "my-workspace",
708
710
  };
709
711
  const result = renderDashboard(data, () => []);
710
712
  const plain = stripAnsi(result);
711
713
 
712
- expect(plain).toContain("Project:");
713
- expect(plain).toContain("My Project");
714
+ expect(plain).toContain("Workspace:");
715
+ expect(plain).toContain("my-workspace");
714
716
  });
715
717
 
716
- test("shows all projects count when no current project", () => {
718
+ test("shows all workspaces count when no current workspace", () => {
717
719
  const data = {
718
720
  tasks: [],
719
- projects: [createProject({ id: "p1" }), createProject({ id: "p2" })],
721
+ workspaces: [createWorkspaceInfo({ name: "ws1" }), createWorkspaceInfo({ name: "ws2" })],
720
722
  };
721
723
  const result = renderDashboard(data, () => []);
722
724
  const plain = stripAnsi(result);
723
725
 
724
- expect(plain).toContain("All Projects");
725
- expect(plain).toContain("2 projects");
726
+ expect(plain).toContain("All Workspaces");
727
+ expect(plain).toContain("2 workspaces");
726
728
  });
727
729
 
728
730
  test("renders inbox widget when items present", () => {
@@ -748,20 +750,20 @@ describe("renderDashboard", () => {
748
750
  expect(plain).not.toContain("Test inbox item");
749
751
  });
750
752
 
751
- test("renders projects table for multi-project view", () => {
753
+ test("renders workspaces table for multi-workspace view", () => {
752
754
  const data = {
753
755
  tasks: [],
754
- projects: [
755
- createProject({ id: "p1", name: "Project One" }),
756
- createProject({ id: "p2", name: "Project Two" }),
756
+ workspaces: [
757
+ createWorkspaceInfo({ name: "workspace-one" }),
758
+ createWorkspaceInfo({ name: "workspace-two" }),
757
759
  ],
758
760
  };
759
- const result = renderDashboard(data, () => [], { showProjects: true });
761
+ const result = renderDashboard(data, () => [], { showWorkspaces: true });
760
762
  const plain = stripAnsi(result);
761
763
 
762
- expect(plain).toContain("Projects");
763
- expect(plain).toContain("Project One");
764
- expect(plain).toContain("Project Two");
764
+ expect(plain).toContain("Workspaces");
765
+ expect(plain).toContain("workspace-one");
766
+ expect(plain).toContain("workspace-two");
765
767
  });
766
768
 
767
769
  test("strips ANSI codes when stripAnsiCodes is true", () => {
@@ -777,42 +779,42 @@ describe("renderDashboard", () => {
777
779
  // =============================================================================
778
780
 
779
781
  describe("renderProjectDashboard", () => {
780
- test("renders single project dashboard", () => {
781
- const project = createProject({ name: "My Project" });
782
+ test("renders single workspace dashboard", () => {
783
+ const workspace = "my-workspace";
782
784
  const tasks = [
783
785
  createTask({ id: "t1", title: "Task 1", status: "pending" }),
784
786
  createTask({ id: "t2", title: "Task 2", status: "completed" }),
785
787
  ];
786
788
 
787
- const result = renderProjectDashboard(project, tasks);
789
+ const result = renderProjectDashboard(workspace, tasks);
788
790
  const plain = stripAnsi(result);
789
791
 
790
- expect(plain).toContain("My Project");
792
+ expect(plain).toContain("my-workspace");
791
793
  expect(plain).toContain("Task 1");
792
794
  });
793
795
 
794
796
  test("includes version when provided", () => {
795
- const project = createProject({ name: "Test" });
796
- const result = renderProjectDashboard(project, [], { version: "2.0.0" });
797
+ const workspace = "test-workspace";
798
+ const result = renderProjectDashboard(workspace, [], { version: "2.0.0" });
797
799
  const plain = stripAnsi(result);
798
800
 
799
801
  expect(plain).toContain("v2.0.0");
800
802
  });
801
803
 
802
804
  test("strips ANSI when requested", () => {
803
- const project = createProject();
804
- const result = renderProjectDashboard(project, [], { stripAnsiCodes: true });
805
+ const workspace = "test-workspace";
806
+ const result = renderProjectDashboard(workspace, [], { stripAnsiCodes: true });
805
807
 
806
808
  expect(result).not.toMatch(/\x1b\[/);
807
809
  });
808
810
 
809
811
  test("shows tasks table", () => {
810
- const project = createProject();
812
+ const workspace = "test-workspace";
811
813
  const tasks = [
812
814
  createTask({ id: "t1", title: "Active Task", status: "pending" }),
813
815
  ];
814
816
 
815
- const result = renderProjectDashboard(project, tasks);
817
+ const result = renderProjectDashboard(workspace, tasks);
816
818
  const plain = stripAnsi(result);
817
819
 
818
820
  expect(plain).toContain("Tasks");
@@ -826,30 +828,30 @@ describe("renderProjectDashboard", () => {
826
828
 
827
829
  describe("renderGlobalDashboard", () => {
828
830
  test("renders global dashboard with all components", () => {
829
- const projects = [
830
- createProject({ id: "p1", name: "Project A" }),
831
- createProject({ id: "p2", name: "Project B" }),
831
+ const workspaces = [
832
+ createWorkspaceInfo({ name: "workspace-a" }),
833
+ createWorkspaceInfo({ name: "workspace-b" }),
832
834
  ];
833
835
  const tasks = [
834
- createTask({ id: "t1", projectId: "p1", status: "pending" }),
835
- createTask({ id: "t2", projectId: "p2", status: "completed" }),
836
+ createTask({ id: "t1", workspace: "workspace-a", status: "pending" }),
837
+ createTask({ id: "t2", workspace: "workspace-b", status: "completed" }),
836
838
  ];
837
839
  const inboxItems = [
838
840
  createInboxItem({ content: "Inbox note", status: "pending" }),
839
841
  ];
840
842
 
841
843
  const result = renderGlobalDashboard(
842
- projects,
844
+ workspaces,
843
845
  tasks,
844
846
  inboxItems,
845
- (projectId) => tasks.filter((t) => t.projectId === projectId)
847
+ (workspace) => tasks.filter((t) => t.workspace === workspace)
846
848
  );
847
849
  const plain = stripAnsi(result);
848
850
 
849
851
  // Banner is rendered as ASCII art blocks
850
852
  expect(plain).toMatch(/████/);
851
- expect(plain).toContain("Project A");
852
- expect(plain).toContain("Project B");
853
+ expect(plain).toContain("workspace-a");
854
+ expect(plain).toContain("workspace-b");
853
855
  expect(plain).toContain("Inbox note");
854
856
  });
855
857
 
@@ -868,39 +870,39 @@ describe("renderGlobalDashboard", () => {
868
870
  expect(result).not.toMatch(/\x1b\[/);
869
871
  });
870
872
 
871
- test("shows tasks table for single project", () => {
872
- const projects = [createProject({ id: "p1", name: "Solo Project" })];
873
+ test("shows tasks table for single workspace", () => {
874
+ const workspaces = [createWorkspaceInfo({ name: "solo-workspace" })];
873
875
  const tasks = [
874
- createTask({ id: "t1", projectId: "p1", title: "Solo Task", status: "pending" }),
876
+ createTask({ id: "t1", workspace: "solo-workspace", title: "Solo Task", status: "pending" }),
875
877
  ];
876
878
 
877
- const result = renderGlobalDashboard(projects, tasks, [], () => tasks);
879
+ const result = renderGlobalDashboard(workspaces, tasks, [], () => tasks);
878
880
  const plain = stripAnsi(result);
879
881
 
880
882
  expect(plain).toContain("Tasks");
881
883
  expect(plain).toContain("Solo Task");
882
884
  });
883
885
 
884
- test("uses getProjectTasks callback correctly", () => {
885
- const projects = [
886
- createProject({ id: "p1", name: "Project One" }),
887
- createProject({ id: "p2", name: "Project Two" }),
886
+ test("uses getWorkspaceTasks callback correctly", () => {
887
+ const workspaces = [
888
+ createWorkspaceInfo({ name: "workspace-one" }),
889
+ createWorkspaceInfo({ name: "workspace-two" }),
888
890
  ];
889
891
  const allTasks = [
890
- createTask({ id: "t1", projectId: "p1", status: "completed" }),
891
- createTask({ id: "t2", projectId: "p1", status: "pending" }),
892
- createTask({ id: "t3", projectId: "p2", status: "pending" }),
892
+ createTask({ id: "t1", workspace: "workspace-one", status: "completed" }),
893
+ createTask({ id: "t2", workspace: "workspace-one", status: "pending" }),
894
+ createTask({ id: "t3", workspace: "workspace-two", status: "pending" }),
893
895
  ];
894
896
 
895
897
  let callCount = 0;
896
- const getProjectTasks = (projectId: string) => {
898
+ const getWorkspaceTasks = (workspace: string) => {
897
899
  callCount++;
898
- return allTasks.filter((t) => t.projectId === projectId);
900
+ return allTasks.filter((t) => t.workspace === workspace);
899
901
  };
900
902
 
901
- renderGlobalDashboard(projects, allTasks, [], getProjectTasks);
903
+ renderGlobalDashboard(workspaces, allTasks, [], getWorkspaceTasks);
902
904
 
903
- // Should be called for each project in the table
905
+ // Should be called for each workspace in the table
904
906
  expect(callCount).toBeGreaterThan(0);
905
907
  });
906
908
  });
@@ -3,7 +3,7 @@
3
3
  * Provides consistent dashboard layout across all interfaces
4
4
  */
5
5
 
6
- import type { Task, Project, InboxItem } from "../schemas/index.js";
6
+ import type { Task, InboxItem } from "../schemas/index.js";
7
7
  import {
8
8
  c,
9
9
  box,
@@ -48,11 +48,17 @@ export interface DependencyMetrics {
48
48
  } | undefined;
49
49
  }
50
50
 
51
+ export interface WorkspaceInfo {
52
+ name: string;
53
+ taskCount: number;
54
+ completedCount: number;
55
+ }
56
+
51
57
  export interface DashboardData {
52
58
  tasks: Task[];
53
- projects: Project[];
59
+ workspaces: WorkspaceInfo[];
54
60
  inboxItems?: InboxItem[] | undefined;
55
- currentProject?: Project | undefined;
61
+ currentWorkspace?: string | undefined;
56
62
  version?: string | undefined;
57
63
  activeTag?: string | undefined;
58
64
  }
@@ -180,10 +186,7 @@ function getTimeAgo(date: Date): string {
180
186
  /**
181
187
  * Render Status widget (progress, counts, priorities, dependencies)
182
188
  */
183
- export function renderStatusWidget(
184
- tasks: Task[],
185
- _projects: Project[]
186
- ): string {
189
+ export function renderStatusWidget(tasks: Task[]): string {
187
190
  const stats = calculateStats(tasks);
188
191
  const depMetrics = calculateDependencyMetrics(tasks);
189
192
  const today = getTodayTasks(tasks);
@@ -315,14 +318,14 @@ export function renderInboxWidget(inboxItems: InboxItem[]): string | null {
315
318
  }
316
319
 
317
320
  /**
318
- * Render Projects table
321
+ * Render Workspaces table
319
322
  */
320
- export function renderProjectsTable(
321
- projects: Project[],
322
- getProjectTasks: (projectId: string) => Task[]
323
+ export function renderWorkspacesTable(
324
+ workspaces: WorkspaceInfo[],
325
+ getWorkspaceTasks: (workspace: string) => Task[]
323
326
  ): string {
324
- if (projects.length === 0) {
325
- return c.gray("No projects found.");
327
+ if (workspaces.length === 0) {
328
+ return c.gray("No workspaces found.");
326
329
  }
327
330
 
328
331
  const rows: {
@@ -333,8 +336,8 @@ export function renderProjectsTable(
333
336
  total: number;
334
337
  }[] = [];
335
338
 
336
- for (const project of projects.slice(0, 10)) {
337
- const tasks = getProjectTasks(project.id);
339
+ for (const ws of workspaces.slice(0, 10)) {
340
+ const tasks = getWorkspaceTasks(ws.name);
338
341
  const stats = calculateStats(tasks);
339
342
  const depMetrics = calculateDependencyMetrics(tasks);
340
343
  const activeTasks = stats.total - stats.cancelled;
@@ -351,7 +354,7 @@ export function renderProjectsTable(
351
354
  c.green("█".repeat(filled)) + c.gray("░".repeat(empty));
352
355
 
353
356
  rows.push({
354
- name: truncateStr(project.name, 20),
357
+ name: truncateStr(ws.name, 20),
355
358
  progress: `${miniBar} ${pad(String(percent) + "%", 4, "right")}`,
356
359
  ready: depMetrics.readyToWork,
357
360
  blocked: depMetrics.blockedByDependencies,
@@ -360,7 +363,7 @@ export function renderProjectsTable(
360
363
  }
361
364
 
362
365
  const columns: TableColumn[] = [
363
- { key: "name", header: "Project", width: 22 },
366
+ { key: "name", header: "Workspace", width: 22 },
364
367
  { key: "progress", header: "Progress", width: 16 },
365
368
  { key: "total", header: "Tasks", width: 6, align: "right" },
366
369
  {
@@ -382,8 +385,11 @@ export function renderProjectsTable(
382
385
  return table(rows as unknown as Record<string, unknown>[], columns);
383
386
  }
384
387
 
388
+ // Legacy export for backwards compatibility
389
+ export const renderProjectsTable = renderWorkspacesTable;
390
+
385
391
  /**
386
- * Render Tasks table for single project view
392
+ * Render Tasks table for single workspace view
387
393
  */
388
394
  export function renderTasksTable(tasks: Task[], limit: number = 10): string {
389
395
  const activeTasks = tasks.filter(
@@ -459,7 +465,7 @@ function getOverdueTasks(tasks: Task[]): Task[] {
459
465
  export interface RenderDashboardOptions {
460
466
  showBanner?: boolean | undefined;
461
467
  showInbox?: boolean | undefined;
462
- showProjects?: boolean | undefined;
468
+ showWorkspaces?: boolean | undefined;
463
469
  showTasks?: boolean | undefined;
464
470
  stripAnsiCodes?: boolean | undefined;
465
471
  }
@@ -469,18 +475,18 @@ export interface RenderDashboardOptions {
469
475
  */
470
476
  export function renderDashboard(
471
477
  data: DashboardData,
472
- getProjectTasks: (projectId: string) => Task[],
478
+ getWorkspaceTasks: (workspace: string) => Task[],
473
479
  options: RenderDashboardOptions = {}
474
480
  ): string {
475
481
  const {
476
482
  showBanner = true,
477
483
  showInbox = true,
478
- showProjects = true,
484
+ showWorkspaces = true,
479
485
  showTasks = true,
480
486
  stripAnsiCodes = false,
481
487
  } = options;
482
488
 
483
- const { tasks, projects, inboxItems = [], currentProject, version, activeTag } = data;
489
+ const { tasks, workspaces, inboxItems = [], currentWorkspace, version, activeTag } = data;
484
490
  const lines: string[] = [];
485
491
 
486
492
  // Banner
@@ -490,10 +496,10 @@ export function renderDashboard(
490
496
  lines.push("");
491
497
  }
492
498
 
493
- // Project info header
494
- const projectInfo = currentProject
495
- ? `${c.bold("Project:")} ${currentProject.name}`
496
- : `${c.bold("All Projects")} (${projects.length} projects)`;
499
+ // Workspace info header
500
+ const workspaceInfo = currentWorkspace
501
+ ? `${c.bold("Workspace:")} ${currentWorkspace}`
502
+ : `${c.bold("All Workspaces")} (${workspaces.length} workspaces)`;
497
503
 
498
504
  // Build header line with version and tag
499
505
  const headerParts: string[] = [];
@@ -501,14 +507,14 @@ export function renderDashboard(
501
507
  if (activeTag) headerParts.push(`#${activeTag}`);
502
508
 
503
509
  if (headerParts.length > 0) {
504
- lines.push(c.dim(`${headerParts.join(" ")} ${projectInfo}`));
510
+ lines.push(c.dim(`${headerParts.join(" ")} ${workspaceInfo}`));
505
511
  } else {
506
- lines.push(projectInfo);
512
+ lines.push(workspaceInfo);
507
513
  }
508
514
  lines.push("");
509
515
 
510
516
  // Status + Actions widgets side by side
511
- const statusWidget = renderStatusWidget(tasks, projects);
517
+ const statusWidget = renderStatusWidget(tasks);
512
518
  const actionsWidget = renderActionsWidget(tasks);
513
519
  lines.push(sideBySide([statusWidget, actionsWidget], 2));
514
520
  lines.push("");
@@ -522,16 +528,16 @@ export function renderDashboard(
522
528
  }
523
529
  }
524
530
 
525
- // Projects table (only for all-projects view)
526
- if (showProjects && !currentProject && projects.length > 1) {
527
- lines.push(c.bold("Projects"));
531
+ // Workspaces table (only for all-workspaces view)
532
+ if (showWorkspaces && !currentWorkspace && workspaces.length > 1) {
533
+ lines.push(c.bold("Workspaces"));
528
534
  lines.push("");
529
- lines.push(renderProjectsTable(projects, getProjectTasks));
535
+ lines.push(renderWorkspacesTable(workspaces, getWorkspaceTasks));
530
536
  lines.push("");
531
537
  }
532
538
 
533
- // Tasks table (for single project view)
534
- if (showTasks && (currentProject || projects.length === 1)) {
539
+ // Tasks table (for single workspace view)
540
+ if (showTasks && (currentWorkspace || workspaces.length === 1)) {
535
541
  const activeTasks = tasks.filter(
536
542
  (t) => t.status !== "completed" && t.status !== "cancelled"
537
543
  );
@@ -549,17 +555,23 @@ export function renderDashboard(
549
555
  }
550
556
 
551
557
  /**
552
- * Render single project dashboard
558
+ * Render single workspace dashboard
553
559
  */
554
- export function renderProjectDashboard(
555
- project: Project,
560
+ export function renderWorkspaceDashboard(
561
+ workspace: string,
556
562
  tasks: Task[],
557
563
  options: { stripAnsiCodes?: boolean; version?: string; activeTag?: string } = {}
558
564
  ): string {
565
+ const wsInfo: WorkspaceInfo = {
566
+ name: workspace,
567
+ taskCount: tasks.length,
568
+ completedCount: tasks.filter(t => t.status === "completed").length,
569
+ };
570
+
559
571
  const data: DashboardData = {
560
572
  tasks,
561
- projects: [project],
562
- currentProject: project,
573
+ workspaces: [wsInfo],
574
+ currentWorkspace: workspace,
563
575
  version: options.version,
564
576
  activeTag: options.activeTag,
565
577
  };
@@ -570,36 +582,39 @@ export function renderProjectDashboard(
570
582
  {
571
583
  showBanner: true,
572
584
  showInbox: false,
573
- showProjects: false,
585
+ showWorkspaces: false,
574
586
  showTasks: true,
575
587
  stripAnsiCodes: options.stripAnsiCodes,
576
588
  }
577
589
  );
578
590
  }
579
591
 
592
+ // Legacy export for backwards compatibility
593
+ export const renderProjectDashboard = renderWorkspaceDashboard;
594
+
580
595
  /**
581
- * Render global dashboard (all projects)
596
+ * Render global dashboard (all workspaces)
582
597
  */
583
598
  export function renderGlobalDashboard(
584
- projects: Project[],
599
+ workspaces: WorkspaceInfo[],
585
600
  allTasks: Task[],
586
601
  inboxItems: InboxItem[],
587
- getProjectTasks: (projectId: string) => Task[],
602
+ getWorkspaceTasks: (workspace: string) => Task[],
588
603
  options: { stripAnsiCodes?: boolean; version?: string; activeTag?: string } = {}
589
604
  ): string {
590
605
  const data: DashboardData = {
591
606
  tasks: allTasks,
592
- projects,
607
+ workspaces,
593
608
  inboxItems,
594
609
  version: options.version,
595
610
  activeTag: options.activeTag,
596
611
  };
597
612
 
598
- return renderDashboard(data, getProjectTasks, {
613
+ return renderDashboard(data, getWorkspaceTasks, {
599
614
  showBanner: true,
600
615
  showInbox: true,
601
- showProjects: true,
602
- showTasks: projects.length === 1,
616
+ showWorkspaces: true,
617
+ showTasks: workspaces.length === 1,
603
618
  stripAnsiCodes: options.stripAnsiCodes,
604
619
  });
605
620
  }