forge-dev-framework 1.0.1
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/.claude/rules/api-patterns.md +98 -0
- package/.claude/rules/security-baseline.md +204 -0
- package/.claude/rules/testing-standards.md +177 -0
- package/.claude/rules/ui-conventions.md +142 -0
- package/README.md +261 -0
- package/bin/forge.js +14 -0
- package/dist/bin/forge.js +14 -0
- package/dist/cli/index.d.ts +22 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +116 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/base.d.ts +31 -0
- package/dist/commands/base.d.ts.map +1 -0
- package/dist/commands/base.js +31 -0
- package/dist/commands/base.js.map +1 -0
- package/dist/commands/config.d.ts +14 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +175 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/generate.d.ts +17 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +159 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/help.d.ts +11 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +65 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/index.d.ts +8 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +22 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/status.d.ts +13 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +101 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stubs.d.ts +14 -0
- package/dist/commands/stubs.d.ts.map +1 -0
- package/dist/commands/stubs.js +30 -0
- package/dist/commands/stubs.js.map +1 -0
- package/dist/generators/index.d.ts +11 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +10 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/required-fields.d.ts +74 -0
- package/dist/generators/required-fields.d.ts.map +1 -0
- package/dist/generators/required-fields.js +179 -0
- package/dist/generators/required-fields.js.map +1 -0
- package/dist/generators/template-engine.d.ts +65 -0
- package/dist/generators/template-engine.d.ts.map +1 -0
- package/dist/generators/template-engine.js +209 -0
- package/dist/generators/template-engine.js.map +1 -0
- package/dist/generators/token-validator.d.ts +51 -0
- package/dist/generators/token-validator.d.ts.map +1 -0
- package/dist/generators/token-validator.js +141 -0
- package/dist/generators/token-validator.js.map +1 -0
- package/dist/generators/types.d.ts +433 -0
- package/dist/generators/types.d.ts.map +1 -0
- package/dist/generators/types.js +5 -0
- package/dist/generators/types.js.map +1 -0
- package/dist/generators/xml-task-generator.d.ts +67 -0
- package/dist/generators/xml-task-generator.d.ts.map +1 -0
- package/dist/generators/xml-task-generator.js +297 -0
- package/dist/generators/xml-task-generator.js.map +1 -0
- package/dist/git/__tests__/worktree.test.d.ts +5 -0
- package/dist/git/__tests__/worktree.test.d.ts.map +1 -0
- package/dist/git/__tests__/worktree.test.js +121 -0
- package/dist/git/__tests__/worktree.test.js.map +1 -0
- package/dist/git/codeowners.d.ts +101 -0
- package/dist/git/codeowners.d.ts.map +1 -0
- package/dist/git/codeowners.js +216 -0
- package/dist/git/codeowners.js.map +1 -0
- package/dist/git/commit.d.ts +135 -0
- package/dist/git/commit.d.ts.map +1 -0
- package/dist/git/commit.js +223 -0
- package/dist/git/commit.js.map +1 -0
- package/dist/git/hooks/commit-msg.d.ts +8 -0
- package/dist/git/hooks/commit-msg.d.ts.map +1 -0
- package/dist/git/hooks/commit-msg.js +34 -0
- package/dist/git/hooks/commit-msg.js.map +1 -0
- package/dist/git/hooks/pre-commit.d.ts +8 -0
- package/dist/git/hooks/pre-commit.d.ts.map +1 -0
- package/dist/git/hooks/pre-commit.js +34 -0
- package/dist/git/hooks/pre-commit.js.map +1 -0
- package/dist/git/pre-commit-hooks.d.ts +117 -0
- package/dist/git/pre-commit-hooks.d.ts.map +1 -0
- package/dist/git/pre-commit-hooks.js +270 -0
- package/dist/git/pre-commit-hooks.js.map +1 -0
- package/dist/git/wipe-protocol.d.ts +281 -0
- package/dist/git/wipe-protocol.d.ts.map +1 -0
- package/dist/git/wipe-protocol.js +237 -0
- package/dist/git/wipe-protocol.js.map +1 -0
- package/dist/git/worktree.d.ts +69 -0
- package/dist/git/worktree.d.ts.map +1 -0
- package/dist/git/worktree.js +202 -0
- package/dist/git/worktree.js.map +1 -0
- package/dist/scripts/install.d.ts +8 -0
- package/dist/scripts/install.d.ts.map +1 -0
- package/dist/scripts/install.js +161 -0
- package/dist/scripts/install.js.map +1 -0
- package/dist/types/config.d.ts +30 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +23 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/state.d.ts +56 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +6 -0
- package/dist/types/state.js.map +1 -0
- package/dist/utils/config.d.ts +15 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +80 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/errors.d.ts +25 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +48 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +34 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +73 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/state-api.d.ts +128 -0
- package/dist/utils/state-api.d.ts.map +1 -0
- package/dist/utils/state-api.js +170 -0
- package/dist/utils/state-api.js.map +1 -0
- package/dist/utils/template-client.d.ts +73 -0
- package/dist/utils/template-client.d.ts.map +1 -0
- package/dist/utils/template-client.js +151 -0
- package/dist/utils/template-client.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-commit Hook Implementation
|
|
3
|
+
*
|
|
4
|
+
* This is the TypeScript file that the shell hook calls.
|
|
5
|
+
* It runs all pre-commit validations and blocks commits that fail.
|
|
6
|
+
*/
|
|
7
|
+
import { runPreCommitValidations, loadPreCommitContext, parseTaskIdFromBranch } from "../pre-commit-hooks.js";
|
|
8
|
+
async function main() {
|
|
9
|
+
// Get the task ID from the branch name
|
|
10
|
+
const taskId = await parseTaskIdFromBranch();
|
|
11
|
+
if (!taskId) {
|
|
12
|
+
// Not a FORGE branch, allow commit
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
// Load validation context from state
|
|
16
|
+
const context = await loadPreCommitContext(taskId);
|
|
17
|
+
// Run validations
|
|
18
|
+
const result = await runPreCommitValidations(context);
|
|
19
|
+
if (!result.isValid) {
|
|
20
|
+
console.error("❌ FORGE Pre-commit Validations Failed:\n");
|
|
21
|
+
for (const error of result.errors) {
|
|
22
|
+
console.error(` - ${error}\n`);
|
|
23
|
+
}
|
|
24
|
+
console.error("Commit blocked. Fix the issues and try again.");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
console.log("✅ FORGE Pre-commit Validations Passed.");
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
main().catch((error) => {
|
|
31
|
+
console.error("Pre-commit hook error:", error);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=pre-commit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-commit.js","sourceRoot":"","sources":["../../../src/git/hooks/pre-commit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE9G,KAAK,UAAU,IAAI;IACjB,uCAAuC;IACvC,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAE7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,mCAAmC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,qCAAqC;IACrC,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAEnD,kBAAkB;IAClB,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAEtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC1D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-commit Hooks for FORGE
|
|
3
|
+
*
|
|
4
|
+
* Creates hooks to enforce:
|
|
5
|
+
* - Out-of-scope file detection (agent tried to write outside allowedPaths)
|
|
6
|
+
* - Commit message format validation
|
|
7
|
+
* - Test execution requirement (if tests defined for task)
|
|
8
|
+
* - Contract file validation (if contract referenced)
|
|
9
|
+
*
|
|
10
|
+
* These hooks are installed to .git/hooks/ and run before each commit.
|
|
11
|
+
*/
|
|
12
|
+
export interface PreCommitContext {
|
|
13
|
+
taskId?: string;
|
|
14
|
+
allowedPaths?: string[];
|
|
15
|
+
requiredTests?: string[];
|
|
16
|
+
requiredContracts?: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate the pre-commit hook script.
|
|
20
|
+
*
|
|
21
|
+
* This shell script is installed to .git/hooks/pre-commit.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generatePreCommitScript(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Generate the commit-msg hook script.
|
|
26
|
+
*
|
|
27
|
+
* This shell script is installed to .git/hooks/commit-msg.
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateCommitMsgScript(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Install pre-commit hooks to the .git/hooks directory.
|
|
32
|
+
*
|
|
33
|
+
* @param basePath - Root of the FORGE project
|
|
34
|
+
*/
|
|
35
|
+
export declare function installHooks(basePath?: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if files are within allowed paths.
|
|
38
|
+
*
|
|
39
|
+
* @param files - Files to check
|
|
40
|
+
* @param allowedPaths - Allowed path patterns
|
|
41
|
+
* @returns Validation result
|
|
42
|
+
*/
|
|
43
|
+
export declare function checkFileScope(files: string[], allowedPaths: string[]): {
|
|
44
|
+
isValid: boolean;
|
|
45
|
+
outOfScopeFiles: string[];
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Get staged files for a commit.
|
|
49
|
+
*
|
|
50
|
+
* @returns Array of staged file paths
|
|
51
|
+
*/
|
|
52
|
+
export declare function getStagedFiles(): Promise<string[]>;
|
|
53
|
+
/**
|
|
54
|
+
* Validate that required contracts exist and are valid.
|
|
55
|
+
*
|
|
56
|
+
* @param contracts - Contract file paths to validate
|
|
57
|
+
* @param basePath - Root of the FORGE project
|
|
58
|
+
* @returns Validation result
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateContracts(contracts: string[], basePath?: string): Promise<{
|
|
61
|
+
isValid: boolean;
|
|
62
|
+
errors: string[];
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Run tests and check if they pass.
|
|
66
|
+
*
|
|
67
|
+
* @param testCommands - Test commands to run (e.g., ["npm test -- --grep 'session auth'"])
|
|
68
|
+
* @returns Test result
|
|
69
|
+
*/
|
|
70
|
+
export declare function runRequiredTests(testCommands: string[]): Promise<{
|
|
71
|
+
passed: boolean;
|
|
72
|
+
output: string;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Run all pre-commit validations.
|
|
76
|
+
*
|
|
77
|
+
* This is the main entry point for the pre-commit hook.
|
|
78
|
+
*
|
|
79
|
+
* @param context - Pre-commit validation context
|
|
80
|
+
* @returns Validation result with errors
|
|
81
|
+
*/
|
|
82
|
+
export declare function runPreCommitValidations(context: PreCommitContext): Promise<{
|
|
83
|
+
isValid: boolean;
|
|
84
|
+
errors: string[];
|
|
85
|
+
}>;
|
|
86
|
+
/**
|
|
87
|
+
* Run commit message validation.
|
|
88
|
+
*
|
|
89
|
+
* This is the main entry point for the commit-msg hook.
|
|
90
|
+
*
|
|
91
|
+
* @param message - Commit message to validate
|
|
92
|
+
* @param expectedTaskId - Expected task ID (optional)
|
|
93
|
+
* @returns Validation result
|
|
94
|
+
*/
|
|
95
|
+
export declare function runCommitMsgValidation(message: string, expectedTaskId?: string): {
|
|
96
|
+
isValid: boolean;
|
|
97
|
+
errors: string[];
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Read pre-commit context from task state.
|
|
101
|
+
*
|
|
102
|
+
* This loads the validation rules from the FORGE state.
|
|
103
|
+
*
|
|
104
|
+
* @param taskId - Task ID to load context for
|
|
105
|
+
* @param basePath - Root of the FORGE project
|
|
106
|
+
* @returns Pre-commit context
|
|
107
|
+
*/
|
|
108
|
+
export declare function loadPreCommitContext(taskId: string, basePath?: string): Promise<PreCommitContext>;
|
|
109
|
+
/**
|
|
110
|
+
* Parse task ID from the current branch name.
|
|
111
|
+
*
|
|
112
|
+
* FORGE branches use format: forge/{taskId}
|
|
113
|
+
*
|
|
114
|
+
* @returns Task ID or null
|
|
115
|
+
*/
|
|
116
|
+
export declare function parseTaskIdFromBranch(): Promise<string | null>;
|
|
117
|
+
//# sourceMappingURL=pre-commit-hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-commit-hooks.d.ts","sourceRoot":"","sources":["../../src/git/pre-commit-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAahD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAahD;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,QAAQ,GAAE,MAAoC,GAAG,OAAO,CAAC,IAAI,CAAC,CAehG;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG;IACvE,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CAuBA;AAED;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAGxD;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EAAE,EACnB,QAAQ,GAAE,MAAoC,GAC7C,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAwBjD;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACtE,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAuBD;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAqCjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,MAAM,GACtB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAOxC;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAAoC,GAC7C,OAAO,CAAC,gBAAgB,CAAC,CAuB3B;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUpE"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-commit Hooks for FORGE
|
|
3
|
+
*
|
|
4
|
+
* Creates hooks to enforce:
|
|
5
|
+
* - Out-of-scope file detection (agent tried to write outside allowedPaths)
|
|
6
|
+
* - Commit message format validation
|
|
7
|
+
* - Test execution requirement (if tests defined for task)
|
|
8
|
+
* - Contract file validation (if contract referenced)
|
|
9
|
+
*
|
|
10
|
+
* These hooks are installed to .git/hooks/ and run before each commit.
|
|
11
|
+
*/
|
|
12
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { execa } from "execa";
|
|
15
|
+
import { validateForPreCommit } from "./commit.js";
|
|
16
|
+
/**
|
|
17
|
+
* Generate the pre-commit hook script.
|
|
18
|
+
*
|
|
19
|
+
* This shell script is installed to .git/hooks/pre-commit.
|
|
20
|
+
*/
|
|
21
|
+
export function generatePreCommitScript() {
|
|
22
|
+
return `#!/bin/bash
|
|
23
|
+
# FORGE Pre-commit Hook
|
|
24
|
+
# Auto-generated by FORGE - DO NOT EDIT MANUALLY
|
|
25
|
+
|
|
26
|
+
set -e
|
|
27
|
+
|
|
28
|
+
# Get the root of the git repository
|
|
29
|
+
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
30
|
+
|
|
31
|
+
# Run the TypeScript pre-commit validator
|
|
32
|
+
node "$REPO_ROOT/dist/git/hooks/pre-commit.js" "$@"
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Generate the commit-msg hook script.
|
|
37
|
+
*
|
|
38
|
+
* This shell script is installed to .git/hooks/commit-msg.
|
|
39
|
+
*/
|
|
40
|
+
export function generateCommitMsgScript() {
|
|
41
|
+
return `#!/bin/bash
|
|
42
|
+
# FORGE Commit Message Hook
|
|
43
|
+
# Auto-generated by FORGE - DO NOT EDIT MANUALLY
|
|
44
|
+
|
|
45
|
+
set -e
|
|
46
|
+
|
|
47
|
+
# Get the root of the git repository
|
|
48
|
+
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
49
|
+
|
|
50
|
+
# Run the TypeScript commit-msg validator
|
|
51
|
+
node "$REPO_ROOT/dist/git/hooks/commit-msg.js" "$1"
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Install pre-commit hooks to the .git/hooks directory.
|
|
56
|
+
*
|
|
57
|
+
* @param basePath - Root of the FORGE project
|
|
58
|
+
*/
|
|
59
|
+
export async function installHooks(basePath = "/home/parz/projects/forge") {
|
|
60
|
+
const hooksDir = join(basePath, ".git", "hooks");
|
|
61
|
+
// Ensure hooks directory exists
|
|
62
|
+
await mkdir(hooksDir, { recursive: true });
|
|
63
|
+
// Write pre-commit hook
|
|
64
|
+
const preCommitPath = join(hooksDir, "pre-commit");
|
|
65
|
+
await writeFile(preCommitPath, generatePreCommitScript(), { mode: 0o755 });
|
|
66
|
+
// Write commit-msg hook
|
|
67
|
+
const commitMsgPath = join(hooksDir, "commit-msg");
|
|
68
|
+
await writeFile(commitMsgPath, generateCommitMsgScript(), { mode: 0o755 });
|
|
69
|
+
console.log("FORGE pre-commit hooks installed.");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if files are within allowed paths.
|
|
73
|
+
*
|
|
74
|
+
* @param files - Files to check
|
|
75
|
+
* @param allowedPaths - Allowed path patterns
|
|
76
|
+
* @returns Validation result
|
|
77
|
+
*/
|
|
78
|
+
export function checkFileScope(files, allowedPaths) {
|
|
79
|
+
const outOfScopeFiles = [];
|
|
80
|
+
for (const file of files) {
|
|
81
|
+
const isAllowed = allowedPaths.some((pattern) => {
|
|
82
|
+
// Simple glob matching - converts gitignore-style patterns to regex
|
|
83
|
+
const regexPattern = pattern
|
|
84
|
+
.replace(/\*\*/g, ".*")
|
|
85
|
+
.replace(/\*/g, "[^/]*")
|
|
86
|
+
.replace(/\?/g, "[^/]");
|
|
87
|
+
const regex = new RegExp(`^${regexPattern}`);
|
|
88
|
+
return regex.test(file);
|
|
89
|
+
});
|
|
90
|
+
if (!isAllowed) {
|
|
91
|
+
outOfScopeFiles.push(file);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
isValid: outOfScopeFiles.length === 0,
|
|
96
|
+
outOfScopeFiles,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get staged files for a commit.
|
|
101
|
+
*
|
|
102
|
+
* @returns Array of staged file paths
|
|
103
|
+
*/
|
|
104
|
+
export async function getStagedFiles() {
|
|
105
|
+
const { stdout } = await execa("git", ["diff", "--cached", "--name-only", "--diff-filter=ACM"]);
|
|
106
|
+
return stdout.split("\n").filter((f) => f.length > 0);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Validate that required contracts exist and are valid.
|
|
110
|
+
*
|
|
111
|
+
* @param contracts - Contract file paths to validate
|
|
112
|
+
* @param basePath - Root of the FORGE project
|
|
113
|
+
* @returns Validation result
|
|
114
|
+
*/
|
|
115
|
+
export async function validateContracts(contracts, basePath = "/home/parz/projects/forge") {
|
|
116
|
+
const errors = [];
|
|
117
|
+
const { readFile } = await import("node:fs/promises");
|
|
118
|
+
for (const contract of contracts) {
|
|
119
|
+
const contractPath = join(basePath, contract);
|
|
120
|
+
try {
|
|
121
|
+
// Check if file exists
|
|
122
|
+
await readFile(contractPath, "utf-8");
|
|
123
|
+
// TODO: Add validation based on contract type
|
|
124
|
+
// - OpenAPI validation for .yaml files
|
|
125
|
+
// - TypeScript type checking for .d.ts files
|
|
126
|
+
// - Zod schema validation
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
errors.push(`Contract file not found or invalid: ${contract}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
isValid: errors.length === 0,
|
|
134
|
+
errors,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Run tests and check if they pass.
|
|
139
|
+
*
|
|
140
|
+
* @param testCommands - Test commands to run (e.g., ["npm test -- --grep 'session auth'"])
|
|
141
|
+
* @returns Test result
|
|
142
|
+
*/
|
|
143
|
+
export async function runRequiredTests(testCommands) {
|
|
144
|
+
const results = [];
|
|
145
|
+
for (const command of testCommands) {
|
|
146
|
+
try {
|
|
147
|
+
const { stdout, stderr } = await execa(command, { shell: true });
|
|
148
|
+
results.push({ passed: true, output: stdout + stderr });
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
if (error instanceof Error) {
|
|
152
|
+
results.push({ passed: false, output: error.message });
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
results.push({ passed: false, output: "Unknown error" });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const allPassed = results.every((r) => r.passed);
|
|
160
|
+
const combinedOutput = results.map((r, i) => `Test ${i + 1}:\n${r.output}`).join("\n\n");
|
|
161
|
+
return {
|
|
162
|
+
passed: allPassed,
|
|
163
|
+
output: combinedOutput,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Run all pre-commit validations.
|
|
168
|
+
*
|
|
169
|
+
* This is the main entry point for the pre-commit hook.
|
|
170
|
+
*
|
|
171
|
+
* @param context - Pre-commit validation context
|
|
172
|
+
* @returns Validation result with errors
|
|
173
|
+
*/
|
|
174
|
+
export async function runPreCommitValidations(context) {
|
|
175
|
+
const errors = [];
|
|
176
|
+
// 1. Get staged files
|
|
177
|
+
const stagedFiles = await getStagedFiles();
|
|
178
|
+
// 2. Check file scope (if allowedPaths provided)
|
|
179
|
+
if (context.allowedPaths && context.allowedPaths.length > 0) {
|
|
180
|
+
const scopeCheck = checkFileScope(stagedFiles, context.allowedPaths);
|
|
181
|
+
if (!scopeCheck.isValid) {
|
|
182
|
+
errors.push(`Out-of-scope files detected: ${scopeCheck.outOfScopeFiles.join(", ")}\n` +
|
|
183
|
+
`Allowed paths: ${context.allowedPaths.join(", ")}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// 3. Validate contracts (if required)
|
|
187
|
+
if (context.requiredContracts && context.requiredContracts.length > 0) {
|
|
188
|
+
const contractValidation = await validateContracts(context.requiredContracts);
|
|
189
|
+
if (!contractValidation.isValid) {
|
|
190
|
+
errors.push(...contractValidation.errors);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// 4. Run required tests (if specified)
|
|
194
|
+
if (context.requiredTests && context.requiredTests.length > 0) {
|
|
195
|
+
const testResult = await runRequiredTests(context.requiredTests);
|
|
196
|
+
if (!testResult.passed) {
|
|
197
|
+
errors.push(`Required tests failed:\n${testResult.output}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
isValid: errors.length === 0,
|
|
202
|
+
errors,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Run commit message validation.
|
|
207
|
+
*
|
|
208
|
+
* This is the main entry point for the commit-msg hook.
|
|
209
|
+
*
|
|
210
|
+
* @param message - Commit message to validate
|
|
211
|
+
* @param expectedTaskId - Expected task ID (optional)
|
|
212
|
+
* @returns Validation result
|
|
213
|
+
*/
|
|
214
|
+
export function runCommitMsgValidation(message, expectedTaskId) {
|
|
215
|
+
const result = validateForPreCommit(message, expectedTaskId);
|
|
216
|
+
return {
|
|
217
|
+
isValid: result.isValid,
|
|
218
|
+
errors: result.errors,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Read pre-commit context from task state.
|
|
223
|
+
*
|
|
224
|
+
* This loads the validation rules from the FORGE state.
|
|
225
|
+
*
|
|
226
|
+
* @param taskId - Task ID to load context for
|
|
227
|
+
* @param basePath - Root of the FORGE project
|
|
228
|
+
* @returns Pre-commit context
|
|
229
|
+
*/
|
|
230
|
+
export async function loadPreCommitContext(taskId, basePath = "/home/parz/projects/forge") {
|
|
231
|
+
const { readFile } = await import("node:fs/promises");
|
|
232
|
+
const statePath = join(basePath, "state", "STATE.json");
|
|
233
|
+
try {
|
|
234
|
+
const stateRaw = await readFile(statePath, "utf-8");
|
|
235
|
+
const state = JSON.parse(stateRaw);
|
|
236
|
+
const task = state.tasks.find((t) => t.id === taskId);
|
|
237
|
+
if (!task) {
|
|
238
|
+
throw new Error(`Task ${taskId} not found in STATE.json`);
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
taskId: task.id,
|
|
242
|
+
allowedPaths: task.allowedPaths,
|
|
243
|
+
requiredTests: task.verify,
|
|
244
|
+
requiredContracts: task.contracts,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
// If state doesn't exist, return empty context
|
|
249
|
+
return {};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Parse task ID from the current branch name.
|
|
254
|
+
*
|
|
255
|
+
* FORGE branches use format: forge/{taskId}
|
|
256
|
+
*
|
|
257
|
+
* @returns Task ID or null
|
|
258
|
+
*/
|
|
259
|
+
export async function parseTaskIdFromBranch() {
|
|
260
|
+
try {
|
|
261
|
+
const { stdout } = await execa("git", ["branch", "--show-current"]);
|
|
262
|
+
const branch = stdout.trim();
|
|
263
|
+
const match = branch.match(/^forge\/(.+)$/);
|
|
264
|
+
return match ? match[1] : null;
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=pre-commit-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-commit-hooks.js","sourceRoot":"","sources":["../../src/git/pre-commit-hooks.ts"],"names":[],"mappings":"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"}
|