@1medium/cli 1.3.0 → 1.4.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.0",
3
+ "version": "1.4.0",
4
4
  "description": "CLI and MCP server for 1Medium AI task management",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/api.js CHANGED
@@ -1,20 +1,20 @@
1
1
  "use strict";
2
2
 
3
3
  const fetch = require("node-fetch");
4
- const config = require("./config");
4
+ const sessionState = require("./session-state");
5
5
 
6
6
  /**
7
7
  * Get API base URL
8
8
  */
9
9
  function getApiUrl() {
10
- return config.get("apiUrl") || "https://1medium.ai/api";
10
+ return sessionState.get("apiUrl") || "https://1medium.ai/api";
11
11
  }
12
12
 
13
13
  /**
14
14
  * Get auth token
15
15
  */
16
16
  function getToken() {
17
- const token = config.get("token");
17
+ const token = sessionState.get("token");
18
18
  if (!token) {
19
19
  throw new Error(
20
20
  "No token configured. Run '1m login' to set your API token."
@@ -27,7 +27,7 @@ function getToken() {
27
27
  * Get configured org ID (if any)
28
28
  */
29
29
  function getOrgId() {
30
- return config.get("orgId") || null;
30
+ return sessionState.get("orgId") || null;
31
31
  }
32
32
 
33
33
  /**
package/src/mcp-server.js CHANGED
@@ -8,7 +8,7 @@ const {
8
8
  ListToolsRequestSchema,
9
9
  } = require("@modelcontextprotocol/sdk/types.js");
10
10
  const api = require("./api");
11
- const config = require("./config");
11
+ const sessionState = require("./session-state");
12
12
 
13
13
  const server = new Server(
14
14
  {
@@ -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
  },
@@ -368,6 +376,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
368
376
  required: ["id"],
369
377
  },
370
378
  },
379
+ {
380
+ name: "config_persist",
381
+ description: "Persist current session settings (org/space/project) to the config file. Use this when you want changes to apply to all Claude Code tabs and persist across restarts.",
382
+ inputSchema: {
383
+ type: "object",
384
+ properties: {},
385
+ },
386
+ },
387
+ {
388
+ name: "config_reset",
389
+ description: "Reset session to values from the config file. Use this to discard any org/space/project changes made in this session and reload from persistent config.",
390
+ inputSchema: {
391
+ type: "object",
392
+ properties: {},
393
+ },
394
+ },
371
395
  ],
372
396
  };
373
397
  });
@@ -377,7 +401,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
377
401
  const { name, arguments: args } = request.params;
378
402
 
379
403
  // Check if configured
380
- const token = config.get("token");
404
+ const token = sessionState.get("token");
381
405
  if (!token) {
382
406
  return {
383
407
  content: [
@@ -397,7 +421,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
397
421
  case "org_list": {
398
422
  result = await api.listOrgs();
399
423
  const orgs = result.orgs || [];
400
- const currentOrgId = config.get("orgId");
424
+ const currentOrgId = sessionState.get("orgId");
401
425
 
402
426
  if (orgs.length === 0) {
403
427
  return {
@@ -424,9 +448,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
424
448
  }
425
449
 
426
450
  case "org_set": {
427
- config.set("orgId", args.id);
451
+ sessionState.set("orgId", args.id);
428
452
  if (args.name) {
429
- config.set("orgName", args.name);
453
+ sessionState.set("orgName", args.name);
430
454
  }
431
455
  return {
432
456
  content: [
@@ -439,8 +463,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
439
463
  }
440
464
 
441
465
  case "org_get": {
442
- const orgId = config.get("orgId");
443
- const orgName = config.get("orgName");
466
+ const orgId = sessionState.get("orgId");
467
+ const orgName = sessionState.get("orgName");
444
468
 
445
469
  if (!orgId) {
446
470
  return {
@@ -460,7 +484,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
460
484
 
461
485
  case "task_create": {
462
486
  // Use provided project_id or fall back to configured default
463
- const projectId = args.project_id || config.get("projectId") || null;
487
+ const projectId = args.project_id || sessionState.get("projectId") || null;
464
488
  const payload = {
465
489
  title: args.title,
466
490
  body_md: args.body,
@@ -490,18 +514,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
490
514
  if (args.priority) params.priority = args.priority;
491
515
  if (args.limit) params.limit = args.limit;
492
516
 
517
+ // Filter by project: explicit arg > active project (unless all_projects is true)
518
+ if (args.project_id) {
519
+ params.project_id = args.project_id;
520
+ } else if (!args.all_projects) {
521
+ const activeProjectId = sessionState.get("projectId");
522
+ if (activeProjectId) {
523
+ params.project_id = activeProjectId;
524
+ }
525
+ }
526
+
493
527
  result = await api.listTasks(params);
494
528
  const tasks = result.tasks || [];
495
529
 
530
+ // Build header with filter info
531
+ const activeProjectName = sessionState.get("projectName");
532
+ const filterInfo = params.project_id
533
+ ? ` in project "${activeProjectName || params.project_id}"`
534
+ : " (all projects)";
535
+
496
536
  if (tasks.length === 0) {
497
537
  return {
498
- content: [{ type: "text", text: "No tasks found." }],
538
+ content: [{ type: "text", text: `No tasks found${filterInfo}.` }],
499
539
  };
500
540
  }
501
541
 
502
542
  const taskList = tasks
503
543
  .map((t) => {
504
- const check = t.completedAt ? "[x]" : "[ ]";
544
+ const check = t.completed_at ? "[x]" : "[ ]";
505
545
  return `${check} ${t.priority} ${t.title}\n ID: ${t.id}`;
506
546
  })
507
547
  .join("\n\n");
@@ -510,7 +550,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
510
550
  content: [
511
551
  {
512
552
  type: "text",
513
- text: `Tasks (${result.total} total):\n\n${taskList}`,
553
+ text: `Tasks${filterInfo} (${result.total} total):\n\n${taskList}`,
514
554
  },
515
555
  ],
516
556
  };
@@ -519,7 +559,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
519
559
  case "task_get": {
520
560
  result = await api.getTask(args.id);
521
561
  const t = result.task;
522
- const status = t.completedAt ? "completed" : "open";
562
+ const status = t.completed_at ? "completed" : "open";
523
563
 
524
564
  let text = `${t.title}\n\n ID: ${t.id}\n Status: ${status}\n Priority: ${t.priority}`;
525
565
 
@@ -586,7 +626,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
586
626
  case "space_list": {
587
627
  result = await api.listSpaces();
588
628
  const spaces = result.spaces || [];
589
- const currentSpaceId = config.get("spaceId");
629
+ const currentSpaceId = sessionState.get("spaceId");
590
630
 
591
631
  if (spaces.length === 0) {
592
632
  return {
@@ -612,9 +652,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
612
652
  }
613
653
 
614
654
  case "space_set": {
615
- config.set("spaceId", args.id);
655
+ sessionState.set("spaceId", args.id);
616
656
  if (args.name) {
617
- config.set("spaceName", args.name);
657
+ sessionState.set("spaceName", args.name);
618
658
  }
619
659
  return {
620
660
  content: [
@@ -627,8 +667,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
627
667
  }
628
668
 
629
669
  case "space_get": {
630
- const spaceId = config.get("spaceId");
631
- const spaceName = config.get("spaceName");
670
+ const spaceId = sessionState.get("spaceId");
671
+ const spaceName = sessionState.get("spaceName");
632
672
 
633
673
  if (!spaceId) {
634
674
  return {
@@ -652,7 +692,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
652
692
 
653
693
  result = await api.listProjects(params);
654
694
  const projects = result.projects || [];
655
- const currentProjectId = config.get("projectId");
695
+ const currentProjectId = sessionState.get("projectId");
656
696
 
657
697
  if (projects.length === 0) {
658
698
  return {
@@ -682,9 +722,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
682
722
  }
683
723
 
684
724
  case "project_set": {
685
- config.set("projectId", args.id);
725
+ sessionState.set("projectId", args.id);
686
726
  if (args.name) {
687
- config.set("projectName", args.name);
727
+ sessionState.set("projectName", args.name);
688
728
  }
689
729
  return {
690
730
  content: [
@@ -697,8 +737,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
697
737
  }
698
738
 
699
739
  case "project_get": {
700
- const projectId = config.get("projectId");
701
- const projectName = config.get("projectName");
740
+ const projectId = sessionState.get("projectId");
741
+ const projectName = sessionState.get("projectName");
702
742
 
703
743
  if (!projectId) {
704
744
  return {
@@ -803,6 +843,52 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
803
843
  };
804
844
  }
805
845
 
846
+ case "config_persist": {
847
+ sessionState.persistSession();
848
+ const state = sessionState.getSessionState();
849
+ let text = "Session settings persisted to config file.";
850
+ if (state.orgName || state.orgId) {
851
+ text += `\n Org: ${state.orgName || state.orgId || "(none)"}`;
852
+ }
853
+ if (state.spaceName || state.spaceId) {
854
+ text += `\n Space: ${state.spaceName || state.spaceId || "(none)"}`;
855
+ }
856
+ if (state.projectName || state.projectId) {
857
+ text += `\n Project: ${state.projectName || state.projectId || "(none)"}`;
858
+ }
859
+ return {
860
+ content: [
861
+ {
862
+ type: "text",
863
+ text,
864
+ },
865
+ ],
866
+ };
867
+ }
868
+
869
+ case "config_reset": {
870
+ sessionState.resetSession();
871
+ const state = sessionState.getSessionState();
872
+ let text = "Session reset from config file.";
873
+ if (state.orgName || state.orgId) {
874
+ text += `\n Org: ${state.orgName || state.orgId || "(none)"}`;
875
+ }
876
+ if (state.spaceName || state.spaceId) {
877
+ text += `\n Space: ${state.spaceName || state.spaceId || "(none)"}`;
878
+ }
879
+ if (state.projectName || state.projectId) {
880
+ text += `\n Project: ${state.projectName || state.projectId || "(none)"}`;
881
+ }
882
+ return {
883
+ content: [
884
+ {
885
+ type: "text",
886
+ text,
887
+ },
888
+ ],
889
+ };
890
+ }
891
+
806
892
  default:
807
893
  return {
808
894
  content: [{ type: "text", text: `Unknown tool: ${name}` }],
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Session-scoped state for MCP server
5
+ *
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.
9
+ *
10
+ * Session-scoped keys (in-memory only):
11
+ * - orgId, orgName, spaceId, spaceName, projectId, projectName
12
+ *
13
+ * Persistent keys (always read/write from config file):
14
+ * - token, apiUrl
15
+ */
16
+
17
+ const config = require("./config");
18
+
19
+ // Keys that are session-scoped (stored in memory, not persisted by default)
20
+ const SESSION_KEYS = [
21
+ "orgId",
22
+ "orgName",
23
+ "spaceId",
24
+ "spaceName",
25
+ "projectId",
26
+ "projectName",
27
+ ];
28
+
29
+ // In-memory session state, initialized from config
30
+ const sessionState = {};
31
+
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
+ /**
38
+ * Get a config value
39
+ * Session-scoped keys return from memory; others fall through to persistent config
40
+ */
41
+ function get(key) {
42
+ if (SESSION_KEYS.includes(key)) {
43
+ return sessionState[key];
44
+ }
45
+ return config.get(key);
46
+ }
47
+
48
+ /**
49
+ * Set a config value
50
+ * Session-scoped keys are stored in memory only; others persist to config file
51
+ */
52
+ function set(key, value) {
53
+ if (SESSION_KEYS.includes(key)) {
54
+ sessionState[key] = value;
55
+ } else {
56
+ config.set(key, value);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Persist current session state to the config file
62
+ * Call this to make session changes permanent across all Claude Code tabs
63
+ */
64
+ function persistSession() {
65
+ for (const key of SESSION_KEYS) {
66
+ config.set(key, sessionState[key]);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Reset session state from the config file
72
+ * Call this to discard session changes and reload from persistent config
73
+ */
74
+ function resetSession() {
75
+ for (const key of SESSION_KEYS) {
76
+ sessionState[key] = config.get(key);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Get the list of session-scoped keys
82
+ */
83
+ function getSessionKeys() {
84
+ return [...SESSION_KEYS];
85
+ }
86
+
87
+ /**
88
+ * Get all current session values (for debugging/display)
89
+ */
90
+ function getSessionState() {
91
+ const state = {};
92
+ for (const key of SESSION_KEYS) {
93
+ state[key] = sessionState[key];
94
+ }
95
+ return state;
96
+ }
97
+
98
+ module.exports = {
99
+ get,
100
+ set,
101
+ persistSession,
102
+ resetSession,
103
+ getSessionKeys,
104
+ getSessionState,
105
+ };