feat(agent-forum): Improve dependency checks and initialization reporting (#73)
Refactored the `.forum/src/install_hooks.ts` initialization script to utilize a robust configuration array (`REQUIRED_TOOLS`) for verifying system dependencies, correctly distinguishing between hard requirements (Git, Deno, NPM) and optional tools that log warnings (tree-sitter, semgrep, scip-typescript, madge, stryker). Additionally improved the status reporting when initializing the `meta-state` orphan branch by using non-mutating `git ls-tree` to read and display the exact status (`[✓] Found`, `[+] Freshly Initialized`, `[~] Replaced`, `[!] Missing/Warning`) of all required branch components (`tasks/`, `ontologies/`, `vectors/`, `transitions.json`), ensuring no unintended new placeholder stubs are created in the process. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
This commit is contained in:
parent
3ea3ada808
commit
8b91df1dca
@ -41,63 +41,78 @@ async function runCommand(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const REQUIRED_TOOLS = [
|
||||||
|
{ name: "git", command: "git", args: ["--version"], required: true, failMsg: "Git is not installed or not in PATH." },
|
||||||
|
{ name: "Deno", command: "deno", args: ["--version"], required: true, failMsg: "Deno is not installed or not in PATH." },
|
||||||
|
{ name: "Node.js / npm", command: "npm", args: ["--version"], required: true, failMsg: "npm is not installed or not in PATH." },
|
||||||
|
{ name: "tree-sitter", command: "tree-sitter", args: ["--version"], required: false, warnMsg: "SCIP/AST generation may be limited." },
|
||||||
|
{ name: "semgrep", command: "semgrep", args: ["--version"], required: false, warnMsg: "Security analysis payloads may be skipped." },
|
||||||
|
{ name: "scip-typescript", command: "scip-typescript", args: ["--version"], required: false, warnMsg: "TypeScript SCIP indexing may be skipped." },
|
||||||
|
{ name: "madge", command: "madge", args: ["--version"], required: false, warnMsg: "Dependency graphing may be skipped." },
|
||||||
|
{ name: "stryker", command: "stryker", args: ["--version"], required: false, warnMsg: "Mutation testing may be skipped." }
|
||||||
|
];
|
||||||
|
|
||||||
async function checkDependencies() {
|
async function checkDependencies() {
|
||||||
console.log("Checking dependencies...");
|
console.log("Checking dependencies...");
|
||||||
|
|
||||||
// Git
|
for (const tool of REQUIRED_TOOLS) {
|
||||||
const gitCheck = await runCommand("git", ["--version"]);
|
const check = await runCommand(tool.command, tool.args);
|
||||||
if (gitCheck.code !== 0) {
|
if (check.code !== 0) {
|
||||||
console.error("❌ Git is not installed or not in PATH.");
|
if (tool.required) {
|
||||||
Deno.exit(1);
|
console.error(`[!] Missing: ${tool.name} - ${tool.failMsg}`);
|
||||||
}
|
Deno.exit(1);
|
||||||
console.log(`✅ ${gitCheck.stdout}`);
|
} else {
|
||||||
|
console.log(`[!] Warning: ${tool.name} not found. ${tool.warnMsg}`);
|
||||||
// Deno
|
}
|
||||||
const denoCheck = await runCommand("deno", ["--version"]);
|
} else {
|
||||||
if (denoCheck.code !== 0) {
|
// For tools like npm or tree-sitter that might output multiline or noisy versions,
|
||||||
console.error("❌ Deno is not installed or not in PATH.");
|
// we take just the first line for a cleaner log.
|
||||||
Deno.exit(1);
|
const versionStr = check.stdout.split('\n')[0].substring(0, 30);
|
||||||
}
|
console.log(`[✓] Found: ${tool.name} (${versionStr})`);
|
||||||
console.log(`✅ Deno installed`);
|
}
|
||||||
|
|
||||||
// Optional: Tree-sitter
|
|
||||||
const treeSitterCheck = await runCommand("tree-sitter", ["--version"]);
|
|
||||||
if (treeSitterCheck.code === 0) {
|
|
||||||
console.log(`✅ tree-sitter found: ${treeSitterCheck.stdout}`);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
"⚠️ tree-sitter not found. SCIP/AST generation may be limited.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional: Semgrep
|
|
||||||
const semgrepCheck = await runCommand("semgrep", ["--version"]);
|
|
||||||
if (semgrepCheck.code === 0) {
|
|
||||||
console.log(`✅ semgrep found: ${semgrepCheck.stdout}`);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
"⚠️ semgrep not found. Security analysis payloads may be skipped.",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initializeMetaStateBranch() {
|
async function initializeMetaStateBranch() {
|
||||||
console.log("\nInitializing 'meta-state' orphan branch...");
|
console.log("\nInitializing 'meta-state' orphan branch...");
|
||||||
|
|
||||||
|
const dirs = ["tasks", "ontologies", "vectors"];
|
||||||
|
|
||||||
// Check if branch exists
|
// Check if branch exists
|
||||||
const checkBranch = await runCommand("git", [
|
const checkBranch = await runCommand("git", [
|
||||||
"show-ref",
|
"show-ref",
|
||||||
"--verify",
|
"--verify",
|
||||||
"refs/heads/meta-state",
|
"refs/heads/meta-state",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (checkBranch.code === 0) {
|
if (checkBranch.code === 0) {
|
||||||
console.log(
|
console.log(
|
||||||
"✅ 'meta-state' branch already exists. Preserving existing data.",
|
"[ Already Exists ] 'meta-state' branch found. Checking existing structure...",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Check which directories exist on the meta-state branch
|
||||||
|
const lsTree = await runCommand("git", ["ls-tree", "refs/heads/meta-state"]);
|
||||||
|
if (lsTree.code === 0) {
|
||||||
|
const existingItems = lsTree.stdout.split('\n').map(line => line.split('\t')[1]);
|
||||||
|
for (const d of dirs) {
|
||||||
|
if (existingItems.includes(d)) {
|
||||||
|
console.log(`[✓] Found directory: ${d}/`);
|
||||||
|
} else {
|
||||||
|
console.log(`[!] Missing directory in meta-state: ${d}/`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (existingItems.includes("transitions.json")) {
|
||||||
|
console.log(`[✓] Found file: transitions.json`);
|
||||||
|
} else {
|
||||||
|
console.log(`[!] Missing file in meta-state: transitions.json`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[~] Preserving existing data. (If missing items are needed, you may need to add them manually to the meta-state branch)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Creating 'meta-state' orphan branch...");
|
console.log("[+] Creating 'meta-state' orphan branch...");
|
||||||
|
|
||||||
// Save current branch name (not strictly needed since we use plumbing, but kept for context if needed later)
|
// Save current branch name (not strictly needed since we use plumbing, but kept for context if needed later)
|
||||||
const currentBranchRes = await runCommand("git", [
|
const currentBranchRes = await runCommand("git", [
|
||||||
@ -111,7 +126,6 @@ async function initializeMetaStateBranch() {
|
|||||||
const tempDir = await Deno.makeTempDir();
|
const tempDir = await Deno.makeTempDir();
|
||||||
|
|
||||||
// Create structure
|
// Create structure
|
||||||
const dirs = ["tasks", "ontologies", "vectors"];
|
|
||||||
for (const d of dirs) {
|
for (const d of dirs) {
|
||||||
await Deno.mkdir(join(tempDir, d), { recursive: true });
|
await Deno.mkdir(join(tempDir, d), { recursive: true });
|
||||||
// Add a .gitkeep so git tracks the directory
|
// Add a .gitkeep so git tracks the directory
|
||||||
@ -220,7 +234,7 @@ async function initializeMetaStateBranch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"✅ 'meta-state' branch initialized successfully without modifying working tree.",
|
"[+] 'meta-state' branch freshly initialized successfully without modifying working tree.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +243,7 @@ async function installHooks() {
|
|||||||
|
|
||||||
if (!existsSync(GIT_DIR)) {
|
if (!existsSync(GIT_DIR)) {
|
||||||
console.error(
|
console.error(
|
||||||
"❌ .git directory not found. Must be run from the root of a git repository.",
|
"[!] Error: .git directory not found. Must be run from the root of a git repository.",
|
||||||
);
|
);
|
||||||
Deno.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
@ -238,13 +252,13 @@ async function installHooks() {
|
|||||||
const preCommitTarget = join(HOOKS_DIR, "pre-commit");
|
const preCommitTarget = join(HOOKS_DIR, "pre-commit");
|
||||||
|
|
||||||
if (!existsSync(preCommitSource)) {
|
if (!existsSync(preCommitSource)) {
|
||||||
console.error(`❌ Hook template not found at ${preCommitSource}`);
|
console.error(`[!] Error: Hook template not found at ${preCommitSource}`);
|
||||||
Deno.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existsSync(preCommitTarget)) {
|
if (existsSync(preCommitTarget)) {
|
||||||
console.log("⚠️ An existing pre-commit hook was found.");
|
console.log("[~] An existing pre-commit hook was found.");
|
||||||
console.log("Moving existing hook to pre-commit.backup...");
|
console.log("[~] Replaced: Moving existing hook to pre-commit.backup...");
|
||||||
await Deno.rename(preCommitTarget, join(HOOKS_DIR, "pre-commit.backup"));
|
await Deno.rename(preCommitTarget, join(HOOKS_DIR, "pre-commit.backup"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +269,7 @@ async function installHooks() {
|
|||||||
await runCommand("chmod", ["+x", preCommitTarget]);
|
await runCommand("chmod", ["+x", preCommitTarget]);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("✅ pre-commit hook installed successfully.");
|
console.log("[+] Freshly Initialized: pre-commit hook installed successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user