cmp-standards 2.0.1 → 2.6.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/README.md +633 -541
- package/dist/cli/index.js +239 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +203 -39
- package/dist/dashboard/server.js.map +1 -1
- package/dist/db/cloud.d.ts +174 -0
- package/dist/db/cloud.d.ts.map +1 -0
- package/dist/db/cloud.js +241 -0
- package/dist/db/cloud.js.map +1 -0
- package/dist/db/drizzle-client.d.ts +3 -3
- package/dist/db/drizzle-client.d.ts.map +1 -1
- package/dist/db/drizzle-client.js +57 -58
- package/dist/db/drizzle-client.js.map +1 -1
- package/dist/db/errors.d.ts +76 -0
- package/dist/db/errors.d.ts.map +1 -0
- package/dist/db/errors.js +135 -0
- package/dist/db/errors.js.map +1 -0
- package/dist/db/turso-client.d.ts +178 -0
- package/dist/db/turso-client.d.ts.map +1 -0
- package/dist/db/turso-client.js +455 -0
- package/dist/db/turso-client.js.map +1 -0
- package/dist/db/upstash-client.d.ts +161 -0
- package/dist/db/upstash-client.d.ts.map +1 -0
- package/dist/db/upstash-client.js +325 -0
- package/dist/db/upstash-client.js.map +1 -0
- package/dist/eslint/rules/no-async-useeffect.js +6 -6
- package/dist/hooks/cloud-post-tool-use.d.ts +30 -0
- package/dist/hooks/cloud-post-tool-use.d.ts.map +1 -0
- package/dist/hooks/cloud-post-tool-use.js +116 -0
- package/dist/hooks/cloud-post-tool-use.js.map +1 -0
- package/dist/hooks/cloud-pre-tool-use.d.ts +19 -0
- package/dist/hooks/cloud-pre-tool-use.d.ts.map +1 -0
- package/dist/hooks/cloud-pre-tool-use.js +149 -0
- package/dist/hooks/cloud-pre-tool-use.js.map +1 -0
- package/dist/hooks/cloud-session-start.d.ts +32 -0
- package/dist/hooks/cloud-session-start.d.ts.map +1 -0
- package/dist/hooks/cloud-session-start.js +257 -0
- package/dist/hooks/cloud-session-start.js.map +1 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/memory-checkpoint.d.ts.map +1 -1
- package/dist/hooks/memory-checkpoint.js +12 -20
- package/dist/hooks/memory-checkpoint.js.map +1 -1
- package/dist/hooks/pre-tool-use.d.ts +2 -8
- package/dist/hooks/pre-tool-use.d.ts.map +1 -1
- package/dist/hooks/pre-tool-use.js +7 -106
- package/dist/hooks/pre-tool-use.js.map +1 -1
- package/dist/hooks/session-start.d.ts +2 -1
- package/dist/hooks/session-start.d.ts.map +1 -1
- package/dist/hooks/session-start.js +111 -77
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +225 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/schema/plans.d.ts +194 -0
- package/dist/schema/plans.d.ts.map +1 -0
- package/dist/schema/plans.js +180 -0
- package/dist/schema/plans.js.map +1 -0
- package/dist/schema/tracking.d.ts +644 -0
- package/dist/schema/tracking.d.ts.map +1 -0
- package/dist/schema/tracking.js +204 -0
- package/dist/schema/tracking.js.map +1 -0
- package/dist/services/ContextGenerator.d.ts +16 -0
- package/dist/services/ContextGenerator.d.ts.map +1 -0
- package/dist/services/ContextGenerator.js +62 -0
- package/dist/services/ContextGenerator.js.map +1 -0
- package/dist/services/PlanManager.d.ts +99 -0
- package/dist/services/PlanManager.d.ts.map +1 -0
- package/dist/services/PlanManager.js +372 -0
- package/dist/services/PlanManager.js.map +1 -0
- package/dist/services/ProjectScaffold.d.ts.map +1 -1
- package/dist/services/ProjectScaffold.js +109 -81
- package/dist/services/ProjectScaffold.js.map +1 -1
- package/dist/services/TaskTracker.d.ts +1 -1
- package/dist/services/TaskTracker.d.ts.map +1 -1
- package/dist/services/TaskTracker.js +4 -8
- package/dist/services/TaskTracker.js.map +1 -1
- package/dist/services/WorkPlanManager.d.ts +1 -1
- package/dist/services/WorkPlanManager.d.ts.map +1 -1
- package/dist/services/WorkPlanManager.js +8 -14
- package/dist/services/WorkPlanManager.js.map +1 -1
- package/dist/services/auto-inject.d.ts +1 -0
- package/dist/services/auto-inject.d.ts.map +1 -1
- package/dist/services/auto-inject.js +12 -17
- package/dist/services/auto-inject.js.map +1 -1
- package/dist/services/context-injector.d.ts +105 -0
- package/dist/services/context-injector.d.ts.map +1 -0
- package/dist/services/context-injector.js +357 -0
- package/dist/services/context-injector.js.map +1 -0
- package/dist/services/cross-project-sync.d.ts +2 -0
- package/dist/services/cross-project-sync.d.ts.map +1 -1
- package/dist/services/cross-project-sync.js +26 -21
- package/dist/services/cross-project-sync.js.map +1 -1
- package/dist/services/index.d.ts +15 -15
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +18 -20
- package/dist/services/index.js.map +1 -1
- package/dist/services/memory-consolidation.d.ts.map +1 -1
- package/dist/services/memory-consolidation.js +30 -27
- package/dist/services/memory-consolidation.js.map +1 -1
- package/dist/services/memory-router.d.ts +98 -0
- package/dist/services/memory-router.d.ts.map +1 -0
- package/dist/services/memory-router.js +373 -0
- package/dist/services/memory-router.js.map +1 -0
- package/dist/services/pattern-tracker.d.ts +93 -0
- package/dist/services/pattern-tracker.d.ts.map +1 -0
- package/dist/services/pattern-tracker.js +347 -0
- package/dist/services/pattern-tracker.js.map +1 -0
- package/dist/services/semantic-search.d.ts +33 -35
- package/dist/services/semantic-search.d.ts.map +1 -1
- package/dist/services/semantic-search.js +207 -165
- package/dist/services/semantic-search.js.map +1 -1
- package/dist/utils/env-loader.d.ts +41 -0
- package/dist/utils/env-loader.d.ts.map +1 -0
- package/dist/utils/env-loader.js +78 -0
- package/dist/utils/env-loader.js.map +1 -0
- package/dist/utils/git.d.ts +52 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +267 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/paths.d.ts +39 -5
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +88 -7
- package/dist/utils/paths.js.map +1 -1
- package/package.json +100 -94
- package/standards/README.md +50 -0
- package/standards/experts/expert-routing.md +215 -0
- package/standards/general/code-quality.md +86 -0
- package/standards/general/memory-usage.md +205 -0
- package/standards/general/sync-workflow.md +235 -0
- package/standards/general/workflow.md +82 -0
- package/standards/hooks/mandatory-tracking.md +446 -0
- package/standards/infrastructure/cloud-database.md +287 -0
- package/standards/mcp/server-design.md +243 -0
- package/standards/mcp/tool-patterns.md +354 -0
- package/standards/skills/skill-structure.md +286 -0
- package/standards/skills/workflow-design.md +323 -0
- package/standards/tools/tool-design.md +297 -0
- package/templates/agents/architecture-expert.md +61 -61
- package/templates/agents/database-expert.md +62 -62
- package/templates/agents/documentation-expert.md +57 -57
- package/templates/agents/memory-expert.md +88 -88
- package/templates/agents/performance-expert.md +61 -61
- package/templates/agents/security-expert.md +59 -59
- package/templates/agents/ux-expert.md +63 -63
- package/templates/agents/worker.md +75 -75
- package/templates/ai-skills/SKILL_TEMPLATE.md +55 -55
- package/templates/claude-settings.json +72 -0
- package/templates/commands/experts.md +138 -138
- package/templates/hooks/README.md +158 -158
- package/templates/hooks/project.config.json.template +77 -77
- package/templates/hooks/settings.local.json.template +57 -57
- package/templates/memory-config.json +56 -82
- package/templates/memory-config.schema.json +212 -212
- package/templates/settings.json +58 -58
- package/templates/skills/continue.md +205 -0
- package/templates/workflows/business-improvement.md +264 -264
- package/templates/workflows/expert-review.md +153 -153
- package/templates/workflows/internal-app.md +245 -245
- package/templates/workflows/sync-docs.md +187 -187
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe Git Utilities
|
|
3
|
+
*
|
|
4
|
+
* Uses simple-git library instead of exec() to prevent command injection.
|
|
5
|
+
* All paths are validated to prevent directory traversal attacks.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Validate that a path is safe and within project bounds
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateGitPath(projectRoot: string): Promise<boolean>;
|
|
11
|
+
export interface GitChangedFile {
|
|
12
|
+
path: string;
|
|
13
|
+
type: 'add' | 'modify' | 'delete';
|
|
14
|
+
linesAdded: number;
|
|
15
|
+
linesRemoved: number;
|
|
16
|
+
}
|
|
17
|
+
export interface SafeGitResult<T> {
|
|
18
|
+
success: boolean;
|
|
19
|
+
data?: T;
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get current git branch name
|
|
24
|
+
*/
|
|
25
|
+
export declare function getCurrentBranch(projectRoot: string): Promise<SafeGitResult<string>>;
|
|
26
|
+
/**
|
|
27
|
+
* Get recently changed files (by comparing commits)
|
|
28
|
+
*/
|
|
29
|
+
export declare function getRecentChanges(projectRoot: string, options?: {
|
|
30
|
+
commits?: number;
|
|
31
|
+
}): Promise<SafeGitResult<string[]>>;
|
|
32
|
+
/**
|
|
33
|
+
* Get files changed in the last N time period
|
|
34
|
+
*/
|
|
35
|
+
export declare function getChangesInPeriod(projectRoot: string, period?: string): Promise<SafeGitResult<string[]>>;
|
|
36
|
+
/**
|
|
37
|
+
* Get staged (cached) file changes with stats
|
|
38
|
+
*/
|
|
39
|
+
export declare function getStagedChanges(projectRoot: string): Promise<SafeGitResult<GitChangedFile[]>>;
|
|
40
|
+
/**
|
|
41
|
+
* Get unstaged file changes with stats
|
|
42
|
+
*/
|
|
43
|
+
export declare function getUnstagedChanges(projectRoot: string): Promise<SafeGitResult<GitChangedFile[]>>;
|
|
44
|
+
/**
|
|
45
|
+
* Get diff between two refs
|
|
46
|
+
*/
|
|
47
|
+
export declare function getDiffBetweenRefs(projectRoot: string, fromRef: string, toRef: string): Promise<SafeGitResult<string[]>>;
|
|
48
|
+
/**
|
|
49
|
+
* Check if directory is a git repository
|
|
50
|
+
*/
|
|
51
|
+
export declare function isGitRepository(projectRoot: string): Promise<boolean>;
|
|
52
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH;;GAEG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAyB3E;AAMD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAsBD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAkB1F;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACjC,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CA8BlC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,MAAM,GAAE,MAAoB,GAC3B,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAoClC;AAmBD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,aAAa,CAAC,cAAc,EAAE,CAAC,CAAC,CA8B1C;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,aAAa,CAAC,cAAc,EAAE,CAAC,CAAC,CA8B1C;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAyBlC;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG3E"}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe Git Utilities
|
|
3
|
+
*
|
|
4
|
+
* Uses simple-git library instead of exec() to prevent command injection.
|
|
5
|
+
* All paths are validated to prevent directory traversal attacks.
|
|
6
|
+
*/
|
|
7
|
+
import { simpleGit } from 'simple-git';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import fs from 'fs/promises';
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// PATH VALIDATION
|
|
12
|
+
// =============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Validate that a path is safe and within project bounds
|
|
15
|
+
*/
|
|
16
|
+
export async function validateGitPath(projectRoot) {
|
|
17
|
+
// 1. Resolve to absolute path
|
|
18
|
+
const absolutePath = path.resolve(projectRoot);
|
|
19
|
+
// 2. Check if path exists
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(absolutePath);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
// 3. Prevent path traversal - ensure no .. in resolved path
|
|
27
|
+
const normalizedPath = path.normalize(absolutePath);
|
|
28
|
+
if (normalizedPath.includes('..')) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
// 4. Check if it's a git repository
|
|
32
|
+
try {
|
|
33
|
+
await fs.access(path.join(absolutePath, '.git'));
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Could be a subdirectory of a git repo
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a safe git instance for a project
|
|
43
|
+
* Returns null on failure instead of error result for simpler handling
|
|
44
|
+
*/
|
|
45
|
+
async function createSafeGit(projectRoot) {
|
|
46
|
+
const isValid = await validateGitPath(projectRoot);
|
|
47
|
+
if (!isValid) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const git = simpleGit(projectRoot);
|
|
52
|
+
// Verify it's a git repo
|
|
53
|
+
await git.status();
|
|
54
|
+
return git;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get current git branch name
|
|
62
|
+
*/
|
|
63
|
+
export async function getCurrentBranch(projectRoot) {
|
|
64
|
+
const git = await createSafeGit(projectRoot);
|
|
65
|
+
if (!git) {
|
|
66
|
+
return { success: false, error: 'Not a valid git repository' };
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const result = await git.branch();
|
|
70
|
+
return {
|
|
71
|
+
success: true,
|
|
72
|
+
data: result.current,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
error: `Failed to get branch: ${error}`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get recently changed files (by comparing commits)
|
|
84
|
+
*/
|
|
85
|
+
export async function getRecentChanges(projectRoot, options = {}) {
|
|
86
|
+
const git = await createSafeGit(projectRoot);
|
|
87
|
+
if (!git) {
|
|
88
|
+
return { success: false, error: 'Not a valid git repository' };
|
|
89
|
+
}
|
|
90
|
+
const commits = options.commits ?? 10;
|
|
91
|
+
try {
|
|
92
|
+
// Try to get diff from HEAD~N to HEAD
|
|
93
|
+
try {
|
|
94
|
+
const diff = await git.diff([`HEAD~${commits}`, 'HEAD', '--name-only']);
|
|
95
|
+
const files = diff.trim().split('\n').filter(Boolean);
|
|
96
|
+
return { success: true, data: files };
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Fallback: get unstaged changes
|
|
100
|
+
try {
|
|
101
|
+
const diff = await git.diff(['--name-only']);
|
|
102
|
+
const files = diff.trim().split('\n').filter(Boolean);
|
|
103
|
+
return { success: true, data: files };
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return { success: true, data: [] };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
success: false,
|
|
113
|
+
error: `Failed to get recent changes: ${error}`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get files changed in the last N time period
|
|
119
|
+
*/
|
|
120
|
+
export async function getChangesInPeriod(projectRoot, period = '1.day.ago') {
|
|
121
|
+
const git = await createSafeGit(projectRoot);
|
|
122
|
+
if (!git) {
|
|
123
|
+
return { success: false, error: 'Not a valid git repository' };
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
// First try with time-based ref
|
|
127
|
+
try {
|
|
128
|
+
const log = await git.log([`--since="${period}"`, '--name-only', '--pretty=format:']);
|
|
129
|
+
const files = new Set();
|
|
130
|
+
// Parse log output for file names
|
|
131
|
+
for (const commit of log.all) {
|
|
132
|
+
if (commit.diff) {
|
|
133
|
+
for (const file of commit.diff.files) {
|
|
134
|
+
files.add(file.file);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (files.size > 0) {
|
|
139
|
+
return { success: true, data: Array.from(files) };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Time-based ref not supported, fallback
|
|
144
|
+
}
|
|
145
|
+
// Fallback to last 10 commits
|
|
146
|
+
return getRecentChanges(projectRoot, { commits: 10 });
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: `Failed to get changes in period: ${error}`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Helper to safely get insertions/deletions from diff file
|
|
157
|
+
*/
|
|
158
|
+
function getFileStats(file) {
|
|
159
|
+
// Binary files don't have insertions/deletions
|
|
160
|
+
if (file.binary) {
|
|
161
|
+
return { insertions: 0, deletions: 0 };
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
insertions: file.insertions ?? 0,
|
|
165
|
+
deletions: file.deletions ?? 0,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get staged (cached) file changes with stats
|
|
170
|
+
*/
|
|
171
|
+
export async function getStagedChanges(projectRoot) {
|
|
172
|
+
const git = await createSafeGit(projectRoot);
|
|
173
|
+
if (!git) {
|
|
174
|
+
return { success: false, error: 'Not a valid git repository' };
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const diff = await git.diffSummary(['--cached']);
|
|
178
|
+
const changes = diff.files.map(file => {
|
|
179
|
+
const stats = getFileStats(file);
|
|
180
|
+
return {
|
|
181
|
+
path: file.file,
|
|
182
|
+
type: stats.insertions === 0 && stats.deletions > 0
|
|
183
|
+
? 'delete'
|
|
184
|
+
: stats.deletions === 0 && stats.insertions > 0
|
|
185
|
+
? 'add'
|
|
186
|
+
: 'modify',
|
|
187
|
+
linesAdded: stats.insertions,
|
|
188
|
+
linesRemoved: stats.deletions,
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
return { success: true, data: changes };
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return {
|
|
195
|
+
success: false,
|
|
196
|
+
error: `Failed to get staged changes: ${error}`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Get unstaged file changes with stats
|
|
202
|
+
*/
|
|
203
|
+
export async function getUnstagedChanges(projectRoot) {
|
|
204
|
+
const git = await createSafeGit(projectRoot);
|
|
205
|
+
if (!git) {
|
|
206
|
+
return { success: false, error: 'Not a valid git repository' };
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
const diff = await git.diffSummary();
|
|
210
|
+
const changes = diff.files.map(file => {
|
|
211
|
+
const stats = getFileStats(file);
|
|
212
|
+
return {
|
|
213
|
+
path: file.file,
|
|
214
|
+
type: stats.insertions === 0 && stats.deletions > 0
|
|
215
|
+
? 'delete'
|
|
216
|
+
: stats.deletions === 0 && stats.insertions > 0
|
|
217
|
+
? 'add'
|
|
218
|
+
: 'modify',
|
|
219
|
+
linesAdded: stats.insertions,
|
|
220
|
+
linesRemoved: stats.deletions,
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
return { success: true, data: changes };
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: `Failed to get unstaged changes: ${error}`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get diff between two refs
|
|
234
|
+
*/
|
|
235
|
+
export async function getDiffBetweenRefs(projectRoot, fromRef, toRef) {
|
|
236
|
+
const git = await createSafeGit(projectRoot);
|
|
237
|
+
if (!git) {
|
|
238
|
+
return { success: false, error: 'Not a valid git repository' };
|
|
239
|
+
}
|
|
240
|
+
// Validate refs - only allow safe characters
|
|
241
|
+
const safeRefPattern = /^[a-zA-Z0-9_.~^/-]+$/;
|
|
242
|
+
if (!safeRefPattern.test(fromRef) || !safeRefPattern.test(toRef)) {
|
|
243
|
+
return {
|
|
244
|
+
success: false,
|
|
245
|
+
error: 'Invalid ref format',
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
const diff = await git.diff([fromRef, toRef, '--name-only']);
|
|
250
|
+
const files = diff.trim().split('\n').filter(Boolean);
|
|
251
|
+
return { success: true, data: files };
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
error: `Failed to get diff: ${error}`,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Check if directory is a git repository
|
|
262
|
+
*/
|
|
263
|
+
export async function isGitRepository(projectRoot) {
|
|
264
|
+
const git = await createSafeGit(projectRoot);
|
|
265
|
+
return git !== null;
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/paths.d.ts
CHANGED
|
@@ -1,24 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Path
|
|
2
|
+
* Path Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides safe path operations with traversal protection.
|
|
3
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* Error thrown when path traversal is detected
|
|
8
|
+
*/
|
|
9
|
+
export declare class PathTraversalError extends Error {
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Validate a path is safe and doesn't escape the base directory
|
|
14
|
+
*
|
|
15
|
+
* @throws PathTraversalError if path attempts to escape base directory
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateSafePath(basePath: string, targetPath: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Safely join paths with traversal protection
|
|
20
|
+
*
|
|
21
|
+
* @throws PathTraversalError if resulting path escapes base directory
|
|
22
|
+
*/
|
|
23
|
+
export declare function safeJoin(basePath: string, ...paths: string[]): string;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a path is safe (doesn't escape base directory)
|
|
26
|
+
*/
|
|
27
|
+
export declare function isPathSafe(basePath: string, targetPath: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Sanitize a filename by removing dangerous characters
|
|
30
|
+
*/
|
|
31
|
+
export declare function sanitizeFilename(filename: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Sanitize a path segment (not a full path)
|
|
34
|
+
*/
|
|
35
|
+
export declare function sanitizePathSegment(segment: string): string;
|
|
4
36
|
/**
|
|
5
37
|
* Find project root by looking for package.json or .git
|
|
6
38
|
*/
|
|
7
39
|
export declare function getProjectRoot(startDir?: string): Promise<string>;
|
|
8
40
|
/**
|
|
9
|
-
* Resolve a path relative to project root
|
|
41
|
+
* Resolve a path relative to project root with traversal protection
|
|
42
|
+
*
|
|
43
|
+
* @throws PathTraversalError if path escapes project root
|
|
10
44
|
*/
|
|
11
45
|
export declare function resolveProjectPath(projectRoot: string, relativePath: string): string;
|
|
12
46
|
/**
|
|
13
|
-
* Get .claude directory path
|
|
47
|
+
* Get .claude directory path (safe - fixed subpath)
|
|
14
48
|
*/
|
|
15
49
|
export declare function getClaudeDir(projectRoot: string): string;
|
|
16
50
|
/**
|
|
17
|
-
* Get hooks directory path
|
|
51
|
+
* Get hooks directory path (safe - fixed subpath)
|
|
18
52
|
*/
|
|
19
53
|
export declare function getHooksDir(projectRoot: string): string;
|
|
20
54
|
/**
|
|
21
|
-
* Get knowledge directory path
|
|
55
|
+
* Get knowledge directory path (safe - fixed subpath)
|
|
22
56
|
*/
|
|
23
57
|
export declare function getKnowledgeDir(projectRoot: string): string;
|
|
24
58
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAa7E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAGrE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAOxE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAM3D;AAMD;;GAEG;AACH,wBAAsB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BvE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEpF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAS5E"}
|
package/dist/utils/paths.js
CHANGED
|
@@ -1,13 +1,92 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Path
|
|
2
|
+
* Path Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides safe path operations with traversal protection.
|
|
3
5
|
*/
|
|
4
6
|
import fs from 'fs/promises';
|
|
5
7
|
import path from 'path';
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// PATH VALIDATION & SECURITY
|
|
10
|
+
// =============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when path traversal is detected
|
|
13
|
+
*/
|
|
14
|
+
export class PathTraversalError extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'PathTraversalError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Validate a path is safe and doesn't escape the base directory
|
|
22
|
+
*
|
|
23
|
+
* @throws PathTraversalError if path attempts to escape base directory
|
|
24
|
+
*/
|
|
25
|
+
export function validateSafePath(basePath, targetPath) {
|
|
26
|
+
// Resolve both paths to absolute
|
|
27
|
+
const resolvedBase = path.resolve(basePath);
|
|
28
|
+
const resolvedTarget = path.resolve(basePath, targetPath);
|
|
29
|
+
// Ensure target is within base (prevents ../ attacks)
|
|
30
|
+
if (!resolvedTarget.startsWith(resolvedBase + path.sep) && resolvedTarget !== resolvedBase) {
|
|
31
|
+
throw new PathTraversalError(`Path traversal detected: "${targetPath}" escapes base directory "${basePath}"`);
|
|
32
|
+
}
|
|
33
|
+
return resolvedTarget;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Safely join paths with traversal protection
|
|
37
|
+
*
|
|
38
|
+
* @throws PathTraversalError if resulting path escapes base directory
|
|
39
|
+
*/
|
|
40
|
+
export function safeJoin(basePath, ...paths) {
|
|
41
|
+
const joined = path.join(...paths);
|
|
42
|
+
return validateSafePath(basePath, joined);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if a path is safe (doesn't escape base directory)
|
|
46
|
+
*/
|
|
47
|
+
export function isPathSafe(basePath, targetPath) {
|
|
48
|
+
try {
|
|
49
|
+
validateSafePath(basePath, targetPath);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Sanitize a filename by removing dangerous characters
|
|
58
|
+
*/
|
|
59
|
+
export function sanitizeFilename(filename) {
|
|
60
|
+
// Remove path separators and null bytes
|
|
61
|
+
return filename
|
|
62
|
+
.replace(/[/\\]/g, '_')
|
|
63
|
+
.replace(/\0/g, '')
|
|
64
|
+
.replace(/\.\./g, '__')
|
|
65
|
+
.trim();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Sanitize a path segment (not a full path)
|
|
69
|
+
*/
|
|
70
|
+
export function sanitizePathSegment(segment) {
|
|
71
|
+
return segment
|
|
72
|
+
.replace(/\.\./g, '')
|
|
73
|
+
.replace(/[<>:"|?*]/g, '_')
|
|
74
|
+
.replace(/\0/g, '')
|
|
75
|
+
.trim();
|
|
76
|
+
}
|
|
77
|
+
// =============================================================================
|
|
78
|
+
// PROJECT ROOT DETECTION
|
|
79
|
+
// =============================================================================
|
|
6
80
|
/**
|
|
7
81
|
* Find project root by looking for package.json or .git
|
|
8
82
|
*/
|
|
9
83
|
export async function getProjectRoot(startDir) {
|
|
10
|
-
|
|
84
|
+
// Validate startDir if provided
|
|
85
|
+
let currentDir = startDir ? path.resolve(startDir) : process.cwd();
|
|
86
|
+
// Prevent null byte injection
|
|
87
|
+
if (currentDir.includes('\0')) {
|
|
88
|
+
throw new PathTraversalError('Null byte detected in path');
|
|
89
|
+
}
|
|
11
90
|
while (currentDir !== path.parse(currentDir).root) {
|
|
12
91
|
// Check for package.json
|
|
13
92
|
try {
|
|
@@ -30,25 +109,27 @@ export async function getProjectRoot(startDir) {
|
|
|
30
109
|
return process.cwd();
|
|
31
110
|
}
|
|
32
111
|
/**
|
|
33
|
-
* Resolve a path relative to project root
|
|
112
|
+
* Resolve a path relative to project root with traversal protection
|
|
113
|
+
*
|
|
114
|
+
* @throws PathTraversalError if path escapes project root
|
|
34
115
|
*/
|
|
35
116
|
export function resolveProjectPath(projectRoot, relativePath) {
|
|
36
|
-
return
|
|
117
|
+
return validateSafePath(projectRoot, relativePath);
|
|
37
118
|
}
|
|
38
119
|
/**
|
|
39
|
-
* Get .claude directory path
|
|
120
|
+
* Get .claude directory path (safe - fixed subpath)
|
|
40
121
|
*/
|
|
41
122
|
export function getClaudeDir(projectRoot) {
|
|
42
123
|
return path.join(projectRoot, '.claude');
|
|
43
124
|
}
|
|
44
125
|
/**
|
|
45
|
-
* Get hooks directory path
|
|
126
|
+
* Get hooks directory path (safe - fixed subpath)
|
|
46
127
|
*/
|
|
47
128
|
export function getHooksDir(projectRoot) {
|
|
48
129
|
return path.join(projectRoot, '.claude', 'hooks');
|
|
49
130
|
}
|
|
50
131
|
/**
|
|
51
|
-
* Get knowledge directory path
|
|
132
|
+
* Get knowledge directory path (safe - fixed subpath)
|
|
52
133
|
*/
|
|
53
134
|
export function getKnowledgeDir(projectRoot) {
|
|
54
135
|
return path.join(projectRoot, '.claude', 'knowledge');
|
package/dist/utils/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"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"}
|