@1medium/cli 1.0.0 → 1.1.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 +1 -1
- package/src/api.js +64 -0
- package/src/config.js +24 -0
- package/src/index.js +358 -3
- package/src/mcp-server.js +279 -3
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -127,6 +127,62 @@ async function listOrgs() {
|
|
|
127
127
|
return request("GET", "/orgs");
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* List spaces (top-level priority groups)
|
|
132
|
+
*/
|
|
133
|
+
async function listSpaces() {
|
|
134
|
+
return request("GET", "/spaces");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Create a space
|
|
139
|
+
*/
|
|
140
|
+
async function createSpace(payload) {
|
|
141
|
+
return request("POST", "/spaces", payload);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Update a space
|
|
146
|
+
*/
|
|
147
|
+
async function updateSpace(id, payload) {
|
|
148
|
+
return request("PATCH", `/spaces/${id}`, payload);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Delete a space
|
|
153
|
+
*/
|
|
154
|
+
async function deleteSpace(id) {
|
|
155
|
+
return request("DELETE", `/spaces/${id}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* List projects (optionally filtered by space)
|
|
160
|
+
*/
|
|
161
|
+
async function listProjects(params = {}) {
|
|
162
|
+
return request("GET", "/projects", null, params);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Create a project
|
|
167
|
+
*/
|
|
168
|
+
async function createProject(payload) {
|
|
169
|
+
return request("POST", "/projects", payload);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Update a project
|
|
174
|
+
*/
|
|
175
|
+
async function updateProject(id, payload) {
|
|
176
|
+
return request("PATCH", `/projects/${id}`, payload);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Delete a project
|
|
181
|
+
*/
|
|
182
|
+
async function deleteProject(id) {
|
|
183
|
+
return request("DELETE", `/projects/${id}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
130
186
|
module.exports = {
|
|
131
187
|
whoami,
|
|
132
188
|
createTask,
|
|
@@ -136,4 +192,12 @@ module.exports = {
|
|
|
136
192
|
addComment,
|
|
137
193
|
completeTask,
|
|
138
194
|
listOrgs,
|
|
195
|
+
listSpaces,
|
|
196
|
+
createSpace,
|
|
197
|
+
updateSpace,
|
|
198
|
+
deleteSpace,
|
|
199
|
+
listProjects,
|
|
200
|
+
createProject,
|
|
201
|
+
updateProject,
|
|
202
|
+
deleteProject,
|
|
139
203
|
};
|
package/src/config.js
CHANGED
|
@@ -21,6 +21,22 @@ const config = new Conf({
|
|
|
21
21
|
type: "string",
|
|
22
22
|
default: "",
|
|
23
23
|
},
|
|
24
|
+
spaceId: {
|
|
25
|
+
type: "string",
|
|
26
|
+
default: "",
|
|
27
|
+
},
|
|
28
|
+
spaceName: {
|
|
29
|
+
type: "string",
|
|
30
|
+
default: "",
|
|
31
|
+
},
|
|
32
|
+
projectId: {
|
|
33
|
+
type: "string",
|
|
34
|
+
default: "",
|
|
35
|
+
},
|
|
36
|
+
projectName: {
|
|
37
|
+
type: "string",
|
|
38
|
+
default: "",
|
|
39
|
+
},
|
|
24
40
|
},
|
|
25
41
|
});
|
|
26
42
|
|
|
@@ -37,4 +53,12 @@ if (process.env.ONEMEDIUM_ORG_ID) {
|
|
|
37
53
|
config.set("orgId", process.env.ONEMEDIUM_ORG_ID);
|
|
38
54
|
}
|
|
39
55
|
|
|
56
|
+
if (process.env.ONEMEDIUM_SPACE_ID) {
|
|
57
|
+
config.set("spaceId", process.env.ONEMEDIUM_SPACE_ID);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (process.env.ONEMEDIUM_PROJECT_ID) {
|
|
61
|
+
config.set("projectId", process.env.ONEMEDIUM_PROJECT_ID);
|
|
62
|
+
}
|
|
63
|
+
|
|
40
64
|
module.exports = config;
|
package/src/index.js
CHANGED
|
@@ -110,6 +110,7 @@ taskCmd
|
|
|
110
110
|
.option("-b, --body <body>", "Task body (markdown)")
|
|
111
111
|
.option("-p, --priority <priority>", "Priority: P0, P1, P2, P3", "P2")
|
|
112
112
|
.option("-s, --status <status>", "Status: inbox, open, doing, blocked, done", "inbox")
|
|
113
|
+
.option("--project <project-id>", "Project ID to associate the task with (uses default if not specified)")
|
|
113
114
|
.option("--tag <tags...>", "Tags to add")
|
|
114
115
|
.option("--repo <repo>", "Repository context (owner/repo)")
|
|
115
116
|
.option("--branch <branch>", "Branch context")
|
|
@@ -132,6 +133,9 @@ taskCmd
|
|
|
132
133
|
if (options.pr) context.pr_url = options.pr;
|
|
133
134
|
if (options.issue) context.issue_url = options.issue;
|
|
134
135
|
|
|
136
|
+
// Use provided project_id or fall back to configured default
|
|
137
|
+
const projectId = options.project || config.get("projectId") || null;
|
|
138
|
+
|
|
135
139
|
const payload = {
|
|
136
140
|
title: options.title,
|
|
137
141
|
body_md: options.body,
|
|
@@ -142,6 +146,7 @@ taskCmd
|
|
|
142
146
|
source: options.source,
|
|
143
147
|
estimate_minutes: options.estimate,
|
|
144
148
|
due_at: options.due,
|
|
149
|
+
project_id: projectId,
|
|
145
150
|
dedupe: {
|
|
146
151
|
mode: options.dedupe === false ? "none" : "auto",
|
|
147
152
|
},
|
|
@@ -161,6 +166,9 @@ taskCmd
|
|
|
161
166
|
console.log(` Title: ${data.task.title}`);
|
|
162
167
|
console.log(` Status: ${data.task.status}`);
|
|
163
168
|
console.log(` Priority: ${data.task.priority}`);
|
|
169
|
+
if (data.task.project_name) {
|
|
170
|
+
console.log(` Project: ${data.task.project_name}`);
|
|
171
|
+
}
|
|
164
172
|
}
|
|
165
173
|
} catch (error) {
|
|
166
174
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
@@ -406,6 +414,306 @@ program
|
|
|
406
414
|
}
|
|
407
415
|
});
|
|
408
416
|
|
|
417
|
+
// ============================================================================
|
|
418
|
+
// Space Commands
|
|
419
|
+
// ============================================================================
|
|
420
|
+
|
|
421
|
+
const spaceCmd = program.command("space").description("Manage spaces");
|
|
422
|
+
|
|
423
|
+
spaceCmd
|
|
424
|
+
.command("list")
|
|
425
|
+
.description("List your spaces (top-level groups)")
|
|
426
|
+
.option("-j, --json", "Output as JSON")
|
|
427
|
+
.action(async (options) => {
|
|
428
|
+
try {
|
|
429
|
+
const data = await api.listSpaces();
|
|
430
|
+
const currentSpaceId = config.get("spaceId");
|
|
431
|
+
|
|
432
|
+
if (options.json) {
|
|
433
|
+
console.log(JSON.stringify(data, null, 2));
|
|
434
|
+
} else {
|
|
435
|
+
console.log(chalk.bold("\nSpaces:\n"));
|
|
436
|
+
if (data.spaces.length === 0) {
|
|
437
|
+
console.log(" No spaces found.");
|
|
438
|
+
} else {
|
|
439
|
+
for (const space of data.spaces) {
|
|
440
|
+
const current = space.id === currentSpaceId ? chalk.green(" (current)") : "";
|
|
441
|
+
console.log(` ${space.name}${current}`);
|
|
442
|
+
console.log(chalk.gray(` ID: ${space.id}`));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
console.log("");
|
|
446
|
+
if (!currentSpaceId) {
|
|
447
|
+
console.log(chalk.yellow("No default space set. Use: 1m config set space <space-id>"));
|
|
448
|
+
console.log("");
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
spaceCmd
|
|
458
|
+
.command("add")
|
|
459
|
+
.description("Create a new space")
|
|
460
|
+
.requiredOption("-n, --name <name>", "Space name (min 2 characters)")
|
|
461
|
+
.option("-j, --json", "Output as JSON")
|
|
462
|
+
.action(async (options) => {
|
|
463
|
+
try {
|
|
464
|
+
const data = await api.createSpace({ name: options.name });
|
|
465
|
+
|
|
466
|
+
if (options.json) {
|
|
467
|
+
console.log(JSON.stringify(data, null, 2));
|
|
468
|
+
} else {
|
|
469
|
+
console.log(chalk.green("Space created:"));
|
|
470
|
+
console.log(` ID: ${data.space.id}`);
|
|
471
|
+
console.log(` Name: ${data.space.name}`);
|
|
472
|
+
}
|
|
473
|
+
} catch (error) {
|
|
474
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
spaceCmd
|
|
480
|
+
.command("update <id>")
|
|
481
|
+
.description("Update a space")
|
|
482
|
+
.option("-n, --name <name>", "New space name")
|
|
483
|
+
.option("-j, --json", "Output as JSON")
|
|
484
|
+
.action(async (id, options) => {
|
|
485
|
+
try {
|
|
486
|
+
const payload = {};
|
|
487
|
+
if (options.name) payload.name = options.name;
|
|
488
|
+
|
|
489
|
+
const data = await api.updateSpace(id, payload);
|
|
490
|
+
|
|
491
|
+
if (options.json) {
|
|
492
|
+
console.log(JSON.stringify(data, null, 2));
|
|
493
|
+
} else {
|
|
494
|
+
console.log(chalk.green("Space updated:"));
|
|
495
|
+
console.log(` ID: ${data.space.id}`);
|
|
496
|
+
console.log(` Name: ${data.space.name}`);
|
|
497
|
+
}
|
|
498
|
+
} catch (error) {
|
|
499
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
500
|
+
process.exit(1);
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
spaceCmd
|
|
505
|
+
.command("delete <id>")
|
|
506
|
+
.description("Delete a space (must be empty)")
|
|
507
|
+
.option("-j, --json", "Output as JSON")
|
|
508
|
+
.action(async (id, options) => {
|
|
509
|
+
try {
|
|
510
|
+
const data = await api.deleteSpace(id);
|
|
511
|
+
|
|
512
|
+
if (options.json) {
|
|
513
|
+
console.log(JSON.stringify(data, null, 2));
|
|
514
|
+
} else {
|
|
515
|
+
console.log(chalk.green(`Space deleted: ${id}`));
|
|
516
|
+
}
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
519
|
+
process.exit(1);
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
// Alias: `1m spaces` -> `1m space list`
|
|
524
|
+
program
|
|
525
|
+
.command("spaces")
|
|
526
|
+
.description("List your spaces (alias for 'space list')")
|
|
527
|
+
.option("-j, --json", "Output as JSON")
|
|
528
|
+
.action(async (options) => {
|
|
529
|
+
try {
|
|
530
|
+
const data = await api.listSpaces();
|
|
531
|
+
const currentSpaceId = config.get("spaceId");
|
|
532
|
+
|
|
533
|
+
if (options.json) {
|
|
534
|
+
console.log(JSON.stringify(data, null, 2));
|
|
535
|
+
} else {
|
|
536
|
+
console.log(chalk.bold("\nSpaces:\n"));
|
|
537
|
+
if (data.spaces.length === 0) {
|
|
538
|
+
console.log(" No spaces found.");
|
|
539
|
+
} else {
|
|
540
|
+
for (const space of data.spaces) {
|
|
541
|
+
const current = space.id === currentSpaceId ? chalk.green(" (current)") : "";
|
|
542
|
+
console.log(` ${space.name}${current}`);
|
|
543
|
+
console.log(chalk.gray(` ID: ${space.id}`));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
console.log("");
|
|
547
|
+
}
|
|
548
|
+
} catch (error) {
|
|
549
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
550
|
+
process.exit(1);
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// ============================================================================
|
|
555
|
+
// Project Commands
|
|
556
|
+
// ============================================================================
|
|
557
|
+
|
|
558
|
+
const projectCmd = program.command("project").description("Manage projects");
|
|
559
|
+
|
|
560
|
+
projectCmd
|
|
561
|
+
.command("list")
|
|
562
|
+
.description("List your projects")
|
|
563
|
+
.option("-s, --space <space-id>", "Filter by space ID")
|
|
564
|
+
.option("-j, --json", "Output as JSON")
|
|
565
|
+
.action(async (options) => {
|
|
566
|
+
try {
|
|
567
|
+
const params = {};
|
|
568
|
+
if (options.space) params.space_id = options.space;
|
|
569
|
+
|
|
570
|
+
const data = await api.listProjects(params);
|
|
571
|
+
const currentProjectId = config.get("projectId");
|
|
572
|
+
|
|
573
|
+
if (options.json) {
|
|
574
|
+
console.log(JSON.stringify(data, null, 2));
|
|
575
|
+
} else {
|
|
576
|
+
console.log(chalk.bold("\nProjects:\n"));
|
|
577
|
+
if (data.projects.length === 0) {
|
|
578
|
+
console.log(" No projects found.");
|
|
579
|
+
} else {
|
|
580
|
+
for (const project of data.projects) {
|
|
581
|
+
const current = project.id === currentProjectId ? chalk.green(" (current)") : "";
|
|
582
|
+
console.log(` ${project.name}${current}`);
|
|
583
|
+
console.log(chalk.gray(` ID: ${project.id}`));
|
|
584
|
+
if (project.space_name) {
|
|
585
|
+
console.log(chalk.gray(` Space: ${project.space_name}`));
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
console.log("");
|
|
590
|
+
if (!currentProjectId) {
|
|
591
|
+
console.log(chalk.yellow("No default project set. Use: 1m config set project <project-id>"));
|
|
592
|
+
console.log("");
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
} catch (error) {
|
|
596
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
597
|
+
process.exit(1);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
projectCmd
|
|
602
|
+
.command("add")
|
|
603
|
+
.description("Create a new project")
|
|
604
|
+
.requiredOption("-n, --name <name>", "Project name (min 2 characters)")
|
|
605
|
+
.option("-s, --space <space-id>", "Space ID to put the project in")
|
|
606
|
+
.option("-j, --json", "Output as JSON")
|
|
607
|
+
.action(async (options) => {
|
|
608
|
+
try {
|
|
609
|
+
const payload = { name: options.name };
|
|
610
|
+
if (options.space) payload.space_id = options.space;
|
|
611
|
+
|
|
612
|
+
const data = await api.createProject(payload);
|
|
613
|
+
|
|
614
|
+
if (options.json) {
|
|
615
|
+
console.log(JSON.stringify(data, null, 2));
|
|
616
|
+
} else {
|
|
617
|
+
console.log(chalk.green("Project created:"));
|
|
618
|
+
console.log(` ID: ${data.project.id}`);
|
|
619
|
+
console.log(` Name: ${data.project.name}`);
|
|
620
|
+
if (data.project.space_name) {
|
|
621
|
+
console.log(` Space: ${data.project.space_name}`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
} catch (error) {
|
|
625
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
projectCmd
|
|
631
|
+
.command("update <id>")
|
|
632
|
+
.description("Update a project")
|
|
633
|
+
.option("-n, --name <name>", "New project name")
|
|
634
|
+
.option("-s, --space <space-id>", "Move to a different space")
|
|
635
|
+
.option("-j, --json", "Output as JSON")
|
|
636
|
+
.action(async (id, options) => {
|
|
637
|
+
try {
|
|
638
|
+
const payload = {};
|
|
639
|
+
if (options.name) payload.name = options.name;
|
|
640
|
+
if (options.space !== undefined) payload.space_id = options.space;
|
|
641
|
+
|
|
642
|
+
const data = await api.updateProject(id, payload);
|
|
643
|
+
|
|
644
|
+
if (options.json) {
|
|
645
|
+
console.log(JSON.stringify(data, null, 2));
|
|
646
|
+
} else {
|
|
647
|
+
console.log(chalk.green("Project updated:"));
|
|
648
|
+
console.log(` ID: ${data.project.id}`);
|
|
649
|
+
console.log(` Name: ${data.project.name}`);
|
|
650
|
+
if (data.project.space_name) {
|
|
651
|
+
console.log(` Space: ${data.project.space_name}`);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
} catch (error) {
|
|
655
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
656
|
+
process.exit(1);
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
projectCmd
|
|
661
|
+
.command("delete <id>")
|
|
662
|
+
.description("Delete a project (must be empty)")
|
|
663
|
+
.option("-j, --json", "Output as JSON")
|
|
664
|
+
.action(async (id, options) => {
|
|
665
|
+
try {
|
|
666
|
+
const data = await api.deleteProject(id);
|
|
667
|
+
|
|
668
|
+
if (options.json) {
|
|
669
|
+
console.log(JSON.stringify(data, null, 2));
|
|
670
|
+
} else {
|
|
671
|
+
console.log(chalk.green(`Project deleted: ${id}`));
|
|
672
|
+
}
|
|
673
|
+
} catch (error) {
|
|
674
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
675
|
+
process.exit(1);
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
// Alias: `1m projects` -> `1m project list`
|
|
680
|
+
program
|
|
681
|
+
.command("projects")
|
|
682
|
+
.description("List your projects (alias for 'project list')")
|
|
683
|
+
.option("-s, --space <space-id>", "Filter by space ID")
|
|
684
|
+
.option("-j, --json", "Output as JSON")
|
|
685
|
+
.action(async (options) => {
|
|
686
|
+
try {
|
|
687
|
+
const params = {};
|
|
688
|
+
if (options.space) params.space_id = options.space;
|
|
689
|
+
|
|
690
|
+
const data = await api.listProjects(params);
|
|
691
|
+
const currentProjectId = config.get("projectId");
|
|
692
|
+
|
|
693
|
+
if (options.json) {
|
|
694
|
+
console.log(JSON.stringify(data, null, 2));
|
|
695
|
+
} else {
|
|
696
|
+
console.log(chalk.bold("\nProjects:\n"));
|
|
697
|
+
if (data.projects.length === 0) {
|
|
698
|
+
console.log(" No projects found.");
|
|
699
|
+
} else {
|
|
700
|
+
for (const project of data.projects) {
|
|
701
|
+
const current = project.id === currentProjectId ? chalk.green(" (current)") : "";
|
|
702
|
+
console.log(` ${project.name}${current}`);
|
|
703
|
+
console.log(chalk.gray(` ID: ${project.id}`));
|
|
704
|
+
if (project.space_name) {
|
|
705
|
+
console.log(chalk.gray(` Space: ${project.space_name}`));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
console.log("");
|
|
710
|
+
}
|
|
711
|
+
} catch (error) {
|
|
712
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
713
|
+
process.exit(1);
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
|
|
409
717
|
// ============================================================================
|
|
410
718
|
// Config Commands
|
|
411
719
|
// ============================================================================
|
|
@@ -420,11 +728,17 @@ configCmd
|
|
|
420
728
|
const apiUrl = config.get("apiUrl");
|
|
421
729
|
const orgId = config.get("orgId");
|
|
422
730
|
const orgName = config.get("orgName");
|
|
731
|
+
const spaceId = config.get("spaceId");
|
|
732
|
+
const spaceName = config.get("spaceName");
|
|
733
|
+
const projectId = config.get("projectId");
|
|
734
|
+
const projectName = config.get("projectName");
|
|
423
735
|
|
|
424
736
|
console.log(chalk.bold("\nConfiguration:\n"));
|
|
425
737
|
console.log(` API URL: ${apiUrl || "https://1medium.ai/api (default)"}`);
|
|
426
738
|
console.log(` Token: ${token ? token.substring(0, 12) + "..." : chalk.yellow("Not set")}`);
|
|
427
739
|
console.log(` Org: ${orgName || chalk.yellow("Not set")}${orgId ? chalk.gray(` (${orgId})`) : ""}`);
|
|
740
|
+
console.log(` Space: ${spaceName || chalk.yellow("Not set")}${spaceId ? chalk.gray(` (${spaceId})`) : ""}`);
|
|
741
|
+
console.log(` Project: ${projectName || chalk.yellow("Not set")}${projectId ? chalk.gray(` (${projectId})`) : ""}`);
|
|
428
742
|
console.log(` Config: ${config.path}`);
|
|
429
743
|
console.log("");
|
|
430
744
|
});
|
|
@@ -432,12 +746,12 @@ configCmd
|
|
|
432
746
|
configCmd
|
|
433
747
|
.command("set")
|
|
434
748
|
.description("Set a configuration value")
|
|
435
|
-
.argument("<key>", "Configuration key (org, url)")
|
|
749
|
+
.argument("<key>", "Configuration key (org, space, project, url)")
|
|
436
750
|
.argument("<value>", "Configuration value")
|
|
437
751
|
.action(async (key, value) => {
|
|
438
752
|
try {
|
|
439
753
|
switch (key) {
|
|
440
|
-
case "org":
|
|
754
|
+
case "org": {
|
|
441
755
|
// Validate org exists and user has access
|
|
442
756
|
const data = await api.listOrgs();
|
|
443
757
|
const org = data.orgs.find((o) => o.id === value || o.name.toLowerCase() === value.toLowerCase());
|
|
@@ -453,13 +767,48 @@ configCmd
|
|
|
453
767
|
config.set("orgName", org.name);
|
|
454
768
|
console.log(chalk.green(`Default org set to: ${org.name}`));
|
|
455
769
|
break;
|
|
770
|
+
}
|
|
771
|
+
case "space": {
|
|
772
|
+
// Validate space exists and user has access
|
|
773
|
+
const spaceData = await api.listSpaces();
|
|
774
|
+
const space = spaceData.spaces.find((s) => s.id === value || s.name.toLowerCase() === value.toLowerCase());
|
|
775
|
+
if (!space) {
|
|
776
|
+
console.error(chalk.red(`Error: Space not found: ${value}`));
|
|
777
|
+
console.log("\nAvailable spaces:");
|
|
778
|
+
for (const s of spaceData.spaces) {
|
|
779
|
+
console.log(` ${s.name} (${s.id})`);
|
|
780
|
+
}
|
|
781
|
+
process.exit(1);
|
|
782
|
+
}
|
|
783
|
+
config.set("spaceId", space.id);
|
|
784
|
+
config.set("spaceName", space.name);
|
|
785
|
+
console.log(chalk.green(`Default space set to: ${space.name}`));
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
case "project": {
|
|
789
|
+
// Validate project exists and user has access
|
|
790
|
+
const projectData = await api.listProjects();
|
|
791
|
+
const project = projectData.projects.find((p) => p.id === value || p.name.toLowerCase() === value.toLowerCase());
|
|
792
|
+
if (!project) {
|
|
793
|
+
console.error(chalk.red(`Error: Project not found: ${value}`));
|
|
794
|
+
console.log("\nAvailable projects:");
|
|
795
|
+
for (const p of projectData.projects) {
|
|
796
|
+
console.log(` ${p.name} (${p.id})`);
|
|
797
|
+
}
|
|
798
|
+
process.exit(1);
|
|
799
|
+
}
|
|
800
|
+
config.set("projectId", project.id);
|
|
801
|
+
config.set("projectName", project.name);
|
|
802
|
+
console.log(chalk.green(`Default project set to: ${project.name}`));
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
456
805
|
case "url":
|
|
457
806
|
config.set("apiUrl", value);
|
|
458
807
|
console.log(chalk.green(`API URL set to: ${value}`));
|
|
459
808
|
break;
|
|
460
809
|
default:
|
|
461
810
|
console.error(chalk.red(`Unknown config key: ${key}`));
|
|
462
|
-
console.log("Available keys: org, url");
|
|
811
|
+
console.log("Available keys: org, space, project, url");
|
|
463
812
|
process.exit(1);
|
|
464
813
|
}
|
|
465
814
|
} catch (error) {
|
|
@@ -474,11 +823,17 @@ configCmd.action(() => {
|
|
|
474
823
|
const apiUrl = config.get("apiUrl");
|
|
475
824
|
const orgId = config.get("orgId");
|
|
476
825
|
const orgName = config.get("orgName");
|
|
826
|
+
const spaceId = config.get("spaceId");
|
|
827
|
+
const spaceName = config.get("spaceName");
|
|
828
|
+
const projectId = config.get("projectId");
|
|
829
|
+
const projectName = config.get("projectName");
|
|
477
830
|
|
|
478
831
|
console.log(chalk.bold("\nConfiguration:\n"));
|
|
479
832
|
console.log(` API URL: ${apiUrl || "https://1medium.ai/api (default)"}`);
|
|
480
833
|
console.log(` Token: ${token ? token.substring(0, 12) + "..." : chalk.yellow("Not set")}`);
|
|
481
834
|
console.log(` Org: ${orgName || chalk.yellow("Not set")}${orgId ? chalk.gray(` (${orgId})`) : ""}`);
|
|
835
|
+
console.log(` Space: ${spaceName || chalk.yellow("Not set")}${spaceId ? chalk.gray(` (${spaceId})`) : ""}`);
|
|
836
|
+
console.log(` Project: ${projectName || chalk.yellow("Not set")}${projectId ? chalk.gray(` (${projectId})`) : ""}`);
|
|
482
837
|
console.log(` Config: ${config.path}`);
|
|
483
838
|
console.log("");
|
|
484
839
|
});
|
package/src/mcp-server.js
CHANGED
|
@@ -45,6 +45,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
45
45
|
enum: ["P1", "P2", "P3", "P4"],
|
|
46
46
|
description: "Task priority (P1=urgent, P4=low)",
|
|
47
47
|
},
|
|
48
|
+
project_id: {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: "Project ID to associate the task with. Uses configured default if not specified.",
|
|
51
|
+
},
|
|
48
52
|
source: {
|
|
49
53
|
type: "string",
|
|
50
54
|
description: "Source identifier (e.g., 'claude-code', 'github')",
|
|
@@ -157,6 +161,127 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
157
161
|
required: ["id", "message"],
|
|
158
162
|
},
|
|
159
163
|
},
|
|
164
|
+
{
|
|
165
|
+
name: "space_list",
|
|
166
|
+
description: "List available spaces (top-level groups) in 1Medium",
|
|
167
|
+
inputSchema: {
|
|
168
|
+
type: "object",
|
|
169
|
+
properties: {},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: "space_create",
|
|
174
|
+
description: "Create a new space (top-level group) in 1Medium",
|
|
175
|
+
inputSchema: {
|
|
176
|
+
type: "object",
|
|
177
|
+
properties: {
|
|
178
|
+
name: {
|
|
179
|
+
type: "string",
|
|
180
|
+
description: "Name for the new space (min 2 characters)",
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
required: ["name"],
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "space_update",
|
|
188
|
+
description: "Update an existing space",
|
|
189
|
+
inputSchema: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
id: {
|
|
193
|
+
type: "string",
|
|
194
|
+
description: "Space ID to update",
|
|
195
|
+
},
|
|
196
|
+
name: {
|
|
197
|
+
type: "string",
|
|
198
|
+
description: "New name for the space",
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
required: ["id"],
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "space_delete",
|
|
206
|
+
description: "Delete a space (must be empty)",
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: "object",
|
|
209
|
+
properties: {
|
|
210
|
+
id: {
|
|
211
|
+
type: "string",
|
|
212
|
+
description: "Space ID to delete",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
required: ["id"],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "project_list",
|
|
220
|
+
description: "List available projects in 1Medium, optionally filtered by space",
|
|
221
|
+
inputSchema: {
|
|
222
|
+
type: "object",
|
|
223
|
+
properties: {
|
|
224
|
+
space_id: {
|
|
225
|
+
type: "string",
|
|
226
|
+
description: "Optional space ID to filter projects",
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: "project_create",
|
|
233
|
+
description: "Create a new project in 1Medium",
|
|
234
|
+
inputSchema: {
|
|
235
|
+
type: "object",
|
|
236
|
+
properties: {
|
|
237
|
+
name: {
|
|
238
|
+
type: "string",
|
|
239
|
+
description: "Name for the new project (min 2 characters)",
|
|
240
|
+
},
|
|
241
|
+
space_id: {
|
|
242
|
+
type: "string",
|
|
243
|
+
description: "Optional space ID to put the project in",
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
required: ["name"],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: "project_update",
|
|
251
|
+
description: "Update an existing project",
|
|
252
|
+
inputSchema: {
|
|
253
|
+
type: "object",
|
|
254
|
+
properties: {
|
|
255
|
+
id: {
|
|
256
|
+
type: "string",
|
|
257
|
+
description: "Project ID to update",
|
|
258
|
+
},
|
|
259
|
+
name: {
|
|
260
|
+
type: "string",
|
|
261
|
+
description: "New name for the project",
|
|
262
|
+
},
|
|
263
|
+
space_id: {
|
|
264
|
+
type: "string",
|
|
265
|
+
description: "Move project to a different space (or null to remove from space)",
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
required: ["id"],
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: "project_delete",
|
|
273
|
+
description: "Delete a project (must be empty)",
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: "object",
|
|
276
|
+
properties: {
|
|
277
|
+
id: {
|
|
278
|
+
type: "string",
|
|
279
|
+
description: "Project ID to delete",
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
required: ["id"],
|
|
283
|
+
},
|
|
284
|
+
},
|
|
160
285
|
],
|
|
161
286
|
};
|
|
162
287
|
});
|
|
@@ -184,19 +309,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
184
309
|
|
|
185
310
|
switch (name) {
|
|
186
311
|
case "task_create": {
|
|
312
|
+
// Use provided project_id or fall back to configured default
|
|
313
|
+
const projectId = args.project_id || config.get("projectId") || null;
|
|
187
314
|
const payload = {
|
|
188
315
|
title: args.title,
|
|
189
316
|
body_md: args.body,
|
|
190
317
|
priority: args.priority || "P2",
|
|
191
318
|
source: args.source || "claude-code",
|
|
192
319
|
context: args.context || {},
|
|
320
|
+
project_id: projectId,
|
|
193
321
|
};
|
|
194
322
|
result = await api.createTask(payload);
|
|
323
|
+
let text = `Task created:\n ID: ${result.task.id}\n Title: ${result.task.title}\n Priority: ${result.task.priority}`;
|
|
324
|
+
if (result.task.project_name) {
|
|
325
|
+
text += `\n Project: ${result.task.project_name}`;
|
|
326
|
+
}
|
|
195
327
|
return {
|
|
196
328
|
content: [
|
|
197
329
|
{
|
|
198
330
|
type: "text",
|
|
199
|
-
text
|
|
331
|
+
text,
|
|
200
332
|
},
|
|
201
333
|
],
|
|
202
334
|
};
|
|
@@ -276,7 +408,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
276
408
|
|
|
277
409
|
case "task_complete": {
|
|
278
410
|
const payload = {};
|
|
279
|
-
if (args.summary) payload.
|
|
411
|
+
if (args.summary) payload.summary_md = args.summary;
|
|
280
412
|
|
|
281
413
|
result = await api.completeTask(args.id, payload);
|
|
282
414
|
return {
|
|
@@ -290,7 +422,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
290
422
|
}
|
|
291
423
|
|
|
292
424
|
case "task_comment": {
|
|
293
|
-
result = await api.addComment(args.id, {
|
|
425
|
+
result = await api.addComment(args.id, { comment_md: args.message });
|
|
294
426
|
return {
|
|
295
427
|
content: [
|
|
296
428
|
{
|
|
@@ -301,6 +433,150 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
301
433
|
};
|
|
302
434
|
}
|
|
303
435
|
|
|
436
|
+
case "space_list": {
|
|
437
|
+
result = await api.listSpaces();
|
|
438
|
+
const spaces = result.spaces || [];
|
|
439
|
+
|
|
440
|
+
if (spaces.length === 0) {
|
|
441
|
+
return {
|
|
442
|
+
content: [{ type: "text", text: "No spaces found." }],
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const spaceList = spaces
|
|
447
|
+
.map((s) => `- ${s.name}\n ID: ${s.id}`)
|
|
448
|
+
.join("\n\n");
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
content: [
|
|
452
|
+
{
|
|
453
|
+
type: "text",
|
|
454
|
+
text: `Spaces:\n\n${spaceList}`,
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
case "project_list": {
|
|
461
|
+
const params = {};
|
|
462
|
+
if (args.space_id) params.space_id = args.space_id;
|
|
463
|
+
|
|
464
|
+
result = await api.listProjects(params);
|
|
465
|
+
const projects = result.projects || [];
|
|
466
|
+
|
|
467
|
+
if (projects.length === 0) {
|
|
468
|
+
return {
|
|
469
|
+
content: [{ type: "text", text: "No projects found." }],
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const projectList = projects
|
|
474
|
+
.map((p) => {
|
|
475
|
+
let text = `- ${p.name}\n ID: ${p.id}`;
|
|
476
|
+
if (p.space_name) {
|
|
477
|
+
text += `\n Space: ${p.space_name}`;
|
|
478
|
+
}
|
|
479
|
+
return text;
|
|
480
|
+
})
|
|
481
|
+
.join("\n\n");
|
|
482
|
+
|
|
483
|
+
return {
|
|
484
|
+
content: [
|
|
485
|
+
{
|
|
486
|
+
type: "text",
|
|
487
|
+
text: `Projects:\n\n${projectList}`,
|
|
488
|
+
},
|
|
489
|
+
],
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
case "space_create": {
|
|
494
|
+
result = await api.createSpace({ name: args.name });
|
|
495
|
+
return {
|
|
496
|
+
content: [
|
|
497
|
+
{
|
|
498
|
+
type: "text",
|
|
499
|
+
text: `Space created:\n ID: ${result.space.id}\n Name: ${result.space.name}`,
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
case "space_update": {
|
|
506
|
+
const payload = {};
|
|
507
|
+
if (args.name) payload.name = args.name;
|
|
508
|
+
result = await api.updateSpace(args.id, payload);
|
|
509
|
+
return {
|
|
510
|
+
content: [
|
|
511
|
+
{
|
|
512
|
+
type: "text",
|
|
513
|
+
text: `Space updated:\n ID: ${result.space.id}\n Name: ${result.space.name}`,
|
|
514
|
+
},
|
|
515
|
+
],
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
case "space_delete": {
|
|
520
|
+
result = await api.deleteSpace(args.id);
|
|
521
|
+
return {
|
|
522
|
+
content: [
|
|
523
|
+
{
|
|
524
|
+
type: "text",
|
|
525
|
+
text: `Space deleted: ${args.id}`,
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
case "project_create": {
|
|
532
|
+
const payload = { name: args.name };
|
|
533
|
+
if (args.space_id) payload.space_id = args.space_id;
|
|
534
|
+
result = await api.createProject(payload);
|
|
535
|
+
let text = `Project created:\n ID: ${result.project.id}\n Name: ${result.project.name}`;
|
|
536
|
+
if (result.project.space_name) {
|
|
537
|
+
text += `\n Space: ${result.project.space_name}`;
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
content: [
|
|
541
|
+
{
|
|
542
|
+
type: "text",
|
|
543
|
+
text,
|
|
544
|
+
},
|
|
545
|
+
],
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
case "project_update": {
|
|
550
|
+
const payload = {};
|
|
551
|
+
if (args.name) payload.name = args.name;
|
|
552
|
+
if (args.space_id !== undefined) payload.space_id = args.space_id;
|
|
553
|
+
result = await api.updateProject(args.id, payload);
|
|
554
|
+
let text = `Project updated:\n ID: ${result.project.id}\n Name: ${result.project.name}`;
|
|
555
|
+
if (result.project.space_name) {
|
|
556
|
+
text += `\n Space: ${result.project.space_name}`;
|
|
557
|
+
}
|
|
558
|
+
return {
|
|
559
|
+
content: [
|
|
560
|
+
{
|
|
561
|
+
type: "text",
|
|
562
|
+
text,
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
case "project_delete": {
|
|
569
|
+
result = await api.deleteProject(args.id);
|
|
570
|
+
return {
|
|
571
|
+
content: [
|
|
572
|
+
{
|
|
573
|
+
type: "text",
|
|
574
|
+
text: `Project deleted: ${args.id}`,
|
|
575
|
+
},
|
|
576
|
+
],
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
304
580
|
default:
|
|
305
581
|
return {
|
|
306
582
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|