@we-scrum/cli 1.0.8 → 6.8.1

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.8",
3
+ "version": "6.8.1",
4
4
  "description": "Cli tool for we-scrum application",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -26,21 +26,21 @@
26
26
  }
27
27
  },
28
28
  "dependencies": {
29
- "@inquirer/prompts": "^8.4.2",
30
- "commander": "^14.0.3",
31
- "firebase": "^12.12.1",
32
- "google-auth-library": "^10.6.2",
29
+ "@inquirer/prompts": "^8.6.0",
30
+ "commander": "^15.0.0",
31
+ "firebase": "^12.18.0",
32
+ "google-auth-library": "^11.0.2",
33
33
  "handlebars": "^4.7.9",
34
- "open": "^11.0.0"
34
+ "open": "^11.0.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/node": "^22.19.17",
37
+ "@types/node": "^24.13.3",
38
38
  "tsup": "^8.5.1",
39
- "typescript": "^5.9.3",
39
+ "typescript": "^6.0.3",
40
40
  "@my-devkit/cli": "2.1.0",
41
+ "@my-devkit/core": "1.0.0",
41
42
  "@we-scrum/enums": "1.0.0",
42
43
  "@we-scrum/commands": "1.0.0",
43
- "@my-devkit/core": "1.0.0",
44
44
  "@we-scrum/models": "1.0.0",
45
45
  "@we-scrum/utils": "1.0.0"
46
46
  },
package/src/cli.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { select } from '@inquirer/prompts';
3
- import { assert, Logger } from '@my-devkit/core';
4
- import { UserProjectModel } from '@we-scrum/models';
2
+ import { Logger } from '@my-devkit/core';
5
3
  import { Command } from 'commander';
6
4
  import { version } from '../package.json';
7
-
8
- import { AuthHelper, FirebaseHelper, ProjectHelper, WeScrumHelper } from './helpers';
5
+ import {
6
+ registerAuthCommands,
7
+ registerIterationCommands,
8
+ registerPolicyCommands,
9
+ registerProjectCommands,
10
+ registerStoryCommands,
11
+ registerTaskCommands,
12
+ } from './commands';
9
13
 
10
14
  Logger.registerLogger(new Logger.ConsoleImplementation());
11
15
 
@@ -13,154 +17,11 @@ const program = new Command();
13
17
 
14
18
  program.name('we-scrum').description('CLI for we-scrum application').version(version);
15
19
 
16
- async function requireAuth(): Promise<void> {
17
- const initialized = await AuthHelper.initializeFromStoredCredentials();
18
- assert(initialized, 'Not authenticated. Please run "we-scrum login" first.');
19
- }
20
-
21
- async function requireProject(): Promise<void> {
22
- const root = ProjectHelper.requireRoot();
23
- await ProjectHelper.init(root);
24
- }
25
-
26
- function runCommand<T = void>(fn: (options: T) => Promise<string | void>) {
27
- return async (options: T) => {
28
- try {
29
- const output = await fn(options);
30
- if (output) process.stdout.write(output + '\n');
31
- process.exit(0);
32
- } catch (error) {
33
- if (error?.name === 'ExitPromptError') process.exit(0);
34
- Logger.error(error?.message ?? 'Command failed');
35
- process.exit(1);
36
- }
37
- };
38
- }
39
-
40
- function runProjectCommand<T = void>(fn: (options: T) => Promise<string | void>) {
41
- return runCommand<T>(async (options) => {
42
- await requireProject();
43
- await requireAuth();
44
- return fn(options);
45
- });
46
- }
47
-
48
- program
49
- .command('login')
50
- .description('Authenticate with your Google account')
51
- .action(
52
- runCommand(async () => {
53
- await AuthHelper.loginWithGoogle();
54
- }),
55
- );
56
-
57
- program
58
- .command('logout')
59
- .description('Log out and remove stored credentials')
60
- .action(
61
- runCommand(async () => {
62
- AuthHelper.logout();
63
- Logger.info('Logged out successfully.');
64
- }),
65
- );
66
-
67
- program
68
- .command('use-project')
69
- .description('Select the active project and initialize we-scrum.json')
70
- .option('--projectId <id>', 'Set the active project directly without the interactive prompt')
71
- .action(
72
- runCommand<{ projectId?: string }>(async (options) => {
73
- await requireAuth();
74
-
75
- const projects = await FirebaseHelper.getCollection<UserProjectModel>(`users/${AuthHelper.userId}/projects`);
76
- assert(projects.length > 0, 'No projects found for your account. Please create a project in the we-scrum web app first.');
77
-
78
- let selectedProjectId: string;
79
-
80
- if (options.projectId) {
81
- const match = projects.find((p) => p.projectId === options.projectId);
82
- assert(!!match, `Project "${options.projectId}" not found in your account.`);
83
- selectedProjectId = match.projectId;
84
- } else {
85
- selectedProjectId = await select({
86
- message: 'Select a project',
87
- choices: projects.map((p) => ({
88
- name: p.name,
89
- value: p.projectId,
90
- description: p.projectId,
91
- })),
92
- });
93
- }
94
-
95
- const projectRoot = await ProjectHelper.confirmAndCreate(selectedProjectId);
96
- await ProjectHelper.init(projectRoot);
97
-
98
- Logger.info(`Project configured: ${projects.find((p) => p.projectId === selectedProjectId)?.name}`);
99
- }),
100
- );
101
-
102
- program
103
- .command('start-task')
104
- .description('Mark a specific task as in progress')
105
- .requiredOption('--identificationNumber <id>', 'Story identification number')
106
- .requiredOption('--taskId <id>', 'Unique identifier of the change to act on')
107
- .action(
108
- runProjectCommand<{ identificationNumber: string; taskId: string }>(async (options) => {
109
- await WeScrumHelper.startTask(options.identificationNumber, options.taskId);
110
- Logger.info('Task started successfully. You can now make changes to the codebase.');
111
- }),
112
- );
113
-
114
- program
115
- .command('complete-task')
116
- .description('Mark the current in-progress task as done')
117
- .requiredOption('--identificationNumber <id>', 'Story identification number')
118
- .requiredOption('--taskId <id>', 'Unique identifier of the change to act on')
119
- .action(
120
- runProjectCommand<{ identificationNumber: string; taskId: string }>(async (options) => {
121
- await WeScrumHelper.completeTask(options.identificationNumber, options.taskId);
122
- Logger.info('Task completed successfully.');
123
- }),
124
- );
125
-
126
- program
127
- .command('get-story-description')
128
- .description('Get the description of a story')
129
- .requiredOption('--identificationNumber <id>', 'Story identification number')
130
- .action(
131
- runProjectCommand<{ identificationNumber: string }>(async (options) => {
132
- return WeScrumHelper.getStoryDescription(options.identificationNumber);
133
- }),
134
- );
135
-
136
- program
137
- .command('get-story-analysis')
138
- .description('Get the analysis DSL for a story')
139
- .requiredOption('--identificationNumber <id>', 'Story identification number')
140
- .action(
141
- runProjectCommand<{ identificationNumber: string }>(async (options) => {
142
- return WeScrumHelper.getStoryAnalysis(options.identificationNumber);
143
- }),
144
- );
145
-
146
- program
147
- .command('get-next-task')
148
- .description('Get the next pending task for a story')
149
- .requiredOption('--identificationNumber <id>', 'Story identification number')
150
- .action(
151
- runProjectCommand<{ identificationNumber: string }>(async (options) => {
152
- return WeScrumHelper.getNextTask(options.identificationNumber);
153
- }),
154
- );
155
-
156
- program
157
- .command('synchronize-development-policies')
158
- .description('Sync development guidelines from we-scrum into the project')
159
- .action(
160
- runProjectCommand(async () => {
161
- const count = await WeScrumHelper.synchronizeDevelopmentPolicies();
162
- Logger.info(`Synchronized ${count} development ${count === 1 ? 'policy' : 'policies'}.`);
163
- }),
164
- );
20
+ registerAuthCommands(program);
21
+ registerProjectCommands(program);
22
+ registerStoryCommands(program);
23
+ registerTaskCommands(program);
24
+ registerPolicyCommands(program);
25
+ registerIterationCommands(program);
165
26
 
166
27
  program.parse(process.argv);
@@ -0,0 +1,25 @@
1
+ import { AuthHelper } from '@helpers';
2
+ import { Logger } from '@my-devkit/core';
3
+ import { runCommand } from '@utils';
4
+ import { Command } from 'commander';
5
+
6
+ export function registerAuthCommands(program: Command): void {
7
+ program
8
+ .command('login')
9
+ .description('Authenticate with your Google account')
10
+ .action(
11
+ runCommand(async () => {
12
+ await AuthHelper.loginWithGoogle();
13
+ }),
14
+ );
15
+
16
+ program
17
+ .command('logout')
18
+ .description('Log out and remove stored credentials')
19
+ .action(
20
+ runCommand(async () => {
21
+ AuthHelper.logout();
22
+ Logger.info('Logged out successfully.');
23
+ }),
24
+ );
25
+ }
@@ -0,0 +1,6 @@
1
+ export * from './auth';
2
+ export * from './iteration';
3
+ export * from './policy';
4
+ export * from './project';
5
+ export * from './story';
6
+ export * from './task';
@@ -0,0 +1,26 @@
1
+ import { WeScrumHelper } from '@helpers';
2
+ import { runProjectCommand } from '@utils';
3
+ import { Command } from 'commander';
4
+
5
+ export function registerIterationCommands(program: Command): void {
6
+ program
7
+ .command('update-iteration')
8
+ .description('Update an iteration')
9
+ .requiredOption('-n, --name <name>', 'Iteration name')
10
+ .option('-ca, --capacity <capacity>', 'Iteration capacity')
11
+ .option('-co, --comment <comment>', 'Iteration comment')
12
+ .option('-sd, --startDate <startDate>', 'Iteration start date (ISO 8601)')
13
+ .option('-ed, --endDate <endDate>', 'Iteration end date (ISO 8601)')
14
+ .action(
15
+ runProjectCommand<{ name: string; capacity?: string; comment?: string; startDate?: string; endDate?: string }>(
16
+ async (options) => {
17
+ await WeScrumHelper.updateIteration(options.name, {
18
+ capacity: options.capacity !== undefined ? Number(options.capacity) : undefined,
19
+ comment: options.comment,
20
+ startDate: options.startDate !== undefined ? new Date(options.startDate) : undefined,
21
+ endDate: options.endDate !== undefined ? new Date(options.endDate) : undefined,
22
+ });
23
+ },
24
+ ),
25
+ );
26
+ }
@@ -0,0 +1,16 @@
1
+ import { WeScrumHelper } from '@helpers';
2
+ import { Logger } from '@my-devkit/core';
3
+ import { runProjectCommand } from '@utils';
4
+ import { Command } from 'commander';
5
+
6
+ export function registerPolicyCommands(program: Command): void {
7
+ program
8
+ .command('synchronize-development-policies')
9
+ .description('Sync development guidelines from we-scrum into the project')
10
+ .action(
11
+ runProjectCommand(async () => {
12
+ const count = await WeScrumHelper.synchronizeDevelopmentPolicies();
13
+ Logger.info(`Synchronized ${count} development ${count === 1 ? 'policy' : 'policies'}.`);
14
+ }),
15
+ );
16
+ }
@@ -0,0 +1,43 @@
1
+ import { AuthHelper, FirebaseHelper, ProjectHelper } from '@helpers';
2
+ import { select } from '@inquirer/prompts';
3
+ import { assert, Logger } from '@my-devkit/core';
4
+ import { requireAuth, runCommand } from '@utils';
5
+ import { UserProjectModel } from '@we-scrum/models';
6
+ import { Command } from 'commander';
7
+
8
+ export function registerProjectCommands(program: Command): void {
9
+ program
10
+ .command('use-project')
11
+ .description('Select the active project and initialize we-scrum.json')
12
+ .option('--projectId <id>', 'Set the active project directly without the interactive prompt')
13
+ .action(
14
+ runCommand<{ projectId?: string }>(async (options) => {
15
+ await requireAuth();
16
+
17
+ const projects = await FirebaseHelper.getCollection<UserProjectModel>(`users/${AuthHelper.userId}/projects`);
18
+ assert(projects.length > 0, 'No projects found for your account. Please create a project in the we-scrum web app first.');
19
+
20
+ let selectedProjectId: string;
21
+
22
+ if (options.projectId) {
23
+ const match = projects.find((p) => p.projectId === options.projectId);
24
+ assert(!!match, `Project "${options.projectId}" not found in your account.`);
25
+ selectedProjectId = match.projectId;
26
+ } else {
27
+ selectedProjectId = await select({
28
+ message: 'Select a project',
29
+ choices: projects.map((p) => ({
30
+ name: p.name,
31
+ value: p.projectId,
32
+ description: p.projectId,
33
+ })),
34
+ });
35
+ }
36
+
37
+ const projectRoot = await ProjectHelper.confirmAndCreate(selectedProjectId);
38
+ await ProjectHelper.init(projectRoot);
39
+
40
+ Logger.info(`Project configured: ${projects.find((p) => p.projectId === selectedProjectId)?.name}`);
41
+ }),
42
+ );
43
+ }
@@ -0,0 +1,35 @@
1
+ import { WeScrumHelper } from '@helpers';
2
+ import { runProjectCommand } from '@utils';
3
+ import { Command } from 'commander';
4
+
5
+ export function registerStoryCommands(program: Command): void {
6
+ program
7
+ .command('get-story-description')
8
+ .description('Get the description of a story')
9
+ .requiredOption('-i, --identificationNumber <id>', 'Story identification number')
10
+ .action(
11
+ runProjectCommand<{ identificationNumber: string }>(async (options) => {
12
+ return WeScrumHelper.getStoryDescription(options.identificationNumber);
13
+ }),
14
+ );
15
+
16
+ program
17
+ .command('get-story-analysis')
18
+ .description('Get the analysis DSL for a story')
19
+ .requiredOption('-i, --identificationNumber <id>', 'Story identification number')
20
+ .action(
21
+ runProjectCommand<{ identificationNumber: string }>(async (options) => {
22
+ return WeScrumHelper.getStoryAnalysis(options.identificationNumber);
23
+ }),
24
+ );
25
+
26
+ program
27
+ .command('get-next-task')
28
+ .description('Get the next pending task for a story')
29
+ .requiredOption('-i, --identificationNumber <id>', 'Story identification number')
30
+ .action(
31
+ runProjectCommand<{ identificationNumber: string }>(async (options) => {
32
+ return WeScrumHelper.getNextTask(options.identificationNumber);
33
+ }),
34
+ );
35
+ }
@@ -0,0 +1,30 @@
1
+ import { WeScrumHelper } from '@helpers';
2
+ import { Logger } from '@my-devkit/core';
3
+ import { runProjectCommand } from '@utils';
4
+ import { Command } from 'commander';
5
+
6
+ export function registerTaskCommands(program: Command): void {
7
+ program
8
+ .command('start-task')
9
+ .description('Mark a specific task as in progress')
10
+ .requiredOption('-i, --identificationNumber <id>', 'Story identification number')
11
+ .requiredOption('-t, --taskId <id>', 'Unique identifier of the change to act on')
12
+ .action(
13
+ runProjectCommand<{ identificationNumber: string; taskId: string }>(async (options) => {
14
+ await WeScrumHelper.startTask(options.identificationNumber, options.taskId);
15
+ Logger.info('Task started successfully. You can now make changes to the codebase.');
16
+ }),
17
+ );
18
+
19
+ program
20
+ .command('complete-task')
21
+ .description('Mark the current in-progress task as done')
22
+ .requiredOption('-i, --identificationNumber <id>', 'Story identification number')
23
+ .requiredOption('-t, --taskId <id>', 'Unique identifier of the change to act on')
24
+ .action(
25
+ runProjectCommand<{ identificationNumber: string; taskId: string }>(async (options) => {
26
+ await WeScrumHelper.completeTask(options.identificationNumber, options.taskId);
27
+ Logger.info('Task completed successfully.');
28
+ }),
29
+ );
30
+ }
@@ -24,7 +24,7 @@ import {
24
24
  } from 'firebase/firestore';
25
25
 
26
26
  for (const i in models) {
27
- new models[i]();
27
+ new (models as unknown as Record<string, new () => unknown>)[i]();
28
28
  }
29
29
 
30
30
  export class FirebaseHelper {
@@ -1,5 +1,5 @@
1
- import { assert, Logger } from '@my-devkit/core';
2
1
  import { confirm, input } from '@inquirer/prompts';
2
+ import { assert, Logger } from '@my-devkit/core';
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
4
  import { dirname, join, resolve } from 'node:path';
5
5
  import { version as cliVersion } from '../../package.json';
@@ -37,7 +37,7 @@ export class ProjectHelper {
37
37
 
38
38
  public static write(root: string, partial: Partial<WeScrumProjectConfig>): void {
39
39
  const existing = existsSync(join(root, CONFIG_FILE)) ? this.read(root) : ({} as WeScrumProjectConfig);
40
- writeFileSync(join(root, CONFIG_FILE), JSON.stringify({ ...existing, ...partial }, null, 2) + '\n');
40
+ writeFileSync(join(root, CONFIG_FILE), JSON.stringify({ ...existing, ...partial }, null, 4) + '\n');
41
41
  }
42
42
 
43
43
  public static async init(root: string): Promise<void> {
@@ -10,10 +10,12 @@ import {
10
10
  TakeRelationChangeCommand,
11
11
  TakeRouteChangeCommand,
12
12
  TakeTaskCommand,
13
+ UpdateIterationCommand,
13
14
  } from '@we-scrum/commands';
14
15
  import { ContentType, ProgressStatus } from '@we-scrum/enums';
15
16
  import {
16
17
  DevelopmentPolicyModel,
18
+ ProjectIterationModel,
17
19
  ProjectStoryModel,
18
20
  ProjectStorySectionModel,
19
21
  ProjectStorySectionModelEnumerationChange,
@@ -163,6 +165,29 @@ export class WeScrumHelper {
163
165
  }
164
166
  }
165
167
 
168
+ public static async updateIteration(
169
+ name: string,
170
+ options: { capacity?: number; comment?: string; startDate?: Date; endDate?: Date },
171
+ ): Promise<void> {
172
+ const iteration = await FirebaseHelper.findDocument<ProjectIterationModel>(`/projects/${this.projectId}/iterations`, [
173
+ ['name', '==', name],
174
+ ]);
175
+
176
+ assert(!!iteration, `Iteration "${name}" not found in the active project.`);
177
+
178
+ await this.post(
179
+ 'project-management/update-iteration',
180
+ TypeHelper.transform(UpdateIterationCommand, {
181
+ iterationId: iteration.iterationId,
182
+ name: iteration.name,
183
+ startDate: options.startDate ?? iteration.startDate,
184
+ endDate: options.endDate ?? iteration.endDate,
185
+ capacity: options.capacity ?? iteration.capacity,
186
+ comment: options.comment ?? iteration.comment,
187
+ }),
188
+ );
189
+ }
190
+
166
191
  public static async getChangeDevelopmentPolicy(change: DevelopmentPolicyHelper.Change) {
167
192
  const developmentPolicies = await this.getDevelopmentPolicies();
168
193
 
@@ -0,0 +1,3 @@
1
+ export * from './require-auth';
2
+ export * from './run-command';
3
+ export * from './run-project-command';
@@ -0,0 +1,7 @@
1
+ import { AuthHelper } from '@helpers';
2
+ import { assert } from '@my-devkit/core';
3
+
4
+ export async function requireAuth(): Promise<void> {
5
+ const initialized = await AuthHelper.initializeFromStoredCredentials();
6
+ assert(initialized, 'Not authenticated. Please run "we-scrum login" first.');
7
+ }
@@ -0,0 +1,6 @@
1
+ import { ProjectHelper } from '@helpers';
2
+
3
+ export async function requireProject(): Promise<void> {
4
+ const root = ProjectHelper.requireRoot();
5
+ await ProjectHelper.init(root);
6
+ }
@@ -0,0 +1,15 @@
1
+ import { Logger } from '@my-devkit/core';
2
+
3
+ export function runCommand<T = void>(fn: (options: T) => Promise<string | void>) {
4
+ return async (options: T) => {
5
+ try {
6
+ const output = await fn(options);
7
+ if (output) process.stdout.write(output + '\n');
8
+ process.exit(0);
9
+ } catch (error) {
10
+ if (error?.name === 'ExitPromptError') process.exit(0);
11
+ Logger.error(error?.message ?? 'Command failed');
12
+ process.exit(1);
13
+ }
14
+ };
15
+ }
@@ -0,0 +1,11 @@
1
+ import { requireAuth } from './require-auth';
2
+ import { requireProject } from './require-project';
3
+ import { runCommand } from './run-command';
4
+
5
+ export function runProjectCommand<T = void>(fn: (options: T) => Promise<string | void>) {
6
+ return runCommand<T>(async (options) => {
7
+ await requireProject();
8
+ await requireAuth();
9
+ return fn(options);
10
+ });
11
+ }
@@ -1,3 +1,3 @@
1
1
  **Analysis DSL** — `+` Create · `-` Delete · `~` Update (operation prefixes, not markdown bullets).
2
2
  These appear on both change headers (`## + Object Foo`) and property lines (`+ name: String`, `~ price: Number`, `- email: String`).
3
- Rename: `~ OldName > NewName`. Metadata: `> key: value`. Description block: `:::...:::`.
3
+ Handler relations use `>>` as separator: `+ ObjectA >> ObjectB >> ObjectC`.
@@ -44,3 +44,15 @@ we-scrum get-next-task --identificationNumber <identificationNumber>
44
44
  ```
45
45
 
46
46
  Repeat until all tasks are complete.
47
+
48
+ ---
49
+
50
+ ## Developer context
51
+
52
+ A TypeScript watcher is already running in parallel — compiled output is always up to date. Do not trigger manual application builds.
53
+
54
+ Before committing any task, run ESLint on every modified file and fix all reported violations:
55
+
56
+ ```bash
57
+ npx eslint <file1> <file2> ...
58
+ ```
package/tsconfig.json CHANGED
@@ -1,10 +1,17 @@
1
1
  {
2
2
  "compilerOptions": {
3
+ "strict": false,
4
+ "strictNullChecks": false,
5
+ "noImplicitThis": true,
6
+ "alwaysStrict": true,
7
+ "strictBindCallApply": true,
8
+ "strictFunctionTypes": true,
9
+ "noImplicitAny": true,
3
10
  "module": "commonjs",
4
11
  "moduleResolution": "bundler",
5
- "target": "es2021",
12
+ "target": "es2023",
13
+ "lib": ["ES2023"],
6
14
  "outDir": "./dist",
7
- "baseUrl": "src",
8
15
  "sourceMap": true,
9
16
  "declaration": false,
10
17
  "emitDecoratorMetadata": true,
@@ -13,7 +20,8 @@
13
20
  "skipLibCheck": true,
14
21
  "resolveJsonModule": true,
15
22
  "paths": {
16
- "@helpers": ["helpers"]
23
+ "@helpers": ["./src/helpers"],
24
+ "@utils": ["./src/utils"]
17
25
  },
18
26
  "forceConsistentCasingInFileNames": true
19
27
  }