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
@@ -8,28 +8,24 @@
8
8
  * TypeScript provides infrastructure; Claude decides via templates.
9
9
  */
10
10
 
11
- import type { CommandResult, ProjectContext } from '../types'
12
- import { generateUUID } from '../schemas'
13
- import {
14
- PrjctCommandsBase,
15
- contextBuilder,
16
- configManager,
17
- dateHelper,
18
- out
19
- } from './base'
20
- import { stateStorage, queueStorage } from '../storage'
21
- import { templateExecutor } from '../agentic/template-executor'
22
11
  import commandExecutor from '../agentic/command-executor'
23
- import { showNextSteps, showStateInfo } from '../utils/next-steps'
24
- import { workflowStateMachine } from '../workflow/state-machine'
12
+ import { templateExecutor } from '../agentic/template-executor'
25
13
  import { linearService } from '../integrations/linear'
26
- import { getProjectCredentials, getLinearApiKey } from '../utils/project-credentials'
14
+ import { generateUUID } from '../schemas'
15
+ import { queueStorage, stateStorage } from '../storage'
16
+ import type { CommandResult } from '../types'
17
+ import { showNextSteps, showStateInfo } from '../utils/next-steps'
18
+ import { getLinearApiKey, getProjectCredentials } from '../utils/project-credentials'
19
+ import { configManager, dateHelper, out, PrjctCommandsBase } from './base'
27
20
 
28
21
  export class WorkflowCommands extends PrjctCommandsBase {
29
22
  /**
30
23
  * /p:now - Set or show current task
31
24
  */
32
- async now(task: string | null = null, projectPath: string = process.cwd()): Promise<CommandResult> {
25
+ async now(
26
+ task: string | null = null,
27
+ projectPath: string = process.cwd()
28
+ ): Promise<CommandResult> {
33
29
  try {
34
30
  const initResult = await this.ensureProjectInit(projectPath)
35
31
  if (!initResult.success) return initResult
@@ -58,10 +54,7 @@ export class WorkflowCommands extends PrjctCommandsBase {
58
54
  const creds = await getProjectCredentials(projectId)
59
55
  const apiKey = await getLinearApiKey(projectId)
60
56
  if (apiKey && creds.linear?.teamId) {
61
- await linearService.initializeFromApiKey(
62
- apiKey,
63
- creds.linear.teamId
64
- )
57
+ await linearService.initializeFromApiKey(apiKey, creds.linear.teamId)
65
58
  const issue = await linearService.fetchIssue(task)
66
59
  if (issue) {
67
60
  linearId = task
@@ -85,11 +78,14 @@ export class WorkflowCommands extends PrjctCommandsBase {
85
78
 
86
79
  // Get available agents for backward compatibility
87
80
  const availableAgents = await templateExecutor.getAvailableAgents(projectPath)
88
- const agentsList = availableAgents.length > 0
89
- ? availableAgents.join(', ')
90
- : 'none (run p. sync)'
81
+ const _agentsList =
82
+ availableAgents.length > 0 ? availableAgents.join(', ') : 'none (run p. sync)'
91
83
 
92
- out.done(`${task}`)
84
+ // Build metrics from orchestrator context
85
+ const agentCount = result.orchestratorContext?.agents?.length || availableAgents.length
86
+ out.done(`${task}`, {
87
+ agents: agentCount > 0 ? agentCount : undefined,
88
+ })
93
89
  showStateInfo('working')
94
90
  showNextSteps('task')
95
91
 
@@ -167,10 +163,7 @@ export class WorkflowCommands extends PrjctCommandsBase {
167
163
  const creds = await getProjectCredentials(projectId)
168
164
  const apiKey = await getLinearApiKey(projectId)
169
165
  if (apiKey && creds.linear?.teamId) {
170
- await linearService.initializeFromApiKey(
171
- apiKey,
172
- creds.linear.teamId
173
- )
166
+ await linearService.initializeFromApiKey(apiKey, creds.linear.teamId)
174
167
  await linearService.markDone(linearId)
175
168
  out.done(`${task}${duration ? ` (${duration})` : ''} → Linear ✓`)
176
169
  } else {
@@ -275,7 +268,10 @@ export class WorkflowCommands extends PrjctCommandsBase {
275
268
  /**
276
269
  * /p:resume - Resume most recently paused task
277
270
  */
278
- async resume(taskId: string | null = null, projectPath: string = process.cwd()): Promise<CommandResult> {
271
+ async resume(
272
+ _taskId: string | null = null,
273
+ projectPath: string = process.cwd()
274
+ ): Promise<CommandResult> {
279
275
  try {
280
276
  const initResult = await this.ensureProjectInit(projectPath)
281
277
  if (!initResult.success) return initResult
@@ -19,17 +19,13 @@ export const NOW = {
19
19
 
20
20
  /** Generate NOW file content */
21
21
  content: (task: string, startedAt: string, agent?: string, confidence?: number): string => {
22
- const lines = [
23
- '# NOW',
24
- '',
25
- `**${task}**`,
26
- '',
27
- `Started: ${startedAt}`,
28
- ]
22
+ const lines = ['# NOW', '', `**${task}**`, '', `Started: ${startedAt}`]
29
23
  if (agent) {
30
- lines.push(`Agent: ${agent}${confidence ? ` (${Math.round(confidence * 100)}% confidence)` : ''}`)
24
+ lines.push(
25
+ `Agent: ${agent}${confidence ? ` (${Math.round(confidence * 100)}% confidence)` : ''}`
26
+ )
31
27
  }
32
- return lines.join('\n') + '\n'
28
+ return `${lines.join('\n')}\n`
33
29
  },
34
30
 
35
31
  /** Extract task from NOW content */
@@ -48,15 +44,11 @@ export const SHIPPED = {
48
44
 
49
45
  /** Generate ship entry */
50
46
  entry: (feature: string, date: string, duration?: string): string => {
51
- const lines = [
52
- `## ${feature}`,
53
- '',
54
- `Shipped: ${date}`,
55
- ]
47
+ const lines = [`## ${feature}`, '', `Shipped: ${date}`]
56
48
  if (duration) {
57
49
  lines.push(`Duration: ${duration}`)
58
50
  }
59
- return lines.join('\n') + '\n\n'
51
+ return `${lines.join('\n')}\n\n`
60
52
  },
61
53
  } as const
62
54
 
@@ -113,16 +105,12 @@ export const ROADMAP = {
113
105
 
114
106
  /** Generate feature entry */
115
107
  entry: (feature: string, status: RoadmapStatusKey, tasks?: string[]): string => {
116
- const lines = [
117
- `## ${feature}`,
118
- '',
119
- `Status: ${ROADMAP_STATUS[status]}`,
120
- ]
108
+ const lines = [`## ${feature}`, '', `Status: ${ROADMAP_STATUS[status]}`]
121
109
  if (tasks && tasks.length > 0) {
122
110
  lines.push('', '### Tasks', '')
123
- tasks.forEach(task => lines.push(`- [ ] ${task}`))
111
+ tasks.forEach((task) => lines.push(`- [ ] ${task}`))
124
112
  }
125
- return lines.join('\n') + '\n\n'
113
+ return `${lines.join('\n')}\n\n`
126
114
  },
127
115
  } as const
128
116
 
@@ -11,12 +11,12 @@
11
11
  * └── summary.md - Project summary
12
12
  */
13
13
 
14
- import fs from 'fs/promises'
15
- import path from 'path'
16
- import { exec } from 'child_process'
17
- import { promisify } from 'util'
18
- import { getStorage } from '../storage'
14
+ import { exec } from 'node:child_process'
15
+ import fs from 'node:fs/promises'
16
+ import path from 'node:path'
17
+ import { promisify } from 'node:util'
19
18
  import pathManager from '../infrastructure/path-manager'
19
+ import { getStorage } from '../storage'
20
20
 
21
21
  const execAsync = promisify(exec)
22
22
 
@@ -59,7 +59,7 @@ interface Agent {
59
59
  * Generate all context MD files from JSON data
60
60
  */
61
61
  export async function generateContext(projectId: string, repoPath: string): Promise<void> {
62
- const globalPath = pathManager.getGlobalProjectPath(projectId)
62
+ const _globalPath = pathManager.getGlobalProjectPath(projectId)
63
63
  const contextPath = pathManager.getContextPath(projectId)
64
64
  const storage = getStorage(projectId)
65
65
 
@@ -67,7 +67,7 @@ export async function generateContext(projectId: string, repoPath: string): Prom
67
67
  await fs.mkdir(contextPath, { recursive: true })
68
68
 
69
69
  // Read all data
70
- const project = await storage.read<ProjectData>(['project']) || {}
70
+ const project = (await storage.read<ProjectData>(['project'])) || {}
71
71
  const taskPaths = await storage.list(['task'])
72
72
  const featurePaths = await storage.list(['feature'])
73
73
  const ideaPaths = await storage.list(['idea'])
@@ -104,7 +104,18 @@ export async function generateContext(projectId: string, repoPath: string): Prom
104
104
  const pkgData = await getPackageData(repoPath)
105
105
 
106
106
  // Generate each context file
107
- await generateClaudeMd(contextPath, projectId, project, tasks, features, ideas, agents, gitData, pkgData, repoPath)
107
+ await generateClaudeMd(
108
+ contextPath,
109
+ projectId,
110
+ project,
111
+ tasks,
112
+ features,
113
+ ideas,
114
+ agents,
115
+ gitData,
116
+ pkgData,
117
+ repoPath
118
+ )
108
119
  await generateNowMd(contextPath, tasks)
109
120
  await generateQueueMd(contextPath, tasks)
110
121
  await generateSummaryMd(contextPath, project, gitData, pkgData)
@@ -116,7 +127,7 @@ async function getGitData(repoPath: string) {
116
127
  commits: 0,
117
128
  contributors: 0,
118
129
  hasChanges: false,
119
- recentCommits: [] as { hash: string; message: string; date: string }[]
130
+ recentCommits: [] as { hash: string; message: string; date: string }[],
120
131
  }
121
132
 
122
133
  try {
@@ -124,19 +135,27 @@ async function getGitData(repoPath: string) {
124
135
  data.branch = branch.trim() || 'main'
125
136
 
126
137
  const { stdout: commits } = await execAsync('git rev-list --count HEAD', { cwd: repoPath })
127
- data.commits = parseInt(commits.trim()) || 0
138
+ data.commits = parseInt(commits.trim(), 10) || 0
128
139
 
129
- const { stdout: contributors } = await execAsync('git shortlog -sn --all | wc -l', { cwd: repoPath })
130
- data.contributors = parseInt(contributors.trim()) || 0
140
+ const { stdout: contributors } = await execAsync('git shortlog -sn --all | wc -l', {
141
+ cwd: repoPath,
142
+ })
143
+ data.contributors = parseInt(contributors.trim(), 10) || 0
131
144
 
132
145
  const { stdout: status } = await execAsync('git status --porcelain', { cwd: repoPath })
133
146
  data.hasChanges = status.trim().length > 0
134
147
 
135
- const { stdout: log } = await execAsync('git log --oneline -10 --pretty=format:"%h|%s|%ad" --date=short', { cwd: repoPath })
136
- data.recentCommits = log.split('\n').filter(Boolean).map(line => {
137
- const [hash, message, date] = line.split('|')
138
- return { hash, message, date }
139
- })
148
+ const { stdout: log } = await execAsync(
149
+ 'git log --oneline -10 --pretty=format:"%h|%s|%ad" --date=short',
150
+ { cwd: repoPath }
151
+ )
152
+ data.recentCommits = log
153
+ .split('\n')
154
+ .filter(Boolean)
155
+ .map((line) => {
156
+ const [hash, message, date] = line.split('|')
157
+ return { hash, message, date }
158
+ })
140
159
  } catch (_error) {
141
160
  // Not a git repo - expected
142
161
  }
@@ -148,7 +167,7 @@ async function getPackageData(repoPath: string) {
148
167
  const data = {
149
168
  dependencies: {} as Record<string, string>,
150
169
  devDependencies: {} as Record<string, string>,
151
- scripts: {} as Record<string, string>
170
+ scripts: {} as Record<string, string>,
152
171
  }
153
172
 
154
173
  try {
@@ -177,9 +196,9 @@ async function generateClaudeMd(
177
196
  repoPath: string
178
197
  ) {
179
198
  const projectName = project.name || path.basename(repoPath)
180
- const currentTask = tasks.find(t => t.status === 'in_progress')
181
- const pendingTasks = tasks.filter(t => t.status === 'pending')
182
- const activeFeatures = features.filter(f => f.status === 'in_progress' || f.status === 'active')
199
+ const currentTask = tasks.find((t) => t.status === 'in_progress')
200
+ const pendingTasks = tasks.filter((t) => t.status === 'pending')
201
+ const activeFeatures = features.filter((f) => f.status === 'in_progress' || f.status === 'active')
183
202
 
184
203
  const deps = Object.keys(pkgData.dependencies)
185
204
  const devDeps = Object.keys(pkgData.devDependencies)
@@ -193,14 +212,20 @@ async function generateClaudeMd(
193
212
  ### Dependencies (${deps.length + devDeps.length})
194
213
 
195
214
  **Production** (${deps.length}):
196
- ${deps.length > 0 ? deps.map(d => `- ${d}: ${pkgData.dependencies[d]}`).join('\n') : '_None_'}
215
+ ${deps.length > 0 ? deps.map((d) => `- ${d}: ${pkgData.dependencies[d]}`).join('\n') : '_None_'}
197
216
 
198
217
  **Dev** (${devDeps.length}):
199
- ${devDeps.length > 0 ? devDeps.map(d => `- ${d}: ${pkgData.devDependencies[d]}`).join('\n') : '_None_'}
218
+ ${devDeps.length > 0 ? devDeps.map((d) => `- ${d}: ${pkgData.devDependencies[d]}`).join('\n') : '_None_'}
200
219
 
201
220
  ### Scripts
202
221
 
203
- ${Object.keys(pkgData.scripts).length > 0 ? Object.entries(pkgData.scripts).map(([k, v]) => `- \`${k}\`: ${v}`).join('\n') : '_None_'}
222
+ ${
223
+ Object.keys(pkgData.scripts).length > 0
224
+ ? Object.entries(pkgData.scripts)
225
+ .map(([k, v]) => `- \`${k}\`: ${v}`)
226
+ .join('\n')
227
+ : '_None_'
228
+ }
204
229
 
205
230
  ### Git
206
231
 
@@ -210,7 +235,14 @@ ${Object.keys(pkgData.scripts).length > 0 ? Object.entries(pkgData.scripts).map(
210
235
  - Uncommitted: ${gitData.hasChanges ? 'Yes' : 'No'}
211
236
 
212
237
  **Recent:**
213
- ${gitData.recentCommits.length > 0 ? gitData.recentCommits.slice(0, 5).map(c => `- \`${c.hash}\` ${c.message}`).join('\n') : '_None_'}
238
+ ${
239
+ gitData.recentCommits.length > 0
240
+ ? gitData.recentCommits
241
+ .slice(0, 5)
242
+ .map((c) => `- \`${c.hash}\` ${c.message}`)
243
+ .join('\n')
244
+ : '_None_'
245
+ }
214
246
 
215
247
  ---
216
248
 
@@ -219,19 +251,33 @@ ${gitData.recentCommits.length > 0 ? gitData.recentCommits.slice(0, 5).map(c =>
219
251
  **Now:** ${currentTask ? currentTask.description : '_No active task_'}
220
252
 
221
253
  **Queue (${pendingTasks.length}):**
222
- ${pendingTasks.length > 0 ? pendingTasks.slice(0, 10).map((t, i) => `${i + 1}. ${t.description}`).join('\n') : '_Empty_'}
254
+ ${
255
+ pendingTasks.length > 0
256
+ ? pendingTasks
257
+ .slice(0, 10)
258
+ .map((t, i) => `${i + 1}. ${t.description}`)
259
+ .join('\n')
260
+ : '_Empty_'
261
+ }
223
262
 
224
263
  **Active Features (${activeFeatures.length}):**
225
- ${activeFeatures.length > 0 ? activeFeatures.map(f => `- ${f.name}`).join('\n') : '_None_'}
264
+ ${activeFeatures.length > 0 ? activeFeatures.map((f) => `- ${f.name}`).join('\n') : '_None_'}
226
265
 
227
266
  **Ideas (${ideas.length}):**
228
- ${ideas.length > 0 ? ideas.slice(0, 5).map(i => `- ${i.title}`).join('\n') : '_None_'}
267
+ ${
268
+ ideas.length > 0
269
+ ? ideas
270
+ .slice(0, 5)
271
+ .map((i) => `- ${i.title}`)
272
+ .join('\n')
273
+ : '_None_'
274
+ }
229
275
 
230
276
  ---
231
277
 
232
278
  ## AGENTS
233
279
 
234
- ${agents.length > 0 ? agents.map(a => `- **${a.name}**: ${a.role || 'Specialist'}`).join('\n') : '_None_'}
280
+ ${agents.length > 0 ? agents.map((a) => `- **${a.name}**: ${a.role || 'Specialist'}`).join('\n') : '_None_'}
235
281
 
236
282
  ---
237
283
 
@@ -258,7 +304,7 @@ ${agents.length > 0 ? agents.map(a => `- **${a.name}**: ${a.role || 'Specialist'
258
304
  }
259
305
 
260
306
  async function generateNowMd(contextPath: string, tasks: Task[]) {
261
- const currentTask = tasks.find(t => t.status === 'in_progress')
307
+ const currentTask = tasks.find((t) => t.status === 'in_progress')
262
308
 
263
309
  const content = currentTask
264
310
  ? `# NOW
@@ -278,12 +324,15 @@ _No active task. Use /p:now to start._
278
324
  }
279
325
 
280
326
  async function generateQueueMd(contextPath: string, tasks: Task[]) {
281
- const pendingTasks = tasks.filter(t => t.status === 'pending')
327
+ const pendingTasks = tasks.filter((t) => t.status === 'pending')
282
328
 
283
329
  const content = `# QUEUE
284
330
 
285
- ${pendingTasks.length > 0
286
- ? pendingTasks.map((t, i) => `${i + 1}. ${t.description}${t.priority ? ` [${t.priority}]` : ''}`).join('\n')
331
+ ${
332
+ pendingTasks.length > 0
333
+ ? pendingTasks
334
+ .map((t, i) => `${i + 1}. ${t.description}${t.priority ? ` [${t.priority}]` : ''}`)
335
+ .join('\n')
287
336
  : '_Empty queue. Use /p:next to add tasks._'
288
337
  }
289
338
  `
@@ -11,12 +11,12 @@
11
11
  * @version 1.0.0
12
12
  */
13
13
 
14
- import fs from 'fs/promises'
15
- import path from 'path'
16
- import { exec as execCallback } from 'child_process'
17
- import { promisify } from 'util'
18
- import type { FilesToolOutput, ScoredFile, ScoreReason } from './types'
14
+ import { exec as execCallback } from 'node:child_process'
15
+ import fs from 'node:fs/promises'
16
+ import path from 'node:path'
17
+ import { promisify } from 'node:util'
19
18
  import { isNotFoundError } from '../types/fs'
19
+ import type { FilesToolOutput, ScoredFile, ScoreReason } from './types'
20
20
 
21
21
  const exec = promisify(execCallback)
22
22
 
@@ -270,7 +270,10 @@ export async function findRelevantFiles(
270
270
  */
271
271
  function extractKeywords(description: string): string[] {
272
272
  // Convert to lowercase and split by non-word characters
273
- const words = description.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)
273
+ const words = description
274
+ .toLowerCase()
275
+ .split(/[^a-z0-9]+/)
276
+ .filter(Boolean)
274
277
 
275
278
  // Remove common stop words
276
279
  const stopWords = new Set([
@@ -430,14 +433,14 @@ async function getGitRecency(
430
433
  for (const line of lines) {
431
434
  const match = line.match(/^(\d+)\s+(\d+)\s+(.+)$/)
432
435
  if (match) {
433
- const commits = parseInt(match[1])
434
- const timestamp = parseInt(match[2])
436
+ const commits = parseInt(match[1], 10)
437
+ const timestamp = parseInt(match[2], 10)
435
438
  const file = match[3]
436
439
  const daysAgo = Math.floor((now - timestamp) / 86400)
437
440
  recency.set(file, { commits, daysAgo })
438
441
  }
439
442
  }
440
- } catch (error) {
443
+ } catch (_error) {
441
444
  // Git not available or not a git repo
442
445
  }
443
446
 
@@ -459,7 +462,10 @@ function scoreFile(
459
462
  let importScore = 0
460
463
 
461
464
  const pathLower = filePath.toLowerCase()
462
- const pathParts = pathLower.split('/').join(' ').split(/[^a-z0-9]+/)
465
+ const pathParts = pathLower
466
+ .split('/')
467
+ .join(' ')
468
+ .split(/[^a-z0-9]+/)
463
469
 
464
470
  // Keyword matching (60% weight)
465
471
  for (const keyword of keywords) {
@@ -483,10 +489,7 @@ function scoreFile(
483
489
  if (pathLower.includes(domainKw)) {
484
490
  // Check if any task keyword relates to this domain
485
491
  const taskRelatesToDomain = keywords.some(
486
- (k) =>
487
- domainKeywords.includes(k) ||
488
- k.includes(domain) ||
489
- domain.includes(k)
492
+ (k) => domainKeywords.includes(k) || k.includes(domain) || domain.includes(k)
490
493
  )
491
494
  if (taskRelatesToDomain) {
492
495
  domainScore += 0.4
@@ -547,11 +550,7 @@ function scoreFile(
547
550
  }
548
551
 
549
552
  // Calculate weighted score
550
- const score =
551
- keywordScore * 0.6 +
552
- domainScore * 0.2 +
553
- recencyScore * 0.15 +
554
- importScore * 0.05
553
+ const score = keywordScore * 0.6 + domainScore * 0.2 + recencyScore * 0.15 + importScore * 0.05
555
554
 
556
555
  return {
557
556
  path: filePath,
@@ -10,17 +10,12 @@
10
10
  * @version 1.0.0
11
11
  */
12
12
 
13
- import fs from 'fs/promises'
14
- import path from 'path'
15
- import { exec as execCallback } from 'child_process'
16
- import { promisify } from 'util'
17
- import type {
18
- ImportsToolOutput,
19
- ImportRelation,
20
- ImportedBy,
21
- DependencyNode,
22
- } from './types'
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'
23
17
  import { isNotFoundError } from '../types/fs'
18
+ import type { DependencyNode, ImportedBy, ImportRelation, ImportsToolOutput } from './types'
24
19
 
25
20
  const exec = promisify(execCallback)
26
21
 
@@ -154,9 +149,7 @@ export async function analyzeImports(
154
149
  depth?: number // Dependency tree depth (0 = no tree)
155
150
  } = {}
156
151
  ): Promise<ImportsToolOutput> {
157
- const absolutePath = path.isAbsolute(filePath)
158
- ? filePath
159
- : path.join(projectPath, filePath)
152
+ const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(projectPath, filePath)
160
153
 
161
154
  // Read file content
162
155
  let content: string
@@ -196,11 +189,7 @@ export async function analyzeImports(
196
189
  // Build dependency tree if requested
197
190
  let dependencyTree: DependencyNode | undefined
198
191
  if (options.depth && options.depth > 0) {
199
- dependencyTree = await buildDependencyTree(
200
- filePath,
201
- projectPath,
202
- options.depth
203
- )
192
+ dependencyTree = await buildDependencyTree(filePath, projectPath, options.depth)
204
193
  }
205
194
 
206
195
  // Calculate metrics
@@ -285,11 +274,7 @@ function extractImports(
285
274
  /**
286
275
  * Resolve a relative import to an absolute path
287
276
  */
288
- function resolveImport(
289
- source: string,
290
- fromFile: string,
291
- projectPath: string
292
- ): string | null {
277
+ function resolveImport(source: string, fromFile: string, projectPath: string): string | null {
293
278
  const fileDir = path.dirname(fromFile)
294
279
 
295
280
  // Handle path alias like @/
@@ -313,13 +298,11 @@ function tryResolve(basePath: string, projectPath: string): string | null {
313
298
  const fullPath = basePath + ext
314
299
  try {
315
300
  // Check synchronously (we're in a hot path)
316
- const fs = require('fs')
301
+ const fs = require('node:fs')
317
302
  if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
318
303
  return path.relative(projectPath, fullPath)
319
304
  }
320
- } catch {
321
- continue
322
- }
305
+ } catch {}
323
306
  }
324
307
 
325
308
  return null
@@ -328,15 +311,12 @@ function tryResolve(basePath: string, projectPath: string): string | null {
328
311
  /**
329
312
  * Find all files that import the target file
330
313
  */
331
- async function findImportedBy(
332
- filePath: string,
333
- projectPath: string
334
- ): Promise<ImportedBy[]> {
314
+ async function findImportedBy(filePath: string, projectPath: string): Promise<ImportedBy[]> {
335
315
  const importedBy: ImportedBy[] = []
336
316
 
337
317
  // Get the base name without extension for matching
338
318
  const baseName = path.basename(filePath, path.extname(filePath))
339
- const dirName = path.dirname(filePath)
319
+ const _dirName = path.dirname(filePath)
340
320
 
341
321
  try {
342
322
  // Use ripgrep if available, otherwise grep
@@ -364,7 +344,7 @@ async function findImportedBy(
364
344
  for (const file of files) {
365
345
  importedBy.push({ file })
366
346
  }
367
- } catch (error) {
347
+ } catch (_error) {
368
348
  // grep not available or error - return empty
369
349
  }
370
350