@braingrid/cli 0.1.0 → 0.1.2

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.
Files changed (118) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +26 -24
  3. package/dist/cli.js +37 -17
  4. package/dist/cli.js.map +1 -1
  5. package/dist/handlers/agent.handlers.d.ts +12 -0
  6. package/dist/handlers/agent.handlers.d.ts.map +1 -0
  7. package/dist/handlers/agent.handlers.js +130 -0
  8. package/dist/handlers/agent.handlers.js.map +1 -0
  9. package/dist/handlers/index.d.ts +1 -0
  10. package/dist/handlers/index.d.ts.map +1 -1
  11. package/dist/handlers/index.js +1 -0
  12. package/dist/handlers/index.js.map +1 -1
  13. package/dist/handlers/init.handlers.d.ts +0 -1
  14. package/dist/handlers/init.handlers.d.ts.map +1 -1
  15. package/dist/handlers/init.handlers.js +481 -43
  16. package/dist/handlers/init.handlers.js.map +1 -1
  17. package/dist/handlers/project.handlers.d.ts +2 -3
  18. package/dist/handlers/project.handlers.d.ts.map +1 -1
  19. package/dist/handlers/project.handlers.js +80 -71
  20. package/dist/handlers/project.handlers.js.map +1 -1
  21. package/dist/handlers/requirement.handlers.d.ts.map +1 -1
  22. package/dist/handlers/requirement.handlers.js +21 -20
  23. package/dist/handlers/requirement.handlers.js.map +1 -1
  24. package/dist/handlers/status.handlers.d.ts.map +1 -1
  25. package/dist/handlers/status.handlers.js +11 -8
  26. package/dist/handlers/status.handlers.js.map +1 -1
  27. package/dist/handlers/task.handlers.d.ts.map +1 -1
  28. package/dist/handlers/task.handlers.js +45 -26
  29. package/dist/handlers/task.handlers.js.map +1 -1
  30. package/dist/rpc/server.d.ts +2 -0
  31. package/dist/rpc/server.d.ts.map +1 -1
  32. package/dist/rpc/server.js +28 -11
  33. package/dist/rpc/server.js.map +1 -1
  34. package/dist/services/agent-service.d.ts +29 -0
  35. package/dist/services/agent-service.d.ts.map +1 -0
  36. package/dist/services/agent-service.js +273 -0
  37. package/dist/services/agent-service.js.map +1 -0
  38. package/dist/services/credential-store.d.ts.map +1 -1
  39. package/dist/services/credential-store.js +1 -0
  40. package/dist/services/credential-store.js.map +1 -1
  41. package/dist/services/internal/github-service.d.ts +67 -0
  42. package/dist/services/internal/github-service.d.ts.map +1 -0
  43. package/dist/services/internal/github-service.js +81 -0
  44. package/dist/services/internal/github-service.js.map +1 -0
  45. package/dist/services/internal/repository-service.d.ts +79 -0
  46. package/dist/services/internal/repository-service.d.ts.map +1 -0
  47. package/dist/services/internal/repository-service.js +88 -0
  48. package/dist/services/internal/repository-service.js.map +1 -0
  49. package/dist/types/github.d.ts +105 -0
  50. package/dist/types/github.d.ts.map +1 -0
  51. package/dist/types/github.js +6 -0
  52. package/dist/types/github.js.map +1 -0
  53. package/dist/types/project.d.ts +1 -0
  54. package/dist/types/project.d.ts.map +1 -1
  55. package/dist/utils/cli-tools.d.ts.map +1 -1
  56. package/dist/utils/cli-tools.js +9 -1
  57. package/dist/utils/cli-tools.js.map +1 -1
  58. package/dist/utils/config.d.ts +1 -0
  59. package/dist/utils/config.d.ts.map +1 -1
  60. package/dist/utils/config.js +18 -0
  61. package/dist/utils/config.js.map +1 -1
  62. package/dist/utils/gh-installer.d.ts +31 -0
  63. package/dist/utils/gh-installer.d.ts.map +1 -0
  64. package/dist/utils/gh-installer.js +296 -0
  65. package/dist/utils/gh-installer.js.map +1 -0
  66. package/dist/utils/git-installer.d.ts +31 -0
  67. package/dist/utils/git-installer.d.ts.map +1 -0
  68. package/dist/utils/git-installer.js +290 -0
  69. package/dist/utils/git-installer.js.map +1 -0
  70. package/dist/utils/git.d.ts +5 -0
  71. package/dist/utils/git.d.ts.map +1 -1
  72. package/dist/utils/git.js +13 -0
  73. package/dist/utils/git.js.map +1 -1
  74. package/dist/utils/github-repo.d.ts +43 -0
  75. package/dist/utils/github-repo.d.ts.map +1 -0
  76. package/dist/utils/github-repo.js +113 -0
  77. package/dist/utils/github-repo.js.map +1 -0
  78. package/dist/utils/id-normalization.d.ts +26 -0
  79. package/dist/utils/id-normalization.d.ts.map +1 -0
  80. package/dist/utils/id-normalization.js +45 -0
  81. package/dist/utils/id-normalization.js.map +1 -0
  82. package/dist/utils/local-store.d.ts +7 -7
  83. package/dist/utils/local-store.d.ts.map +1 -1
  84. package/dist/utils/local-store.js +29 -17
  85. package/dist/utils/local-store.js.map +1 -1
  86. package/dist/utils/package-manager-installer.d.ts +36 -0
  87. package/dist/utils/package-manager-installer.d.ts.map +1 -0
  88. package/dist/utils/package-manager-installer.js +106 -0
  89. package/dist/utils/package-manager-installer.js.map +1 -0
  90. package/dist/utils/projects.d.ts +23 -0
  91. package/dist/utils/projects.d.ts.map +1 -0
  92. package/dist/utils/projects.js +36 -0
  93. package/dist/utils/projects.js.map +1 -0
  94. package/dist/utils/repository-access.d.ts +89 -0
  95. package/dist/utils/repository-access.d.ts.map +1 -0
  96. package/dist/utils/repository-access.js +132 -0
  97. package/dist/utils/repository-access.js.map +1 -0
  98. package/dist/utils/requirements.d.ts +15 -0
  99. package/dist/utils/requirements.d.ts.map +1 -1
  100. package/dist/utils/requirements.js +32 -7
  101. package/dist/utils/requirements.js.map +1 -1
  102. package/dist/utils/spinner.d.ts +47 -0
  103. package/dist/utils/spinner.d.ts.map +1 -0
  104. package/dist/utils/spinner.js +101 -0
  105. package/dist/utils/spinner.js.map +1 -0
  106. package/dist/utils/tasks.d.ts +12 -0
  107. package/dist/utils/tasks.d.ts.map +1 -1
  108. package/dist/utils/tasks.js +31 -6
  109. package/dist/utils/tasks.js.map +1 -1
  110. package/dist/utils/workspace-manager.d.ts +65 -0
  111. package/dist/utils/workspace-manager.d.ts.map +1 -0
  112. package/dist/utils/workspace-manager.js +98 -0
  113. package/dist/utils/workspace-manager.js.map +1 -0
  114. package/package.json +4 -1
  115. package/dist/services/context-manager.d.ts +0 -170
  116. package/dist/services/context-manager.d.ts.map +0 -1
  117. package/dist/services/context-manager.js +0 -261
  118. package/dist/services/context-manager.js.map +0 -1
@@ -0,0 +1,23 @@
1
+ import { Project } from '../types/project.js';
2
+ /**
3
+ * Format a project with friendly PROJ-<number> format
4
+ * Note: short_id already contains "PROJ-123" format
5
+ */
6
+ export declare function formatProjectId(project: Project): string;
7
+ /**
8
+ * Normalize project ID from various formats to API-compatible format:
9
+ * - "PROJ-123" -> "PROJ-123" (already normalized)
10
+ * - "proj-123" -> "PROJ-123" (uppercase)
11
+ * - "PROJ 123" -> "PROJ-123" (add dash, uppercase)
12
+ * - "proj 123" -> "PROJ-123" (add dash, uppercase)
13
+ * - "123" -> "PROJ-123" (add prefix)
14
+ * - "uuid-string" -> "uuid-string" (UUID, return as-is)
15
+ *
16
+ * This function takes user input and returns the ID that should be used in API calls
17
+ */
18
+ export declare function parseProjectId(input: string): string;
19
+ /**
20
+ * Find a project by either short_id or UUID
21
+ */
22
+ export declare function findProjectById(projects: Project[], identifier: string): Project | undefined;
23
+ //# sourceMappingURL=projects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/utils/projects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAG9C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAExD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAW5F"}
@@ -0,0 +1,36 @@
1
+ import { normalizeId } from './id-normalization.js';
2
+ /**
3
+ * Format a project with friendly PROJ-<number> format
4
+ * Note: short_id already contains "PROJ-123" format
5
+ */
6
+ export function formatProjectId(project) {
7
+ return project.short_id;
8
+ }
9
+ /**
10
+ * Normalize project ID from various formats to API-compatible format:
11
+ * - "PROJ-123" -> "PROJ-123" (already normalized)
12
+ * - "proj-123" -> "PROJ-123" (uppercase)
13
+ * - "PROJ 123" -> "PROJ-123" (add dash, uppercase)
14
+ * - "proj 123" -> "PROJ-123" (add dash, uppercase)
15
+ * - "123" -> "PROJ-123" (add prefix)
16
+ * - "uuid-string" -> "uuid-string" (UUID, return as-is)
17
+ *
18
+ * This function takes user input and returns the ID that should be used in API calls
19
+ */
20
+ export function parseProjectId(input) {
21
+ return normalizeId('PROJ', input);
22
+ }
23
+ /**
24
+ * Find a project by either short_id or UUID
25
+ */
26
+ export function findProjectById(projects, identifier) {
27
+ const normalized = parseProjectId(identifier);
28
+ // Try to find by short_id first (handles PROJ-X format)
29
+ const byShortId = projects.find(proj => proj.short_id === normalized);
30
+ if (byShortId) {
31
+ return byShortId;
32
+ }
33
+ // Try to find by UUID
34
+ return projects.find(proj => proj.id === normalized);
35
+ }
36
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/utils/projects.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC/C,OAAO,OAAO,CAAC,QAAQ,CAAC;AACzB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC3C,OAAO,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAmB,EAAE,UAAkB;IACtE,MAAM,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAE9C,wDAAwD;IACxD,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC;IACtE,IAAI,SAAS,EAAE,CAAC;QACf,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,sBAAsB;IACtB,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Repository Access Utility
3
+ *
4
+ * Helpers for checking GitHub installation and repository access during project initialization.
5
+ * Handles detection of owner-specific installations and repository visibility in BrainGrid.
6
+ */
7
+ import { GitHubInstallation } from '../types/github.js';
8
+ import { RepositoryService } from '../services/internal/repository-service.js';
9
+ /**
10
+ * Find a GitHub installation for a specific repository owner
11
+ *
12
+ * @param owner - The GitHub repository owner (org or user)
13
+ * @param installations - List of GitHub installations to search
14
+ * @returns The matching installation or null if not found
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const installation = findInstallationForOwner('myorg', installations);
19
+ * if (installation) {
20
+ * console.log(`Found installation for ${installation.account_name}`);
21
+ * }
22
+ * ```
23
+ */
24
+ export declare function findInstallationForOwner(owner: string, installations: GitHubInstallation[]): GitHubInstallation | null;
25
+ /**
26
+ * Check if a repository is accessible in BrainGrid
27
+ *
28
+ * Queries the repository API to see if the specific owner/name combination
29
+ * is visible to the authenticated user's organization.
30
+ *
31
+ * @param repositoryService - Repository service instance
32
+ * @param owner - GitHub repository owner
33
+ * @param name - GitHub repository name
34
+ * @returns True if repository is accessible, false otherwise
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const hasAccess = await checkRepositoryAccess(repoService, 'myorg', 'myrepo');
39
+ * if (hasAccess) {
40
+ * console.log('Repository is accessible in BrainGrid');
41
+ * }
42
+ * ```
43
+ */
44
+ export declare function checkRepositoryAccess(repositoryService: RepositoryService, owner: string, name: string): Promise<boolean>;
45
+ /**
46
+ * Poll for repository access with timeout
47
+ *
48
+ * Continuously checks if a repository becomes accessible in BrainGrid.
49
+ * Useful when waiting for a user to grant access through the web UI.
50
+ *
51
+ * @param repositoryService - Repository service instance
52
+ * @param owner - GitHub repository owner
53
+ * @param name - GitHub repository name
54
+ * @param intervalMs - Polling interval in milliseconds (default: 3000)
55
+ * @param maxAttempts - Maximum number of polling attempts (default: 60 = 3 minutes at 3s interval)
56
+ * @returns Promise that resolves to true if access is granted, false if timeout
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * const granted = await pollForRepositoryAccess(repoService, 'myorg', 'myrepo');
61
+ * if (granted) {
62
+ * console.log('Access granted!');
63
+ * } else {
64
+ * console.log('Timeout - access not granted within 3 minutes');
65
+ * }
66
+ * ```
67
+ */
68
+ export declare function pollForRepositoryAccess(repositoryService: RepositoryService, owner: string, name: string, intervalMs?: number, maxAttempts?: number): Promise<boolean>;
69
+ /**
70
+ * Get repository ID from BrainGrid API
71
+ *
72
+ * Fetches the repository details to get its BrainGrid UUID, which is needed
73
+ * for linking repositories to projects.
74
+ *
75
+ * @param repositoryService - Repository service instance
76
+ * @param owner - GitHub repository owner
77
+ * @param name - GitHub repository name
78
+ * @returns Repository UUID or null if not found
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const repoId = await getRepositoryId(repoService, 'myorg', 'myrepo');
83
+ * if (repoId) {
84
+ * await projectService.createProject({ name: 'My Project', repository_ids: [repoId] });
85
+ * }
86
+ * ```
87
+ */
88
+ export declare function getRepositoryId(repositoryService: RepositoryService, owner: string, name: string): Promise<string | null>;
89
+ //# sourceMappingURL=repository-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-access.d.ts","sourceRoot":"","sources":["../../src/utils/repository-access.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAE/E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACvC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,kBAAkB,EAAE,GACjC,kBAAkB,GAAG,IAAI,CAK3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,qBAAqB,CAC1C,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,uBAAuB,CAC5C,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAAa,EACzB,WAAW,GAAE,MAAW,GACtB,OAAO,CAAC,OAAO,CAAC,CAelB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CACpC,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBxB"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Repository Access Utility
3
+ *
4
+ * Helpers for checking GitHub installation and repository access during project initialization.
5
+ * Handles detection of owner-specific installations and repository visibility in BrainGrid.
6
+ */
7
+ /**
8
+ * Find a GitHub installation for a specific repository owner
9
+ *
10
+ * @param owner - The GitHub repository owner (org or user)
11
+ * @param installations - List of GitHub installations to search
12
+ * @returns The matching installation or null if not found
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const installation = findInstallationForOwner('myorg', installations);
17
+ * if (installation) {
18
+ * console.log(`Found installation for ${installation.account_name}`);
19
+ * }
20
+ * ```
21
+ */
22
+ export function findInstallationForOwner(owner, installations) {
23
+ // Case-insensitive match on account_name
24
+ const normalizedOwner = owner.toLowerCase();
25
+ return installations.find(inst => inst.account_name.toLowerCase() === normalizedOwner) || null;
26
+ }
27
+ /**
28
+ * Check if a repository is accessible in BrainGrid
29
+ *
30
+ * Queries the repository API to see if the specific owner/name combination
31
+ * is visible to the authenticated user's organization.
32
+ *
33
+ * @param repositoryService - Repository service instance
34
+ * @param owner - GitHub repository owner
35
+ * @param name - GitHub repository name
36
+ * @returns True if repository is accessible, false otherwise
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * const hasAccess = await checkRepositoryAccess(repoService, 'myorg', 'myrepo');
41
+ * if (hasAccess) {
42
+ * console.log('Repository is accessible in BrainGrid');
43
+ * }
44
+ * ```
45
+ */
46
+ export async function checkRepositoryAccess(repositoryService, owner, name) {
47
+ try {
48
+ const response = await repositoryService.listRepositories({
49
+ owner,
50
+ name,
51
+ limit: 1,
52
+ });
53
+ // If we get results, the repository is accessible
54
+ return response.repositories.length > 0;
55
+ }
56
+ catch {
57
+ // If the API call fails, assume no access
58
+ return false;
59
+ }
60
+ }
61
+ /**
62
+ * Poll for repository access with timeout
63
+ *
64
+ * Continuously checks if a repository becomes accessible in BrainGrid.
65
+ * Useful when waiting for a user to grant access through the web UI.
66
+ *
67
+ * @param repositoryService - Repository service instance
68
+ * @param owner - GitHub repository owner
69
+ * @param name - GitHub repository name
70
+ * @param intervalMs - Polling interval in milliseconds (default: 3000)
71
+ * @param maxAttempts - Maximum number of polling attempts (default: 60 = 3 minutes at 3s interval)
72
+ * @returns Promise that resolves to true if access is granted, false if timeout
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const granted = await pollForRepositoryAccess(repoService, 'myorg', 'myrepo');
77
+ * if (granted) {
78
+ * console.log('Access granted!');
79
+ * } else {
80
+ * console.log('Timeout - access not granted within 3 minutes');
81
+ * }
82
+ * ```
83
+ */
84
+ export async function pollForRepositoryAccess(repositoryService, owner, name, intervalMs = 3000, maxAttempts = 60) {
85
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
86
+ const hasAccess = await checkRepositoryAccess(repositoryService, owner, name);
87
+ if (hasAccess) {
88
+ return true;
89
+ }
90
+ // Wait before next attempt (unless this was the last attempt)
91
+ if (attempt < maxAttempts - 1) {
92
+ await new Promise(resolve => setTimeout(resolve, intervalMs));
93
+ }
94
+ }
95
+ return false;
96
+ }
97
+ /**
98
+ * Get repository ID from BrainGrid API
99
+ *
100
+ * Fetches the repository details to get its BrainGrid UUID, which is needed
101
+ * for linking repositories to projects.
102
+ *
103
+ * @param repositoryService - Repository service instance
104
+ * @param owner - GitHub repository owner
105
+ * @param name - GitHub repository name
106
+ * @returns Repository UUID or null if not found
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const repoId = await getRepositoryId(repoService, 'myorg', 'myrepo');
111
+ * if (repoId) {
112
+ * await projectService.createProject({ name: 'My Project', repository_ids: [repoId] });
113
+ * }
114
+ * ```
115
+ */
116
+ export async function getRepositoryId(repositoryService, owner, name) {
117
+ try {
118
+ const response = await repositoryService.listRepositories({
119
+ owner,
120
+ name,
121
+ limit: 1,
122
+ });
123
+ if (response.repositories.length === 0) {
124
+ return null;
125
+ }
126
+ return response.repositories[0].id;
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ //# sourceMappingURL=repository-access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-access.js","sourceRoot":"","sources":["../../src/utils/repository-access.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,wBAAwB,CACvC,KAAa,EACb,aAAmC;IAEnC,yCAAyC;IACzC,MAAM,eAAe,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAE5C,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,eAAe,CAAC,IAAI,IAAI,CAAC;AAChG,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,iBAAoC,EACpC,KAAa,EACb,IAAY;IAEZ,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,gBAAgB,CAAC;YACzD,KAAK;YACL,IAAI;YACJ,KAAK,EAAE,CAAC;SACR,CAAC,CAAC;QAEH,kDAAkD;QAClD,OAAO,QAAQ,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACR,0CAA0C;QAC1C,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC5C,iBAAoC,EACpC,KAAa,EACb,IAAY,EACZ,aAAqB,IAAI,EACzB,cAAsB,EAAE;IAExB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,iBAAiB,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAE9E,IAAI,SAAS,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACb,CAAC;QAED,8DAA8D;QAC9D,IAAI,OAAO,GAAG,WAAW,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;QAC/D,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,iBAAoC,EACpC,KAAa,EACb,IAAY;IAEZ,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,gBAAgB,CAAC;YACzD,KAAK;YACL,IAAI;YACJ,KAAK,EAAE,CAAC;SACR,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxC,OAAO,IAAI,CAAC;QACb,CAAC;QAED,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC"}
@@ -4,9 +4,24 @@ import { Requirement } from '../types/requirement.js';
4
4
  * Note: short_id already contains "REQ-123" format
5
5
  */
6
6
  export declare function formatRequirementId(requirement: Requirement): string;
7
+ /**
8
+ * Normalize requirement ID from various formats to API-compatible format:
9
+ * - "REQ-123" -> "REQ-123" (already normalized)
10
+ * - "req-123" -> "REQ-123" (uppercase)
11
+ * - "REQ 123" -> "REQ-123" (add dash, uppercase)
12
+ * - "req 123" -> "REQ-123" (add dash, uppercase)
13
+ * - "123" -> "REQ-123" (add prefix)
14
+ * - "uuid-string" -> "uuid-string" (UUID, return as-is)
15
+ *
16
+ * This function takes user input and returns the ID that should be used in API calls
17
+ */
18
+ export declare function normalizeRequirementId(input: string): string;
7
19
  /**
8
20
  * Parse a requirement ID from various formats:
9
21
  * - "REQ-123" -> { type: 'req_id', value: 123 }
22
+ * - "req-123" -> { type: 'req_id', value: 123 }
23
+ * - "REQ 123" -> { type: 'req_id', value: 123 }
24
+ * - "req 123" -> { type: 'req_id', value: 123 }
10
25
  * - "123" -> { type: 'req_id', value: 123 }
11
26
  * - "uuid-string" -> { type: 'uuid', value: 'uuid-string' }
12
27
  */
@@ -1 +1 @@
1
- {"version":3,"file":"requirements.d.ts","sourceRoot":"","sources":["../../src/utils/requirements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAEpE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;IAClD,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CAeA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAClC,YAAY,EAAE,WAAW,EAAE,EAC3B,UAAU,EAAE,MAAM,GAChB,WAAW,GAAG,SAAS,CAQzB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAS7E"}
1
+ {"version":3,"file":"requirements.d.ts","sourceRoot":"","sources":["../../src/utils/requirements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAEpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;IAClD,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CAwBA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAClC,YAAY,EAAE,WAAW,EAAE,EAC3B,UAAU,EAAE,MAAM,GAChB,WAAW,GAAG,SAAS,CAQzB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAS7E"}
@@ -1,3 +1,4 @@
1
+ import { normalizeId } from './id-normalization.js';
1
2
  /**
2
3
  * Format a requirement with friendly REQ-<number> format
3
4
  * Note: short_id already contains "REQ-123" format
@@ -5,25 +6,49 @@
5
6
  export function formatRequirementId(requirement) {
6
7
  return requirement.short_id || `REQ-${requirement.req_id || 0}`;
7
8
  }
9
+ /**
10
+ * Normalize requirement ID from various formats to API-compatible format:
11
+ * - "REQ-123" -> "REQ-123" (already normalized)
12
+ * - "req-123" -> "REQ-123" (uppercase)
13
+ * - "REQ 123" -> "REQ-123" (add dash, uppercase)
14
+ * - "req 123" -> "REQ-123" (add dash, uppercase)
15
+ * - "123" -> "REQ-123" (add prefix)
16
+ * - "uuid-string" -> "uuid-string" (UUID, return as-is)
17
+ *
18
+ * This function takes user input and returns the ID that should be used in API calls
19
+ */
20
+ export function normalizeRequirementId(input) {
21
+ return normalizeId('REQ', input);
22
+ }
8
23
  /**
9
24
  * Parse a requirement ID from various formats:
10
25
  * - "REQ-123" -> { type: 'req_id', value: 123 }
26
+ * - "req-123" -> { type: 'req_id', value: 123 }
27
+ * - "REQ 123" -> { type: 'req_id', value: 123 }
28
+ * - "req 123" -> { type: 'req_id', value: 123 }
11
29
  * - "123" -> { type: 'req_id', value: 123 }
12
30
  * - "uuid-string" -> { type: 'uuid', value: 'uuid-string' }
13
31
  */
14
32
  export function parseRequirementId(input) {
15
- // Handle REQ-123 format
16
- const reqMatch = input.match(/^REQ-(\d+)$/i);
17
- if (reqMatch) {
18
- return { type: 'req_id', value: parseInt(reqMatch[1], 10) };
33
+ // Trim whitespace
34
+ const trimmed = input.trim();
35
+ // Handle "REQ-123" or "req-123" format (with dash)
36
+ const reqDashMatch = trimmed.match(/^REQ-(\d+)$/i);
37
+ if (reqDashMatch) {
38
+ return { type: 'req_id', value: parseInt(reqDashMatch[1], 10) };
39
+ }
40
+ // Handle "REQ 123" or "req 123" format (with space)
41
+ const reqSpaceMatch = trimmed.match(/^REQ\s+(\d+)$/i);
42
+ if (reqSpaceMatch) {
43
+ return { type: 'req_id', value: parseInt(reqSpaceMatch[1], 10) };
19
44
  }
20
45
  // Handle plain number format
21
- const numberMatch = input.match(/^\d+$/);
46
+ const numberMatch = trimmed.match(/^\d+$/);
22
47
  if (numberMatch) {
23
- return { type: 'req_id', value: parseInt(input, 10) };
48
+ return { type: 'req_id', value: parseInt(trimmed, 10) };
24
49
  }
25
50
  // Assume it's a UUID
26
- return { type: 'uuid', value: input };
51
+ return { type: 'uuid', value: trimmed };
27
52
  }
28
53
  /**
29
54
  * Find a requirement by either req_id or UUID
@@ -1 +1 @@
1
- {"version":3,"file":"requirements.js","sourceRoot":"","sources":["../../src/utils/requirements.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAAwB;IAC3D,OAAO,WAAW,CAAC,QAAQ,IAAI,OAAO,WAAW,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;AACjE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAI/C,wBAAwB;IACxB,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC7C,IAAI,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAC7D,CAAC;IAED,6BAA6B;IAC7B,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC;IAED,qBAAqB;IACrB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAClC,YAA2B,EAC3B,UAAkB;IAElB,MAAM,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE9C,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IACzE,CAAC;SAAM,CAAC;QACP,OAAO,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1D,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,UAAkB;IAC3D,MAAM,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE9C,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,KAAe,EAAE,CAAC;IACvC,CAAC;SAAM,CAAC;QACP,+CAA+C;QAC/C,OAAO,EAAE,CAAC;IACX,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"requirements.js","sourceRoot":"","sources":["../../src/utils/requirements.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAAwB;IAC3D,OAAO,WAAW,CAAC,QAAQ,IAAI,OAAO,WAAW,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;AACjE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAa;IACnD,OAAO,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAI/C,kBAAkB;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAE7B,mDAAmD;IACnD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACnD,IAAI,YAAY,EAAE,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACjE,CAAC;IAED,oDAAoD;IACpD,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACtD,IAAI,aAAa,EAAE,CAAC;QACnB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAClE,CAAC;IAED,6BAA6B;IAC7B,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;IACzD,CAAC;IAED,qBAAqB;IACrB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAClC,YAA2B,EAC3B,UAAkB;IAElB,MAAM,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE9C,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IACzE,CAAC;SAAM,CAAC;QACP,OAAO,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1D,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,UAAkB;IAC3D,MAAM,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE9C,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,KAAe,EAAE,CAAC;IACvC,CAAC;SAAM,CAAC;QACP,+CAA+C;QAC/C,OAAO,EAAE,CAAC;IACX,CAAC;AACF,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Spinner Utility
3
+ *
4
+ * Provides a simple polling spinner for async operations with user feedback.
5
+ * Shows an animated spinner while waiting for a condition to become true.
6
+ */
7
+ /**
8
+ * Wait for a condition with animated spinner
9
+ *
10
+ * Polls an async check function and displays a spinner while waiting.
11
+ * Clears the spinner on completion or timeout.
12
+ *
13
+ * @param message - Message to display next to spinner
14
+ * @param checkFn - Async function that returns true when condition is met
15
+ * @param intervalMs - Polling interval in milliseconds (default: 3000)
16
+ * @param maxAttempts - Maximum number of attempts (default: 60 = 3 minutes at 3s interval)
17
+ * @returns Promise that resolves to true if condition met, false if timeout
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const success = await waitWithSpinner(
22
+ * 'Waiting for repository access',
23
+ * async () => await checkAccess(),
24
+ * 3000,
25
+ * 60
26
+ * );
27
+ * ```
28
+ */
29
+ export declare function waitWithSpinner(message: string, checkFn: () => Promise<boolean>, intervalMs?: number, maxAttempts?: number): Promise<boolean>;
30
+ /**
31
+ * Simple one-time spinner display
32
+ *
33
+ * Shows a spinner without polling. Useful for operations that manage their own timing.
34
+ * Returns a cleanup function to clear the spinner.
35
+ *
36
+ * @param message - Message to display
37
+ * @returns Cleanup function to stop and clear the spinner
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const stop = showSpinner('Loading');
42
+ * await doWork();
43
+ * stop();
44
+ * ```
45
+ */
46
+ export declare function showSpinner(message: string): () => void;
47
+ //# sourceMappingURL=spinner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/utils/spinner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAC/B,UAAU,GAAE,MAAa,EACzB,WAAW,GAAE,MAAW,GACtB,OAAO,CAAC,OAAO,CAAC,CA4ClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,IAAI,CAYvD"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Spinner Utility
3
+ *
4
+ * Provides a simple polling spinner for async operations with user feedback.
5
+ * Shows an animated spinner while waiting for a condition to become true.
6
+ */
7
+ import chalk from 'chalk';
8
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
9
+ /**
10
+ * Wait for a condition with animated spinner
11
+ *
12
+ * Polls an async check function and displays a spinner while waiting.
13
+ * Clears the spinner on completion or timeout.
14
+ *
15
+ * @param message - Message to display next to spinner
16
+ * @param checkFn - Async function that returns true when condition is met
17
+ * @param intervalMs - Polling interval in milliseconds (default: 3000)
18
+ * @param maxAttempts - Maximum number of attempts (default: 60 = 3 minutes at 3s interval)
19
+ * @returns Promise that resolves to true if condition met, false if timeout
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const success = await waitWithSpinner(
24
+ * 'Waiting for repository access',
25
+ * async () => await checkAccess(),
26
+ * 3000,
27
+ * 60
28
+ * );
29
+ * ```
30
+ */
31
+ export async function waitWithSpinner(message, checkFn, intervalMs = 3000, maxAttempts = 60) {
32
+ let frameIndex = 0;
33
+ let spinnerInterval = null;
34
+ // Function to update spinner
35
+ const updateSpinner = () => {
36
+ const frame = SPINNER_FRAMES[frameIndex];
37
+ frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
38
+ // Clear current line and write new spinner
39
+ process.stdout.write(`\r${chalk.cyan(frame)} ${message}...`);
40
+ };
41
+ // Start spinner animation (update every 100ms for smooth animation)
42
+ spinnerInterval = setInterval(updateSpinner, 100);
43
+ try {
44
+ // Poll with the specified interval
45
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
46
+ const result = await checkFn();
47
+ if (result) {
48
+ // Success! Clear spinner and return
49
+ if (spinnerInterval)
50
+ clearInterval(spinnerInterval);
51
+ process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r'); // Clear line
52
+ return true;
53
+ }
54
+ // Wait before next attempt (unless this was the last attempt)
55
+ if (attempt < maxAttempts - 1) {
56
+ await new Promise(resolve => setTimeout(resolve, intervalMs));
57
+ }
58
+ }
59
+ // Timeout - clear spinner and return false
60
+ if (spinnerInterval)
61
+ clearInterval(spinnerInterval);
62
+ process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r'); // Clear line
63
+ return false;
64
+ }
65
+ catch (error) {
66
+ // Error during polling - clean up and rethrow
67
+ if (spinnerInterval)
68
+ clearInterval(spinnerInterval);
69
+ process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r'); // Clear line
70
+ throw error;
71
+ }
72
+ }
73
+ /**
74
+ * Simple one-time spinner display
75
+ *
76
+ * Shows a spinner without polling. Useful for operations that manage their own timing.
77
+ * Returns a cleanup function to clear the spinner.
78
+ *
79
+ * @param message - Message to display
80
+ * @returns Cleanup function to stop and clear the spinner
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const stop = showSpinner('Loading');
85
+ * await doWork();
86
+ * stop();
87
+ * ```
88
+ */
89
+ export function showSpinner(message) {
90
+ let frameIndex = 0;
91
+ const interval = setInterval(() => {
92
+ const frame = SPINNER_FRAMES[frameIndex];
93
+ frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
94
+ process.stdout.write(`\r${chalk.cyan(frame)} ${message}...`);
95
+ }, 100);
96
+ return () => {
97
+ clearInterval(interval);
98
+ process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r');
99
+ };
100
+ }
101
+ //# sourceMappingURL=spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/utils/spinner.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,17 @@
1
1
  import { RequirementTask as Task } from '../types/requirement.js';
2
2
  export declare function formatTaskId(task: Task): string;
3
+ /**
4
+ * Normalize task ID from various formats to API-compatible format:
5
+ * - "TASK-123" -> "TASK-123" (already normalized)
6
+ * - "task-123" -> "TASK-123" (uppercase)
7
+ * - "TASK 123" -> "TASK-123" (add dash, uppercase)
8
+ * - "task 123" -> "TASK-123" (add dash, uppercase)
9
+ * - "123" -> "TASK-123" (add prefix)
10
+ * - "uuid-string" -> "uuid-string" (UUID, return as-is)
11
+ *
12
+ * This function takes user input and returns the ID that should be used in API calls
13
+ */
14
+ export declare function normalizeTaskId(input: string): string;
3
15
  export declare function parseTaskId(input: string): {
4
16
  type: 'number' | 'uuid';
5
17
  value: string | number;
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/utils/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAElE,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE/C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAY9F;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAQ3E;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAaxD"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/utils/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAGlE,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAwB9F;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAQ3E;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAaxD"}
@@ -1,16 +1,41 @@
1
+ import { normalizeId } from './id-normalization.js';
1
2
  export function formatTaskId(task) {
2
3
  return `TASK-${task.number}`;
3
4
  }
5
+ /**
6
+ * Normalize task ID from various formats to API-compatible format:
7
+ * - "TASK-123" -> "TASK-123" (already normalized)
8
+ * - "task-123" -> "TASK-123" (uppercase)
9
+ * - "TASK 123" -> "TASK-123" (add dash, uppercase)
10
+ * - "task 123" -> "TASK-123" (add dash, uppercase)
11
+ * - "123" -> "TASK-123" (add prefix)
12
+ * - "uuid-string" -> "uuid-string" (UUID, return as-is)
13
+ *
14
+ * This function takes user input and returns the ID that should be used in API calls
15
+ */
16
+ export function normalizeTaskId(input) {
17
+ return normalizeId('TASK', input);
18
+ }
4
19
  export function parseTaskId(input) {
5
- const taskMatch = input.match(/^TASK-(\d+)$/i);
6
- if (taskMatch) {
7
- return { type: 'number', value: parseInt(taskMatch[1], 10) };
20
+ // Trim whitespace
21
+ const trimmed = input.trim();
22
+ // Handle "TASK-123" or "task-123" format (with dash)
23
+ const taskDashMatch = trimmed.match(/^TASK-(\d+)$/i);
24
+ if (taskDashMatch) {
25
+ return { type: 'number', value: parseInt(taskDashMatch[1], 10) };
26
+ }
27
+ // Handle "TASK 123" or "task 123" format (with space)
28
+ const taskSpaceMatch = trimmed.match(/^TASK\s+(\d+)$/i);
29
+ if (taskSpaceMatch) {
30
+ return { type: 'number', value: parseInt(taskSpaceMatch[1], 10) };
8
31
  }
9
- const numberMatch = input.match(/^\d+$/);
32
+ // Handle plain number format
33
+ const numberMatch = trimmed.match(/^\d+$/);
10
34
  if (numberMatch) {
11
- return { type: 'number', value: parseInt(input, 10) };
35
+ return { type: 'number', value: parseInt(trimmed, 10) };
12
36
  }
13
- return { type: 'uuid', value: input };
37
+ // Assume it's a UUID
38
+ return { type: 'uuid', value: trimmed };
14
39
  }
15
40
  export function findTaskById(tasks, identifier) {
16
41
  const parsed = parseTaskId(identifier);
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/utils/tasks.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAAC,IAAU;IACtC,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC/C,IAAI,SAAS,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAC9D,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,UAAkB;IAC7D,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IAEvC,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IACjE,CAAC;SAAM,CAAC;QACP,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IAC7D,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC/C,QAAQ,MAAM,EAAE,CAAC;QAChB,KAAK,SAAS;YACb,OAAO,IAAI,CAAC;QACb,KAAK,aAAa;YACjB,OAAO,IAAI,CAAC;QACb,KAAK,WAAW;YACf,OAAO,GAAG,CAAC;QACZ,KAAK,WAAW;YACf,OAAO,GAAG,CAAC;QACZ;YACC,OAAO,IAAI,CAAC;IACd,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/utils/tasks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,UAAU,YAAY,CAAC,IAAU;IACtC,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC5C,OAAO,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,kBAAkB;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAE7B,qDAAqD;IACrD,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACrD,IAAI,aAAa,EAAE,CAAC;QACnB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAClE,CAAC;IAED,sDAAsD;IACtD,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACxD,IAAI,cAAc,EAAE,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACnE,CAAC;IAED,6BAA6B;IAC7B,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;IACzD,CAAC;IAED,qBAAqB;IACrB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,UAAkB;IAC7D,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IAEvC,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IACjE,CAAC;SAAM,CAAC;QACP,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IAC7D,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC/C,QAAQ,MAAM,EAAE,CAAC;QAChB,KAAK,SAAS;YACb,OAAO,IAAI,CAAC;QACb,KAAK,aAAa;YACjB,OAAO,IAAI,CAAC;QACb,KAAK,WAAW;YACf,OAAO,GAAG,CAAC;QACZ,KAAK,WAAW;YACf,OAAO,GAAG,CAAC;QACZ;YACC,OAAO,IAAI,CAAC;IACd,CAAC;AACF,CAAC"}