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
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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 ?
|
|
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
|
|
17
|
-
import
|
|
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(
|
|
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 (
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
|
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,
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
14
|
-
import
|
|
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(
|
|
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
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
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
|
|
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:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
|
|
@@ -17,23 +17,22 @@
|
|
|
17
17
|
* - scripts/postinstall.js (if npm scripts are enabled)
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { execSync } from 'child_process'
|
|
21
|
-
import fs from 'fs'
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
import installer from './command-installer'
|
|
25
|
-
import editorsConfig from './editors-config'
|
|
26
|
-
import { VERSION, getPackageRoot } from '../utils/version'
|
|
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'
|
|
27
24
|
import { isNotFoundError } from '../types/fs'
|
|
25
|
+
import type { AIProviderConfig, AIProviderName } from '../types/provider'
|
|
26
|
+
import { getPackageRoot, VERSION } from '../utils/version'
|
|
28
27
|
import {
|
|
29
|
-
selectProvider,
|
|
30
|
-
detectProvider,
|
|
31
28
|
detectAllProviders,
|
|
32
29
|
detectAntigravity,
|
|
30
|
+
detectProvider,
|
|
33
31
|
Providers,
|
|
34
|
-
|
|
32
|
+
selectProvider,
|
|
35
33
|
} from './ai-provider'
|
|
36
|
-
import
|
|
34
|
+
import installer from './command-installer'
|
|
35
|
+
import editorsConfig from './editors-config'
|
|
37
36
|
|
|
38
37
|
// Colors
|
|
39
38
|
const GREEN = '\x1b[32m'
|
|
@@ -50,8 +49,8 @@ interface ProviderSetupResult {
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
interface SetupResults {
|
|
53
|
-
provider: AIProviderName
|
|
54
|
-
providers: ProviderSetupResult[]
|
|
52
|
+
provider: AIProviderName // Primary provider (for backward compat)
|
|
53
|
+
providers: ProviderSetupResult[] // All installed providers
|
|
55
54
|
cliInstalled: boolean
|
|
56
55
|
commandsAdded: number
|
|
57
56
|
commandsUpdated: number
|
|
@@ -61,7 +60,7 @@ interface SetupResults {
|
|
|
61
60
|
/**
|
|
62
61
|
* Check if an AI CLI is installed
|
|
63
62
|
*/
|
|
64
|
-
async function
|
|
63
|
+
async function _hasAICLI(provider: AIProviderConfig): Promise<boolean> {
|
|
65
64
|
const detection = detectProvider(provider.name)
|
|
66
65
|
return detection.installed
|
|
67
66
|
}
|
|
@@ -70,9 +69,8 @@ async function hasAICLI(provider: AIProviderConfig): Promise<boolean> {
|
|
|
70
69
|
* Install AI CLI for the specified provider
|
|
71
70
|
*/
|
|
72
71
|
async function installAICLI(provider: AIProviderConfig): Promise<boolean> {
|
|
73
|
-
const packageName =
|
|
74
|
-
? '@anthropic-ai/claude-code'
|
|
75
|
-
: '@google/gemini-cli'
|
|
72
|
+
const packageName =
|
|
73
|
+
provider.name === 'claude' ? '@anthropic-ai/claude-code' : '@google/gemini-cli'
|
|
76
74
|
|
|
77
75
|
try {
|
|
78
76
|
console.log(`${YELLOW}📦 ${provider.displayName} not found. Installing...${NC}`)
|
|
@@ -83,7 +81,9 @@ async function installAICLI(provider: AIProviderConfig): Promise<boolean> {
|
|
|
83
81
|
console.log('')
|
|
84
82
|
return true
|
|
85
83
|
} catch (error) {
|
|
86
|
-
console.log(
|
|
84
|
+
console.log(
|
|
85
|
+
`${YELLOW}⚠️ Failed to install ${provider.displayName}: ${(error as Error).message}${NC}`
|
|
86
|
+
)
|
|
87
87
|
console.log(`${DIM}Please install manually: npm install -g ${packageName}${NC}`)
|
|
88
88
|
console.log('')
|
|
89
89
|
return false
|
|
@@ -97,7 +97,7 @@ export async function run(): Promise<SetupResults> {
|
|
|
97
97
|
// Step 0: Detect all available providers
|
|
98
98
|
const detection = detectAllProviders()
|
|
99
99
|
const selection = selectProvider()
|
|
100
|
-
const
|
|
100
|
+
const _primaryProvider = Providers[selection.provider]
|
|
101
101
|
|
|
102
102
|
const results: SetupResults = {
|
|
103
103
|
provider: selection.provider,
|
|
@@ -281,7 +281,7 @@ async function installGeminiGlobalConfig(): Promise<{ success: boolean; action:
|
|
|
281
281
|
|
|
282
282
|
if (!hasMarkers) {
|
|
283
283
|
// No markers - append prjct section at the end
|
|
284
|
-
const updatedContent = existingContent
|
|
284
|
+
const updatedContent = `${existingContent}\n\n${templateContent}`
|
|
285
285
|
fs.writeFileSync(globalConfigPath, updatedContent, 'utf-8')
|
|
286
286
|
return { success: true, action: 'appended' }
|
|
287
287
|
}
|
|
@@ -317,7 +317,10 @@ async function installGeminiGlobalConfig(): Promise<{ success: boolean; action:
|
|
|
317
317
|
* Antigravity uses SKILL.md files in ~/.gemini/antigravity/skills/
|
|
318
318
|
* This is the recommended integration method (not MCP).
|
|
319
319
|
*/
|
|
320
|
-
export async function installAntigravitySkill(): Promise<{
|
|
320
|
+
export async function installAntigravitySkill(): Promise<{
|
|
321
|
+
success: boolean
|
|
322
|
+
action: string | null
|
|
323
|
+
}> {
|
|
321
324
|
try {
|
|
322
325
|
const antigravitySkillsDir = path.join(os.homedir(), '.gemini', 'antigravity', 'skills')
|
|
323
326
|
const prjctSkillDir = path.join(antigravitySkillsDir, 'prjct')
|
|
@@ -408,8 +411,7 @@ export async function installCursorProject(projectRoot: string): Promise<{
|
|
|
408
411
|
// Copy individual command files → .cursor/commands/
|
|
409
412
|
// This enables /sync, /task, /done, /ship, etc. syntax in Cursor
|
|
410
413
|
if (fs.existsSync(cursorCommandsSource)) {
|
|
411
|
-
const commandFiles = fs.readdirSync(cursorCommandsSource)
|
|
412
|
-
.filter(f => f.endsWith('.md'))
|
|
414
|
+
const commandFiles = fs.readdirSync(cursorCommandsSource).filter((f) => f.endsWith('.md'))
|
|
413
415
|
|
|
414
416
|
for (const file of commandFiles) {
|
|
415
417
|
const src = path.join(cursorCommandsSource, file)
|
|
@@ -469,8 +471,8 @@ async function addCursorToGitignore(projectRoot: string): Promise<boolean> {
|
|
|
469
471
|
|
|
470
472
|
// Append to .gitignore
|
|
471
473
|
const newContent = fileExists
|
|
472
|
-
? content.trimEnd()
|
|
473
|
-
: entriesToAdd.join('\n')
|
|
474
|
+
? `${content.trimEnd()}\n\n${entriesToAdd.join('\n')}\n`
|
|
475
|
+
: `${entriesToAdd.join('\n')}\n`
|
|
474
476
|
|
|
475
477
|
fs.writeFileSync(gitignorePath, newContent, 'utf-8')
|
|
476
478
|
return true
|
|
@@ -537,7 +539,12 @@ export async function installWindsurfProject(projectRoot: string): Promise<{
|
|
|
537
539
|
const routerDest = path.join(rulesDir, 'prjct.md')
|
|
538
540
|
|
|
539
541
|
const routerSource = path.join(getPackageRoot(), 'templates', 'windsurf', 'router.md')
|
|
540
|
-
const windsurfWorkflowsSource = path.join(
|
|
542
|
+
const windsurfWorkflowsSource = path.join(
|
|
543
|
+
getPackageRoot(),
|
|
544
|
+
'templates',
|
|
545
|
+
'windsurf',
|
|
546
|
+
'workflows'
|
|
547
|
+
)
|
|
541
548
|
|
|
542
549
|
// Ensure directories exist
|
|
543
550
|
fs.mkdirSync(rulesDir, { recursive: true })
|
|
@@ -552,8 +559,7 @@ export async function installWindsurfProject(projectRoot: string): Promise<{
|
|
|
552
559
|
// Copy individual workflow files → .windsurf/workflows/
|
|
553
560
|
// This enables /sync, /task, /done, /ship, etc. syntax in Windsurf
|
|
554
561
|
if (fs.existsSync(windsurfWorkflowsSource)) {
|
|
555
|
-
const workflowFiles = fs.readdirSync(windsurfWorkflowsSource)
|
|
556
|
-
.filter(f => f.endsWith('.md'))
|
|
562
|
+
const workflowFiles = fs.readdirSync(windsurfWorkflowsSource).filter((f) => f.endsWith('.md'))
|
|
557
563
|
|
|
558
564
|
for (const file of workflowFiles) {
|
|
559
565
|
const src = path.join(windsurfWorkflowsSource, file)
|
|
@@ -613,8 +619,8 @@ async function addWindsurfToGitignore(projectRoot: string): Promise<boolean> {
|
|
|
613
619
|
|
|
614
620
|
// Append to .gitignore
|
|
615
621
|
const newContent = fileExists
|
|
616
|
-
? content.trimEnd()
|
|
617
|
-
: entriesToAdd.join('\n')
|
|
622
|
+
? `${content.trimEnd()}\n\n${entriesToAdd.join('\n')}\n`
|
|
623
|
+
: `${entriesToAdd.join('\n')}\n`
|
|
618
624
|
|
|
619
625
|
fs.writeFileSync(gitignorePath, newContent, 'utf-8')
|
|
620
626
|
return true
|
|
@@ -654,9 +660,10 @@ async function migrateProjectsCliVersion(): Promise<void> {
|
|
|
654
660
|
return
|
|
655
661
|
}
|
|
656
662
|
|
|
657
|
-
const projectDirs = fs
|
|
658
|
-
.
|
|
659
|
-
.
|
|
663
|
+
const projectDirs = fs
|
|
664
|
+
.readdirSync(projectsDir, { withFileTypes: true })
|
|
665
|
+
.filter((dirent) => dirent.isDirectory())
|
|
666
|
+
.map((dirent) => dirent.name)
|
|
660
667
|
|
|
661
668
|
let migrated = 0
|
|
662
669
|
|
|
@@ -795,10 +802,7 @@ async function installStatusLine(): Promise<void> {
|
|
|
795
802
|
if (fs.existsSync(sourceScript)) {
|
|
796
803
|
// Copy script and update version
|
|
797
804
|
let scriptContent = fs.readFileSync(sourceScript, 'utf8')
|
|
798
|
-
scriptContent = scriptContent.replace(
|
|
799
|
-
/CLI_VERSION="[^"]*"/,
|
|
800
|
-
`CLI_VERSION="${VERSION}"`
|
|
801
|
-
)
|
|
805
|
+
scriptContent = scriptContent.replace(/CLI_VERSION="[^"]*"/, `CLI_VERSION="${VERSION}"`)
|
|
802
806
|
fs.writeFileSync(prjctStatusLinePath, scriptContent, { mode: 0o755 })
|
|
803
807
|
|
|
804
808
|
// Copy lib/ modules
|
|
@@ -910,7 +914,7 @@ function ensureStatusLineSymlink(linkPath: string, targetPath: string): void {
|
|
|
910
914
|
}
|
|
911
915
|
// Create symlink
|
|
912
916
|
fs.symlinkSync(targetPath, linkPath)
|
|
913
|
-
} catch (
|
|
917
|
+
} catch (_error) {
|
|
914
918
|
// If symlink fails (e.g., Windows, permission issues), try copy instead
|
|
915
919
|
try {
|
|
916
920
|
if (fs.existsSync(targetPath)) {
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* @version 0.5.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
7
|
+
import fs from 'node:fs'
|
|
8
|
+
import https from 'node:https'
|
|
9
|
+
import os from 'node:os'
|
|
10
|
+
import path from 'node:path'
|
|
11
11
|
import chalk from 'chalk'
|
|
12
12
|
|
|
13
13
|
interface UpdateCache {
|
|
@@ -162,7 +162,7 @@ class UpdateChecker {
|
|
|
162
162
|
const cache = this.readCache()
|
|
163
163
|
const now = Date.now()
|
|
164
164
|
|
|
165
|
-
if (cache
|
|
165
|
+
if (cache?.lastCheck && now - cache.lastCheck < this.checkInterval) {
|
|
166
166
|
// Cache is still valid
|
|
167
167
|
if (cache.latestVersion && this.compareVersions(cache.latestVersion, currentVersion) > 0) {
|
|
168
168
|
return {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* The actual AI call happens through Claude Code's execution context.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type {
|
|
9
|
+
import type { EnrichedIssue, Issue } from './types'
|
|
10
10
|
|
|
11
11
|
// =============================================================================
|
|
12
12
|
// Enrichment Templates
|
|
@@ -81,13 +81,9 @@ export function generateLLMPrompt(
|
|
|
81
81
|
projectContext: ProjectContext,
|
|
82
82
|
enrichment: EnrichmentResult
|
|
83
83
|
): string {
|
|
84
|
-
const filesList = enrichment.affectedFiles
|
|
85
|
-
.map(f => `- ${f}`)
|
|
86
|
-
.join('\n')
|
|
84
|
+
const filesList = enrichment.affectedFiles.map((f) => `- ${f}`).join('\n')
|
|
87
85
|
|
|
88
|
-
const acList = enrichment.acceptanceCriteria
|
|
89
|
-
.map(ac => `- [ ] ${ac}`)
|
|
90
|
-
.join('\n')
|
|
86
|
+
const acList = enrichment.acceptanceCriteria.map((ac) => `- [ ] ${ac}`).join('\n')
|
|
91
87
|
|
|
92
88
|
return `## Task: ${issue.title}
|
|
93
89
|
|
|
@@ -150,10 +146,7 @@ export interface EnrichmentResult {
|
|
|
150
146
|
/**
|
|
151
147
|
* Build enriched issue from result
|
|
152
148
|
*/
|
|
153
|
-
export function buildEnrichedIssue(
|
|
154
|
-
issue: Issue,
|
|
155
|
-
enrichment: EnrichmentResult
|
|
156
|
-
): EnrichedIssue {
|
|
149
|
+
export function buildEnrichedIssue(issue: Issue, enrichment: EnrichmentResult): EnrichedIssue {
|
|
157
150
|
return {
|
|
158
151
|
...issue,
|
|
159
152
|
enrichment: {
|
|
@@ -213,8 +206,8 @@ export function formatEnrichmentAsMarkdown(enrichment: EnrichmentResult): string
|
|
|
213
206
|
export function parseEnrichmentResponse(response: string): EnrichmentResult | null {
|
|
214
207
|
try {
|
|
215
208
|
// Extract JSON from response (may be wrapped in markdown code block)
|
|
216
|
-
const jsonMatch =
|
|
217
|
-
|
|
209
|
+
const jsonMatch =
|
|
210
|
+
response.match(/```(?:json)?\s*([\s\S]*?)```/) || response.match(/\{[\s\S]*\}/)
|
|
218
211
|
|
|
219
212
|
if (!jsonMatch) {
|
|
220
213
|
console.error('[enricher] No JSON found in response')
|
|
@@ -232,12 +225,8 @@ export function parseEnrichmentResponse(response: string): EnrichmentResult | nu
|
|
|
232
225
|
|
|
233
226
|
return {
|
|
234
227
|
description: parsed.description,
|
|
235
|
-
acceptanceCriteria: Array.isArray(parsed.acceptanceCriteria)
|
|
236
|
-
|
|
237
|
-
: [],
|
|
238
|
-
affectedFiles: Array.isArray(parsed.affectedFiles)
|
|
239
|
-
? parsed.affectedFiles
|
|
240
|
-
: [],
|
|
228
|
+
acceptanceCriteria: Array.isArray(parsed.acceptanceCriteria) ? parsed.acceptanceCriteria : [],
|
|
229
|
+
affectedFiles: Array.isArray(parsed.affectedFiles) ? parsed.affectedFiles : [],
|
|
241
230
|
technicalNotes: parsed.technicalNotes || '',
|
|
242
231
|
estimatedComplexity: parsed.estimatedComplexity,
|
|
243
232
|
suggestedApproach: parsed.suggestedApproach,
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* Unified interface for Linear, Jira, Monday, and other issue trackers.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export * from './types'
|
|
7
6
|
export * from './enricher'
|
|
8
|
-
export {
|
|
7
|
+
export { IssueTrackerManager, issueTrackerManager } from './manager'
|
|
8
|
+
export * from './types'
|