@we-scrum/cli 1.0.4 → 1.0.5

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": "@we-scrum/cli",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Cli tool for we-scrum application",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -20,27 +20,31 @@
20
20
  "noExternal": [
21
21
  "@we-scrum/*",
22
22
  "@my-devkit/*"
23
- ]
23
+ ],
24
+ "loader": {
25
+ ".hbs": "text"
26
+ }
24
27
  },
25
28
  "dependencies": {
26
- "@inquirer/prompts": "8.3.0",
27
- "zod": "4.3.6",
28
- "@modelcontextprotocol/sdk": "1.27.1",
29
- "commander": "14.0.3",
30
- "firebase": "12.10.0",
31
- "google-auth-library": "^10.6.1",
32
- "open": "^11.0.0"
29
+ "@inquirer/prompts": "^8.4.2",
30
+ "@modelcontextprotocol/sdk": "^1.29.0",
31
+ "commander": "^14.0.3",
32
+ "firebase": "^12.12.1",
33
+ "google-auth-library": "^10.6.2",
34
+ "handlebars": "^4.7.9",
35
+ "open": "^11.0.0",
36
+ "zod": "^4.4.1"
33
37
  },
34
38
  "devDependencies": {
35
- "@types/node": "22.18.6",
36
- "@modelcontextprotocol/inspector": "0.21.1",
37
- "tsup": "8.5.1",
38
- "typescript": "5.9.3",
39
+ "@modelcontextprotocol/inspector": "^0.21.2",
40
+ "@types/node": "^22.19.17",
41
+ "tsup": "^8.5.1",
42
+ "typescript": "^5.9.3",
39
43
  "@my-devkit/cli": "2.1.0",
40
44
  "@my-devkit/core": "1.0.0",
41
45
  "@we-scrum/commands": "1.0.0",
42
- "@we-scrum/enums": "1.0.0",
43
46
  "@we-scrum/models": "1.0.0",
47
+ "@we-scrum/enums": "1.0.0",
44
48
  "@we-scrum/utils": "1.0.0"
45
49
  },
46
50
  "scripts": {
package/src/cli.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { select } from '@inquirer/prompts';
3
- import { Logger } from '@my-devkit/core';
3
+ import { assert, Logger } from '@my-devkit/core';
4
4
  import { UserProjectModel } from '@we-scrum/models';
5
5
  import { Command } from 'commander';
6
6
  import { version } from '../package.json';
7
7
 
8
- import { AuthHelper, FirebaseHelper, SettingsHelper } from './helpers';
8
+ import { AuthHelper, FirebaseHelper, SettingsHelper, WeScrumHelper } from './helpers';
9
9
  import { startMcpServer } from './mcp-server';
10
10
 
11
11
  Logger.registerLogger(new Logger.ConsoleImplementation());
@@ -14,54 +14,59 @@ const program = new Command();
14
14
 
15
15
  program.name('we-scrum').description('CLI for we-scrum application').version(version);
16
16
 
17
- program
18
- .command('login')
19
- .description('Authenticate with your Google account')
20
- .action(async () => {
17
+ async function requireAuth(): Promise<void> {
18
+ const initialized = await AuthHelper.initializeFromStoredCredentials();
19
+ assert(initialized, 'Not authenticated. Please run "we-scrum login" first.');
20
+ }
21
+
22
+ function runCommand<T = void>(fn: (options: T) => Promise<void>) {
23
+ return async (options: T) => {
21
24
  try {
22
- await AuthHelper.loginWithGoogle();
25
+ await fn(options);
23
26
  process.exit(0);
24
27
  } catch (error) {
25
- Logger.error(error?.message ?? 'Login failed');
28
+ if (error?.name === 'ExitPromptError') process.exit(0);
29
+ Logger.error(error?.message ?? 'Command failed');
26
30
  process.exit(1);
27
31
  }
28
- });
32
+ };
33
+ }
34
+
35
+ program
36
+ .command('login')
37
+ .description('Authenticate with your Google account')
38
+ .action(
39
+ runCommand(async () => {
40
+ await AuthHelper.loginWithGoogle();
41
+ }),
42
+ );
29
43
 
30
44
  program
31
45
  .command('logout')
32
46
  .description('Log out and remove stored credentials')
33
- .action(() => {
34
- AuthHelper.logout();
35
- Logger.info('Logged out successfully.');
36
- process.exit(0);
37
- });
47
+ .action(
48
+ runCommand(async () => {
49
+ AuthHelper.logout();
50
+ Logger.info('Logged out successfully.');
51
+ }),
52
+ );
38
53
 
39
54
  program
40
55
  .command('use-project')
41
56
  .description('Select the active project')
42
57
  .option('--projectId <id>', 'Set the active project directly without the interactive prompt')
43
- .action(async (options: { projectId?: string }) => {
44
- try {
45
- const initialized = await AuthHelper.initializeFromStoredCredentials();
46
- if (!initialized) {
47
- Logger.error('Not authenticated. Please run "we-scrum login" first.');
48
- process.exit(1);
49
- }
58
+ .action(
59
+ runCommand<{ projectId?: string }>(async (options) => {
60
+ await requireAuth();
50
61
 
51
62
  const projects = await FirebaseHelper.getCollection<UserProjectModel>(`users/${AuthHelper.userId}/projects`);
52
- if (projects.length === 0) {
53
- Logger.error('No projects found for your account.');
54
- process.exit(1);
55
- }
63
+ assert(projects.length > 0, 'No projects found for your account. Please create a project in the we-scrum web app first.');
56
64
 
57
65
  let selectedProjectId: string;
58
66
 
59
67
  if (options.projectId) {
60
68
  const match = projects.find((p) => p.projectId === options.projectId);
61
- if (!match) {
62
- Logger.error(`Project "${options.projectId}" not found in your account.`);
63
- process.exit(1);
64
- }
69
+ assert(!!match, `Project "${options.projectId}" not found in your account.`);
65
70
  selectedProjectId = match.projectId;
66
71
  } else {
67
72
  selectedProjectId = await select({
@@ -77,15 +82,45 @@ program
77
82
 
78
83
  SettingsHelper.set({ selectedProjectId });
79
84
  Logger.info(`Active project set to: ${projects.find((p) => p.projectId === selectedProjectId)?.name}`);
80
- process.exit(0);
81
- } catch (error) {
82
- if (error?.name === 'ExitPromptError') {
83
- process.exit(0);
84
- }
85
- Logger.error(error?.message ?? 'Failed to select project');
86
- process.exit(1);
87
- }
88
- });
85
+ }),
86
+ );
87
+
88
+ program
89
+ .command('start-task')
90
+ .description('Mark a specific task as in progress')
91
+ .requiredOption('--identificationNumber <id>', 'Story identification number')
92
+ .requiredOption('--taskId <id>', 'Unique identifier of the change to act on')
93
+ .action(
94
+ runCommand<{ identificationNumber: string; taskId: string }>(async (options) => {
95
+ await requireAuth();
96
+ await WeScrumHelper.startTask(options.identificationNumber, options.taskId);
97
+ Logger.info('Task started successfully. You can now make changes to the codebase.');
98
+ }),
99
+ );
100
+
101
+ program
102
+ .command('complete-task')
103
+ .description('Mark the current in-progress task as done')
104
+ .requiredOption('--identificationNumber <id>', 'Story identification number')
105
+ .requiredOption('--taskId <id>', 'Unique identifier of the change to act on')
106
+ .action(
107
+ runCommand<{ identificationNumber: string; taskId: string }>(async (options) => {
108
+ await requireAuth();
109
+ await WeScrumHelper.completeTask(options.identificationNumber, options.taskId);
110
+ Logger.info('Task completed successfully.');
111
+ }),
112
+ );
113
+
114
+ program
115
+ .command('synchronize-development-policies')
116
+ .description('Sync development guidelines from we-scrum into .claude/')
117
+ .action(
118
+ runCommand(async () => {
119
+ await requireAuth();
120
+ const count = await WeScrumHelper.synchronizeDevelopmentPolicies(process.cwd());
121
+ Logger.info(`Synchronized ${count} development ${count === 1 ? 'policy' : 'policies'}.`);
122
+ }),
123
+ );
89
124
 
90
125
  program
91
126
  .command('mcp')
package/src/hbs.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ declare module '*.hbs' {
2
+ const content: string;
3
+ export default content;
4
+ }
@@ -70,7 +70,7 @@ export class AuthHelper {
70
70
 
71
71
  // Exchange the Authorization Code for tokens using the PKCE Verifier
72
72
  const { tokens } = await oAuth2Client.getToken({
73
- code: code,
73
+ code,
74
74
  codeVerifier: verifier,
75
75
  });
76
76
 
@@ -1,4 +1,5 @@
1
1
  export * from './auth-helper';
2
2
  export * from './firebase-helper';
3
3
  export * from './settings-helper';
4
+ export * from './template.helper';
4
5
  export * from './we-scrum.helper';
@@ -0,0 +1,23 @@
1
+ import Handlebars from 'handlebars';
2
+ import getNextTaskDescription from '../../templates/get-next-task-description.hbs';
3
+ import getNextTask from '../../templates/get-next-task.hbs';
4
+
5
+ const sources = {
6
+ 'get-next-task': getNextTask,
7
+ 'get-next-task-description': getNextTaskDescription,
8
+ } as const;
9
+
10
+ export type TemplateName = keyof typeof sources;
11
+
12
+ const compiled = new Map<TemplateName, Handlebars.TemplateDelegate>();
13
+
14
+ function getTemplate(name: TemplateName): Handlebars.TemplateDelegate {
15
+ if (!compiled.has(name)) {
16
+ compiled.set(name, Handlebars.compile(sources[name]));
17
+ }
18
+ return compiled.get(name)!;
19
+ }
20
+
21
+ export function renderTemplate(name: TemplateName, context?: Record<string, unknown>): string {
22
+ return getTemplate(name)(context ?? {}).trim();
23
+ }
@@ -1,4 +1,4 @@
1
- import { _sortBy, Command, serialize, TypeHelper } from '@my-devkit/core';
1
+ import { _sortBy, assert, Command, serialize, TypeHelper } from '@my-devkit/core';
2
2
  import {
3
3
  MarkEnumerationChangeAsDoneCommand,
4
4
  MarkObjectChangeAsDoneCommand,
@@ -23,13 +23,16 @@ import {
23
23
  ProjectStorySectionModelTask,
24
24
  } from '@we-scrum/models';
25
25
  import { DevelopmentPolicyHelper } from '@we-scrum/utils';
26
- import { assert } from 'node:console';
26
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
27
+ import { join } from 'node:path';
27
28
  import { AuthHelper } from './auth-helper';
28
29
  import { FirebaseHelper } from './firebase-helper';
29
30
  import { SettingsHelper } from './settings-helper';
30
31
 
31
32
  export class WeScrumHelper {
32
- private static backendUrl = 'https://europe-west1-we-scrum-prod.cloudfunctions.net';
33
+ private static readonly backendUrl = 'https://europe-west1-we-scrum-prod.cloudfunctions.net';
34
+ private static readonly claudeSubdir = '.claude';
35
+ private static readonly relPoliciesDir = `${WeScrumHelper.claudeSubdir}/development-policies`;
33
36
  constructor(private userId: string) {}
34
37
 
35
38
  public static get projectId(): string {
@@ -56,11 +59,33 @@ export class WeScrumHelper {
56
59
  return task;
57
60
  }
58
61
 
59
- public static async startTask(identificationNumber: string): Promise<void> {
60
- const change = await this.getNextChange(identificationNumber);
62
+ public static async getChangeById(identificationNumber: string, changeId: string) {
63
+ this.assertProjectIsSelected();
64
+
65
+ const story = await this.findStoryByIterationNumber(identificationNumber);
66
+ assert(!!story, `Story "${identificationNumber}" not found in the active project.`);
61
67
 
62
- assert(!!change, `All tasks for story "${identificationNumber}" are complete.`);
63
- assert(change.progress.status === ProgressStatus.ToDo, `Task is already in progress (current status: ${change.progress.status}).`);
68
+ const sections = await this.getStorySections(story.storyId);
69
+
70
+ const change = _sortBy(sections, (s) => s.name)
71
+ .flatMap((s) => [...s.enumerationChanges, ...s.objectChanges, ...s.routeChanges, ...s.relationChanges, ...s.tasks])
72
+ .find((t) => {
73
+ if (t instanceof ProjectStorySectionModelEnumerationChange) return t.enumerationChangeId === changeId;
74
+ if (t instanceof ProjectStorySectionModelObjectChange) return t.objectChangeId === changeId;
75
+ if (t instanceof ProjectStorySectionModelRouteChange) return t.routeChangeId === changeId;
76
+ if (t instanceof ProjectStorySectionModelRelationChange) return t.relationChangeId === changeId;
77
+ if (t instanceof ProjectStorySectionModelTask) return t.taskId === changeId;
78
+ return false;
79
+ });
80
+
81
+ assert(!!change, `Change "${changeId}" not found in story "${identificationNumber}".`);
82
+ return change;
83
+ }
84
+
85
+ public static async startTask(identificationNumber: string, taskId: string): Promise<void> {
86
+ const change = await this.getChangeById(identificationNumber, taskId);
87
+
88
+ assert(change.progress.status === ProgressStatus.ToDo, `Task is not in ToDo status (current status: ${change.progress.status}).`);
64
89
 
65
90
  if (change instanceof ProjectStorySectionModelEnumerationChange) {
66
91
  await this.post(
@@ -87,10 +112,9 @@ export class WeScrumHelper {
87
112
  }
88
113
  }
89
114
 
90
- public static async completeTask(identificationNumber: string): Promise<void> {
91
- const change = await this.getNextChange(identificationNumber);
115
+ public static async completeTask(identificationNumber: string, taskId: string): Promise<void> {
116
+ const change = await this.getChangeById(identificationNumber, taskId);
92
117
 
93
- assert(!!change, `All tasks for story "${identificationNumber}" are complete.`);
94
118
  assert(change.progress.status === ProgressStatus.Doing, `Task is not in progress (current status: ${change.progress.status}).`);
95
119
 
96
120
  if (change instanceof ProjectStorySectionModelEnumerationChange) {
@@ -124,6 +148,20 @@ export class WeScrumHelper {
124
148
  return DevelopmentPolicyHelper.getMatchingDevelopmentPolicy(change, developmentPolicies);
125
149
  }
126
150
 
151
+ public static getDevelopmentPolicyPaths(
152
+ policy: DevelopmentPolicyModel | null,
153
+ projectDir = '.',
154
+ ): {
155
+ guidelinesPath: string | null;
156
+ unitTestsPath: string | null;
157
+ } {
158
+ const policiesDir = join(projectDir, this.relPoliciesDir);
159
+ return {
160
+ guidelinesPath: policy?.slug ? join(policiesDir, `develop-${policy.slug}.md`) : null,
161
+ unitTestsPath: policy?.slug ? join(policiesDir, `test-${policy.slug}.md`) : null,
162
+ };
163
+ }
164
+
127
165
  private static async findStoryByIterationNumber(identificationNumber: string) {
128
166
  return FirebaseHelper.findDocument<ProjectStoryModel>(`/projects/${this.projectId}/stories`, [
129
167
  ['identificationNumber', '==', identificationNumber],
@@ -134,92 +172,38 @@ export class WeScrumHelper {
134
172
  return FirebaseHelper.getCollection<ProjectStorySectionModel>(`projects/${this.projectId}/stories/${storyId}/sections`);
135
173
  }
136
174
 
137
- private static async getDevelopmentPolicies() {
175
+ public static async getDevelopmentPolicies() {
138
176
  return FirebaseHelper.getCollection<DevelopmentPolicyModel>(`/projects/${this.projectId}/development-policies`);
139
177
  }
140
178
 
141
- // public async createEnumeration(enumeration: Enumeration): Promise<string> {
142
- // Logger.info(`Creating enumeration ${enumeration.name}`);
143
-
144
- // const createCommand = TypeHelper.transform(CreateEnumerationCommand, {
145
- // projectId: this.projectId,
146
- // name: enumeration.name,
147
- // });
148
-
149
- // const enumerationId = await this.post<CreateEnumerationCommand, string>('enumeration-management/create-enumeration', createCommand);
150
- // for (const property of enumeration.properties) {
151
- // const propertyCommand = TypeHelper.transform(CreateEnumerationPropertyCommand, {
152
- // enumerationId,
153
- // enumerationPropertyId: guid(),
154
- // name: property.label,
155
- // value: property.value,
156
- // });
157
- // await this.post<CreateEnumerationPropertyCommand, string>(
158
- // 'enumeration-management/create-enumeration-property',
159
- // propertyCommand,
160
- // );
161
- // }
162
-
163
- // return enumerationId;
164
- // }
165
-
166
- // public async createObject(object: Object, getEnumerationId: (name: string) => string): Promise<string> {
167
- // Logger.info(`Creating object ${object.name}`);
168
-
169
- // const createCommand = TypeHelper.transform(CreateObjectCommand, {
170
- // projectId: this.projectId,
171
- // name: object.name,
172
- // });
173
- // const objectId = await this.post<CreateObjectCommand, string>('object-management/create-object', createCommand);
174
-
175
- // const propertyMap = new Map<string, string>();
176
- // for (const property of object.properties) {
177
- // const objectPropertyId = guid();
178
- // const propertyCommand = TypeHelper.transform(CreateObjectPropertyCommand, {
179
- // objectId,
180
- // objectPropertyId,
181
- // objectPropertyParentId: propertyMap.get(property.parent) || null,
182
- // name: property.name,
183
- // objectPropertyType: property.type,
184
- // isArray: property.isArray,
185
- // enumerationId: property.enumerationName ? getEnumerationId(property.enumerationName) : null,
186
- // });
187
- // await this.post<CreateObjectPropertyCommand, string>('object-management/create-object-property', propertyCommand);
188
-
189
- // propertyMap.set(property.path, objectPropertyId);
190
- // }
191
-
192
- // return objectId;
193
- // }
194
-
195
- // public async createHandler(handler: Handler, getObjectId: (name: string) => string): Promise<void> {
196
- // Logger.info(`Creating handler ${handler.name}`);
197
-
198
- // for (const relation of handler.relations) {
199
- // const createCommand = TypeHelper.transform(CreateRelationCommand, {
200
- // projectId: this.projectId,
201
- // boundedContext: handler.boundedContext,
202
- // handlerName: handler.name,
203
- // objectIds: relation.map((o) => getObjectId(o)),
204
- // subscriptionName: null,
205
- // });
206
- // await this.post<CreateRelationCommand, string>('relation-management/create-relation', createCommand);
207
- // }
208
- // }
209
-
210
- // public async createRoute(route: Route, getObjectId: (name: string) => string): Promise<string> {
211
- // Logger.info(`Creating route ${route.method} ${route.path}`);
212
-
213
- // const createCommand = TypeHelper.transform(CreateRouteCommand, {
214
- // projectId: this.projectId,
215
- // method: route.method,
216
- // path: route.path,
217
- // objectId: getObjectId(route.object),
218
- // permission: null,
219
- // });
220
-
221
- // return this.post<CreateRouteCommand, string>('route-management/create-route', createCommand);
222
- // }
179
+ public static async synchronizeDevelopmentPolicies(projectDir: string): Promise<number> {
180
+ this.assertProjectIsSelected();
181
+
182
+ const claudeDir = join(projectDir, this.claudeSubdir);
183
+ assert(
184
+ existsSync(claudeDir),
185
+ `No ${this.claudeSubdir} directory found in ${projectDir}. Please run this command from the root of your project.`,
186
+ );
187
+
188
+ const policiesDir = join(projectDir, this.relPoliciesDir);
189
+
190
+ rmSync(policiesDir, { recursive: true, force: true });
191
+ mkdirSync(policiesDir, { recursive: true });
192
+
193
+ const policies = await this.getDevelopmentPolicies();
194
+
195
+ for (const policy of policies) {
196
+ const { guidelinesPath, unitTestsPath } = this.getDevelopmentPolicyPaths(policy, projectDir);
197
+ if (policy.codeGuidelines && guidelinesPath) {
198
+ writeFileSync(guidelinesPath, policy.codeGuidelines);
199
+ }
200
+ if (policy.unitTestsGuidelines && unitTestsPath) {
201
+ writeFileSync(unitTestsPath, policy.unitTestsGuidelines);
202
+ }
203
+ }
204
+
205
+ return policies.length;
206
+ }
223
207
 
224
208
  private static async post<C extends Command, R>(route: string, command: C): Promise<R> {
225
209
  const userIdToken = await AuthHelper.getUserIdToken();
@@ -1,29 +1,23 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
3
5
  import { z } from 'zod';
4
6
 
5
- import { AuthHelper } from '@helpers';
6
- import { completeTask, getNextTask, getUnitTestGuidelines, startTask } from './tools';
7
+ import { AuthHelper, renderTemplate } from '@helpers';
8
+ import developStorySkill from '../../templates/develop-story-skill.hbs';
9
+ import { getNextTask } from './tools';
7
10
 
8
- const identificationNumberSchema = z.object({
9
- identificationNumber: z.string().describe('The story identification number, e.g. "144154"'),
10
- });
11
-
12
- function createToolCallback<T extends Record<string, string>>(fn: (identificationNumber: string) => Promise<T>) {
13
- return async ({ identificationNumber }: { identificationNumber: string }) => {
14
- try {
15
- const result = await fn(identificationNumber);
16
- return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
17
- } catch (error) {
18
- return {
19
- content: [{ type: 'text' as const, text: JSON.stringify({ error: error.message }) }],
20
- isError: true,
21
- };
22
- }
23
- };
11
+ function installSkills(): void {
12
+ const skillDir = join(process.cwd(), '.claude', 'skills', 'develop-story');
13
+ if (!existsSync(join(process.cwd(), '.claude'))) return;
14
+ mkdirSync(skillDir, { recursive: true });
15
+ writeFileSync(join(skillDir, 'SKILL.md'), developStorySkill);
24
16
  }
25
17
 
26
18
  export async function startMcpServer(): Promise<void> {
19
+ installSkills();
20
+
27
21
  const initialized = await AuthHelper.initializeFromStoredCredentials();
28
22
  if (!initialized) {
29
23
  process.stderr.write('Error: Not authenticated. Please run "we-scrum login" first.\n');
@@ -35,44 +29,22 @@ export async function startMcpServer(): Promise<void> {
35
29
  server.registerTool(
36
30
  'get_next_task',
37
31
  {
38
- description: `Get the next pending task (status: ToDo) for a story.
39
- Returns the task description, the task type template (development guidelines),
40
- and the full section analysis specifying what must be implemented
41
- (route changes, object/DTO changes, enumeration changes, relation/handler changes).
42
- Also returns storyId, sectionId and taskId needed to call start_task and complete_task.`,
43
- inputSchema: identificationNumberSchema,
44
- },
45
- createToolCallback(getNextTask),
46
- );
47
-
48
- server.registerTool(
49
- 'start_task',
50
- {
51
- description: `Mark the next pending task of a story as in progress.
52
- Call this before making any changes to the codebase, as instructed by get_next_task.`,
53
- inputSchema: identificationNumberSchema,
54
- },
55
- createToolCallback(startTask),
56
- );
57
-
58
- server.registerTool(
59
- 'get_unit_test_guidelines',
60
- {
61
- description: `Get unit test writing guidelines for the current pending task of a story.
62
- Call this tool when get_next_task returns unitTests instructions to run it.`,
63
- inputSchema: identificationNumberSchema,
32
+ description: renderTemplate('get-next-task-description'),
33
+ inputSchema: z.object({
34
+ identificationNumber: z.string().describe('The story identification number, e.g. "144154"'),
35
+ }),
64
36
  },
65
- createToolCallback(getUnitTestGuidelines),
66
- );
67
-
68
- server.registerTool(
69
- 'complete_task',
70
- {
71
- description: `Mark the current in-progress task of a story as done.
72
- Call this after committing all changes, as instructed by get_next_task.`,
73
- inputSchema: identificationNumberSchema,
37
+ async ({ identificationNumber }) => {
38
+ try {
39
+ const markdown = await getNextTask(identificationNumber);
40
+ return { content: [{ type: 'text' as const, text: markdown }] };
41
+ } catch (error) {
42
+ return {
43
+ content: [{ type: 'text' as const, text: JSON.stringify({ error: error.message }) }],
44
+ isError: true,
45
+ };
46
+ }
74
47
  },
75
- createToolCallback(completeTask),
76
48
  );
77
49
 
78
50
  const transport = new StdioServerTransport();