prjct-cli 0.45.0 → 0.45.3

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 (207) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/bin/prjct.ts +117 -10
  3. package/core/__tests__/agentic/memory-system.test.ts +39 -26
  4. package/core/__tests__/agentic/plan-mode.test.ts +64 -46
  5. package/core/__tests__/agentic/prompt-builder.test.ts +14 -14
  6. package/core/__tests__/services/project-index.test.ts +353 -0
  7. package/core/__tests__/types/fs.test.ts +3 -3
  8. package/core/__tests__/utils/date-helper.test.ts +10 -10
  9. package/core/__tests__/utils/output.test.ts +9 -6
  10. package/core/__tests__/utils/project-commands.test.ts +5 -6
  11. package/core/agentic/agent-router.ts +9 -10
  12. package/core/agentic/chain-of-thought.ts +16 -4
  13. package/core/agentic/command-executor.ts +66 -40
  14. package/core/agentic/context-builder.ts +8 -5
  15. package/core/agentic/ground-truth.ts +15 -9
  16. package/core/agentic/index.ts +145 -152
  17. package/core/agentic/loop-detector.ts +40 -11
  18. package/core/agentic/memory-system.ts +98 -35
  19. package/core/agentic/orchestrator-executor.ts +135 -71
  20. package/core/agentic/plan-mode.ts +46 -16
  21. package/core/agentic/prompt-builder.ts +108 -42
  22. package/core/agentic/services.ts +10 -9
  23. package/core/agentic/skill-loader.ts +9 -15
  24. package/core/agentic/smart-context.ts +129 -79
  25. package/core/agentic/template-executor.ts +13 -12
  26. package/core/agentic/template-loader.ts +7 -4
  27. package/core/agentic/tool-registry.ts +16 -13
  28. package/core/agents/index.ts +1 -1
  29. package/core/agents/performance.ts +10 -27
  30. package/core/ai-tools/formatters.ts +8 -6
  31. package/core/ai-tools/generator.ts +4 -4
  32. package/core/ai-tools/index.ts +1 -1
  33. package/core/ai-tools/registry.ts +21 -11
  34. package/core/bus/bus.ts +23 -16
  35. package/core/bus/index.ts +2 -2
  36. package/core/cli/linear.ts +3 -5
  37. package/core/cli/start.ts +28 -25
  38. package/core/commands/analysis.ts +58 -39
  39. package/core/commands/analytics.ts +52 -44
  40. package/core/commands/base.ts +15 -13
  41. package/core/commands/cleanup.ts +6 -13
  42. package/core/commands/command-data.ts +28 -4
  43. package/core/commands/commands.ts +57 -24
  44. package/core/commands/context.ts +4 -4
  45. package/core/commands/design.ts +3 -10
  46. package/core/commands/index.ts +5 -8
  47. package/core/commands/maintenance.ts +7 -4
  48. package/core/commands/planning.ts +179 -56
  49. package/core/commands/register.ts +13 -9
  50. package/core/commands/registry.ts +15 -14
  51. package/core/commands/setup.ts +26 -14
  52. package/core/commands/shipping.ts +11 -16
  53. package/core/commands/snapshots.ts +16 -32
  54. package/core/commands/uninstall.ts +541 -0
  55. package/core/commands/workflow.ts +24 -28
  56. package/core/constants/index.ts +10 -22
  57. package/core/context/generator.ts +82 -33
  58. package/core/context-tools/files-tool.ts +18 -19
  59. package/core/context-tools/imports-tool.ts +13 -33
  60. package/core/context-tools/index.ts +29 -54
  61. package/core/context-tools/recent-tool.ts +16 -22
  62. package/core/context-tools/signatures-tool.ts +17 -26
  63. package/core/context-tools/summary-tool.ts +20 -22
  64. package/core/context-tools/token-counter.ts +25 -20
  65. package/core/context-tools/types.ts +5 -5
  66. package/core/domain/agent-generator.ts +7 -5
  67. package/core/domain/agent-loader.ts +2 -2
  68. package/core/domain/analyzer.ts +19 -16
  69. package/core/domain/architecture-generator.ts +6 -3
  70. package/core/domain/context-estimator.ts +3 -4
  71. package/core/domain/snapshot-manager.ts +25 -22
  72. package/core/domain/task-stack.ts +24 -14
  73. package/core/errors.ts +1 -1
  74. package/core/events/events.ts +2 -4
  75. package/core/events/index.ts +1 -2
  76. package/core/index.ts +28 -16
  77. package/core/infrastructure/agent-detector.ts +3 -3
  78. package/core/infrastructure/ai-provider.ts +23 -20
  79. package/core/infrastructure/author-detector.ts +16 -10
  80. package/core/infrastructure/capability-installer.ts +2 -2
  81. package/core/infrastructure/claude-agent.ts +6 -6
  82. package/core/infrastructure/command-installer.ts +22 -17
  83. package/core/infrastructure/config-manager.ts +18 -14
  84. package/core/infrastructure/editors-config.ts +8 -4
  85. package/core/infrastructure/path-manager.ts +8 -6
  86. package/core/infrastructure/permission-manager.ts +20 -17
  87. package/core/infrastructure/setup.ts +42 -38
  88. package/core/infrastructure/update-checker.ts +5 -5
  89. package/core/integrations/issue-tracker/enricher.ts +8 -19
  90. package/core/integrations/issue-tracker/index.ts +2 -2
  91. package/core/integrations/issue-tracker/manager.ts +15 -15
  92. package/core/integrations/issue-tracker/types.ts +5 -22
  93. package/core/integrations/jira/client.ts +67 -59
  94. package/core/integrations/jira/index.ts +11 -14
  95. package/core/integrations/jira/mcp-adapter.ts +5 -10
  96. package/core/integrations/jira/service.ts +10 -10
  97. package/core/integrations/linear/client.ts +27 -18
  98. package/core/integrations/linear/index.ts +9 -12
  99. package/core/integrations/linear/service.ts +11 -11
  100. package/core/integrations/linear/sync.ts +8 -8
  101. package/core/outcomes/analyzer.ts +5 -18
  102. package/core/outcomes/index.ts +2 -2
  103. package/core/outcomes/recorder.ts +3 -3
  104. package/core/plugin/builtin/webhook.ts +19 -15
  105. package/core/plugin/hooks.ts +29 -21
  106. package/core/plugin/index.ts +7 -7
  107. package/core/plugin/loader.ts +19 -19
  108. package/core/plugin/registry.ts +12 -23
  109. package/core/schemas/agents.ts +1 -1
  110. package/core/schemas/analysis.ts +1 -1
  111. package/core/schemas/enriched-task.ts +62 -49
  112. package/core/schemas/ideas.ts +13 -13
  113. package/core/schemas/index.ts +17 -27
  114. package/core/schemas/issues.ts +40 -25
  115. package/core/schemas/metrics.ts +25 -25
  116. package/core/schemas/outcomes.ts +70 -62
  117. package/core/schemas/permissions.ts +15 -12
  118. package/core/schemas/prd.ts +27 -14
  119. package/core/schemas/project.ts +3 -3
  120. package/core/schemas/roadmap.ts +47 -34
  121. package/core/schemas/schemas.ts +3 -4
  122. package/core/schemas/shipped.ts +3 -3
  123. package/core/schemas/state.ts +43 -29
  124. package/core/server/index.ts +5 -6
  125. package/core/server/routes-extended.ts +68 -72
  126. package/core/server/routes.ts +3 -3
  127. package/core/server/server.ts +31 -26
  128. package/core/services/agent-generator.ts +237 -0
  129. package/core/services/agent-service.ts +2 -2
  130. package/core/services/breakdown-service.ts +2 -4
  131. package/core/services/context-generator.ts +299 -0
  132. package/core/services/context-selector.ts +420 -0
  133. package/core/services/doctor-service.ts +426 -0
  134. package/core/services/file-categorizer.ts +448 -0
  135. package/core/services/file-scorer.ts +270 -0
  136. package/core/services/git-analyzer.ts +267 -0
  137. package/core/services/index.ts +27 -10
  138. package/core/services/memory-service.ts +3 -4
  139. package/core/services/project-index.ts +911 -0
  140. package/core/services/project-service.ts +4 -4
  141. package/core/services/skill-installer.ts +14 -17
  142. package/core/services/skill-lock.ts +3 -3
  143. package/core/services/skill-service.ts +12 -6
  144. package/core/services/stack-detector.ts +245 -0
  145. package/core/services/sync-service.ts +87 -345
  146. package/core/services/watch-service.ts +294 -0
  147. package/core/session/compaction.ts +23 -31
  148. package/core/session/index.ts +11 -5
  149. package/core/session/log-migration.ts +3 -3
  150. package/core/session/metrics.ts +19 -14
  151. package/core/session/session-log-manager.ts +12 -17
  152. package/core/session/task-session-manager.ts +25 -25
  153. package/core/session/utils.ts +1 -1
  154. package/core/storage/ideas-storage.ts +41 -57
  155. package/core/storage/index-storage.ts +514 -0
  156. package/core/storage/index.ts +41 -17
  157. package/core/storage/metrics-storage.ts +39 -34
  158. package/core/storage/queue-storage.ts +35 -45
  159. package/core/storage/shipped-storage.ts +17 -20
  160. package/core/storage/state-storage.ts +50 -30
  161. package/core/storage/storage-manager.ts +6 -6
  162. package/core/storage/storage.ts +18 -15
  163. package/core/sync/auth-config.ts +3 -3
  164. package/core/sync/index.ts +13 -19
  165. package/core/sync/oauth-handler.ts +3 -3
  166. package/core/sync/sync-client.ts +4 -9
  167. package/core/sync/sync-manager.ts +12 -14
  168. package/core/types/commands.ts +42 -7
  169. package/core/types/index.ts +284 -305
  170. package/core/types/integrations.ts +3 -3
  171. package/core/types/storage.ts +14 -14
  172. package/core/types/utils.ts +3 -3
  173. package/core/utils/agent-stream.ts +3 -1
  174. package/core/utils/animations.ts +14 -11
  175. package/core/utils/branding.ts +7 -7
  176. package/core/utils/cache.ts +1 -3
  177. package/core/utils/collection-filters.ts +3 -15
  178. package/core/utils/date-helper.ts +2 -7
  179. package/core/utils/file-helper.ts +13 -8
  180. package/core/utils/jsonl-helper.ts +13 -10
  181. package/core/utils/keychain.ts +4 -8
  182. package/core/utils/logger.ts +1 -1
  183. package/core/utils/next-steps.ts +3 -3
  184. package/core/utils/output.ts +58 -11
  185. package/core/utils/project-commands.ts +6 -6
  186. package/core/utils/project-credentials.ts +5 -12
  187. package/core/utils/runtime.ts +2 -2
  188. package/core/utils/session-helper.ts +3 -4
  189. package/core/utils/version.ts +3 -3
  190. package/core/wizard/index.ts +13 -0
  191. package/core/wizard/onboarding.ts +633 -0
  192. package/core/workflow/state-machine.ts +7 -7
  193. package/dist/bin/prjct.mjs +18755 -15574
  194. package/dist/core/infrastructure/command-installer.js +86 -79
  195. package/dist/core/infrastructure/editors-config.js +6 -6
  196. package/dist/core/infrastructure/setup.js +246 -225
  197. package/dist/core/utils/version.js +9 -9
  198. package/package.json +11 -12
  199. package/scripts/build.js +3 -3
  200. package/scripts/postinstall.js +2 -2
  201. package/templates/mcp-config.json +6 -1
  202. package/templates/permissions/permissive.jsonc +1 -1
  203. package/templates/permissions/strict.jsonc +5 -9
  204. package/templates/global/docs/agents.md +0 -88
  205. package/templates/global/docs/architecture.md +0 -103
  206. package/templates/global/docs/commands.md +0 -96
  207. package/templates/global/docs/validation.md +0 -95
@@ -10,10 +10,10 @@
10
10
  * @version 0.6.0 - Fully agentic refactor
11
11
  */
12
12
 
13
- import fs from 'fs/promises'
14
- import path from 'path'
15
- import { promisify } from 'util'
16
- import { exec as execCallback } from 'child_process'
13
+ import { exec as execCallback } from 'node:child_process'
14
+ import fs from 'node:fs/promises'
15
+ import path from 'node:path'
16
+ import { promisify } from 'node:util'
17
17
  import { isNotFoundError } from '../types/fs'
18
18
 
19
19
  const exec = promisify(execCallback)
@@ -209,11 +209,11 @@ class CodebaseAnalyzer {
209
209
  .forEach((line) => {
210
210
  const match = line.trim().match(/^\s*(\d+)\s+(\.\w+)$/)
211
211
  if (match) {
212
- extensions[match[2]] = parseInt(match[1])
212
+ extensions[match[2]] = parseInt(match[1], 10)
213
213
  }
214
214
  })
215
215
  return extensions
216
- } catch (error) {
216
+ } catch (_error) {
217
217
  // exec errors (find command not available, etc.) - return empty
218
218
  return {}
219
219
  }
@@ -278,7 +278,7 @@ class CodebaseAnalyzer {
278
278
  cwd: this.projectPath!,
279
279
  })
280
280
  return stdout
281
- } catch (error) {
281
+ } catch (_error) {
282
282
  // Git errors (not a repo, git not installed) - return empty
283
283
  return ''
284
284
  }
@@ -297,16 +297,19 @@ class CodebaseAnalyzer {
297
297
  cwd: this.projectPath!,
298
298
  })
299
299
 
300
- const { stdout: firstCommit } = await exec('git log --reverse --pretty=format:"%ar" | head -1', {
301
- cwd: this.projectPath!,
302
- })
300
+ const { stdout: firstCommit } = await exec(
301
+ 'git log --reverse --pretty=format:"%ar" | head -1',
302
+ {
303
+ cwd: this.projectPath!,
304
+ }
305
+ )
303
306
 
304
307
  return {
305
- totalCommits: parseInt(totalCommits.trim()) || 0,
306
- contributors: parseInt(contributors.trim()) || 0,
308
+ totalCommits: parseInt(totalCommits.trim(), 10) || 0,
309
+ contributors: parseInt(contributors.trim(), 10) || 0,
307
310
  age: firstCommit.trim() || 'unknown',
308
311
  }
309
- } catch (error) {
312
+ } catch (_error) {
310
313
  // Git errors (not a repo, git not installed) - return defaults
311
314
  return {
312
315
  totalCommits: 0,
@@ -325,8 +328,8 @@ class CodebaseAnalyzer {
325
328
  'find . -type f ! -path "*/node_modules/*" ! -path "*/.git/*" ! -path "*/dist/*" | wc -l',
326
329
  { cwd: this.projectPath! }
327
330
  )
328
- return parseInt(stdout.trim()) || 0
329
- } catch (error) {
331
+ return parseInt(stdout.trim(), 10) || 0
332
+ } catch (_error) {
330
333
  // exec errors (find command not available, etc.) - return 0
331
334
  return 0
332
335
  }
@@ -372,7 +375,7 @@ class CodebaseAnalyzer {
372
375
  { cwd: this.projectPath! }
373
376
  )
374
377
  return stdout.trim().split('\n').filter(Boolean)
375
- } catch (error) {
378
+ } catch (_error) {
376
379
  // exec errors (find command not available, etc.) - return empty
377
380
  return []
378
381
  }
@@ -4,8 +4,8 @@
4
4
  * This file only provides structure - real content from Claude
5
5
  */
6
6
 
7
- import path from 'path'
8
- import fs from 'fs/promises'
7
+ import fs from 'node:fs/promises'
8
+ import path from 'node:path'
9
9
 
10
10
  interface ArchitectureContext {
11
11
  [key: string]: unknown
@@ -40,7 +40,10 @@ class ArchitectureGenerator {
40
40
  * Generate architecture skeleton
41
41
  * AGENTIC: Claude fills in content using templates
42
42
  */
43
- async generateArchitecture(idea: string, context: ArchitectureContext = {}): Promise<Architecture> {
43
+ async generateArchitecture(
44
+ idea: string,
45
+ _context: ArchitectureContext = {}
46
+ ): Promise<Architecture> {
44
47
  // Return skeleton - Claude generates actual content via templates
45
48
  return {
46
49
  id: `arch-${Date.now()}`,
@@ -7,7 +7,6 @@
7
7
  * @version 1.0.0
8
8
  */
9
9
 
10
- import path from 'path'
11
10
  import { glob } from 'glob'
12
11
  import log from '../utils/logger'
13
12
 
@@ -79,7 +78,7 @@ class ContextEstimator {
79
78
  * 100% AGENTIC: Uses REAL project data, not hardcoded patterns.
80
79
  * No domain-specific assumptions or language→extension mapping.
81
80
  */
82
- getPatternsForDomain(domain: string, projectData: ProjectTech): FilePatterns {
81
+ getPatternsForDomain(_domain: string, projectData: ProjectTech): FilePatterns {
83
82
  const patterns: FilePatterns = {
84
83
  include: [],
85
84
  extensions: [],
@@ -87,7 +86,7 @@ class ContextEstimator {
87
86
  }
88
87
 
89
88
  // Use REAL extensions from project (if provided in projectData)
90
- if (projectData && projectData.extensions) {
89
+ if (projectData?.extensions) {
91
90
  // projectData.extensions is {'.js': 45, '.ts': 23, ...}
92
91
  patterns.extensions = Object.keys(projectData.extensions)
93
92
  .filter((ext) => ext.startsWith('.'))
@@ -95,7 +94,7 @@ class ContextEstimator {
95
94
  }
96
95
 
97
96
  // Use REAL directories from project (if provided in projectData)
98
- if (projectData && projectData.directories) {
97
+ if (projectData?.directories) {
99
98
  patterns.include = projectData.directories.filter((dir) => !patterns.exclude.includes(dir))
100
99
  }
101
100
 
@@ -9,13 +9,13 @@
9
9
  * @version 1.0.0
10
10
  */
11
11
 
12
- import fs from 'fs/promises'
13
- import path from 'path'
14
- import { exec } from 'child_process'
15
- import { promisify } from 'util'
16
- import pathManager from '../infrastructure/path-manager'
17
- import configManager from '../infrastructure/config-manager'
12
+ import { exec } from 'node:child_process'
13
+ import fs from 'node:fs/promises'
14
+ import path from 'node:path'
15
+ import { promisify } from 'node:util'
18
16
  import { emit } from '../bus'
17
+ import configManager from '../infrastructure/config-manager'
18
+ import pathManager from '../infrastructure/path-manager'
19
19
  import { isNotFoundError } from '../types/fs'
20
20
 
21
21
  const execAsync = promisify(exec)
@@ -100,7 +100,9 @@ class SnapshotManager {
100
100
  await execAsync(`git config user.name "prjct-snapshots"`, { cwd: this.snapshotDir! })
101
101
 
102
102
  // Create initial empty commit
103
- await execAsync(`git commit --allow-empty -m "init: snapshot system"`, { cwd: this.snapshotDir! })
103
+ await execAsync(`git commit --allow-empty -m "init: snapshot system"`, {
104
+ cwd: this.snapshotDir!,
105
+ })
104
106
  }
105
107
 
106
108
  /**
@@ -236,9 +238,12 @@ class SnapshotManager {
236
238
  if (!this.initialized) await this.initialize()
237
239
 
238
240
  // Get files changed in that commit
239
- const { stdout: filesOutput } = await execAsync(`git diff-tree --no-commit-id --name-only -r ${hash}`, {
240
- cwd: this.snapshotDir!,
241
- })
241
+ const { stdout: filesOutput } = await execAsync(
242
+ `git diff-tree --no-commit-id --name-only -r ${hash}`,
243
+ {
244
+ cwd: this.snapshotDir!,
245
+ }
246
+ )
242
247
 
243
248
  const files = filesOutput.split('\n').filter(Boolean)
244
249
 
@@ -337,11 +342,10 @@ class SnapshotManager {
337
342
  */
338
343
  async logSnapshot(snapshot: SnapshotInfo): Promise<void> {
339
344
  const manifestPath = path.join(this.snapshotDir!, 'manifest.jsonl')
340
- const entry =
341
- JSON.stringify({
342
- type: 'snapshot',
343
- ...snapshot,
344
- }) + '\n'
345
+ const entry = `${JSON.stringify({
346
+ type: 'snapshot',
347
+ ...snapshot,
348
+ })}\n`
345
349
 
346
350
  await fs.appendFile(manifestPath, entry)
347
351
  }
@@ -351,13 +355,12 @@ class SnapshotManager {
351
355
  */
352
356
  async logRestore(hash: string, files: string[]): Promise<void> {
353
357
  const manifestPath = path.join(this.snapshotDir!, 'manifest.jsonl')
354
- const entry =
355
- JSON.stringify({
356
- type: 'restore',
357
- hash,
358
- files,
359
- timestamp: new Date().toISOString(),
360
- }) + '\n'
358
+ const entry = `${JSON.stringify({
359
+ type: 'restore',
360
+ hash,
361
+ files,
362
+ timestamp: new Date().toISOString(),
363
+ })}\n`
361
364
 
362
365
  await fs.appendFile(manifestPath, entry)
363
366
  }
@@ -3,23 +3,22 @@
3
3
  * Manages task breakdown and hierarchical task tracking.
4
4
  */
5
5
 
6
- import path from 'path'
7
- import fs from 'fs/promises'
8
- import { exec } from 'child_process'
9
- import { promisify } from 'util'
10
- import log from '../utils/logger'
11
- import { isNotFoundError } from '../types/fs'
6
+ import { exec } from 'node:child_process'
7
+ import fs from 'node:fs/promises'
8
+ import path from 'node:path'
9
+ import { promisify } from 'node:util'
12
10
  import type {
13
- TaskStackEntry,
14
11
  ParsedNowFile,
12
+ TaskStackEntry,
15
13
  TaskStackMigrationResult,
16
- TaskSwitchResult,
17
14
  TaskStackSummary,
15
+ TaskSwitchResult,
18
16
  } from '../types'
17
+ import { isNotFoundError } from '../types/fs'
18
+ import log from '../utils/logger'
19
19
 
20
20
  const execAsync = promisify(exec)
21
21
 
22
-
23
22
  // =============================================================================
24
23
  // Parser
25
24
  // =============================================================================
@@ -129,7 +128,7 @@ export async function ensureStackFile(stackPath: string): Promise<void> {
129
128
  */
130
129
  export async function appendToStack(stackPath: string, entry: TaskStackEntry): Promise<void> {
131
130
  await ensureStackFile(stackPath)
132
- const line = JSON.stringify(entry) + '\n'
131
+ const line = `${JSON.stringify(entry)}\n`
133
132
  await fs.appendFile(stackPath, line)
134
133
  }
135
134
 
@@ -162,14 +161,18 @@ export async function readStack(stackPath: string): Promise<TaskStackEntry[]> {
162
161
  * Write full stack to file
163
162
  */
164
163
  export async function writeStack(stackPath: string, stack: TaskStackEntry[]): Promise<void> {
165
- const content = stack.map((task) => JSON.stringify(task)).join('\n') + '\n'
164
+ const content = `${stack.map((task) => JSON.stringify(task)).join('\n')}\n`
166
165
  await fs.writeFile(stackPath, content)
167
166
  }
168
167
 
169
168
  /**
170
169
  * Generate now.md content for a task
171
170
  */
172
- export function generateNowContent(task: TaskStackEntry | null, customContent: string | null, formatDurationFn: (ms: number) => string): string {
171
+ export function generateNowContent(
172
+ task: TaskStackEntry | null,
173
+ customContent: string | null,
174
+ formatDurationFn: (ms: number) => string
175
+ ): string {
173
176
  if (customContent !== undefined && customContent !== null) {
174
177
  return customContent
175
178
  }
@@ -340,7 +343,11 @@ export class TaskStack {
340
343
  /**
341
344
  * Start a new task
342
345
  */
343
- async startTask(description: string, agent: string = 'general', complexity: string = 'moderate'): Promise<TaskStackEntry> {
346
+ async startTask(
347
+ description: string,
348
+ agent: string = 'general',
349
+ complexity: string = 'moderate'
350
+ ): Promise<TaskStackEntry> {
344
351
  // Check if there's already an active task
345
352
  const active = await this.getActiveTask()
346
353
  if (active) {
@@ -524,7 +531,10 @@ export class TaskStack {
524
531
  /**
525
532
  * Update now.md to reflect current state
526
533
  */
527
- async updateNowFile(task: TaskStackEntry | null, customContent: string | null = null): Promise<void> {
534
+ async updateNowFile(
535
+ task: TaskStackEntry | null,
536
+ customContent: string | null = null
537
+ ): Promise<void> {
528
538
  await updateNowFile(this.nowPath, task, customContent, formatDuration)
529
539
  }
530
540
 
package/core/errors.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * @version 2.0.0
14
14
  */
15
15
 
16
- import { z, type ZodType } from 'zod'
16
+ import { type ZodType, z } from 'zod'
17
17
 
18
18
  // =============================================================================
19
19
  // Named Error Pattern (Zod-based)
@@ -6,10 +6,9 @@
6
6
  */
7
7
 
8
8
  import pathManager from '../infrastructure/path-manager'
9
- import * as fileHelper from '../utils/file-helper'
9
+ import type { SyncEvent, SyncEventType } from '../types'
10
10
  import { getTimestamp } from '../utils/date-helper'
11
-
12
- import type { SyncEventType, SyncEvent } from '../types'
11
+ import * as fileHelper from '../utils/file-helper'
13
12
 
14
13
  /**
15
14
  * Infer event type from path and action
@@ -84,4 +83,3 @@ class EventBus {
84
83
 
85
84
  export const eventBus = new EventBus()
86
85
  export default eventBus
87
-
@@ -4,6 +4,5 @@
4
4
  * Barrel export for events module
5
5
  */
6
6
 
7
- export { eventBus, inferEventType } from './events'
8
- export { default } from './events'
9
7
  export type { SyncEvent, SyncEventType } from '../types'
8
+ export { default, eventBus, inferEventType } from './events'
package/core/index.ts CHANGED
@@ -7,12 +7,12 @@
7
7
  import { PrjctCommands } from './commands/index'
8
8
  import { commandRegistry } from './commands/registry'
9
9
  import './commands/register' // Ensure commands are registered
10
- import out from './utils/output'
10
+ import fs from 'node:fs'
11
+ import os from 'node:os'
12
+ import path from 'node:path'
11
13
  import type { CommandMeta } from './commands/registry'
12
- import { detectAllProviders, detectAntigravity, Providers } from './infrastructure/ai-provider'
13
- import fs from 'fs'
14
- import path from 'path'
15
- import os from 'os'
14
+ import { detectAllProviders, detectAntigravity } from './infrastructure/ai-provider'
15
+ import out from './utils/output'
16
16
 
17
17
  interface ParsedCommandArgs {
18
18
  parsedArgs: string[]
@@ -111,10 +111,11 @@ async function main(): Promise<void> {
111
111
  ship: (p) => commands.ship(p),
112
112
  // Analytics
113
113
  dash: (p) => commands.dash(p || 'default'),
114
- stats: () => commands.stats(process.cwd(), {
115
- json: options.json === true,
116
- export: options.export === true,
117
- }),
114
+ stats: () =>
115
+ commands.stats(process.cwd(), {
116
+ json: options.json === true,
117
+ export: options.export === true,
118
+ }),
118
119
  help: (p) => commands.help(p || ''),
119
120
  // Maintenance
120
121
  recover: () => commands.recover(),
@@ -122,9 +123,10 @@ async function main(): Promise<void> {
122
123
  redo: () => commands.redo(),
123
124
  history: () => commands.history(),
124
125
  // Setup
125
- sync: () => commands.sync(process.cwd(), {
126
- aiTools: options.agents ? String(options.agents).split(',') : undefined,
127
- }),
126
+ sync: () =>
127
+ commands.sync(process.cwd(), {
128
+ aiTools: options.agents ? String(options.agents).split(',') : undefined,
129
+ }),
128
130
  start: () => commands.start(),
129
131
  // Context (for Claude templates)
130
132
  context: (p) => commands.context(p),
@@ -139,13 +141,13 @@ async function main(): Promise<void> {
139
141
  }
140
142
 
141
143
  // 7. Display result
142
- if (result && result.message) {
144
+ if (result?.message) {
143
145
  console.log(result.message)
144
146
  }
145
147
 
146
148
  // Show branding footer
147
149
  out.end()
148
- process.exit(result && result.success ? 0 : 1)
150
+ process.exit(result?.success ? 0 : 1)
149
151
  } catch (error) {
150
152
  console.error('Error:', (error as Error).message)
151
153
  if (process.env.DEBUG) {
@@ -160,7 +162,7 @@ async function main(): Promise<void> {
160
162
  /**
161
163
  * Parse command arguments dynamically
162
164
  */
163
- function parseCommandArgs(cmd: CommandMeta, rawArgs: string[]): ParsedCommandArgs {
165
+ function parseCommandArgs(_cmd: CommandMeta, rawArgs: string[]): ParsedCommandArgs {
164
166
  const parsedArgs: string[] = []
165
167
  const options: Record<string, string | boolean> = {}
166
168
 
@@ -235,7 +237,9 @@ ${DIM}Providers:${RESET}`)
235
237
  // Antigravity status (global, skills-based)
236
238
  const antigravityDetection = detectAntigravity()
237
239
  if (antigravityDetection.installed) {
238
- const status = antigravityDetection.skillInstalled ? `${GREEN}✓ ready${RESET}` : `${YELLOW}● detected${RESET}`
240
+ const status = antigravityDetection.skillInstalled
241
+ ? `${GREEN}✓ ready${RESET}`
242
+ : `${YELLOW}● detected${RESET}`
239
243
  const hint = antigravityDetection.skillInstalled ? '' : ` ${DIM}(run prjct start)${RESET}`
240
244
  console.log(` Antigravity ${status}${hint}`)
241
245
  } else {
@@ -293,6 +297,8 @@ TERMINAL COMMANDS (this CLI)
293
297
  prjct init Initialize project (required for Cursor)
294
298
  prjct setup Reconfigure installations
295
299
  prjct sync Sync project state
300
+ prjct watch Auto-sync on file changes (Ctrl+C to stop)
301
+ prjct doctor Check system health and dependencies
296
302
 
297
303
  EXAMPLES
298
304
  --------
@@ -307,6 +313,12 @@ EXAMPLES
307
313
  > /sync
308
314
  > /task "add user authentication"
309
315
 
316
+ FLAGS
317
+ -----
318
+ --quiet, -q Suppress all output (only errors to stderr)
319
+ --version, -v Show version
320
+ --help, -h Show this help
321
+
310
322
  MORE INFO
311
323
  ---------
312
324
  Documentation: https://prjct.app
@@ -5,8 +5,8 @@
5
5
  * @module infrastructure/agent-detector
6
6
  */
7
7
 
8
- import fs from 'fs'
9
- import path from 'path'
8
+ import fs from 'node:fs'
9
+ import path from 'node:path'
10
10
  import type { DetectedAgent } from '../types'
11
11
 
12
12
  declare const global: typeof globalThis & {
@@ -144,5 +144,5 @@ export default {
144
144
  setAgent,
145
145
  reset,
146
146
  isClaude,
147
- isTerminal
147
+ isTerminal,
148
148
  }
@@ -17,17 +17,17 @@
17
17
  * @see https://docs.windsurf.com/windsurf/cascade/memories
18
18
  */
19
19
 
20
- import { execSync } from 'child_process'
21
- import fs from 'fs'
22
- import path from 'path'
23
- import os from 'os'
20
+ import { execSync } from 'node:child_process'
21
+ import fs from 'node:fs'
22
+ import os from 'node:os'
23
+ import path from 'node:path'
24
24
  import type {
25
- AIProviderName,
26
25
  AIProviderConfig,
26
+ AIProviderName,
27
+ CursorProjectDetection,
28
+ ProviderBranding,
27
29
  ProviderDetectionResult,
28
30
  ProviderSelectionResult,
29
- ProviderBranding,
30
- CursorProjectDetection,
31
31
  WindsurfProjectDetection,
32
32
  } from '../types/provider'
33
33
 
@@ -110,17 +110,17 @@ export const AntigravityProvider: AIProviderConfig = {
110
110
  export const CursorProvider: AIProviderConfig = {
111
111
  name: 'cursor',
112
112
  displayName: 'Cursor IDE',
113
- cliCommand: null, // Not a CLI - GUI app
114
- configDir: null, // No global config directory
115
- contextFile: 'prjct.mdc', // Uses .mdc format with frontmatter
116
- skillsDir: null, // No skills directory
113
+ cliCommand: null, // Not a CLI - GUI app
114
+ configDir: null, // No global config directory
115
+ contextFile: 'prjct.mdc', // Uses .mdc format with frontmatter
116
+ skillsDir: null, // No skills directory
117
117
  commandsDir: '.cursor/commands',
118
- rulesDir: '.cursor/rules', // Cursor-specific: rules directory
118
+ rulesDir: '.cursor/rules', // Cursor-specific: rules directory
119
119
  commandFormat: 'md',
120
120
  settingsFile: null,
121
121
  projectSettingsFile: null,
122
122
  ignoreFile: '.cursorignore',
123
- isProjectLevel: true, // Config is project-level only
123
+ isProjectLevel: true, // Config is project-level only
124
124
  websiteUrl: 'https://cursor.com',
125
125
  docsUrl: 'https://cursor.com/docs',
126
126
  }
@@ -140,17 +140,17 @@ export const CursorProvider: AIProviderConfig = {
140
140
  export const WindsurfProvider: AIProviderConfig = {
141
141
  name: 'windsurf',
142
142
  displayName: 'Windsurf IDE',
143
- cliCommand: null, // Not a CLI - GUI app
144
- configDir: null, // No global config directory
145
- contextFile: 'prjct.md', // Uses .md format (not .mdc)
146
- skillsDir: null, // No skills directory
147
- commandsDir: '.windsurf/workflows', // Windsurf uses "workflows" not "commands"
143
+ cliCommand: null, // Not a CLI - GUI app
144
+ configDir: null, // No global config directory
145
+ contextFile: 'prjct.md', // Uses .md format (not .mdc)
146
+ skillsDir: null, // No skills directory
147
+ commandsDir: '.windsurf/workflows', // Windsurf uses "workflows" not "commands"
148
148
  rulesDir: '.windsurf/rules',
149
149
  commandFormat: 'md',
150
150
  settingsFile: null,
151
151
  projectSettingsFile: null,
152
152
  ignoreFile: '.windsurfignore',
153
- isProjectLevel: true, // Config is project-level only
153
+ isProjectLevel: true, // Config is project-level only
154
154
  websiteUrl: 'https://windsurf.com',
155
155
  docsUrl: 'https://docs.windsurf.com',
156
156
  }
@@ -227,7 +227,10 @@ export function detectProvider(provider: AIProviderName): ProviderDetectionResul
227
227
  * Detect all available CLI-based providers
228
228
  * Note: Cursor detection is project-level, use detectCursorProject() separately
229
229
  */
230
- export function detectAllProviders(): { claude: ProviderDetectionResult; gemini: ProviderDetectionResult } {
230
+ export function detectAllProviders(): {
231
+ claude: ProviderDetectionResult
232
+ gemini: ProviderDetectionResult
233
+ } {
231
234
  return {
232
235
  claude: detectProvider('claude'),
233
236
  gemini: detectProvider('gemini'),
@@ -9,9 +9,9 @@
9
9
  * @module infrastructure/author-detector
10
10
  */
11
11
 
12
- import { promisify } from 'util'
13
- import { exec as execCallback } from 'child_process'
14
- import type { DetectedAuthorInfo, AuthorConfigStatus } from '../types'
12
+ import { exec as execCallback } from 'node:child_process'
13
+ import { promisify } from 'node:util'
14
+ import type { AuthorConfigStatus, DetectedAuthorInfo } from '../types'
15
15
 
16
16
  const exec = promisify(execCallback)
17
17
 
@@ -52,13 +52,13 @@ export async function detect(): Promise<DetectedAuthorInfo> {
52
52
  const [github, name, email] = await Promise.all([
53
53
  detectGitHubUsername(),
54
54
  detectGitName(),
55
- detectGitEmail()
55
+ detectGitEmail(),
56
56
  ])
57
57
 
58
58
  return {
59
59
  github,
60
60
  email,
61
- name: name || github || 'Unknown'
61
+ name: name || github || 'Unknown',
62
62
  }
63
63
  }
64
64
 
@@ -83,7 +83,7 @@ export async function getConfigStatus(): Promise<AuthorConfigStatus> {
83
83
  const [hasGitHub, hasGit, author] = await Promise.all([
84
84
  isGitHubCLIAvailable(),
85
85
  isGitConfigured(),
86
- detect()
86
+ detect(),
87
87
  ])
88
88
 
89
89
  return {
@@ -91,15 +91,21 @@ export async function getConfigStatus(): Promise<AuthorConfigStatus> {
91
91
  hasGit,
92
92
  author,
93
93
  isComplete: !!(author.github || (author.name !== 'Unknown' && author.email)),
94
- recommendations: getRecommendations(hasGitHub, hasGit, author)
94
+ recommendations: getRecommendations(hasGitHub, hasGit, author),
95
95
  }
96
96
  }
97
97
 
98
- function getRecommendations(hasGitHub: boolean, hasGit: boolean, author: DetectedAuthorInfo): string[] {
98
+ function getRecommendations(
99
+ hasGitHub: boolean,
100
+ hasGit: boolean,
101
+ author: DetectedAuthorInfo
102
+ ): string[] {
99
103
  const recommendations: string[] = []
100
104
 
101
105
  if (!hasGitHub && !author.github) {
102
- recommendations.push('Install GitHub CLI (gh) for better collaboration support: https://cli.github.com/')
106
+ recommendations.push(
107
+ 'Install GitHub CLI (gh) for better collaboration support: https://cli.github.com/'
108
+ )
103
109
  }
104
110
 
105
111
  if (!hasGit) {
@@ -137,5 +143,5 @@ export default {
137
143
  isGitHubCLIAvailable,
138
144
  isGitConfigured,
139
145
  getConfigStatus,
140
- formatAuthor
146
+ formatAuthor,
141
147
  }
@@ -8,8 +8,8 @@
8
8
  * Configuration should be template-driven, not code-driven.
9
9
  */
10
10
 
11
- import { exec } from 'child_process'
12
- import { promisify } from 'util'
11
+ import { exec } from 'node:child_process'
12
+ import { promisify } from 'node:util'
13
13
 
14
14
  const execAsync = promisify(exec)
15
15