prjct-cli 1.3.0 → 1.5.0
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 +134 -0
- package/core/__tests__/ai-tools/formatters.test.ts +118 -0
- package/core/ai-tools/formatters.ts +18 -0
- package/core/commands/analysis.ts +22 -0
- package/core/index.ts +91 -10
- package/core/services/context-generator.ts +12 -3
- package/core/services/sync-service.ts +77 -3
- package/core/services/sync-verifier.ts +273 -0
- package/core/types/config.ts +14 -0
- package/core/utils/citations.ts +53 -0
- package/core/utils/error-messages.ts +11 -0
- package/dist/bin/prjct.mjs +721 -342
- package/package.json +1 -1
|
@@ -30,11 +30,13 @@ import commandInstaller from '../infrastructure/command-installer'
|
|
|
30
30
|
import configManager from '../infrastructure/config-manager'
|
|
31
31
|
import pathManager from '../infrastructure/path-manager'
|
|
32
32
|
import { metricsStorage } from '../storage/metrics-storage'
|
|
33
|
+
import { type ContextSources, defaultSources, type SourceInfo } from '../utils/citations'
|
|
33
34
|
import dateHelper from '../utils/date-helper'
|
|
34
35
|
import { ContextFileGenerator } from './context-generator'
|
|
35
36
|
import type { SyncDiff } from './diff-generator'
|
|
36
37
|
import { localStateGenerator } from './local-state-generator'
|
|
37
38
|
import { type StackDetection, StackDetector } from './stack-detector'
|
|
39
|
+
import { syncVerifier, type VerificationReport } from './sync-verifier'
|
|
38
40
|
|
|
39
41
|
const execAsync = promisify(exec)
|
|
40
42
|
|
|
@@ -106,6 +108,7 @@ interface SyncResult {
|
|
|
106
108
|
contextFiles: string[]
|
|
107
109
|
aiTools: AIToolResult[]
|
|
108
110
|
syncMetrics?: SyncMetrics
|
|
111
|
+
verification?: VerificationReport
|
|
109
112
|
error?: string
|
|
110
113
|
// Preview mode fields
|
|
111
114
|
isPreview?: boolean
|
|
@@ -200,7 +203,8 @@ class SyncService {
|
|
|
200
203
|
// 4. Generate all files (depends on gathered data)
|
|
201
204
|
const agents = await this.generateAgents(stack, stats)
|
|
202
205
|
const skills = this.configureSkills(agents)
|
|
203
|
-
const
|
|
206
|
+
const sources = this.buildSources(stats, commands)
|
|
207
|
+
const contextFiles = await this.generateContextFiles(git, stats, commands, agents, sources)
|
|
204
208
|
|
|
205
209
|
// 5. Generate AI tool context files (multi-agent output)
|
|
206
210
|
const projectContext: ProjectContext = {
|
|
@@ -221,6 +225,7 @@ class SyncService {
|
|
|
221
225
|
workflow: agents.filter((a) => a.type === 'workflow').map((a) => a.name),
|
|
222
226
|
domain: agents.filter((a) => a.type === 'domain').map((a) => a.name),
|
|
223
227
|
},
|
|
228
|
+
sources,
|
|
224
229
|
}
|
|
225
230
|
|
|
226
231
|
const aiToolResults = await generateAIToolContexts(
|
|
@@ -246,6 +251,19 @@ class SyncService {
|
|
|
246
251
|
await commandInstaller.installGlobalConfig()
|
|
247
252
|
await commandInstaller.syncCommands()
|
|
248
253
|
|
|
254
|
+
// 11. Run verification checks (built-in + custom from config)
|
|
255
|
+
let verification: VerificationReport | undefined
|
|
256
|
+
try {
|
|
257
|
+
const localConfig = await configManager.readConfig(this.projectPath)
|
|
258
|
+
verification = await syncVerifier.verify(
|
|
259
|
+
this.projectPath,
|
|
260
|
+
this.globalPath,
|
|
261
|
+
localConfig?.verification
|
|
262
|
+
)
|
|
263
|
+
} catch {
|
|
264
|
+
// Verification is non-critical — don't fail sync
|
|
265
|
+
}
|
|
266
|
+
|
|
249
267
|
return {
|
|
250
268
|
success: true,
|
|
251
269
|
projectId: this.projectId,
|
|
@@ -263,6 +281,7 @@ class SyncService {
|
|
|
263
281
|
success: r.success,
|
|
264
282
|
})),
|
|
265
283
|
syncMetrics,
|
|
284
|
+
verification,
|
|
266
285
|
}
|
|
267
286
|
} catch (error) {
|
|
268
287
|
return {
|
|
@@ -519,6 +538,54 @@ class SyncService {
|
|
|
519
538
|
return commands
|
|
520
539
|
}
|
|
521
540
|
|
|
541
|
+
// ==========================================================================
|
|
542
|
+
// SOURCE CITATIONS
|
|
543
|
+
// ==========================================================================
|
|
544
|
+
|
|
545
|
+
private buildSources(stats: ProjectStats, commands: Commands): ContextSources {
|
|
546
|
+
const sources = defaultSources()
|
|
547
|
+
|
|
548
|
+
// Determine ecosystem source file
|
|
549
|
+
const ecosystemFiles: Record<string, string> = {
|
|
550
|
+
JavaScript: 'package.json',
|
|
551
|
+
Rust: 'Cargo.toml',
|
|
552
|
+
Go: 'go.mod',
|
|
553
|
+
Python: 'pyproject.toml',
|
|
554
|
+
}
|
|
555
|
+
const ecosystemFile = ecosystemFiles[stats.ecosystem] || 'filesystem'
|
|
556
|
+
const detected = (file: string): SourceInfo => ({ file, type: 'detected' })
|
|
557
|
+
const inferred = (file: string): SourceInfo => ({ file, type: 'inferred' })
|
|
558
|
+
|
|
559
|
+
sources.ecosystem = detected(ecosystemFile)
|
|
560
|
+
sources.name = detected(ecosystemFile)
|
|
561
|
+
sources.version = detected(ecosystemFile)
|
|
562
|
+
sources.languages = detected(ecosystemFile)
|
|
563
|
+
sources.frameworks = detected(ecosystemFile)
|
|
564
|
+
|
|
565
|
+
// Commands source is the lock file or ecosystem file
|
|
566
|
+
if (commands.install.startsWith('bun')) {
|
|
567
|
+
sources.commands = detected('bun.lockb')
|
|
568
|
+
} else if (commands.install.startsWith('pnpm')) {
|
|
569
|
+
sources.commands = detected('pnpm-lock.yaml')
|
|
570
|
+
} else if (commands.install === 'yarn') {
|
|
571
|
+
sources.commands = detected('yarn.lock')
|
|
572
|
+
} else if (commands.install.startsWith('cargo')) {
|
|
573
|
+
sources.commands = detected('Cargo.toml')
|
|
574
|
+
} else if (commands.install.startsWith('go')) {
|
|
575
|
+
sources.commands = detected('go.mod')
|
|
576
|
+
} else {
|
|
577
|
+
sources.commands = detected('package.json')
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Project type is inferred from file count + framework count
|
|
581
|
+
sources.projectType = inferred('file count + frameworks')
|
|
582
|
+
|
|
583
|
+
// Git is always from git
|
|
584
|
+
sources.git = detected('git')
|
|
585
|
+
|
|
586
|
+
return sources
|
|
587
|
+
}
|
|
588
|
+
|
|
522
589
|
// ==========================================================================
|
|
523
590
|
// STACK DETECTION
|
|
524
591
|
// ==========================================================================
|
|
@@ -741,7 +808,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
741
808
|
git: GitData,
|
|
742
809
|
stats: ProjectStats,
|
|
743
810
|
commands: Commands,
|
|
744
|
-
agents: AgentInfo[]
|
|
811
|
+
agents: AgentInfo[],
|
|
812
|
+
sources?: ContextSources
|
|
745
813
|
): Promise<string[]> {
|
|
746
814
|
const generator = new ContextFileGenerator({
|
|
747
815
|
projectId: this.projectId!,
|
|
@@ -749,7 +817,13 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
749
817
|
globalPath: this.globalPath,
|
|
750
818
|
})
|
|
751
819
|
|
|
752
|
-
return generator.generate(
|
|
820
|
+
return generator.generate(
|
|
821
|
+
{ branch: git.branch, commits: git.commits },
|
|
822
|
+
stats,
|
|
823
|
+
commands,
|
|
824
|
+
agents,
|
|
825
|
+
sources
|
|
826
|
+
)
|
|
753
827
|
}
|
|
754
828
|
|
|
755
829
|
// ==========================================================================
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SyncVerifier - Programmatic verification checks for sync workflow
|
|
3
|
+
*
|
|
4
|
+
* Runs configurable checks after sync to validate generated output.
|
|
5
|
+
* Supports built-in checks and custom user commands.
|
|
6
|
+
*
|
|
7
|
+
* @see PRJ-106
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { exec } from 'node:child_process'
|
|
11
|
+
import fs from 'node:fs/promises'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import { promisify } from 'node:util'
|
|
14
|
+
import { isNotFoundError } from '../types/fs'
|
|
15
|
+
|
|
16
|
+
const execAsync = promisify(exec)
|
|
17
|
+
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// TYPES
|
|
20
|
+
// =============================================================================
|
|
21
|
+
|
|
22
|
+
export interface VerificationCheck {
|
|
23
|
+
name: string
|
|
24
|
+
command?: string
|
|
25
|
+
script?: string
|
|
26
|
+
enabled?: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface VerificationConfig {
|
|
30
|
+
checks?: VerificationCheck[]
|
|
31
|
+
failFast?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CheckResult {
|
|
35
|
+
name: string
|
|
36
|
+
passed: boolean
|
|
37
|
+
output?: string
|
|
38
|
+
error?: string
|
|
39
|
+
durationMs: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface VerificationReport {
|
|
43
|
+
passed: boolean
|
|
44
|
+
checks: CheckResult[]
|
|
45
|
+
totalMs: number
|
|
46
|
+
failedCount: number
|
|
47
|
+
passedCount: number
|
|
48
|
+
skippedCount: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// BUILT-IN CHECKS
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
const BUILTIN_CHECKS = {
|
|
56
|
+
/**
|
|
57
|
+
* Verify all expected context files exist after sync
|
|
58
|
+
*/
|
|
59
|
+
async contextFilesExist(globalPath: string): Promise<CheckResult> {
|
|
60
|
+
const start = Date.now()
|
|
61
|
+
const expected = ['context/CLAUDE.md']
|
|
62
|
+
const missing: string[] = []
|
|
63
|
+
|
|
64
|
+
for (const file of expected) {
|
|
65
|
+
const filePath = path.join(globalPath, file)
|
|
66
|
+
try {
|
|
67
|
+
await fs.access(filePath)
|
|
68
|
+
} catch {
|
|
69
|
+
missing.push(file)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
name: 'Context files exist',
|
|
75
|
+
passed: missing.length === 0,
|
|
76
|
+
output: missing.length === 0 ? `${expected.length} files verified` : undefined,
|
|
77
|
+
error: missing.length > 0 ? `Missing: ${missing.join(', ')}` : undefined,
|
|
78
|
+
durationMs: Date.now() - start,
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Verify generated JSON files are valid
|
|
84
|
+
*/
|
|
85
|
+
async jsonFilesValid(globalPath: string): Promise<CheckResult> {
|
|
86
|
+
const start = Date.now()
|
|
87
|
+
const jsonFiles = ['storage/state.json']
|
|
88
|
+
const invalid: string[] = []
|
|
89
|
+
|
|
90
|
+
for (const file of jsonFiles) {
|
|
91
|
+
const filePath = path.join(globalPath, file)
|
|
92
|
+
try {
|
|
93
|
+
const content = await fs.readFile(filePath, 'utf-8')
|
|
94
|
+
JSON.parse(content)
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (!isNotFoundError(error)) {
|
|
97
|
+
invalid.push(`${file}: ${error instanceof SyntaxError ? 'invalid JSON' : 'read error'}`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
name: 'JSON files valid',
|
|
104
|
+
passed: invalid.length === 0,
|
|
105
|
+
output: invalid.length === 0 ? `${jsonFiles.length} files validated` : undefined,
|
|
106
|
+
error: invalid.length > 0 ? invalid.join('; ') : undefined,
|
|
107
|
+
durationMs: Date.now() - start,
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Verify no sensitive data leaked into context files
|
|
113
|
+
*/
|
|
114
|
+
async noSensitiveData(globalPath: string): Promise<CheckResult> {
|
|
115
|
+
const start = Date.now()
|
|
116
|
+
const contextDir = path.join(globalPath, 'context')
|
|
117
|
+
const patterns = [
|
|
118
|
+
/(?:api[_-]?key|apikey)\s*[:=]\s*['"][^'"]{10,}/i,
|
|
119
|
+
/(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]{4,}/i,
|
|
120
|
+
/(?:secret|token)\s*[:=]\s*['"][^'"]{10,}/i,
|
|
121
|
+
]
|
|
122
|
+
const violations: string[] = []
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const files = await fs.readdir(contextDir)
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
if (!file.endsWith('.md')) continue
|
|
128
|
+
const content = await fs.readFile(path.join(contextDir, file), 'utf-8')
|
|
129
|
+
for (const pattern of patterns) {
|
|
130
|
+
if (pattern.test(content)) {
|
|
131
|
+
violations.push(`${file}: potential sensitive data detected`)
|
|
132
|
+
break
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (!isNotFoundError(error)) {
|
|
138
|
+
return {
|
|
139
|
+
name: 'No sensitive data',
|
|
140
|
+
passed: false,
|
|
141
|
+
error: `Could not scan: ${(error as Error).message}`,
|
|
142
|
+
durationMs: Date.now() - start,
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
name: 'No sensitive data',
|
|
149
|
+
passed: violations.length === 0,
|
|
150
|
+
output: violations.length === 0 ? 'No sensitive patterns found' : undefined,
|
|
151
|
+
error: violations.length > 0 ? violations.join('; ') : undefined,
|
|
152
|
+
durationMs: Date.now() - start,
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// =============================================================================
|
|
158
|
+
// SYNC VERIFIER
|
|
159
|
+
// =============================================================================
|
|
160
|
+
|
|
161
|
+
class SyncVerifier {
|
|
162
|
+
/**
|
|
163
|
+
* Run all verification checks (built-in + custom)
|
|
164
|
+
*/
|
|
165
|
+
async verify(
|
|
166
|
+
projectPath: string,
|
|
167
|
+
globalPath: string,
|
|
168
|
+
config?: VerificationConfig
|
|
169
|
+
): Promise<VerificationReport> {
|
|
170
|
+
const totalStart = Date.now()
|
|
171
|
+
const checks: CheckResult[] = []
|
|
172
|
+
const failFast = config?.failFast ?? false
|
|
173
|
+
let skipped = 0
|
|
174
|
+
|
|
175
|
+
// 1. Run built-in checks
|
|
176
|
+
const builtinChecks = [
|
|
177
|
+
BUILTIN_CHECKS.contextFilesExist(globalPath),
|
|
178
|
+
BUILTIN_CHECKS.jsonFilesValid(globalPath),
|
|
179
|
+
BUILTIN_CHECKS.noSensitiveData(globalPath),
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
for (const checkPromise of builtinChecks) {
|
|
183
|
+
const result = await checkPromise
|
|
184
|
+
checks.push(result)
|
|
185
|
+
if (!result.passed && failFast) {
|
|
186
|
+
skipped = config?.checks?.filter((c) => c.enabled !== false).length ?? 0
|
|
187
|
+
break
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 2. Run custom checks (if configured and not fail-fast-stopped)
|
|
192
|
+
const shouldContinue = !failFast || checks.every((c) => c.passed)
|
|
193
|
+
if (shouldContinue && config?.checks) {
|
|
194
|
+
for (const check of config.checks) {
|
|
195
|
+
if (check.enabled === false) {
|
|
196
|
+
skipped++
|
|
197
|
+
continue
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const result = await this.runCustomCheck(check, projectPath)
|
|
201
|
+
checks.push(result)
|
|
202
|
+
|
|
203
|
+
if (!result.passed && failFast) {
|
|
204
|
+
// Count remaining enabled checks as skipped
|
|
205
|
+
const remaining = config.checks.slice(config.checks.indexOf(check) + 1)
|
|
206
|
+
skipped += remaining.filter((c) => c.enabled !== false).length
|
|
207
|
+
break
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const failedCount = checks.filter((c) => !c.passed).length
|
|
213
|
+
const passedCount = checks.filter((c) => c.passed).length
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
passed: failedCount === 0,
|
|
217
|
+
checks,
|
|
218
|
+
totalMs: Date.now() - totalStart,
|
|
219
|
+
failedCount,
|
|
220
|
+
passedCount,
|
|
221
|
+
skippedCount: skipped,
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Run a single custom verification check
|
|
227
|
+
*/
|
|
228
|
+
private async runCustomCheck(
|
|
229
|
+
check: VerificationCheck,
|
|
230
|
+
projectPath: string
|
|
231
|
+
): Promise<CheckResult> {
|
|
232
|
+
const start = Date.now()
|
|
233
|
+
const command = check.command || (check.script ? `sh ${check.script}` : null)
|
|
234
|
+
|
|
235
|
+
if (!command) {
|
|
236
|
+
return {
|
|
237
|
+
name: check.name,
|
|
238
|
+
passed: false,
|
|
239
|
+
error: 'No command or script specified',
|
|
240
|
+
durationMs: Date.now() - start,
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
246
|
+
cwd: projectPath,
|
|
247
|
+
timeout: 30_000,
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
name: check.name,
|
|
252
|
+
passed: true,
|
|
253
|
+
output: (stdout.trim() || stderr.trim()).slice(0, 200) || undefined,
|
|
254
|
+
durationMs: Date.now() - start,
|
|
255
|
+
}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
const execError = error as { stdout?: string; stderr?: string; message: string }
|
|
258
|
+
return {
|
|
259
|
+
name: check.name,
|
|
260
|
+
passed: false,
|
|
261
|
+
error: (execError.stderr?.trim() || execError.message).slice(0, 200),
|
|
262
|
+
durationMs: Date.now() - start,
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// =============================================================================
|
|
269
|
+
// EXPORTS
|
|
270
|
+
// =============================================================================
|
|
271
|
+
|
|
272
|
+
export const syncVerifier = new SyncVerifier()
|
|
273
|
+
export default syncVerifier
|
package/core/types/config.ts
CHANGED
|
@@ -18,6 +18,20 @@ export interface LocalConfig {
|
|
|
18
18
|
* @see PRJ-70
|
|
19
19
|
*/
|
|
20
20
|
showMetrics?: boolean
|
|
21
|
+
/**
|
|
22
|
+
* Verification checks to run after sync.
|
|
23
|
+
* Built-in checks always run; custom checks are additive.
|
|
24
|
+
* @see PRJ-106
|
|
25
|
+
*/
|
|
26
|
+
verification?: {
|
|
27
|
+
checks?: Array<{
|
|
28
|
+
name: string
|
|
29
|
+
command?: string
|
|
30
|
+
script?: string
|
|
31
|
+
enabled?: boolean
|
|
32
|
+
}>
|
|
33
|
+
failFast?: boolean
|
|
34
|
+
}
|
|
21
35
|
}
|
|
22
36
|
|
|
23
37
|
/**
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citation utilities for context source tracking
|
|
3
|
+
*
|
|
4
|
+
* Generates HTML comments indicating where each section's data came from.
|
|
5
|
+
* Source types: detected (from files), user-defined (from config), inferred (from heuristics)
|
|
6
|
+
*
|
|
7
|
+
* @see PRJ-113
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type SourceType = 'detected' | 'user-defined' | 'inferred'
|
|
11
|
+
|
|
12
|
+
export interface SourceInfo {
|
|
13
|
+
file: string
|
|
14
|
+
type: SourceType
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ContextSources {
|
|
18
|
+
name: SourceInfo
|
|
19
|
+
version: SourceInfo
|
|
20
|
+
ecosystem: SourceInfo
|
|
21
|
+
languages: SourceInfo
|
|
22
|
+
frameworks: SourceInfo
|
|
23
|
+
commands: SourceInfo
|
|
24
|
+
projectType: SourceInfo
|
|
25
|
+
git: SourceInfo
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generate an HTML citation comment
|
|
30
|
+
*
|
|
31
|
+
* @example cite({ file: 'package.json', type: 'detected' })
|
|
32
|
+
* // => '<!-- source: package.json, detected -->'
|
|
33
|
+
*/
|
|
34
|
+
export function cite(source: SourceInfo): string {
|
|
35
|
+
return `<!-- source: ${source.file}, ${source.type} -->`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create default sources (all unknown) - used as fallback
|
|
40
|
+
*/
|
|
41
|
+
export function defaultSources(): ContextSources {
|
|
42
|
+
const unknown: SourceInfo = { file: 'unknown', type: 'detected' }
|
|
43
|
+
return {
|
|
44
|
+
name: { ...unknown },
|
|
45
|
+
version: { ...unknown },
|
|
46
|
+
ecosystem: { ...unknown },
|
|
47
|
+
languages: { ...unknown },
|
|
48
|
+
frameworks: { ...unknown },
|
|
49
|
+
commands: { ...unknown },
|
|
50
|
+
projectType: { ...unknown },
|
|
51
|
+
git: { file: 'git', type: 'detected' },
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -128,6 +128,17 @@ export const ERRORS = {
|
|
|
128
128
|
hint: "Run 'prjct start' to configure your provider",
|
|
129
129
|
},
|
|
130
130
|
|
|
131
|
+
// Command errors
|
|
132
|
+
UNKNOWN_COMMAND: {
|
|
133
|
+
message: 'Unknown command',
|
|
134
|
+
hint: "Run 'prjct --help' to see available commands",
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
MISSING_PARAM: {
|
|
138
|
+
message: 'Missing required parameter',
|
|
139
|
+
hint: 'Check command usage below',
|
|
140
|
+
},
|
|
141
|
+
|
|
131
142
|
// Generic
|
|
132
143
|
UNKNOWN: {
|
|
133
144
|
message: 'An unexpected error occurred',
|