prjct-cli 1.6.8 → 1.6.10
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 +82 -0
- package/README.md +46 -0
- package/core/ai-tools/registry.ts +2 -9
- package/core/bus/bus.ts +24 -0
- package/core/commands/planning.ts +3 -5
- package/core/infrastructure/agent-detector.ts +2 -2
- package/core/infrastructure/path-manager.ts +3 -17
- package/core/integrations/jira/client.ts +3 -77
- package/core/server/server.ts +2 -4
- package/core/server/sse.ts +115 -59
- package/core/services/context-generator.ts +22 -47
- package/core/services/diff-generator.ts +18 -43
- package/core/services/stack-detector.ts +4 -20
- package/core/services/sync-service.ts +35 -106
- package/core/services/sync-verifier.ts +17 -37
- package/core/services/watch-service.ts +20 -3
- package/core/types/citations.ts +22 -0
- package/core/types/commands.ts +10 -0
- package/core/types/diff.ts +41 -0
- package/core/types/errors.ts +111 -0
- package/core/types/index.ts +80 -0
- package/core/types/infrastructure.ts +14 -0
- package/core/types/jira.ts +51 -0
- package/core/types/logger.ts +17 -0
- package/core/types/output.ts +47 -0
- package/core/types/project-sync.ts +109 -0
- package/core/types/server.ts +28 -10
- package/core/types/services.ts +14 -0
- package/core/types/stack.ts +19 -0
- package/core/types/sync-verifier.ts +33 -0
- package/core/types/workflow.ts +23 -0
- package/core/utils/citations.ts +2 -16
- package/core/utils/error-messages.ts +5 -139
- package/core/utils/logger.ts +3 -11
- package/core/utils/output.ts +6 -45
- package/core/workflow/workflow-preferences.ts +14 -18
- package/dist/bin/prjct.mjs +137 -54
- package/package.json +1 -1
|
@@ -12,50 +12,25 @@
|
|
|
12
12
|
import fs from 'node:fs/promises'
|
|
13
13
|
import path from 'node:path'
|
|
14
14
|
import pathManager from '../infrastructure/path-manager'
|
|
15
|
+
import type {
|
|
16
|
+
ContextGeneratorConfig,
|
|
17
|
+
GitData,
|
|
18
|
+
ProjectCommands,
|
|
19
|
+
ProjectStats,
|
|
20
|
+
SyncAgentInfo,
|
|
21
|
+
} from '../types'
|
|
15
22
|
import { type ContextSources, cite, defaultSources } from '../utils/citations'
|
|
16
23
|
import * as dateHelper from '../utils/date-helper'
|
|
17
24
|
import { mergePreservedSections, validatePreserveBlocks } from '../utils/preserve-sections'
|
|
18
25
|
import { NestedContextResolver } from './nested-context-resolver'
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface ProjectStats {
|
|
30
|
-
name: string
|
|
31
|
-
version: string
|
|
32
|
-
ecosystem: string
|
|
33
|
-
projectType: string
|
|
34
|
-
fileCount: number
|
|
35
|
-
languages: string[]
|
|
36
|
-
frameworks: string[]
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface Commands {
|
|
40
|
-
install: string
|
|
41
|
-
dev: string
|
|
42
|
-
test: string
|
|
43
|
-
build: string
|
|
44
|
-
lint: string
|
|
45
|
-
format: string
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface AgentInfo {
|
|
49
|
-
name: string
|
|
50
|
-
type: 'workflow' | 'domain'
|
|
51
|
-
skill?: string
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface ContextGeneratorConfig {
|
|
55
|
-
projectId: string
|
|
56
|
-
projectPath: string
|
|
57
|
-
globalPath: string
|
|
58
|
-
}
|
|
27
|
+
export type {
|
|
28
|
+
ContextGeneratorConfig,
|
|
29
|
+
GitData,
|
|
30
|
+
ProjectCommands,
|
|
31
|
+
ProjectStats,
|
|
32
|
+
SyncAgentInfo,
|
|
33
|
+
} from '../types'
|
|
59
34
|
|
|
60
35
|
// ============================================================================
|
|
61
36
|
// CONTEXT FILE GENERATOR
|
|
@@ -103,8 +78,8 @@ export class ContextFileGenerator {
|
|
|
103
78
|
async generate(
|
|
104
79
|
git: GitData,
|
|
105
80
|
stats: ProjectStats,
|
|
106
|
-
commands:
|
|
107
|
-
agents:
|
|
81
|
+
commands: ProjectCommands,
|
|
82
|
+
agents: SyncAgentInfo[],
|
|
108
83
|
sources?: ContextSources
|
|
109
84
|
): Promise<string[]> {
|
|
110
85
|
const contextPath = path.join(this.config.globalPath, 'context')
|
|
@@ -138,8 +113,8 @@ export class ContextFileGenerator {
|
|
|
138
113
|
contextPath: string,
|
|
139
114
|
git: GitData,
|
|
140
115
|
stats: ProjectStats,
|
|
141
|
-
commands:
|
|
142
|
-
agents:
|
|
116
|
+
commands: ProjectCommands,
|
|
117
|
+
agents: SyncAgentInfo[],
|
|
143
118
|
sources?: ContextSources
|
|
144
119
|
): Promise<void> {
|
|
145
120
|
const workflowAgents = agents.filter((a) => a.type === 'workflow').map((a) => a.name)
|
|
@@ -348,8 +323,8 @@ ${
|
|
|
348
323
|
async generateMonorepoContexts(
|
|
349
324
|
git: GitData,
|
|
350
325
|
stats: ProjectStats,
|
|
351
|
-
commands:
|
|
352
|
-
agents:
|
|
326
|
+
commands: ProjectCommands,
|
|
327
|
+
agents: SyncAgentInfo[]
|
|
353
328
|
): Promise<string[]> {
|
|
354
329
|
const monoInfo = await pathManager.detectMonorepo(this.config.projectPath)
|
|
355
330
|
|
|
@@ -394,8 +369,8 @@ ${
|
|
|
394
369
|
resolvedCtx: { content: string; sources: string[]; overrides: string[] },
|
|
395
370
|
git: GitData,
|
|
396
371
|
stats: ProjectStats,
|
|
397
|
-
commands:
|
|
398
|
-
agents:
|
|
372
|
+
commands: ProjectCommands,
|
|
373
|
+
agents: SyncAgentInfo[]
|
|
399
374
|
): Promise<string> {
|
|
400
375
|
const workflowAgents = agents.filter((a) => a.type === 'workflow').map((a) => a.name)
|
|
401
376
|
const domainAgents = agents.filter((a) => a.type === 'domain').map((a) => a.name)
|
|
@@ -9,39 +9,21 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import chalk from 'chalk'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
lineCount: number
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface SyncDiff {
|
|
31
|
-
hasChanges: boolean
|
|
32
|
-
added: DiffSection[]
|
|
33
|
-
modified: DiffSection[]
|
|
34
|
-
removed: DiffSection[]
|
|
35
|
-
preserved: PreservedInfo[]
|
|
36
|
-
tokensBefore: number
|
|
37
|
-
tokensAfter: number
|
|
38
|
-
tokenDelta: number
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface DiffOptions {
|
|
42
|
-
showFullDiff?: boolean
|
|
43
|
-
colorize?: boolean
|
|
44
|
-
}
|
|
12
|
+
import type {
|
|
13
|
+
DiffOptions,
|
|
14
|
+
DiffSection,
|
|
15
|
+
ParsedMarkdownSection,
|
|
16
|
+
PreservedInfo,
|
|
17
|
+
SyncDiff,
|
|
18
|
+
} from '../types'
|
|
19
|
+
|
|
20
|
+
export type {
|
|
21
|
+
DiffOptions,
|
|
22
|
+
DiffSection,
|
|
23
|
+
ParsedMarkdownSection,
|
|
24
|
+
PreservedInfo,
|
|
25
|
+
SyncDiff,
|
|
26
|
+
} from '../types'
|
|
45
27
|
|
|
46
28
|
// =============================================================================
|
|
47
29
|
// Constants
|
|
@@ -64,20 +46,13 @@ export function estimateTokens(content: string): number {
|
|
|
64
46
|
// Section Parsing
|
|
65
47
|
// =============================================================================
|
|
66
48
|
|
|
67
|
-
interface ParsedSection {
|
|
68
|
-
name: string
|
|
69
|
-
content: string
|
|
70
|
-
startLine: number
|
|
71
|
-
endLine: number
|
|
72
|
-
}
|
|
73
|
-
|
|
74
49
|
/**
|
|
75
50
|
* Parse markdown into sections based on headers
|
|
76
51
|
*/
|
|
77
|
-
export function parseMarkdownSections(content: string):
|
|
52
|
+
export function parseMarkdownSections(content: string): ParsedMarkdownSection[] {
|
|
78
53
|
const lines = content.split('\n')
|
|
79
|
-
const sections:
|
|
80
|
-
let currentSection:
|
|
54
|
+
const sections: ParsedMarkdownSection[] = []
|
|
55
|
+
let currentSection: ParsedMarkdownSection | null = null
|
|
81
56
|
|
|
82
57
|
for (let i = 0; i < lines.length; i++) {
|
|
83
58
|
const line = lines[i]
|
|
@@ -11,25 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
import fs from 'node:fs/promises'
|
|
13
13
|
import path from 'node:path'
|
|
14
|
+
import type { StackDetection, StackPackageJson } from '../types'
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
// TYPES
|
|
17
|
-
// ============================================================================
|
|
18
|
-
|
|
19
|
-
export interface StackDetection {
|
|
20
|
-
hasFrontend: boolean
|
|
21
|
-
hasBackend: boolean
|
|
22
|
-
hasDatabase: boolean
|
|
23
|
-
hasDocker: boolean
|
|
24
|
-
hasTesting: boolean
|
|
25
|
-
frontendType: 'web' | 'mobile' | 'both' | null
|
|
26
|
-
frameworks: string[]
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface PackageJson {
|
|
30
|
-
dependencies?: Record<string, string>
|
|
31
|
-
devDependencies?: Record<string, string>
|
|
32
|
-
}
|
|
16
|
+
export type { StackDetection, StackPackageJson } from '../types'
|
|
33
17
|
|
|
34
18
|
// ============================================================================
|
|
35
19
|
// STACK DETECTOR
|
|
@@ -148,7 +132,7 @@ export class StackDetector {
|
|
|
148
132
|
*/
|
|
149
133
|
private detectTesting(
|
|
150
134
|
deps: Record<string, string>,
|
|
151
|
-
pkg:
|
|
135
|
+
pkg: StackPackageJson,
|
|
152
136
|
stack: StackDetection
|
|
153
137
|
): void {
|
|
154
138
|
const testingFrameworks = [
|
|
@@ -219,7 +203,7 @@ export class StackDetector {
|
|
|
219
203
|
/**
|
|
220
204
|
* Read and parse package.json
|
|
221
205
|
*/
|
|
222
|
-
private async readPackageJson(): Promise<
|
|
206
|
+
private async readPackageJson(): Promise<StackPackageJson | null> {
|
|
223
207
|
try {
|
|
224
208
|
const pkgPath = path.join(this.projectPath, 'package.json')
|
|
225
209
|
const content = await fs.readFile(pkgPath, 'utf-8')
|
|
@@ -32,6 +32,18 @@ import commandInstaller from '../infrastructure/command-installer'
|
|
|
32
32
|
import configManager from '../infrastructure/config-manager'
|
|
33
33
|
import pathManager from '../infrastructure/path-manager'
|
|
34
34
|
import { metricsStorage } from '../storage/metrics-storage'
|
|
35
|
+
import type {
|
|
36
|
+
AIToolResult,
|
|
37
|
+
GitData,
|
|
38
|
+
ProjectCommands,
|
|
39
|
+
ProjectStats,
|
|
40
|
+
ProjectSyncResult,
|
|
41
|
+
StackDetection,
|
|
42
|
+
SyncAgentInfo,
|
|
43
|
+
SyncMetrics,
|
|
44
|
+
SyncOptions,
|
|
45
|
+
VerificationReport,
|
|
46
|
+
} from '../types'
|
|
35
47
|
import { type ContextSources, defaultSources, type SourceInfo } from '../utils/citations'
|
|
36
48
|
import * as dateHelper from '../utils/date-helper'
|
|
37
49
|
import log from '../utils/logger'
|
|
@@ -39,95 +51,11 @@ import { ContextFileGenerator } from './context-generator'
|
|
|
39
51
|
import type { SyncDiff } from './diff-generator'
|
|
40
52
|
import { localStateGenerator } from './local-state-generator'
|
|
41
53
|
import { skillInstaller } from './skill-installer'
|
|
42
|
-
import {
|
|
43
|
-
import { syncVerifier
|
|
54
|
+
import { StackDetector } from './stack-detector'
|
|
55
|
+
import { syncVerifier } from './sync-verifier'
|
|
44
56
|
|
|
45
57
|
const execAsync = promisify(exec)
|
|
46
58
|
|
|
47
|
-
// ============================================================================
|
|
48
|
-
// TYPES
|
|
49
|
-
// ============================================================================
|
|
50
|
-
|
|
51
|
-
interface GitData {
|
|
52
|
-
branch: string
|
|
53
|
-
commits: number
|
|
54
|
-
contributors: number
|
|
55
|
-
hasChanges: boolean
|
|
56
|
-
stagedFiles: string[]
|
|
57
|
-
modifiedFiles: string[]
|
|
58
|
-
untrackedFiles: string[]
|
|
59
|
-
recentCommits: { hash: string; message: string; date: string }[]
|
|
60
|
-
weeklyCommits: number
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface ProjectStats {
|
|
64
|
-
fileCount: number
|
|
65
|
-
version: string
|
|
66
|
-
name: string
|
|
67
|
-
ecosystem: string
|
|
68
|
-
projectType: string
|
|
69
|
-
languages: string[]
|
|
70
|
-
frameworks: string[]
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
interface Commands {
|
|
74
|
-
install: string
|
|
75
|
-
run: string
|
|
76
|
-
test: string
|
|
77
|
-
build: string
|
|
78
|
-
dev: string
|
|
79
|
-
lint: string
|
|
80
|
-
format: string
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
interface AgentInfo {
|
|
84
|
-
name: string
|
|
85
|
-
type: 'workflow' | 'domain'
|
|
86
|
-
skill?: string
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
interface AIToolResult {
|
|
90
|
-
toolId: string
|
|
91
|
-
outputFile: string
|
|
92
|
-
success: boolean
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
interface SyncMetrics {
|
|
96
|
-
duration: number // Sync duration in ms
|
|
97
|
-
originalSize: number // Estimated tokens before compression
|
|
98
|
-
filteredSize: number // Actual tokens in context files
|
|
99
|
-
compressionRate: number // Percentage saved
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
interface SyncResult {
|
|
103
|
-
success: boolean
|
|
104
|
-
projectId: string
|
|
105
|
-
cliVersion: string
|
|
106
|
-
git: GitData
|
|
107
|
-
stats: ProjectStats
|
|
108
|
-
commands: Commands
|
|
109
|
-
stack: StackDetection
|
|
110
|
-
agents: AgentInfo[]
|
|
111
|
-
skills: { agent: string; skill: string }[]
|
|
112
|
-
skillsInstalled: { name: string; agent: string; status: 'installed' | 'skipped' | 'error' }[]
|
|
113
|
-
contextFiles: string[]
|
|
114
|
-
aiTools: AIToolResult[]
|
|
115
|
-
syncMetrics?: SyncMetrics
|
|
116
|
-
verification?: VerificationReport
|
|
117
|
-
error?: string
|
|
118
|
-
// Preview mode fields
|
|
119
|
-
isPreview?: boolean
|
|
120
|
-
previewDiff?: SyncDiff
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
interface SyncOptions {
|
|
124
|
-
aiTools?: string[] // AI tools to generate context for (default: claude, cursor)
|
|
125
|
-
preview?: boolean // If true, return diff without applying changes
|
|
126
|
-
skipConfirmation?: boolean // If true, apply changes without confirmation (--yes flag)
|
|
127
|
-
packagePath?: string // For monorepo: sync only this package path
|
|
128
|
-
packageName?: string // For monorepo: package name for display
|
|
129
|
-
}
|
|
130
|
-
|
|
131
59
|
// ============================================================================
|
|
132
60
|
// SYNC SERVICE
|
|
133
61
|
// ============================================================================
|
|
@@ -145,7 +73,10 @@ class SyncService {
|
|
|
145
73
|
/**
|
|
146
74
|
* Main sync method - does everything in one call
|
|
147
75
|
*/
|
|
148
|
-
async sync(
|
|
76
|
+
async sync(
|
|
77
|
+
projectPath: string = process.cwd(),
|
|
78
|
+
options: SyncOptions = {}
|
|
79
|
+
): Promise<ProjectSyncResult> {
|
|
149
80
|
this.projectPath = projectPath
|
|
150
81
|
const startTime = Date.now()
|
|
151
82
|
|
|
@@ -486,8 +417,8 @@ class SyncService {
|
|
|
486
417
|
// COMMAND DETECTION
|
|
487
418
|
// ==========================================================================
|
|
488
419
|
|
|
489
|
-
private async detectCommands(): Promise<
|
|
490
|
-
const commands:
|
|
420
|
+
private async detectCommands(): Promise<ProjectCommands> {
|
|
421
|
+
const commands: ProjectCommands = {
|
|
491
422
|
install: 'npm install',
|
|
492
423
|
run: 'npm run',
|
|
493
424
|
test: 'npm test',
|
|
@@ -552,7 +483,7 @@ class SyncService {
|
|
|
552
483
|
// SOURCE CITATIONS
|
|
553
484
|
// ==========================================================================
|
|
554
485
|
|
|
555
|
-
private buildSources(stats: ProjectStats, commands:
|
|
486
|
+
private buildSources(stats: ProjectStats, commands: ProjectCommands): ContextSources {
|
|
556
487
|
const sources = defaultSources()
|
|
557
488
|
|
|
558
489
|
// Determine ecosystem source file
|
|
@@ -609,8 +540,11 @@ class SyncService {
|
|
|
609
540
|
// AGENT GENERATION
|
|
610
541
|
// ==========================================================================
|
|
611
542
|
|
|
612
|
-
private async generateAgents(
|
|
613
|
-
|
|
543
|
+
private async generateAgents(
|
|
544
|
+
stack: StackDetection,
|
|
545
|
+
stats: ProjectStats
|
|
546
|
+
): Promise<SyncAgentInfo[]> {
|
|
547
|
+
const agents: SyncAgentInfo[] = []
|
|
614
548
|
const agentsPath = path.join(this.globalPath, 'agents')
|
|
615
549
|
|
|
616
550
|
// Purge old agents
|
|
@@ -822,7 +756,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
822
756
|
// SKILL CONFIGURATION
|
|
823
757
|
// ==========================================================================
|
|
824
758
|
|
|
825
|
-
private configureSkills(agents:
|
|
759
|
+
private configureSkills(agents: SyncAgentInfo[]): { agent: string; skill: string }[] {
|
|
826
760
|
const skills: { agent: string; skill: string }[] = []
|
|
827
761
|
|
|
828
762
|
for (const agent of agents) {
|
|
@@ -862,7 +796,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
862
796
|
* Reads the mapping, checks which packages are needed, and installs missing ones.
|
|
863
797
|
*/
|
|
864
798
|
private async autoInstallSkills(
|
|
865
|
-
agents:
|
|
799
|
+
agents: SyncAgentInfo[]
|
|
866
800
|
): Promise<{ name: string; agent: string; status: 'installed' | 'skipped' | 'error' }[]> {
|
|
867
801
|
const results: { name: string; agent: string; status: 'installed' | 'skipped' | 'error' }[] = []
|
|
868
802
|
|
|
@@ -963,8 +897,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
963
897
|
private async generateContextFiles(
|
|
964
898
|
git: GitData,
|
|
965
899
|
stats: ProjectStats,
|
|
966
|
-
commands:
|
|
967
|
-
agents:
|
|
900
|
+
commands: ProjectCommands,
|
|
901
|
+
agents: SyncAgentInfo[],
|
|
968
902
|
sources?: ContextSources
|
|
969
903
|
): Promise<string[]> {
|
|
970
904
|
const generator = new ContextFileGenerator({
|
|
@@ -973,13 +907,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
973
907
|
globalPath: this.globalPath,
|
|
974
908
|
})
|
|
975
909
|
|
|
976
|
-
return generator.generate(
|
|
977
|
-
{ branch: git.branch, commits: git.commits },
|
|
978
|
-
stats,
|
|
979
|
-
commands,
|
|
980
|
-
agents,
|
|
981
|
-
sources
|
|
982
|
-
)
|
|
910
|
+
return generator.generate(git, stats, commands, agents, sources)
|
|
983
911
|
}
|
|
984
912
|
|
|
985
913
|
// ==========================================================================
|
|
@@ -1112,7 +1040,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
1112
1040
|
private async recordSyncMetrics(
|
|
1113
1041
|
stats: ProjectStats,
|
|
1114
1042
|
contextFiles: string[],
|
|
1115
|
-
agents:
|
|
1043
|
+
agents: SyncAgentInfo[],
|
|
1116
1044
|
duration: number
|
|
1117
1045
|
): Promise<SyncMetrics> {
|
|
1118
1046
|
const CHARS_PER_TOKEN = 4
|
|
@@ -1229,7 +1157,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
1229
1157
|
}
|
|
1230
1158
|
}
|
|
1231
1159
|
|
|
1232
|
-
private emptyCommands():
|
|
1160
|
+
private emptyCommands(): ProjectCommands {
|
|
1233
1161
|
return {
|
|
1234
1162
|
install: 'npm install',
|
|
1235
1163
|
run: 'npm run',
|
|
@@ -1255,4 +1183,5 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
1255
1183
|
}
|
|
1256
1184
|
|
|
1257
1185
|
export const syncService = new SyncService()
|
|
1258
|
-
export { SyncService
|
|
1186
|
+
export { SyncService }
|
|
1187
|
+
export type { ProjectSyncResult as SyncResult } from '../types'
|
|
@@ -11,42 +11,22 @@ import { exec } from 'node:child_process'
|
|
|
11
11
|
import fs from 'node:fs/promises'
|
|
12
12
|
import path from 'node:path'
|
|
13
13
|
import { promisify } from 'node:util'
|
|
14
|
+
import type {
|
|
15
|
+
VerificationCheck,
|
|
16
|
+
VerificationCheckResult,
|
|
17
|
+
VerificationConfig,
|
|
18
|
+
VerificationReport,
|
|
19
|
+
} from '../types'
|
|
14
20
|
import { getErrorMessage, isNotFoundError } from '../types/fs'
|
|
15
21
|
|
|
16
22
|
const execAsync = promisify(exec)
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
}
|
|
24
|
+
export type {
|
|
25
|
+
VerificationCheck,
|
|
26
|
+
VerificationCheckResult,
|
|
27
|
+
VerificationConfig,
|
|
28
|
+
VerificationReport,
|
|
29
|
+
} from '../types'
|
|
50
30
|
|
|
51
31
|
// =============================================================================
|
|
52
32
|
// BUILT-IN CHECKS
|
|
@@ -56,7 +36,7 @@ const BUILTIN_CHECKS = {
|
|
|
56
36
|
/**
|
|
57
37
|
* Verify all expected context files exist after sync
|
|
58
38
|
*/
|
|
59
|
-
async contextFilesExist(globalPath: string): Promise<
|
|
39
|
+
async contextFilesExist(globalPath: string): Promise<VerificationCheckResult> {
|
|
60
40
|
const start = Date.now()
|
|
61
41
|
const expected = ['context/CLAUDE.md']
|
|
62
42
|
const missing: string[] = []
|
|
@@ -82,7 +62,7 @@ const BUILTIN_CHECKS = {
|
|
|
82
62
|
/**
|
|
83
63
|
* Verify generated JSON files are valid
|
|
84
64
|
*/
|
|
85
|
-
async jsonFilesValid(globalPath: string): Promise<
|
|
65
|
+
async jsonFilesValid(globalPath: string): Promise<VerificationCheckResult> {
|
|
86
66
|
const start = Date.now()
|
|
87
67
|
const jsonFiles = ['storage/state.json']
|
|
88
68
|
const invalid: string[] = []
|
|
@@ -111,7 +91,7 @@ const BUILTIN_CHECKS = {
|
|
|
111
91
|
/**
|
|
112
92
|
* Verify no sensitive data leaked into context files
|
|
113
93
|
*/
|
|
114
|
-
async noSensitiveData(globalPath: string): Promise<
|
|
94
|
+
async noSensitiveData(globalPath: string): Promise<VerificationCheckResult> {
|
|
115
95
|
const start = Date.now()
|
|
116
96
|
const contextDir = path.join(globalPath, 'context')
|
|
117
97
|
const patterns = [
|
|
@@ -168,7 +148,7 @@ class SyncVerifier {
|
|
|
168
148
|
config?: VerificationConfig
|
|
169
149
|
): Promise<VerificationReport> {
|
|
170
150
|
const totalStart = Date.now()
|
|
171
|
-
const checks:
|
|
151
|
+
const checks: VerificationCheckResult[] = []
|
|
172
152
|
const failFast = config?.failFast ?? false
|
|
173
153
|
let skipped = 0
|
|
174
154
|
|
|
@@ -228,7 +208,7 @@ class SyncVerifier {
|
|
|
228
208
|
private async runCustomCheck(
|
|
229
209
|
check: VerificationCheck,
|
|
230
210
|
projectPath: string
|
|
231
|
-
): Promise<
|
|
211
|
+
): Promise<VerificationCheckResult> {
|
|
232
212
|
const start = Date.now()
|
|
233
213
|
const command = check.command || (check.script ? `sh ${check.script}` : null)
|
|
234
214
|
|
|
@@ -102,6 +102,8 @@ class WatchService {
|
|
|
102
102
|
}
|
|
103
103
|
private isRunning: boolean = false
|
|
104
104
|
private syncCount: number = 0
|
|
105
|
+
private sigintHandler: (() => void) | null = null
|
|
106
|
+
private sigtermHandler: (() => void) | null = null
|
|
105
107
|
|
|
106
108
|
/**
|
|
107
109
|
* Start watching for file changes
|
|
@@ -151,9 +153,13 @@ class WatchService {
|
|
|
151
153
|
.on('unlink', (filePath: string) => this.handleChange('unlink', filePath))
|
|
152
154
|
.on('error', (error: unknown) => this.handleError(error as Error))
|
|
153
155
|
|
|
154
|
-
// Handle graceful shutdown
|
|
155
|
-
process.
|
|
156
|
-
process.
|
|
156
|
+
// Handle graceful shutdown — store references for proper removal
|
|
157
|
+
if (this.sigintHandler) process.off('SIGINT', this.sigintHandler)
|
|
158
|
+
if (this.sigtermHandler) process.off('SIGTERM', this.sigtermHandler)
|
|
159
|
+
this.sigintHandler = () => this.stop()
|
|
160
|
+
this.sigtermHandler = () => this.stop()
|
|
161
|
+
process.on('SIGINT', this.sigintHandler)
|
|
162
|
+
process.on('SIGTERM', this.sigtermHandler)
|
|
157
163
|
|
|
158
164
|
return { success: true }
|
|
159
165
|
}
|
|
@@ -177,6 +183,17 @@ class WatchService {
|
|
|
177
183
|
this.watcher = null
|
|
178
184
|
}
|
|
179
185
|
|
|
186
|
+
// Remove signal handlers to prevent accumulation
|
|
187
|
+
if (this.sigintHandler) {
|
|
188
|
+
process.off('SIGINT', this.sigintHandler)
|
|
189
|
+
this.sigintHandler = null
|
|
190
|
+
}
|
|
191
|
+
if (this.sigtermHandler) {
|
|
192
|
+
process.off('SIGTERM', this.sigtermHandler)
|
|
193
|
+
this.sigtermHandler = null
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this.pendingChanges.clear()
|
|
180
197
|
this.isRunning = false
|
|
181
198
|
process.exit(0)
|
|
182
199
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citation Types
|
|
3
|
+
* Types for context source tracking.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type SourceType = 'detected' | 'user-defined' | 'inferred'
|
|
7
|
+
|
|
8
|
+
export interface SourceInfo {
|
|
9
|
+
file: string
|
|
10
|
+
type: SourceType
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ContextSources {
|
|
14
|
+
name: SourceInfo
|
|
15
|
+
version: SourceInfo
|
|
16
|
+
ecosystem: SourceInfo
|
|
17
|
+
languages: SourceInfo
|
|
18
|
+
frameworks: SourceInfo
|
|
19
|
+
commands: SourceInfo
|
|
20
|
+
projectType: SourceInfo
|
|
21
|
+
git: SourceInfo
|
|
22
|
+
}
|
package/core/types/commands.ts
CHANGED
|
@@ -157,6 +157,16 @@ export interface AnalyzeOptions {
|
|
|
157
157
|
[key: string]: unknown
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Options for the init (planning) command.
|
|
162
|
+
*/
|
|
163
|
+
export interface InitOptions {
|
|
164
|
+
/** Skip interactive wizard, use defaults */
|
|
165
|
+
yes?: boolean
|
|
166
|
+
/** Initial idea for architect mode */
|
|
167
|
+
idea?: string | null
|
|
168
|
+
}
|
|
169
|
+
|
|
160
170
|
// ============================================
|
|
161
171
|
// Migration Types
|
|
162
172
|
// ============================================
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff Types
|
|
3
|
+
* Types for sync diff and preserved sections.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface DiffSection {
|
|
7
|
+
name: string
|
|
8
|
+
type: 'added' | 'modified' | 'removed' | 'unchanged'
|
|
9
|
+
before?: string
|
|
10
|
+
after?: string
|
|
11
|
+
lineCount: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PreservedInfo {
|
|
15
|
+
name: string
|
|
16
|
+
lineCount: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SyncDiff {
|
|
20
|
+
hasChanges: boolean
|
|
21
|
+
added: DiffSection[]
|
|
22
|
+
modified: DiffSection[]
|
|
23
|
+
removed: DiffSection[]
|
|
24
|
+
preserved: PreservedInfo[]
|
|
25
|
+
tokensBefore: number
|
|
26
|
+
tokensAfter: number
|
|
27
|
+
tokenDelta: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DiffOptions {
|
|
31
|
+
showFullDiff?: boolean
|
|
32
|
+
colorize?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Parsed markdown section (header + content range) */
|
|
36
|
+
export interface ParsedMarkdownSection {
|
|
37
|
+
name: string
|
|
38
|
+
content: string
|
|
39
|
+
startLine: number
|
|
40
|
+
endLine: number
|
|
41
|
+
}
|