@1medium/cli 1.3.1 → 1.6.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1medium/cli",
3
- "version": "1.3.1",
3
+ "version": "1.6.0",
4
4
  "description": "CLI and MCP server for 1Medium AI task management",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/mcp-server.js CHANGED
@@ -54,7 +54,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
54
54
  },
55
55
  {
56
56
  name: "org_get",
57
- description: "Get the currently active organization",
57
+ description: "Get the currently active organization. NOTE: Sessions start with NO org selected to prevent cross-contamination between terminals. Use org_list and org_set first, or config_reset to load saved defaults.",
58
58
  inputSchema: {
59
59
  type: "object",
60
60
  properties: {},
@@ -97,7 +97,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
97
97
  },
98
98
  {
99
99
  name: "task_list",
100
- description: "List tasks from 1Medium. Returns open tasks by default.",
100
+ description: "List tasks from 1Medium. Returns open tasks by default. Filters by active project unless project_id is explicitly set or all_projects is true.",
101
101
  inputSchema: {
102
102
  type: "object",
103
103
  properties: {
@@ -115,6 +115,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
115
115
  type: "number",
116
116
  description: "Maximum number of tasks to return",
117
117
  },
118
+ project_id: {
119
+ type: "string",
120
+ description: "Filter by specific project ID. Overrides active project.",
121
+ },
122
+ all_projects: {
123
+ type: "boolean",
124
+ description: "Set to true to list tasks from all projects, ignoring active project filter.",
125
+ },
118
126
  },
119
127
  },
120
128
  },
@@ -134,7 +142,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
134
142
  },
135
143
  {
136
144
  name: "task_update",
137
- description: "Update an existing task",
145
+ description: "Update an existing task. Can also move task to a different project.",
138
146
  inputSchema: {
139
147
  type: "object",
140
148
  properties: {
@@ -155,6 +163,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
155
163
  enum: ["P1", "P2", "P3", "P4"],
156
164
  description: "New priority",
157
165
  },
166
+ project_id: {
167
+ type: "string",
168
+ description: "Move task to a different project (provide project ID)",
169
+ },
158
170
  },
159
171
  required: ["id"],
160
172
  },
@@ -506,18 +518,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
506
518
  if (args.priority) params.priority = args.priority;
507
519
  if (args.limit) params.limit = args.limit;
508
520
 
521
+ // Filter by project: explicit arg > active project (unless all_projects is true)
522
+ if (args.project_id) {
523
+ params.project_id = args.project_id;
524
+ } else if (!args.all_projects) {
525
+ const activeProjectId = sessionState.get("projectId");
526
+ if (activeProjectId) {
527
+ params.project_id = activeProjectId;
528
+ }
529
+ }
530
+
509
531
  result = await api.listTasks(params);
510
532
  const tasks = result.tasks || [];
511
533
 
534
+ // Build header with filter info
535
+ const activeProjectName = sessionState.get("projectName");
536
+ const filterInfo = params.project_id
537
+ ? ` in project "${activeProjectName || params.project_id}"`
538
+ : " (all projects)";
539
+
512
540
  if (tasks.length === 0) {
513
541
  return {
514
- content: [{ type: "text", text: "No tasks found." }],
542
+ content: [{ type: "text", text: `No tasks found${filterInfo}.` }],
515
543
  };
516
544
  }
517
545
 
518
546
  const taskList = tasks
519
547
  .map((t) => {
520
- const check = t.completedAt ? "[x]" : "[ ]";
548
+ const check = t.completed_at ? "[x]" : "[ ]";
521
549
  return `${check} ${t.priority} ${t.title}\n ID: ${t.id}`;
522
550
  })
523
551
  .join("\n\n");
@@ -526,7 +554,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
526
554
  content: [
527
555
  {
528
556
  type: "text",
529
- text: `Tasks (${result.total} total):\n\n${taskList}`,
557
+ text: `Tasks${filterInfo} (${result.total} total):\n\n${taskList}`,
530
558
  },
531
559
  ],
532
560
  };
@@ -535,7 +563,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
535
563
  case "task_get": {
536
564
  result = await api.getTask(args.id);
537
565
  const t = result.task;
538
- const status = t.completedAt ? "completed" : "open";
566
+ const status = t.completed_at ? "completed" : "open";
539
567
 
540
568
  let text = `${t.title}\n\n ID: ${t.id}\n Status: ${status}\n Priority: ${t.priority}`;
541
569
 
@@ -560,13 +588,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
560
588
  if (args.title) payload.title = args.title;
561
589
  if (args.body) payload.body_md = args.body;
562
590
  if (args.priority) payload.priority = args.priority;
591
+ if (args.project_id) payload.project_id = args.project_id;
563
592
 
564
593
  result = await api.updateTask(args.id, payload);
594
+ const movedMsg = args.project_id ? `\n Moved to project: ${args.project_id}` : "";
565
595
  return {
566
596
  content: [
567
597
  {
568
598
  type: "text",
569
- text: `Task updated:\n ID: ${result.task.id}\n Title: ${result.task.title}\n Priority: ${result.task.priority}`,
599
+ text: `Task updated:\n ID: ${result.task.id}\n Title: ${result.task.title}\n Priority: ${result.task.priority}${movedMsg}`,
570
600
  },
571
601
  ],
572
602
  };
@@ -4,10 +4,13 @@
4
4
  * Session-scoped state for MCP server
5
5
  *
6
6
  * This module wraps the persistent config with an in-memory layer for session-scoped keys.
7
- * Multiple Claude Code tabs can run separate MCP server processes without affecting each other's
8
- * org/space/project selections.
7
+ * Each Claude Code tab runs its own MCP server process with ISOLATED session state.
9
8
  *
10
- * Session-scoped keys (in-memory only):
9
+ * IMPORTANT: Session state starts EMPTY - no org/space/project is pre-selected.
10
+ * This prevents cross-contamination when working on multiple projects in different terminals.
11
+ * Use config_reset to load from persistent config if you want the saved defaults.
12
+ *
13
+ * Session-scoped keys (in-memory only, start empty):
11
14
  * - orgId, orgName, spaceId, spaceName, projectId, projectName
12
15
  *
13
16
  * Persistent keys (always read/write from config file):
@@ -26,14 +29,11 @@ const SESSION_KEYS = [
26
29
  "projectName",
27
30
  ];
28
31
 
29
- // In-memory session state, initialized from config
32
+ // In-memory session state - starts empty, NOT initialized from config
33
+ // Each MCP server process (each Claude Code tab) starts with no org/project context
34
+ // User must explicitly set org/project or call config_reset to load from persistent config
30
35
  const sessionState = {};
31
36
 
32
- // Initialize session state from config on module load
33
- for (const key of SESSION_KEYS) {
34
- sessionState[key] = config.get(key);
35
- }
36
-
37
37
  /**
38
38
  * Get a config value
39
39
  * Session-scoped keys return from memory; others fall through to persistent config