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.
- package/CHANGELOG.md +75 -0
- package/bin/prjct.ts +117 -10
- package/core/__tests__/agentic/memory-system.test.ts +39 -26
- package/core/__tests__/agentic/plan-mode.test.ts +64 -46
- package/core/__tests__/agentic/prompt-builder.test.ts +14 -14
- package/core/__tests__/services/project-index.test.ts +353 -0
- package/core/__tests__/types/fs.test.ts +3 -3
- package/core/__tests__/utils/date-helper.test.ts +10 -10
- package/core/__tests__/utils/output.test.ts +9 -6
- package/core/__tests__/utils/project-commands.test.ts +5 -6
- package/core/agentic/agent-router.ts +9 -10
- package/core/agentic/chain-of-thought.ts +16 -4
- package/core/agentic/command-executor.ts +66 -40
- package/core/agentic/context-builder.ts +8 -5
- package/core/agentic/ground-truth.ts +15 -9
- package/core/agentic/index.ts +145 -152
- package/core/agentic/loop-detector.ts +40 -11
- package/core/agentic/memory-system.ts +98 -35
- package/core/agentic/orchestrator-executor.ts +135 -71
- package/core/agentic/plan-mode.ts +46 -16
- package/core/agentic/prompt-builder.ts +108 -42
- package/core/agentic/services.ts +10 -9
- package/core/agentic/skill-loader.ts +9 -15
- package/core/agentic/smart-context.ts +129 -79
- package/core/agentic/template-executor.ts +13 -12
- package/core/agentic/template-loader.ts +7 -4
- package/core/agentic/tool-registry.ts +16 -13
- package/core/agents/index.ts +1 -1
- package/core/agents/performance.ts +10 -27
- package/core/ai-tools/formatters.ts +8 -6
- package/core/ai-tools/generator.ts +4 -4
- package/core/ai-tools/index.ts +1 -1
- package/core/ai-tools/registry.ts +21 -11
- package/core/bus/bus.ts +23 -16
- package/core/bus/index.ts +2 -2
- package/core/cli/linear.ts +3 -5
- package/core/cli/start.ts +28 -25
- package/core/commands/analysis.ts +58 -39
- package/core/commands/analytics.ts +52 -44
- package/core/commands/base.ts +15 -13
- package/core/commands/cleanup.ts +6 -13
- package/core/commands/command-data.ts +28 -4
- package/core/commands/commands.ts +57 -24
- package/core/commands/context.ts +4 -4
- package/core/commands/design.ts +3 -10
- package/core/commands/index.ts +5 -8
- package/core/commands/maintenance.ts +7 -4
- package/core/commands/planning.ts +179 -56
- package/core/commands/register.ts +13 -9
- package/core/commands/registry.ts +15 -14
- package/core/commands/setup.ts +26 -14
- package/core/commands/shipping.ts +11 -16
- package/core/commands/snapshots.ts +16 -32
- package/core/commands/uninstall.ts +541 -0
- package/core/commands/workflow.ts +24 -28
- package/core/constants/index.ts +10 -22
- package/core/context/generator.ts +82 -33
- package/core/context-tools/files-tool.ts +18 -19
- package/core/context-tools/imports-tool.ts +13 -33
- package/core/context-tools/index.ts +29 -54
- package/core/context-tools/recent-tool.ts +16 -22
- package/core/context-tools/signatures-tool.ts +17 -26
- package/core/context-tools/summary-tool.ts +20 -22
- package/core/context-tools/token-counter.ts +25 -20
- package/core/context-tools/types.ts +5 -5
- package/core/domain/agent-generator.ts +7 -5
- package/core/domain/agent-loader.ts +2 -2
- package/core/domain/analyzer.ts +19 -16
- package/core/domain/architecture-generator.ts +6 -3
- package/core/domain/context-estimator.ts +3 -4
- package/core/domain/snapshot-manager.ts +25 -22
- package/core/domain/task-stack.ts +24 -14
- package/core/errors.ts +1 -1
- package/core/events/events.ts +2 -4
- package/core/events/index.ts +1 -2
- package/core/index.ts +28 -16
- package/core/infrastructure/agent-detector.ts +3 -3
- package/core/infrastructure/ai-provider.ts +23 -20
- package/core/infrastructure/author-detector.ts +16 -10
- package/core/infrastructure/capability-installer.ts +2 -2
- package/core/infrastructure/claude-agent.ts +6 -6
- package/core/infrastructure/command-installer.ts +22 -17
- package/core/infrastructure/config-manager.ts +18 -14
- package/core/infrastructure/editors-config.ts +8 -4
- package/core/infrastructure/path-manager.ts +8 -6
- package/core/infrastructure/permission-manager.ts +20 -17
- package/core/infrastructure/setup.ts +42 -38
- package/core/infrastructure/update-checker.ts +5 -5
- package/core/integrations/issue-tracker/enricher.ts +8 -19
- package/core/integrations/issue-tracker/index.ts +2 -2
- package/core/integrations/issue-tracker/manager.ts +15 -15
- package/core/integrations/issue-tracker/types.ts +5 -22
- package/core/integrations/jira/client.ts +67 -59
- package/core/integrations/jira/index.ts +11 -14
- package/core/integrations/jira/mcp-adapter.ts +5 -10
- package/core/integrations/jira/service.ts +10 -10
- package/core/integrations/linear/client.ts +27 -18
- package/core/integrations/linear/index.ts +9 -12
- package/core/integrations/linear/service.ts +11 -11
- package/core/integrations/linear/sync.ts +8 -8
- package/core/outcomes/analyzer.ts +5 -18
- package/core/outcomes/index.ts +2 -2
- package/core/outcomes/recorder.ts +3 -3
- package/core/plugin/builtin/webhook.ts +19 -15
- package/core/plugin/hooks.ts +29 -21
- package/core/plugin/index.ts +7 -7
- package/core/plugin/loader.ts +19 -19
- package/core/plugin/registry.ts +12 -23
- package/core/schemas/agents.ts +1 -1
- package/core/schemas/analysis.ts +1 -1
- package/core/schemas/enriched-task.ts +62 -49
- package/core/schemas/ideas.ts +13 -13
- package/core/schemas/index.ts +17 -27
- package/core/schemas/issues.ts +40 -25
- package/core/schemas/metrics.ts +25 -25
- package/core/schemas/outcomes.ts +70 -62
- package/core/schemas/permissions.ts +15 -12
- package/core/schemas/prd.ts +27 -14
- package/core/schemas/project.ts +3 -3
- package/core/schemas/roadmap.ts +47 -34
- package/core/schemas/schemas.ts +3 -4
- package/core/schemas/shipped.ts +3 -3
- package/core/schemas/state.ts +43 -29
- package/core/server/index.ts +5 -6
- package/core/server/routes-extended.ts +68 -72
- package/core/server/routes.ts +3 -3
- package/core/server/server.ts +31 -26
- package/core/services/agent-generator.ts +237 -0
- package/core/services/agent-service.ts +2 -2
- package/core/services/breakdown-service.ts +2 -4
- package/core/services/context-generator.ts +299 -0
- package/core/services/context-selector.ts +420 -0
- package/core/services/doctor-service.ts +426 -0
- package/core/services/file-categorizer.ts +448 -0
- package/core/services/file-scorer.ts +270 -0
- package/core/services/git-analyzer.ts +267 -0
- package/core/services/index.ts +27 -10
- package/core/services/memory-service.ts +3 -4
- package/core/services/project-index.ts +911 -0
- package/core/services/project-service.ts +4 -4
- package/core/services/skill-installer.ts +14 -17
- package/core/services/skill-lock.ts +3 -3
- package/core/services/skill-service.ts +12 -6
- package/core/services/stack-detector.ts +245 -0
- package/core/services/sync-service.ts +87 -345
- package/core/services/watch-service.ts +294 -0
- package/core/session/compaction.ts +23 -31
- package/core/session/index.ts +11 -5
- package/core/session/log-migration.ts +3 -3
- package/core/session/metrics.ts +19 -14
- package/core/session/session-log-manager.ts +12 -17
- package/core/session/task-session-manager.ts +25 -25
- package/core/session/utils.ts +1 -1
- package/core/storage/ideas-storage.ts +41 -57
- package/core/storage/index-storage.ts +514 -0
- package/core/storage/index.ts +41 -17
- package/core/storage/metrics-storage.ts +39 -34
- package/core/storage/queue-storage.ts +35 -45
- package/core/storage/shipped-storage.ts +17 -20
- package/core/storage/state-storage.ts +50 -30
- package/core/storage/storage-manager.ts +6 -6
- package/core/storage/storage.ts +18 -15
- package/core/sync/auth-config.ts +3 -3
- package/core/sync/index.ts +13 -19
- package/core/sync/oauth-handler.ts +3 -3
- package/core/sync/sync-client.ts +4 -9
- package/core/sync/sync-manager.ts +12 -14
- package/core/types/commands.ts +42 -7
- package/core/types/index.ts +284 -305
- package/core/types/integrations.ts +3 -3
- package/core/types/storage.ts +14 -14
- package/core/types/utils.ts +3 -3
- package/core/utils/agent-stream.ts +3 -1
- package/core/utils/animations.ts +14 -11
- package/core/utils/branding.ts +7 -7
- package/core/utils/cache.ts +1 -3
- package/core/utils/collection-filters.ts +3 -15
- package/core/utils/date-helper.ts +2 -7
- package/core/utils/file-helper.ts +13 -8
- package/core/utils/jsonl-helper.ts +13 -10
- package/core/utils/keychain.ts +4 -8
- package/core/utils/logger.ts +1 -1
- package/core/utils/next-steps.ts +3 -3
- package/core/utils/output.ts +58 -11
- package/core/utils/project-commands.ts +6 -6
- package/core/utils/project-credentials.ts +5 -12
- package/core/utils/runtime.ts +2 -2
- package/core/utils/session-helper.ts +3 -4
- package/core/utils/version.ts +3 -3
- package/core/wizard/index.ts +13 -0
- package/core/wizard/onboarding.ts +633 -0
- package/core/workflow/state-machine.ts +7 -7
- package/dist/bin/prjct.mjs +18755 -15574
- package/dist/core/infrastructure/command-installer.js +86 -79
- package/dist/core/infrastructure/editors-config.js +6 -6
- package/dist/core/infrastructure/setup.js +246 -225
- package/dist/core/utils/version.js +9 -9
- package/package.json +11 -12
- package/scripts/build.js +3 -3
- package/scripts/postinstall.js +2 -2
- package/templates/mcp-config.json +6 -1
- package/templates/permissions/permissive.jsonc +1 -1
- package/templates/permissions/strict.jsonc +5 -9
- package/templates/global/docs/agents.md +0 -88
- package/templates/global/docs/architecture.md +0 -103
- package/templates/global/docs/commands.md +0 -96
- 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 {
|
|
24
|
-
import { workflowStateMachine } from '../workflow/state-machine'
|
|
12
|
+
import { templateExecutor } from '../agentic/template-executor'
|
|
25
13
|
import { linearService } from '../integrations/linear'
|
|
26
|
-
import {
|
|
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(
|
|
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
|
|
89
|
-
? availableAgents.join(', ')
|
|
90
|
-
: 'none (run p. sync)'
|
|
81
|
+
const _agentsList =
|
|
82
|
+
availableAgents.length > 0 ? availableAgents.join(', ') : 'none (run p. sync)'
|
|
91
83
|
|
|
92
|
-
|
|
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(
|
|
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
|
package/core/constants/index.ts
CHANGED
|
@@ -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(
|
|
24
|
+
lines.push(
|
|
25
|
+
`Agent: ${agent}${confidence ? ` (${Math.round(confidence * 100)}% confidence)` : ''}`
|
|
26
|
+
)
|
|
31
27
|
}
|
|
32
|
-
return lines.join('\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')
|
|
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')
|
|
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
|
|
15
|
-
import
|
|
16
|
-
import
|
|
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
|
|
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(
|
|
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', {
|
|
130
|
-
|
|
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(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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
|
-
${
|
|
286
|
-
|
|
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
|
|
15
|
-
import
|
|
16
|
-
import
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
14
|
-
import
|
|
15
|
-
import
|
|
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
|
|
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 (
|
|
347
|
+
} catch (_error) {
|
|
368
348
|
// grep not available or error - return empty
|
|
369
349
|
}
|
|
370
350
|
|