javi-forge 1.6.0 → 1.6.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/dist/commands/analyze.d.ts +1 -1
- package/dist/commands/analyze.js +15 -15
- package/dist/commands/atlassian-mcp.d.ts +42 -0
- package/dist/commands/atlassian-mcp.js +98 -0
- package/dist/commands/ci.d.ts +3 -3
- package/dist/commands/ci.js +185 -147
- package/dist/commands/crash-recovery.d.ts +34 -0
- package/dist/commands/crash-recovery.js +123 -0
- package/dist/commands/doctor.d.ts +2 -2
- package/dist/commands/doctor.js +113 -61
- package/dist/commands/harness-audit.d.ts +35 -0
- package/dist/commands/harness-audit.js +277 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +384 -141
- package/dist/commands/llmstxt.d.ts +1 -1
- package/dist/commands/llmstxt.js +36 -34
- package/dist/commands/parallel-batch.d.ts +42 -0
- package/dist/commands/parallel-batch.js +90 -0
- package/dist/commands/plugin.d.ts +10 -1
- package/dist/commands/plugin.js +92 -47
- package/dist/commands/secret-scanner.d.ts +30 -0
- package/dist/commands/secret-scanner.js +272 -0
- package/dist/commands/security-analysis.d.ts +74 -0
- package/dist/commands/security-analysis.js +487 -0
- package/dist/commands/security.d.ts +11 -5
- package/dist/commands/security.js +216 -76
- package/dist/commands/skill-scanner.d.ts +63 -0
- package/dist/commands/skill-scanner.js +383 -0
- package/dist/commands/skills.d.ts +62 -5
- package/dist/commands/skills.js +439 -54
- package/dist/commands/supply-chain.d.ts +23 -0
- package/dist/commands/supply-chain.js +126 -0
- package/dist/commands/tdd-pipeline.d.ts +17 -0
- package/dist/commands/tdd-pipeline.js +144 -0
- package/dist/commands/tdd.d.ts +1 -1
- package/dist/commands/tdd.js +21 -18
- package/dist/commands/team-presets.d.ts +53 -0
- package/dist/commands/team-presets.js +201 -0
- package/dist/commands/workflow.d.ts +23 -0
- package/dist/commands/workflow.js +114 -0
- package/dist/constants.d.ts +15 -1
- package/dist/constants.js +161 -122
- package/dist/index.js +308 -98
- package/dist/lib/agent-skills.d.ts +36 -1
- package/dist/lib/agent-skills.js +168 -19
- package/dist/lib/auto-skill-install.d.ts +37 -0
- package/dist/lib/auto-skill-install.js +92 -0
- package/dist/lib/auto-wire.d.ts +20 -0
- package/dist/lib/auto-wire.js +240 -0
- package/dist/lib/claudemd.d.ts +13 -1
- package/dist/lib/claudemd.js +174 -24
- package/dist/lib/codex-export.d.ts +1 -1
- package/dist/lib/codex-export.js +29 -31
- package/dist/lib/common.d.ts +1 -1
- package/dist/lib/common.js +52 -44
- package/dist/lib/context.d.ts +17 -2
- package/dist/lib/context.js +142 -13
- package/dist/lib/docker.d.ts +1 -1
- package/dist/lib/docker.js +141 -112
- package/dist/lib/frontmatter.d.ts +1 -1
- package/dist/lib/frontmatter.js +29 -15
- package/dist/lib/plugin.d.ts +9 -3
- package/dist/lib/plugin.js +128 -69
- package/dist/lib/skill-publish.d.ts +40 -0
- package/dist/lib/skill-publish.js +146 -0
- package/dist/lib/stack-detector.d.ts +38 -0
- package/dist/lib/stack-detector.js +207 -0
- package/dist/lib/template.d.ts +16 -1
- package/dist/lib/template.js +46 -17
- package/dist/lib/workflow/discovery.d.ts +19 -0
- package/dist/lib/workflow/discovery.js +68 -0
- package/dist/lib/workflow/index.d.ts +5 -0
- package/dist/lib/workflow/index.js +5 -0
- package/dist/lib/workflow/parser.d.ts +16 -0
- package/dist/lib/workflow/parser.js +198 -0
- package/dist/lib/workflow/renderer.d.ts +9 -0
- package/dist/lib/workflow/renderer.js +152 -0
- package/dist/lib/workflow/validator.d.ts +10 -0
- package/dist/lib/workflow/validator.js +189 -0
- package/dist/tasks/index.d.ts +4 -0
- package/dist/tasks/index.js +4 -0
- package/dist/tasks/scaffold-tasks.d.ts +3 -0
- package/dist/tasks/scaffold-tasks.js +14 -0
- package/dist/tasks/task-id.d.ts +30 -0
- package/dist/tasks/task-id.js +55 -0
- package/dist/tasks/task-tracker.d.ts +15 -0
- package/dist/tasks/task-tracker.js +81 -0
- package/dist/types/index.d.ts +134 -6
- package/dist/types/index.js +11 -1
- package/dist/ui/AnalyzeUI.d.ts +1 -1
- package/dist/ui/AnalyzeUI.js +38 -39
- package/dist/ui/App.d.ts +5 -3
- package/dist/ui/App.js +86 -46
- package/dist/ui/AutoSkills.d.ts +9 -0
- package/dist/ui/AutoSkills.js +124 -0
- package/dist/ui/CI.d.ts +2 -2
- package/dist/ui/CI.js +24 -26
- package/dist/ui/CIContext.d.ts +1 -1
- package/dist/ui/CIContext.js +3 -2
- package/dist/ui/CISelector.d.ts +2 -2
- package/dist/ui/CISelector.js +23 -15
- package/dist/ui/Doctor.d.ts +1 -1
- package/dist/ui/Doctor.js +35 -29
- package/dist/ui/Header.d.ts +1 -1
- package/dist/ui/Header.js +14 -14
- package/dist/ui/HookProfileSelector.d.ts +9 -0
- package/dist/ui/HookProfileSelector.js +54 -0
- package/dist/ui/LlmsTxt.d.ts +1 -1
- package/dist/ui/LlmsTxt.js +31 -22
- package/dist/ui/MemorySelector.d.ts +2 -2
- package/dist/ui/MemorySelector.js +28 -16
- package/dist/ui/NameInput.d.ts +1 -1
- package/dist/ui/NameInput.js +21 -21
- package/dist/ui/OptionSelector.d.ts +6 -2
- package/dist/ui/OptionSelector.js +83 -32
- package/dist/ui/Plugin.d.ts +4 -3
- package/dist/ui/Plugin.js +78 -35
- package/dist/ui/Progress.d.ts +3 -3
- package/dist/ui/Progress.js +23 -22
- package/dist/ui/Skills.d.ts +2 -2
- package/dist/ui/Skills.js +61 -32
- package/dist/ui/StackSelector.d.ts +2 -2
- package/dist/ui/StackSelector.js +26 -16
- package/dist/ui/Summary.d.ts +3 -3
- package/dist/ui/Summary.js +60 -50
- package/dist/ui/Welcome.d.ts +1 -1
- package/dist/ui/Welcome.js +15 -16
- package/dist/ui/theme.d.ts +1 -1
- package/dist/ui/theme.js +6 -6
- package/package.json +9 -6
- package/templates/common/atlassian/mcp-atlassian-snippet.json +16 -0
- package/templates/common/repoforge/mcp-repoforge-snippet.json +11 -0
- package/templates/common/repoforge/repoforge.yaml +34 -0
- package/templates/github/deploy-docker-zero-downtime.yml +140 -0
- package/templates/github/repoforge-graph.yml +45 -0
- package/templates/gitlab/deploy-docker-zero-downtime.yml +57 -0
- package/templates/local-ai/.env.example +17 -0
- package/templates/local-ai/docker-compose.yml +95 -0
- package/templates/security-hooks/claude-settings-security.json +30 -0
- package/templates/security-hooks/commit-msg-signing +29 -0
- package/templates/security-hooks/pre-commit-permissions +74 -0
- package/templates/security-hooks/pre-commit-secrets +74 -0
- package/templates/security-hooks/pre-push-branch-protection +62 -0
- package/templates/security-hooks/pre-push-deps +83 -0
- package/templates/security-hooks/pre-push-signing +67 -0
- package/templates/woodpecker/deploy-docker-zero-downtime.yml +50 -0
- package/templates/workflows/ci-pipeline.dot +15 -0
- package/templates/workflows/feature-flow.dot +21 -0
- package/templates/workflows/release.dot +16 -0
- package/dist/__integration__/helpers.d.ts +0 -20
- package/dist/__integration__/helpers.d.ts.map +0 -1
- package/dist/__integration__/helpers.js +0 -31
- package/dist/__integration__/helpers.js.map +0 -1
- package/dist/commands/analyze.d.ts.map +0 -1
- package/dist/commands/analyze.js.map +0 -1
- package/dist/commands/ci.d.ts.map +0 -1
- package/dist/commands/ci.js.map +0 -1
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/llmstxt.d.ts.map +0 -1
- package/dist/commands/llmstxt.js.map +0 -1
- package/dist/commands/plugin.d.ts.map +0 -1
- package/dist/commands/plugin.js.map +0 -1
- package/dist/commands/security.d.ts.map +0 -1
- package/dist/commands/security.js.map +0 -1
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/tdd.d.ts.map +0 -1
- package/dist/commands/tdd.js.map +0 -1
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/agent-skills.d.ts.map +0 -1
- package/dist/lib/agent-skills.js.map +0 -1
- package/dist/lib/claudemd.d.ts.map +0 -1
- package/dist/lib/claudemd.js.map +0 -1
- package/dist/lib/codex-export.d.ts.map +0 -1
- package/dist/lib/codex-export.js.map +0 -1
- package/dist/lib/common.d.ts.map +0 -1
- package/dist/lib/common.js.map +0 -1
- package/dist/lib/context.d.ts.map +0 -1
- package/dist/lib/context.js.map +0 -1
- package/dist/lib/docker.d.ts.map +0 -1
- package/dist/lib/docker.js.map +0 -1
- package/dist/lib/frontmatter.d.ts.map +0 -1
- package/dist/lib/frontmatter.js.map +0 -1
- package/dist/lib/plugin.d.ts.map +0 -1
- package/dist/lib/plugin.js.map +0 -1
- package/dist/lib/template.d.ts.map +0 -1
- package/dist/lib/template.js.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/ui/AnalyzeUI.d.ts.map +0 -1
- package/dist/ui/AnalyzeUI.js.map +0 -1
- package/dist/ui/App.d.ts.map +0 -1
- package/dist/ui/App.js.map +0 -1
- package/dist/ui/CI.d.ts.map +0 -1
- package/dist/ui/CI.js.map +0 -1
- package/dist/ui/CIContext.d.ts.map +0 -1
- package/dist/ui/CIContext.js.map +0 -1
- package/dist/ui/CISelector.d.ts.map +0 -1
- package/dist/ui/CISelector.js.map +0 -1
- package/dist/ui/Doctor.d.ts.map +0 -1
- package/dist/ui/Doctor.js.map +0 -1
- package/dist/ui/Header.d.ts.map +0 -1
- package/dist/ui/Header.js.map +0 -1
- package/dist/ui/LlmsTxt.d.ts.map +0 -1
- package/dist/ui/LlmsTxt.js.map +0 -1
- package/dist/ui/MemorySelector.d.ts.map +0 -1
- package/dist/ui/MemorySelector.js.map +0 -1
- package/dist/ui/NameInput.d.ts.map +0 -1
- package/dist/ui/NameInput.js.map +0 -1
- package/dist/ui/OptionSelector.d.ts.map +0 -1
- package/dist/ui/OptionSelector.js.map +0 -1
- package/dist/ui/Plugin.d.ts.map +0 -1
- package/dist/ui/Plugin.js.map +0 -1
- package/dist/ui/Progress.d.ts.map +0 -1
- package/dist/ui/Progress.js.map +0 -1
- package/dist/ui/Skills.d.ts.map +0 -1
- package/dist/ui/Skills.js.map +0 -1
- package/dist/ui/StackSelector.d.ts.map +0 -1
- package/dist/ui/StackSelector.js.map +0 -1
- package/dist/ui/Summary.d.ts.map +0 -1
- package/dist/ui/Summary.js.map +0 -1
- package/dist/ui/Welcome.d.ts.map +0 -1
- package/dist/ui/Welcome.js.map +0 -1
- package/dist/ui/theme.d.ts.map +0 -1
- package/dist/ui/theme.js.map +0 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supply chain protection — configures Socket Firewall and enforces
|
|
3
|
+
* minimum package release age to protect against typosquatting,
|
|
4
|
+
* dependency confusion, and malicious package takeovers.
|
|
5
|
+
*/
|
|
6
|
+
import type { Stack } from "../types/index.js";
|
|
7
|
+
export type PackageManager = "npm" | "pnpm" | "yarn" | "pip" | "cargo" | "go";
|
|
8
|
+
export interface SupplyChainConfig {
|
|
9
|
+
packageManager: PackageManager;
|
|
10
|
+
minReleaseAgeHours: number;
|
|
11
|
+
socketEnabled: boolean;
|
|
12
|
+
enforceLockfile: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface SupplyChainResult {
|
|
15
|
+
configWritten: string[];
|
|
16
|
+
warnings: string[];
|
|
17
|
+
}
|
|
18
|
+
export declare function detectPackageManager(projectDir: string): PackageManager;
|
|
19
|
+
export declare function stackForPM(pm: PackageManager): Stack;
|
|
20
|
+
export declare function buildSocketConfig(pm: PackageManager): Record<string, unknown>;
|
|
21
|
+
export declare function buildNpmrcLines(minAgeHours: number): string[];
|
|
22
|
+
export declare function configureSupplyChain(projectDir: string, options?: Partial<SupplyChainConfig>): Promise<SupplyChainResult>;
|
|
23
|
+
//# sourceMappingURL=supply-chain.d.ts.map
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supply chain protection — configures Socket Firewall and enforces
|
|
3
|
+
* minimum package release age to protect against typosquatting,
|
|
4
|
+
* dependency confusion, and malicious package takeovers.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "fs-extra";
|
|
7
|
+
import path from "path";
|
|
8
|
+
// ── Detection ──
|
|
9
|
+
export function detectPackageManager(projectDir) {
|
|
10
|
+
if (fs.existsSync(path.join(projectDir, "pnpm-lock.yaml")))
|
|
11
|
+
return "pnpm";
|
|
12
|
+
if (fs.existsSync(path.join(projectDir, "yarn.lock")))
|
|
13
|
+
return "yarn";
|
|
14
|
+
if (fs.existsSync(path.join(projectDir, "package-lock.json")))
|
|
15
|
+
return "npm";
|
|
16
|
+
if (fs.existsSync(path.join(projectDir, "Pipfile.lock")))
|
|
17
|
+
return "pip";
|
|
18
|
+
if (fs.existsSync(path.join(projectDir, "Cargo.lock")))
|
|
19
|
+
return "cargo";
|
|
20
|
+
if (fs.existsSync(path.join(projectDir, "go.sum")))
|
|
21
|
+
return "go";
|
|
22
|
+
if (fs.existsSync(path.join(projectDir, "package.json")))
|
|
23
|
+
return "npm";
|
|
24
|
+
return "npm";
|
|
25
|
+
}
|
|
26
|
+
export function stackForPM(pm) {
|
|
27
|
+
switch (pm) {
|
|
28
|
+
case "npm":
|
|
29
|
+
case "pnpm":
|
|
30
|
+
case "yarn":
|
|
31
|
+
return "node";
|
|
32
|
+
case "pip":
|
|
33
|
+
return "python";
|
|
34
|
+
case "cargo":
|
|
35
|
+
return "rust";
|
|
36
|
+
case "go":
|
|
37
|
+
return "go";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// ── Socket Firewall config ──
|
|
41
|
+
export function buildSocketConfig(pm) {
|
|
42
|
+
return {
|
|
43
|
+
version: 2,
|
|
44
|
+
projectType: stackForPM(pm),
|
|
45
|
+
issueRules: {
|
|
46
|
+
"pkg:npm/*": {
|
|
47
|
+
installScripts: "error",
|
|
48
|
+
telemetry: "warn",
|
|
49
|
+
troll: "error",
|
|
50
|
+
unpublished: "error",
|
|
51
|
+
deprecated: "warn",
|
|
52
|
+
unmaintained: "warn",
|
|
53
|
+
"new-author": "warn",
|
|
54
|
+
shellAccess: "error",
|
|
55
|
+
filesystemAccess: "warn",
|
|
56
|
+
networkAccess: "warn",
|
|
57
|
+
envAccess: "warn",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// ── npmrc / lockfile enforcement ──
|
|
63
|
+
export function buildNpmrcLines(minAgeHours) {
|
|
64
|
+
const lines = [
|
|
65
|
+
"# Supply chain protection — generated by javi-forge",
|
|
66
|
+
"engine-strict=true",
|
|
67
|
+
"save-exact=true",
|
|
68
|
+
];
|
|
69
|
+
if (minAgeHours > 0) {
|
|
70
|
+
lines.push(`# Minimum package age: ${minAgeHours}h (enforced via CI/pre-install hook)`);
|
|
71
|
+
}
|
|
72
|
+
return lines;
|
|
73
|
+
}
|
|
74
|
+
// ── Orchestrator ──
|
|
75
|
+
export async function configureSupplyChain(projectDir, options = {}) {
|
|
76
|
+
const pm = options.packageManager ?? detectPackageManager(projectDir);
|
|
77
|
+
const minAge = options.minReleaseAgeHours ?? 48;
|
|
78
|
+
const socketEnabled = options.socketEnabled ?? true;
|
|
79
|
+
const enforceLockfile = options.enforceLockfile ?? true;
|
|
80
|
+
const configWritten = [];
|
|
81
|
+
const warnings = [];
|
|
82
|
+
// 1. Socket Firewall config
|
|
83
|
+
if (socketEnabled) {
|
|
84
|
+
const socketPath = path.join(projectDir, ".socketrc");
|
|
85
|
+
await fs.writeJson(socketPath, buildSocketConfig(pm), { spaces: 2 });
|
|
86
|
+
configWritten.push(".socketrc");
|
|
87
|
+
}
|
|
88
|
+
// 2. npmrc for node projects
|
|
89
|
+
if (stackForPM(pm) === "node") {
|
|
90
|
+
const npmrcPath = path.join(projectDir, ".npmrc");
|
|
91
|
+
const lines = buildNpmrcLines(minAge);
|
|
92
|
+
if (enforceLockfile) {
|
|
93
|
+
if (pm === "pnpm") {
|
|
94
|
+
lines.push("frozen-lockfile=true");
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
lines.push("package-lock=true");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Append to existing .npmrc if present
|
|
101
|
+
const existing = (await fs.pathExists(npmrcPath))
|
|
102
|
+
? await fs.readFile(npmrcPath, "utf-8")
|
|
103
|
+
: "";
|
|
104
|
+
const newContent = existing
|
|
105
|
+
? `${existing.trimEnd()}\n\n${lines.join("\n")}\n`
|
|
106
|
+
: `${lines.join("\n")}\n`;
|
|
107
|
+
await fs.writeFile(npmrcPath, newContent);
|
|
108
|
+
configWritten.push(".npmrc");
|
|
109
|
+
}
|
|
110
|
+
// 3. Supply chain metadata in baseline dir
|
|
111
|
+
const metaPath = path.join(projectDir, ".javi-forge", "supply-chain.json");
|
|
112
|
+
await fs.ensureDir(path.dirname(metaPath));
|
|
113
|
+
await fs.writeJson(metaPath, {
|
|
114
|
+
packageManager: pm,
|
|
115
|
+
minReleaseAgeHours: minAge,
|
|
116
|
+
socketEnabled,
|
|
117
|
+
enforceLockfile,
|
|
118
|
+
configuredAt: new Date().toISOString(),
|
|
119
|
+
}, { spaces: 2 });
|
|
120
|
+
configWritten.push(".javi-forge/supply-chain.json");
|
|
121
|
+
if (!socketEnabled) {
|
|
122
|
+
warnings.push("Socket Firewall disabled. Consider enabling it for supply chain protection.");
|
|
123
|
+
}
|
|
124
|
+
return { configWritten, warnings };
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=supply-chain.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Stack, TddPipelineMode, TddPipelineResult } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate a TDD pipeline enforcement pre-push hook script.
|
|
4
|
+
*
|
|
5
|
+
* - strict: tests MUST pass or push is blocked (exit 1).
|
|
6
|
+
* - warn: tests are run and results shown, but push is never blocked.
|
|
7
|
+
*
|
|
8
|
+
* If testCmd is null, generates a skip-only hook regardless of mode.
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateTddPipelineHook(mode: TddPipelineMode, testCmd: string | null, stack: Stack): string;
|
|
11
|
+
/**
|
|
12
|
+
* Install TDD pipeline pre-push hook into .git/hooks/.
|
|
13
|
+
* Detects the project stack automatically and generates the appropriate hook.
|
|
14
|
+
* Backs up any existing pre-push hook to pre-push.bak.
|
|
15
|
+
*/
|
|
16
|
+
export declare function installTddPipelineHook(projectDir: string, mode: TddPipelineMode): Promise<TddPipelineResult>;
|
|
17
|
+
//# sourceMappingURL=tdd-pipeline.d.ts.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { detectCIStack } from "./ci.js";
|
|
4
|
+
import { getTddTestCommand } from "./tdd.js";
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// Hook generation
|
|
7
|
+
// =============================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Generate a TDD pipeline enforcement pre-push hook script.
|
|
10
|
+
*
|
|
11
|
+
* - strict: tests MUST pass or push is blocked (exit 1).
|
|
12
|
+
* - warn: tests are run and results shown, but push is never blocked.
|
|
13
|
+
*
|
|
14
|
+
* If testCmd is null, generates a skip-only hook regardless of mode.
|
|
15
|
+
*/
|
|
16
|
+
export function generateTddPipelineHook(mode, testCmd, stack) {
|
|
17
|
+
if (!testCmd) {
|
|
18
|
+
return `#!/bin/bash
|
|
19
|
+
# =============================================================================
|
|
20
|
+
# TDD PIPELINE (pre-push): No test command detected for stack "${stack}"
|
|
21
|
+
# =============================================================================
|
|
22
|
+
# Install a test runner and re-run: javi-forge tdd pipeline --mode ${mode}
|
|
23
|
+
# =============================================================================
|
|
24
|
+
|
|
25
|
+
echo "TDD PIPELINE: No test command configured for stack '${stack}' — skipping."
|
|
26
|
+
exit 0
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
if (mode === "warn") {
|
|
30
|
+
return `#!/bin/bash
|
|
31
|
+
# =============================================================================
|
|
32
|
+
# TDD PIPELINE (pre-push): WARN mode
|
|
33
|
+
# =============================================================================
|
|
34
|
+
# Flow: Spec → Tests → Fail → Implement → Pass
|
|
35
|
+
# Stack: ${stack} | Command: ${testCmd}
|
|
36
|
+
# Mode: warn — tests run but push is NEVER blocked
|
|
37
|
+
# To skip: git push --no-verify
|
|
38
|
+
# =============================================================================
|
|
39
|
+
|
|
40
|
+
echo "TDD PIPELINE [WARN]: Running tests before push..."
|
|
41
|
+
echo " Stack: ${stack}"
|
|
42
|
+
echo " Command: ${testCmd}"
|
|
43
|
+
echo " Mode: warn (push will proceed regardless)"
|
|
44
|
+
echo ""
|
|
45
|
+
|
|
46
|
+
${testCmd} && {
|
|
47
|
+
echo ""
|
|
48
|
+
echo "TDD PIPELINE [WARN]: All tests passed."
|
|
49
|
+
} || {
|
|
50
|
+
echo ""
|
|
51
|
+
echo "TDD PIPELINE [WARN]: Tests FAILED — but push will proceed (warn mode)."
|
|
52
|
+
echo " Consider fixing tests before merging."
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
exit 0
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
// strict mode (default)
|
|
59
|
+
return `#!/bin/bash
|
|
60
|
+
# =============================================================================
|
|
61
|
+
# TDD PIPELINE (pre-push): STRICT mode
|
|
62
|
+
# =============================================================================
|
|
63
|
+
# Flow: Spec → Tests → Fail → Implement → Pass
|
|
64
|
+
# Stack: ${stack} | Command: ${testCmd}
|
|
65
|
+
# Mode: strict — push is BLOCKED if tests fail
|
|
66
|
+
# To skip: git push --no-verify
|
|
67
|
+
# =============================================================================
|
|
68
|
+
|
|
69
|
+
set -e
|
|
70
|
+
|
|
71
|
+
echo "TDD PIPELINE [STRICT]: Running tests before push..."
|
|
72
|
+
echo " Stack: ${stack}"
|
|
73
|
+
echo " Command: ${testCmd}"
|
|
74
|
+
echo " Mode: strict (push blocked on failure)"
|
|
75
|
+
echo ""
|
|
76
|
+
|
|
77
|
+
${testCmd} || {
|
|
78
|
+
echo ""
|
|
79
|
+
echo "TDD PIPELINE [STRICT]: FAILED — Tests did not pass."
|
|
80
|
+
echo " Fix failing tests before pushing."
|
|
81
|
+
echo " To skip: git push --no-verify"
|
|
82
|
+
exit 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
echo ""
|
|
86
|
+
echo "TDD PIPELINE [STRICT]: All tests passed. Push allowed."
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
// =============================================================================
|
|
90
|
+
// Hook installation
|
|
91
|
+
// =============================================================================
|
|
92
|
+
/**
|
|
93
|
+
* Install TDD pipeline pre-push hook into .git/hooks/.
|
|
94
|
+
* Detects the project stack automatically and generates the appropriate hook.
|
|
95
|
+
* Backs up any existing pre-push hook to pre-push.bak.
|
|
96
|
+
*/
|
|
97
|
+
export async function installTddPipelineHook(projectDir, mode) {
|
|
98
|
+
const result = {
|
|
99
|
+
installed: [],
|
|
100
|
+
skipped: [],
|
|
101
|
+
errors: [],
|
|
102
|
+
mode,
|
|
103
|
+
};
|
|
104
|
+
const gitDir = path.join(projectDir, ".git");
|
|
105
|
+
if (!(await fs.pathExists(gitDir))) {
|
|
106
|
+
result.errors.push("Not a git repository. Run git init first.");
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
const hooksDir = path.join(gitDir, "hooks");
|
|
110
|
+
await fs.ensureDir(hooksDir);
|
|
111
|
+
// Detect stack
|
|
112
|
+
let stackInfo;
|
|
113
|
+
try {
|
|
114
|
+
stackInfo = await detectCIStack(projectDir);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
result.errors.push("Failed to detect project stack.");
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
const testCmd = await getTddTestCommand(stackInfo.stackType, stackInfo.buildTool, projectDir);
|
|
121
|
+
const hookContent = generateTddPipelineHook(mode, testCmd, stackInfo.stackType);
|
|
122
|
+
const hookPath = path.join(hooksDir, "pre-push");
|
|
123
|
+
// Backup existing hook
|
|
124
|
+
if (await fs.pathExists(hookPath)) {
|
|
125
|
+
const backupPath = path.join(hooksDir, "pre-push.bak");
|
|
126
|
+
try {
|
|
127
|
+
await fs.copy(hookPath, backupPath, { overwrite: true });
|
|
128
|
+
result.skipped.push("pre-push (backed up to pre-push.bak)");
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
result.errors.push(`backup: ${e instanceof Error ? e.message : String(e)}`);
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
await fs.writeFile(hookPath, hookContent, { mode: 0o755 });
|
|
137
|
+
result.installed.push("pre-push");
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
result.errors.push(`pre-push: ${e instanceof Error ? e.message : String(e)}`);
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=tdd-pipeline.js.map
|
package/dist/commands/tdd.d.ts
CHANGED
package/dist/commands/tdd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import { detectCIStack } from
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { detectCIStack } from "./ci.js";
|
|
4
4
|
// =============================================================================
|
|
5
5
|
// Test command resolution
|
|
6
6
|
// =============================================================================
|
|
@@ -10,22 +10,22 @@ import { detectCIStack } from './ci.js';
|
|
|
10
10
|
*/
|
|
11
11
|
export async function getTddTestCommand(stack, buildTool, projectDir) {
|
|
12
12
|
switch (stack) {
|
|
13
|
-
case
|
|
14
|
-
const pkgPath = path.join(projectDir,
|
|
13
|
+
case "node": {
|
|
14
|
+
const pkgPath = path.join(projectDir, "package.json");
|
|
15
15
|
try {
|
|
16
|
-
const pkgContent = await fs.readFile(pkgPath,
|
|
16
|
+
const pkgContent = await fs.readFile(pkgPath, "utf-8");
|
|
17
17
|
if (!pkgContent.includes('"test"'))
|
|
18
18
|
return null;
|
|
19
|
-
return buildTool ===
|
|
19
|
+
return buildTool === "npm" ? "npm test" : `${buildTool} run test`;
|
|
20
20
|
}
|
|
21
21
|
catch {
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
case
|
|
26
|
-
return
|
|
27
|
-
case
|
|
28
|
-
return
|
|
25
|
+
case "python":
|
|
26
|
+
return "pytest";
|
|
27
|
+
case "go":
|
|
28
|
+
return "go test ./...";
|
|
29
29
|
default:
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
@@ -86,11 +86,14 @@ echo "TDD PASSED — All tests green. Commit allowed."
|
|
|
86
86
|
* Detects the project stack automatically and generates the appropriate hook.
|
|
87
87
|
*/
|
|
88
88
|
export async function installTddHooks(projectDir) {
|
|
89
|
-
const gitDir = path.join(projectDir,
|
|
90
|
-
if (!await fs.pathExists(gitDir)) {
|
|
91
|
-
return {
|
|
89
|
+
const gitDir = path.join(projectDir, ".git");
|
|
90
|
+
if (!(await fs.pathExists(gitDir))) {
|
|
91
|
+
return {
|
|
92
|
+
installed: [],
|
|
93
|
+
errors: ["Not a git repository. Run git init first."],
|
|
94
|
+
};
|
|
92
95
|
}
|
|
93
|
-
const hooksDir = path.join(gitDir,
|
|
96
|
+
const hooksDir = path.join(gitDir, "hooks");
|
|
94
97
|
await fs.ensureDir(hooksDir);
|
|
95
98
|
// Detect stack
|
|
96
99
|
let stackInfo;
|
|
@@ -98,16 +101,16 @@ export async function installTddHooks(projectDir) {
|
|
|
98
101
|
stackInfo = await detectCIStack(projectDir);
|
|
99
102
|
}
|
|
100
103
|
catch {
|
|
101
|
-
return { installed: [], errors: [
|
|
104
|
+
return { installed: [], errors: ["Failed to detect project stack."] };
|
|
102
105
|
}
|
|
103
106
|
const testCmd = await getTddTestCommand(stackInfo.stackType, stackInfo.buildTool, projectDir);
|
|
104
107
|
const hookContent = generateTddHook(testCmd, stackInfo.stackType);
|
|
105
108
|
const installed = [];
|
|
106
109
|
const errors = [];
|
|
107
|
-
const hookPath = path.join(hooksDir,
|
|
110
|
+
const hookPath = path.join(hooksDir, "pre-commit");
|
|
108
111
|
try {
|
|
109
112
|
await fs.writeFile(hookPath, hookContent, { mode: 0o755 });
|
|
110
|
-
installed.push(
|
|
113
|
+
installed.push("pre-commit");
|
|
111
114
|
}
|
|
112
115
|
catch (e) {
|
|
113
116
|
errors.push(`pre-commit: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Teams parallel dispatch presets — predefined team configurations
|
|
3
|
+
* for common workflows like review, debug, and security scanning.
|
|
4
|
+
*
|
|
5
|
+
* Each preset defines which sub-agents to spawn in parallel, their roles,
|
|
6
|
+
* and how to aggregate results.
|
|
7
|
+
*/
|
|
8
|
+
export interface AgentRole {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
skill: string;
|
|
12
|
+
perspective: string;
|
|
13
|
+
priority: "critical" | "high" | "medium" | "low";
|
|
14
|
+
}
|
|
15
|
+
export interface TeamPreset {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
roles: AgentRole[];
|
|
19
|
+
aggregation: "all-must-pass" | "majority" | "any-pass";
|
|
20
|
+
maxParallel: number;
|
|
21
|
+
}
|
|
22
|
+
export interface TeamDispatch {
|
|
23
|
+
preset: string;
|
|
24
|
+
roles: AgentRole[];
|
|
25
|
+
targetFiles: string[];
|
|
26
|
+
context: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
export interface AgentResult {
|
|
29
|
+
roleId: string;
|
|
30
|
+
roleName: string;
|
|
31
|
+
passed: boolean;
|
|
32
|
+
findings: string[];
|
|
33
|
+
severity: "critical" | "high" | "medium" | "low" | "info";
|
|
34
|
+
durationMs: number;
|
|
35
|
+
}
|
|
36
|
+
export interface TeamResult {
|
|
37
|
+
preset: string;
|
|
38
|
+
passed: boolean;
|
|
39
|
+
agents: AgentResult[];
|
|
40
|
+
totalDurationMs: number;
|
|
41
|
+
summary: string;
|
|
42
|
+
}
|
|
43
|
+
export declare const TEAM_PRESETS: Record<string, TeamPreset>;
|
|
44
|
+
export declare function getPreset(name: string): TeamPreset | null;
|
|
45
|
+
export declare function listPresets(): Array<{
|
|
46
|
+
name: string;
|
|
47
|
+
description: string;
|
|
48
|
+
roleCount: number;
|
|
49
|
+
}>;
|
|
50
|
+
export declare function createDispatch(presetName: string, targetFiles: string[], context?: Record<string, string>): TeamDispatch | null;
|
|
51
|
+
export declare function aggregateResults(preset: TeamPreset, results: AgentResult[]): TeamResult;
|
|
52
|
+
export declare function formatTeamResult(result: TeamResult): string;
|
|
53
|
+
//# sourceMappingURL=team-presets.d.ts.map
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Teams parallel dispatch presets — predefined team configurations
|
|
3
|
+
* for common workflows like review, debug, and security scanning.
|
|
4
|
+
*
|
|
5
|
+
* Each preset defines which sub-agents to spawn in parallel, their roles,
|
|
6
|
+
* and how to aggregate results.
|
|
7
|
+
*/
|
|
8
|
+
// ── Built-in presets ──
|
|
9
|
+
export const TEAM_PRESETS = {
|
|
10
|
+
review: {
|
|
11
|
+
name: "review",
|
|
12
|
+
description: "Multi-perspective code review team",
|
|
13
|
+
roles: [
|
|
14
|
+
{
|
|
15
|
+
id: "quality",
|
|
16
|
+
name: "Quality Reviewer",
|
|
17
|
+
skill: "adversarial-review",
|
|
18
|
+
perspective: "code quality, readability, maintainability",
|
|
19
|
+
priority: "high",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "security",
|
|
23
|
+
name: "Security Auditor",
|
|
24
|
+
skill: "adversarial-review",
|
|
25
|
+
perspective: "security vulnerabilities, injection, auth",
|
|
26
|
+
priority: "critical",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "testing",
|
|
30
|
+
name: "Test Reviewer",
|
|
31
|
+
skill: "testing:test-coverage",
|
|
32
|
+
perspective: "test coverage, edge cases, test quality",
|
|
33
|
+
priority: "medium",
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
aggregation: "all-must-pass",
|
|
37
|
+
maxParallel: 3,
|
|
38
|
+
},
|
|
39
|
+
debug: {
|
|
40
|
+
name: "debug",
|
|
41
|
+
description: "Parallel debugging team",
|
|
42
|
+
roles: [
|
|
43
|
+
{
|
|
44
|
+
id: "hypothesis",
|
|
45
|
+
name: "Hypothesis Generator",
|
|
46
|
+
skill: "debug-mode",
|
|
47
|
+
perspective: "generate and rank failure hypotheses",
|
|
48
|
+
priority: "high",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "logs",
|
|
52
|
+
name: "Log Analyzer",
|
|
53
|
+
skill: "debug-mode",
|
|
54
|
+
perspective: "parse logs, stack traces, error patterns",
|
|
55
|
+
priority: "high",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "repro",
|
|
59
|
+
name: "Reproducer",
|
|
60
|
+
skill: "testing:e2e",
|
|
61
|
+
perspective: "create minimal reproduction steps",
|
|
62
|
+
priority: "medium",
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
aggregation: "any-pass",
|
|
66
|
+
maxParallel: 3,
|
|
67
|
+
},
|
|
68
|
+
security: {
|
|
69
|
+
name: "security",
|
|
70
|
+
description: "Security scanning team",
|
|
71
|
+
roles: [
|
|
72
|
+
{
|
|
73
|
+
id: "sast",
|
|
74
|
+
name: "SAST Scanner",
|
|
75
|
+
skill: "adversarial-review",
|
|
76
|
+
perspective: "static analysis, code patterns, OWASP",
|
|
77
|
+
priority: "critical",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "deps",
|
|
81
|
+
name: "Dependency Auditor",
|
|
82
|
+
skill: "adversarial-review",
|
|
83
|
+
perspective: "supply chain, outdated deps, known CVEs",
|
|
84
|
+
priority: "critical",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "secrets",
|
|
88
|
+
name: "Secret Scanner",
|
|
89
|
+
skill: "adversarial-review",
|
|
90
|
+
perspective: "hardcoded secrets, API keys, credentials",
|
|
91
|
+
priority: "critical",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
aggregation: "all-must-pass",
|
|
95
|
+
maxParallel: 3,
|
|
96
|
+
},
|
|
97
|
+
"tdd-cycle": {
|
|
98
|
+
name: "tdd-cycle",
|
|
99
|
+
description: "TDD pipeline team",
|
|
100
|
+
roles: [
|
|
101
|
+
{
|
|
102
|
+
id: "test-writer",
|
|
103
|
+
name: "Test Writer",
|
|
104
|
+
skill: "testing:tdd",
|
|
105
|
+
perspective: "write failing tests first",
|
|
106
|
+
priority: "high",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "implementer",
|
|
110
|
+
name: "Implementer",
|
|
111
|
+
skill: "sdd-apply",
|
|
112
|
+
perspective: "make tests pass with minimal code",
|
|
113
|
+
priority: "high",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "refactorer",
|
|
117
|
+
name: "Refactorer",
|
|
118
|
+
skill: "refactoring:cleanup",
|
|
119
|
+
perspective: "clean up without breaking tests",
|
|
120
|
+
priority: "medium",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
aggregation: "all-must-pass",
|
|
124
|
+
maxParallel: 1, // sequential for TDD
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
// ── Preset management ──
|
|
128
|
+
export function getPreset(name) {
|
|
129
|
+
return TEAM_PRESETS[name] ?? null;
|
|
130
|
+
}
|
|
131
|
+
export function listPresets() {
|
|
132
|
+
return Object.values(TEAM_PRESETS).map((p) => ({
|
|
133
|
+
name: p.name,
|
|
134
|
+
description: p.description,
|
|
135
|
+
roleCount: p.roles.length,
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
// ── Dispatch creation ──
|
|
139
|
+
export function createDispatch(presetName, targetFiles, context = {}) {
|
|
140
|
+
const preset = getPreset(presetName);
|
|
141
|
+
if (!preset)
|
|
142
|
+
return null;
|
|
143
|
+
return {
|
|
144
|
+
preset: presetName,
|
|
145
|
+
roles: preset.roles,
|
|
146
|
+
targetFiles,
|
|
147
|
+
context,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// ── Result aggregation ──
|
|
151
|
+
export function aggregateResults(preset, results) {
|
|
152
|
+
const totalDurationMs = Math.max(...results.map((r) => r.durationMs), 0);
|
|
153
|
+
let passed;
|
|
154
|
+
switch (preset.aggregation) {
|
|
155
|
+
case "all-must-pass":
|
|
156
|
+
passed = results.every((r) => r.passed);
|
|
157
|
+
break;
|
|
158
|
+
case "majority":
|
|
159
|
+
passed = results.filter((r) => r.passed).length > results.length / 2;
|
|
160
|
+
break;
|
|
161
|
+
case "any-pass":
|
|
162
|
+
passed = results.some((r) => r.passed);
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
const criticalFindings = results
|
|
166
|
+
.filter((r) => r.severity === "critical" && !r.passed)
|
|
167
|
+
.flatMap((r) => r.findings);
|
|
168
|
+
const summary = passed
|
|
169
|
+
? `All ${results.length} agents passed.`
|
|
170
|
+
: `${results.filter((r) => !r.passed).length}/${results.length} agents reported issues.${criticalFindings.length > 0 ? ` ${criticalFindings.length} critical findings.` : ""}`;
|
|
171
|
+
return {
|
|
172
|
+
preset: preset.name,
|
|
173
|
+
passed,
|
|
174
|
+
agents: results,
|
|
175
|
+
totalDurationMs,
|
|
176
|
+
summary,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
// ── Formatting ──
|
|
180
|
+
export function formatTeamResult(result) {
|
|
181
|
+
const icon = result.passed ? "✅" : "❌";
|
|
182
|
+
const lines = [
|
|
183
|
+
`${icon} Team: ${result.preset} — ${result.summary}`,
|
|
184
|
+
"",
|
|
185
|
+
];
|
|
186
|
+
for (const agent of result.agents) {
|
|
187
|
+
const aIcon = agent.passed ? "✓" : "✗";
|
|
188
|
+
const dur = `${(agent.durationMs / 1000).toFixed(1)}s`;
|
|
189
|
+
lines.push(` ${aIcon} ${agent.roleName} [${agent.severity}] ${dur}`);
|
|
190
|
+
if (agent.findings.length > 0) {
|
|
191
|
+
for (const f of agent.findings.slice(0, 3)) {
|
|
192
|
+
lines.push(` - ${f}`);
|
|
193
|
+
}
|
|
194
|
+
if (agent.findings.length > 3) {
|
|
195
|
+
lines.push(` ... +${agent.findings.length - 3} more`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return lines.join("\n");
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=team-presets.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { InitStep } from "../types/index.js";
|
|
2
|
+
export type WorkflowAction = "show" | "validate" | "list";
|
|
3
|
+
type StepCallback = (step: InitStep) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Show a workflow graph as ASCII art.
|
|
6
|
+
*/
|
|
7
|
+
export declare function runWorkflowShow(projectDir: string, onStep: StepCallback, options?: {
|
|
8
|
+
target?: string;
|
|
9
|
+
template?: string;
|
|
10
|
+
}): Promise<string | null>;
|
|
11
|
+
/**
|
|
12
|
+
* Validate a project against a workflow graph.
|
|
13
|
+
*/
|
|
14
|
+
export declare function runWorkflowValidate(projectDir: string, onStep: StepCallback, options?: {
|
|
15
|
+
target?: string;
|
|
16
|
+
template?: string;
|
|
17
|
+
}): Promise<string | null>;
|
|
18
|
+
/**
|
|
19
|
+
* List available workflows (project + built-in templates).
|
|
20
|
+
*/
|
|
21
|
+
export declare function runWorkflowList(projectDir: string, onStep: StepCallback): Promise<string>;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=workflow.d.ts.map
|