prjct-cli 0.44.1 → 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 +114 -0
  2. package/bin/prjct.ts +131 -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 +287 -29
  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 +49 -8
  43. package/core/commands/commands.ts +60 -23
  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 +14 -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 +583 -0
  59. package/core/context-tools/imports-tool.ts +403 -0
  60. package/core/context-tools/index.ts +433 -0
  61. package/core/context-tools/recent-tool.ts +307 -0
  62. package/core/context-tools/signatures-tool.ts +501 -0
  63. package/core/context-tools/summary-tool.ts +307 -0
  64. package/core/context-tools/token-counter.ts +284 -0
  65. package/core/context-tools/types.ts +253 -0
  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 -12
  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 +143 -0
  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 +170 -329
  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 -13
  157. package/core/storage/metrics-storage.ts +320 -0
  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 -302
  170. package/core/types/integrations.ts +3 -3
  171. package/core/types/storage.ts +49 -0
  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 +18907 -13189
  194. package/dist/core/infrastructure/command-installer.js +96 -111
  195. package/dist/core/infrastructure/editors-config.js +6 -6
  196. package/dist/core/infrastructure/setup.js +256 -257
  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
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,6 +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: () =>
115
+ commands.stats(process.cwd(), {
116
+ json: options.json === true,
117
+ export: options.export === true,
118
+ }),
114
119
  help: (p) => commands.help(p || ''),
115
120
  // Maintenance
116
121
  recover: () => commands.recover(),
@@ -118,9 +123,10 @@ async function main(): Promise<void> {
118
123
  redo: () => commands.redo(),
119
124
  history: () => commands.history(),
120
125
  // Setup
121
- sync: () => commands.sync(process.cwd(), {
122
- aiTools: options.agents ? String(options.agents).split(',') : undefined,
123
- }),
126
+ sync: () =>
127
+ commands.sync(process.cwd(), {
128
+ aiTools: options.agents ? String(options.agents).split(',') : undefined,
129
+ }),
124
130
  start: () => commands.start(),
125
131
  // Context (for Claude templates)
126
132
  context: (p) => commands.context(p),
@@ -135,13 +141,13 @@ async function main(): Promise<void> {
135
141
  }
136
142
 
137
143
  // 7. Display result
138
- if (result && result.message) {
144
+ if (result?.message) {
139
145
  console.log(result.message)
140
146
  }
141
147
 
142
148
  // Show branding footer
143
149
  out.end()
144
- process.exit(result && result.success ? 0 : 1)
150
+ process.exit(result?.success ? 0 : 1)
145
151
  } catch (error) {
146
152
  console.error('Error:', (error as Error).message)
147
153
  if (process.env.DEBUG) {
@@ -156,7 +162,7 @@ async function main(): Promise<void> {
156
162
  /**
157
163
  * Parse command arguments dynamically
158
164
  */
159
- function parseCommandArgs(cmd: CommandMeta, rawArgs: string[]): ParsedCommandArgs {
165
+ function parseCommandArgs(_cmd: CommandMeta, rawArgs: string[]): ParsedCommandArgs {
160
166
  const parsedArgs: string[] = []
161
167
  const options: Record<string, string | boolean> = {}
162
168
 
@@ -231,7 +237,9 @@ ${DIM}Providers:${RESET}`)
231
237
  // Antigravity status (global, skills-based)
232
238
  const antigravityDetection = detectAntigravity()
233
239
  if (antigravityDetection.installed) {
234
- const status = antigravityDetection.skillInstalled ? `${GREEN}✓ ready${RESET}` : `${YELLOW}● detected${RESET}`
240
+ const status = antigravityDetection.skillInstalled
241
+ ? `${GREEN}✓ ready${RESET}`
242
+ : `${YELLOW}● detected${RESET}`
235
243
  const hint = antigravityDetection.skillInstalled ? '' : ` ${DIM}(run prjct start)${RESET}`
236
244
  console.log(` Antigravity ${status}${hint}`)
237
245
  } else {
@@ -289,6 +297,8 @@ TERMINAL COMMANDS (this CLI)
289
297
  prjct init Initialize project (required for Cursor)
290
298
  prjct setup Reconfigure installations
291
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
292
302
 
293
303
  EXAMPLES
294
304
  --------
@@ -303,6 +313,12 @@ EXAMPLES
303
313
  > /sync
304
314
  > /task "add user authentication"
305
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
+
306
322
  MORE INFO
307
323
  ---------
308
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
 
@@ -3,7 +3,7 @@
3
3
  * Implements prjct commands for Claude Code environment
4
4
  */
5
5
 
6
- import fs from 'fs/promises'
6
+ import fs from 'node:fs/promises'
7
7
  import { isNotFoundError } from '../types/fs'
8
8
 
9
9
  declare const global: typeof globalThis & {
@@ -71,7 +71,7 @@ class ClaudeAgent {
71
71
  */
72
72
  async readFile(filePath: string): Promise<string> {
73
73
  try {
74
- if (global.mcp && global.mcp.filesystem) {
74
+ if (global.mcp?.filesystem) {
75
75
  return await global.mcp.filesystem.read(filePath)
76
76
  }
77
77
  } catch (_error) {
@@ -86,7 +86,7 @@ class ClaudeAgent {
86
86
  */
87
87
  async writeFile(filePath: string, content: string): Promise<void> {
88
88
  try {
89
- if (global.mcp && global.mcp.filesystem) {
89
+ if (global.mcp?.filesystem) {
90
90
  return await global.mcp.filesystem.write(filePath, content)
91
91
  }
92
92
  } catch (_error) {
@@ -101,7 +101,7 @@ class ClaudeAgent {
101
101
  */
102
102
  async listDirectory(dirPath: string): Promise<string[]> {
103
103
  try {
104
- if (global.mcp && global.mcp.filesystem) {
104
+ if (global.mcp?.filesystem) {
105
105
  return await global.mcp.filesystem.list(dirPath)
106
106
  }
107
107
  } catch (_error) {
@@ -148,7 +148,7 @@ class ClaudeAgent {
148
148
  return '📋 No tasks queued'
149
149
  }
150
150
 
151
- return '📋 Queue:\n' + tasks.map((t, i) => `${i + 1}. ${t}`).join('\n')
151
+ return `📋 Queue:\n${tasks.map((t, i) => `${i + 1}. ${t}`).join('\n')}`
152
152
  }
153
153
 
154
154
  /**
@@ -161,7 +161,7 @@ class ClaudeAgent {
161
161
  🚀 Shipped: ${data.shippedCount}
162
162
  📝 Queue: ${data.queuedCount}
163
163
  💡 Ideas: ${data.ideasCount}
164
- ${data.recentActivity ? '\n' + data.recentActivity : ''}`
164
+ ${data.recentActivity ? `\n${data.recentActivity}` : ''}`
165
165
  }
166
166
 
167
167
  /**
@@ -12,18 +12,18 @@
12
12
  * @version 0.6.0 - Multi-provider support
13
13
  */
14
14
 
15
- import fs from 'fs/promises'
16
- import path from 'path'
17
- import os from 'os'
18
- import { getPackageRoot } from '../utils/version'
19
- import { isNotFoundError } from '../types/fs'
15
+ import fs from 'node:fs/promises'
16
+ import os from 'node:os'
17
+ import path from 'node:path'
20
18
  import type {
21
- InstallResult,
22
- UninstallResult,
23
19
  CheckResult,
24
- SyncResult,
25
20
  GlobalConfigResult,
21
+ InstallResult,
22
+ SyncResult,
23
+ UninstallResult,
26
24
  } from '../types'
25
+ import { isNotFoundError } from '../types/fs'
26
+ import { getPackageRoot } from '../utils/version'
27
27
 
28
28
  // =============================================================================
29
29
  // Global Config
@@ -82,13 +82,18 @@ export async function installGlobalConfig(): Promise<GlobalConfigResult> {
82
82
  await fs.mkdir(activeProvider.configDir, { recursive: true })
83
83
 
84
84
  const globalConfigPath = path.join(activeProvider.configDir, activeProvider.contextFile)
85
- const templatePath = path.join(getPackageRoot(), 'templates', 'global', activeProvider.contextFile)
85
+ const templatePath = path.join(
86
+ getPackageRoot(),
87
+ 'templates',
88
+ 'global',
89
+ activeProvider.contextFile
90
+ )
86
91
 
87
92
  // Read template content
88
93
  let templateContent = ''
89
94
  try {
90
95
  templateContent = await fs.readFile(templatePath, 'utf-8')
91
- } catch (error) {
96
+ } catch (_error) {
92
97
  // Fallback if provider-specific template not found
93
98
  const fallbackTemplatePath = path.join(getPackageRoot(), 'templates/global/CLAUDE.md')
94
99
  templateContent = await fs.readFile(fallbackTemplatePath, 'utf-8')
@@ -133,7 +138,7 @@ export async function installGlobalConfig(): Promise<GlobalConfigResult> {
133
138
 
134
139
  if (!hasMarkers) {
135
140
  // No markers - append prjct section at the end
136
- const updatedContent = existingContent + '\n\n' + templateContent
141
+ const updatedContent = `${existingContent}\n\n${templateContent}`
137
142
  await fs.writeFile(globalConfigPath, updatedContent, 'utf-8')
138
143
  return {
139
144
  success: true,
@@ -183,10 +188,10 @@ export class CommandInstaller {
183
188
 
184
189
  constructor() {
185
190
  this.homeDir = os.homedir()
186
-
191
+
187
192
  const aiProvider = require('./ai-provider')
188
193
  const activeProvider = aiProvider.getActiveProvider()
189
-
194
+
190
195
  // Command paths are provider-specific
191
196
  if (activeProvider.name === 'gemini') {
192
197
  this.claudeCommandsPath = path.join(activeProvider.configDir, 'commands')
@@ -194,7 +199,7 @@ export class CommandInstaller {
194
199
  // Claude: Commands are in p/ subdirectory to avoid cluttering commands/
195
200
  this.claudeCommandsPath = path.join(activeProvider.configDir, 'commands', 'p')
196
201
  }
197
-
202
+
198
203
  this.claudeConfigPath = activeProvider.configDir
199
204
  this.templatesDir = path.join(getPackageRoot(), 'templates', 'commands')
200
205
  }
@@ -228,7 +233,7 @@ export class CommandInstaller {
228
233
  try {
229
234
  const files = await fs.readdir(this.templatesDir)
230
235
  return files.filter((f) => f.endsWith('.md'))
231
- } catch (error) {
236
+ } catch (_error) {
232
237
  // Fallback to core commands if template directory not accessible (ENOENT or other)
233
238
  return [
234
239
  'init.md',
@@ -332,7 +337,7 @@ export class CommandInstaller {
332
337
  // Try to remove the /p directory if empty
333
338
  try {
334
339
  await fs.rmdir(this.claudeCommandsPath)
335
- } catch (error) {
340
+ } catch (_error) {
336
341
  // Directory not empty or doesn't exist - that's fine (ENOTEMPTY or ENOENT)
337
342
  }
338
343
 
@@ -438,7 +443,7 @@ export class CommandInstaller {
438
443
  const aiProvider = require('./ai-provider')
439
444
  const activeProvider = aiProvider.getActiveProvider()
440
445
  const routerFile = activeProvider.name === 'gemini' ? 'p.toml' : 'p.md'
441
-
446
+
442
447
  try {
443
448
  const routerSource = path.join(this.templatesDir, routerFile)
444
449
  const routerDest = path.join(activeProvider.configDir, 'commands', routerFile)
@@ -10,19 +10,19 @@
10
10
  * @version 0.3.0
11
11
  */
12
12
 
13
- import fs from 'fs/promises'
14
- import path from 'path'
13
+ import fs from 'node:fs/promises'
14
+ import path from 'node:path'
15
15
  import * as jsonc from 'jsonc-parser'
16
- import pathManager from './path-manager'
17
- import authorDetector from './author-detector'
18
- import { VERSION } from '../utils/version'
19
- import { getTimestamp } from '../utils/date-helper'
20
16
  import { getErrorMessage } from '../errors'
17
+ import type { Author, GlobalConfig, LocalConfig } from '../types'
21
18
  import { isNotFoundError } from '../types/fs'
22
- import type { Author, LocalConfig, GlobalConfig } from '../types'
19
+ import { getTimestamp } from '../utils/date-helper'
20
+ import { VERSION } from '../utils/version'
21
+ import authorDetector from './author-detector'
22
+ import pathManager from './path-manager'
23
23
 
24
24
  // Re-export types for convenience
25
- export type { Author, LocalConfig, GlobalConfig } from '../types'
25
+ export type { Author, GlobalConfig, LocalConfig } from '../types'
26
26
 
27
27
  /**
28
28
  * Parse JSON or JSONC content safely
@@ -36,7 +36,9 @@ function parseJsonc<T>(content: string): T {
36
36
  })
37
37
  if (errors.length > 0) {
38
38
  const firstError = errors[0]
39
- throw new SyntaxError(`JSON parse error at offset ${firstError.offset}: ${jsonc.printParseErrorCode(firstError.error)}`)
39
+ throw new SyntaxError(
40
+ `JSON parse error at offset ${firstError.offset}: ${jsonc.printParseErrorCode(firstError.error)}`
41
+ )
40
42
  }
41
43
  return result
42
44
  }
@@ -72,7 +74,7 @@ class ConfigManager {
72
74
  await fs.mkdir(configDir, { recursive: true })
73
75
 
74
76
  const content = JSON.stringify(config, null, 2)
75
- await fs.writeFile(configPath, content + '\n', 'utf-8')
77
+ await fs.writeFile(configPath, `${content}\n`, 'utf-8')
76
78
  }
77
79
 
78
80
  /**
@@ -91,7 +93,9 @@ class ConfigManager {
91
93
  return null
92
94
  }
93
95
  // Log other errors for debugging
94
- console.warn(`Warning: Could not read global config for ${projectId}: ${getErrorMessage(error)}`)
96
+ console.warn(
97
+ `Warning: Could not read global config for ${projectId}: ${getErrorMessage(error)}`
98
+ )
95
99
  return null
96
100
  }
97
101
  }
@@ -106,7 +110,7 @@ class ConfigManager {
106
110
  await fs.mkdir(configDir, { recursive: true })
107
111
 
108
112
  const content = JSON.stringify(config, null, 2)
109
- await fs.writeFile(configPath, content + '\n', 'utf-8')
113
+ await fs.writeFile(configPath, `${content}\n`, 'utf-8')
110
114
  }
111
115
 
112
116
  /**
@@ -231,7 +235,7 @@ class ConfigManager {
231
235
  */
232
236
  async getProjectId(projectPath: string): Promise<string> {
233
237
  const config = await this.readConfig(projectPath)
234
- if (config && config.projectId) {
238
+ if (config?.projectId) {
235
239
  return config.projectId
236
240
  }
237
241
  return pathManager.generateProjectId(projectPath)
@@ -300,7 +304,7 @@ class ConfigManager {
300
304
  await this.addAuthor(projectId, {
301
305
  name: author.name ?? undefined,
302
306
  email: author.email ?? undefined,
303
- github: author.github ?? undefined
307
+ github: author.github ?? undefined,
304
308
  })
305
309
 
306
310
  return author.github || author.name || 'Unknown'
@@ -9,9 +9,9 @@
9
9
  * @version 0.6.0
10
10
  */
11
11
 
12
- import fs from 'fs/promises'
13
- import path from 'path'
14
- import os from 'os'
12
+ import fs from 'node:fs/promises'
13
+ import os from 'node:os'
14
+ import path from 'node:path'
15
15
  import type { AIProviderName } from '../types/provider'
16
16
 
17
17
  interface EditorConfig {
@@ -65,7 +65,11 @@ class EditorsConfig {
65
65
  /**
66
66
  * Save installation configuration
67
67
  */
68
- async saveConfig(version: string, installPath: string, provider: AIProviderName = 'claude'): Promise<boolean> {
68
+ async saveConfig(
69
+ version: string,
70
+ installPath: string,
71
+ provider: AIProviderName = 'claude'
72
+ ): Promise<boolean> {
69
73
  try {
70
74
  await this.ensureConfigDir()
71
75
 
@@ -9,13 +9,13 @@
9
9
  * @version 0.2.1
10
10
  */
11
11
 
12
- import fs from 'fs/promises'
13
- import path from 'path'
14
- import crypto from 'crypto'
15
- import os from 'os'
12
+ import crypto from 'node:crypto'
13
+ import fs from 'node:fs/promises'
14
+ import os from 'node:os'
15
+ import path from 'node:path'
16
+ import type { SessionInfo } from '../types'
16
17
  import * as dateHelper from '../utils/date-helper'
17
18
  import * as fileHelper from '../utils/file-helper'
18
- import type { SessionInfo } from '../types'
19
19
 
20
20
  class PathManager {
21
21
  globalBaseDir: string
@@ -24,7 +24,9 @@ class PathManager {
24
24
 
25
25
  constructor() {
26
26
  const envOverride = process.env.PRJCT_CLI_HOME?.trim()
27
- this.globalBaseDir = envOverride ? path.resolve(envOverride) : path.join(os.homedir(), '.prjct-cli')
27
+ this.globalBaseDir = envOverride
28
+ ? path.resolve(envOverride)
29
+ : path.join(os.homedir(), '.prjct-cli')
28
30
  this.globalProjectsDir = path.join(this.globalBaseDir, 'projects')
29
31
  this.globalConfigDir = path.join(this.globalBaseDir, 'config')
30
32
  }
@@ -9,9 +9,9 @@
9
9
  */
10
10
 
11
11
  import {
12
- type PermissionsConfig,
13
- type PermissionLevel,
14
12
  buildDefaultPermissions,
13
+ type PermissionLevel,
14
+ type PermissionsConfig,
15
15
  } from '../schemas/permissions'
16
16
  import type { PermissionCheckResult } from '../types'
17
17
 
@@ -114,11 +114,12 @@ class PermissionManager {
114
114
  allowed: match.level === 'allow',
115
115
  level: match.level,
116
116
  matchedPattern: match.pattern,
117
- reason: match.level === 'deny'
118
- ? `Command denied by pattern: ${match.pattern}`
119
- : match.level === 'ask'
120
- ? `Command requires approval: ${match.pattern}`
121
- : undefined,
117
+ reason:
118
+ match.level === 'deny'
119
+ ? `Command denied by pattern: ${match.pattern}`
120
+ : match.level === 'ask'
121
+ ? `Command requires approval: ${match.pattern}`
122
+ : undefined,
122
123
  }
123
124
  }
124
125
 
@@ -145,11 +146,12 @@ class PermissionManager {
145
146
  allowed: match.level === 'allow',
146
147
  level: match.level,
147
148
  matchedPattern: match.pattern,
148
- reason: match.level === 'deny'
149
- ? `File operation denied: ${operation} on ${match.pattern}`
150
- : match.level === 'ask'
151
- ? `File operation requires approval: ${operation}`
152
- : undefined,
149
+ reason:
150
+ match.level === 'deny'
151
+ ? `File operation denied: ${operation} on ${match.pattern}`
152
+ : match.level === 'ask'
153
+ ? `File operation requires approval: ${operation}`
154
+ : undefined,
153
155
  }
154
156
  }
155
157
 
@@ -239,11 +241,12 @@ class PermissionManager {
239
241
  return {
240
242
  allowed: level === 'allow',
241
243
  level,
242
- reason: level === 'deny'
243
- ? 'External directory access denied'
244
- : level === 'ask'
245
- ? 'External directory access requires approval'
246
- : undefined,
244
+ reason:
245
+ level === 'deny'
246
+ ? 'External directory access denied'
247
+ : level === 'ask'
248
+ ? 'External directory access requires approval'
249
+ : undefined,
247
250
  }
248
251
  }
249
252