@jnyross/code-factory 1.0.0 → 1.1.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/README.md +99 -68
- package/bin/code-factory.mjs +147 -25
- package/package.json +12 -1
package/README.md
CHANGED
|
@@ -1,88 +1,119 @@
|
|
|
1
1
|
# Code Factory Template
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
3
|
+
Code Factory is a repo control-plane for autonomous coding + deterministic review.
|
|
4
|
+
|
|
5
|
+
This template implements the full pattern:
|
|
6
|
+
- one machine-readable contract
|
|
7
|
+
- risk-policy gate before expensive CI fanout
|
|
8
|
+
- current-head SHA review discipline
|
|
9
|
+
- canonical rerun comment dedupe
|
|
10
|
+
- bot-only thread auto-resolve after clean rerun
|
|
11
|
+
- browser evidence verification for UI/user-flow changes
|
|
12
|
+
- incident -> harness-gap loop with weekly metrics
|
|
13
|
+
|
|
14
|
+
## Contract (single source of truth)
|
|
15
|
+
All control-plane policy lives in `ARCHITECTURE.yaml` under `control_plane`:
|
|
16
|
+
- `riskTierRules`
|
|
17
|
+
- `mergePolicy`
|
|
18
|
+
- `docsDriftRules`
|
|
19
|
+
- `reviewAgent`
|
|
20
|
+
- `browserEvidence`
|
|
21
|
+
- `harnessGapLoop`
|
|
22
|
+
|
|
23
|
+
## Workflow Order
|
|
24
|
+
`Control Plane` workflow (`.github/workflows/preflight.yml`) runs jobs in this order:
|
|
25
|
+
1. `risk-policy-gate`
|
|
26
|
+
2. fanout: `CI Pipeline`, `harness-smoke`, `Browser Evidence`
|
|
27
|
+
3. `risk-policy-finalize`
|
|
28
|
+
|
|
29
|
+
For `high` tier changes, the gate auto-applies the PR label `high-risk`.
|
|
30
|
+
|
|
31
|
+
`Code Review Agent` workflow (`.github/workflows/auto-review.yml`) runs in parallel and is enforced by SHA-aware policy checks.
|
|
32
|
+
|
|
33
|
+
## SHA Discipline and Reruns
|
|
34
|
+
`scripts/control-plane/risk-policy-gate.mjs` enforces:
|
|
35
|
+
- review check must be for current PR head SHA
|
|
36
|
+
- stale review state is rejected
|
|
37
|
+
- actionable findings in review summary comment fail the gate
|
|
38
|
+
- canonical rerun request comment is deduped by marker + `sha:<head>`
|
|
39
|
+
|
|
40
|
+
## Browser Evidence
|
|
41
|
+
For UI-sensitive paths, `Browser Evidence` requires a valid manifest:
|
|
42
|
+
- path: `harness/browser-evidence/manifest.json`
|
|
43
|
+
- freshness window and required flows defined in `ARCHITECTURE.yaml`
|
|
44
|
+
|
|
45
|
+
Generate/update evidence manifest:
|
|
46
|
+
```bash
|
|
47
|
+
npm run harness:ui:capture-browser-evidence
|
|
48
|
+
npm run harness:ui:verify-browser-evidence
|
|
49
|
+
```
|
|
10
50
|
|
|
11
|
-
##
|
|
12
|
-
-
|
|
13
|
-
- `
|
|
14
|
-
-
|
|
15
|
-
- `progress.txt`: Append-only execution journal.
|
|
51
|
+
## Harness Gap Loop
|
|
52
|
+
`harness-gap-loop` workflow:
|
|
53
|
+
- creates a `harness-gap` issue when a `production-regression` issue appears
|
|
54
|
+
- runs weekly metrics (`npm run harness:weekly-metrics`)
|
|
16
55
|
|
|
17
|
-
##
|
|
18
|
-
- `.cursorrules`: Cursor Ultra native behavior.
|
|
19
|
-
- `CLAUDE.md`: Claude Code CLI behavior.
|
|
20
|
-
- `.agent/rules/CodeFactory.md`: Antigravity/GLM/Minimax behavior.
|
|
21
|
-
- `prompt_template.txt`: ChatGPT Pro project-manager prompt.
|
|
22
|
-
|
|
23
|
-
## Local Runner
|
|
24
|
-
`ralph.sh` is rewritten to use local CLI tools only.
|
|
25
|
-
|
|
26
|
-
Runner behavior:
|
|
27
|
-
1. Reads next open task from `prd.json`.
|
|
28
|
-
2. Invokes a local agent CLI for implementation (`codex` by default).
|
|
29
|
-
3. Runs preflight checks:
|
|
30
|
-
- `npm run typecheck`
|
|
31
|
-
- `npm run lint`
|
|
32
|
-
- `npm test`
|
|
33
|
-
4. Appends progress to `progress.txt`.
|
|
34
|
-
5. Marks task done only when preflight passes.
|
|
35
|
-
|
|
36
|
-
Engine selection:
|
|
37
|
-
- Default: `codex` (`AGENT_ENGINE=codex`).
|
|
38
|
-
- Claude: `AGENT_ENGINE=claude`.
|
|
39
|
-
- OpenCode: `AGENT_ENGINE=opencode` (defaults to `minimax-coding-plan/MiniMax-M2.5`).
|
|
40
|
-
- Custom CLI: `AGENT_ENGINE=custom AGENT_CMD='<your command>'`.
|
|
41
|
-
|
|
42
|
-
Examples:
|
|
56
|
+
## Local Command Set
|
|
43
57
|
```bash
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
npm run typecheck
|
|
59
|
+
npm test
|
|
60
|
+
npm run build:ci --if-present
|
|
61
|
+
npm run harness:legal-chat:smoke
|
|
62
|
+
npm run harness:ui:pre-pr
|
|
63
|
+
npm run harness:ui:capture-browser-evidence
|
|
64
|
+
npm run harness:ui:verify-browser-evidence
|
|
65
|
+
npm run harness:risk-tier
|
|
66
|
+
npm run harness:weekly-metrics
|
|
49
67
|
```
|
|
50
68
|
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
|
|
56
|
-
## CI Safety Layer
|
|
57
|
-
- `.github/workflows/preflight.yml`
|
|
58
|
-
- Labels risky PRs as `high-risk` when touching paths like `db/`, `auth/`, or `infra/`.
|
|
59
|
-
- Runs typecheck/lint/tests in root or fallback `flowchart/`.
|
|
60
|
-
- `.github/workflows/auto-review.yml`
|
|
61
|
-
- Adds or updates a secondary agent-review comment on PRs.
|
|
69
|
+
## Agent Loop Files
|
|
70
|
+
- `ARCHITECTURE.yaml`
|
|
71
|
+
- `AGENTS.md`
|
|
72
|
+
- `prd.json`
|
|
73
|
+
- `progress.txt`
|
|
62
74
|
|
|
63
|
-
##
|
|
64
|
-
|
|
75
|
+
## Tool Rule Files
|
|
76
|
+
- `.cursorrules`
|
|
77
|
+
- `CLAUDE.md`
|
|
78
|
+
- `.agent/rules/CodeFactory.md`
|
|
79
|
+
- `prompt_template.txt`
|
|
80
|
+
- `chatgpt_architecture_prd_prompt.txt`
|
|
81
|
+
- `chatgpt_prd_format_prompt.txt`
|
|
82
|
+
|
|
83
|
+
## CLI Install
|
|
84
|
+
```bash
|
|
85
|
+
npm install -g @jnyross/code-factory
|
|
86
|
+
```
|
|
65
87
|
|
|
66
|
-
|
|
88
|
+
## Create a New Project
|
|
89
|
+
Local only:
|
|
67
90
|
```bash
|
|
68
91
|
code-factory my-next-app ~/Projects
|
|
69
92
|
```
|
|
70
93
|
|
|
71
|
-
|
|
94
|
+
Create + push GitHub repo in one command:
|
|
72
95
|
```bash
|
|
73
|
-
|
|
96
|
+
code-factory my-next-app ~/Projects --github --private
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Examples:
|
|
100
|
+
```bash
|
|
101
|
+
code-factory my-next-app --github --owner my-org --repo my-org/my-next-app --public
|
|
74
102
|
```
|
|
75
103
|
|
|
76
104
|
Compatibility alias:
|
|
77
105
|
- `new-project` points to the same CLI command as `code-factory`.
|
|
78
106
|
|
|
79
|
-
##
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
107
|
+
## Ralph Runner
|
|
108
|
+
`ralph.sh` supports:
|
|
109
|
+
- `codex` (default)
|
|
110
|
+
- `claude`
|
|
111
|
+
- `opencode` (default model `minimax-coding-plan/MiniMax-M2.5`)
|
|
112
|
+
- `custom`
|
|
85
113
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
114
|
+
Examples:
|
|
115
|
+
```bash
|
|
116
|
+
./ralph.sh --once
|
|
117
|
+
AGENT_ENGINE=claude ./ralph.sh --once
|
|
118
|
+
AGENT_ENGINE=opencode ./ralph.sh --once
|
|
119
|
+
```
|
package/bin/code-factory.mjs
CHANGED
|
@@ -5,62 +5,184 @@ import { existsSync, rmSync } from "node:fs";
|
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const DEFAULT_TEMPLATE_URL =
|
|
9
9
|
process.env.CODE_FACTORY_TEMPLATE_URL ??
|
|
10
10
|
"https://github.com/jnyross/code-factory-template.git";
|
|
11
11
|
|
|
12
12
|
function usage(exitCode = 0) {
|
|
13
13
|
console.log(`Usage:
|
|
14
|
-
code-factory <project-name> [destination-directory]
|
|
15
|
-
code-factory new <project-name> [destination-directory]
|
|
14
|
+
code-factory <project-name> [destination-directory] [options]
|
|
15
|
+
code-factory new <project-name> [destination-directory] [options]
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--github Create and push a new GitHub repo via gh CLI
|
|
19
|
+
--public Use public visibility for --github
|
|
20
|
+
--private Use private visibility for --github (default)
|
|
21
|
+
--owner <owner> GitHub owner/org for --github (defaults to gh auth user)
|
|
22
|
+
--repo <name|owner/name>
|
|
23
|
+
GitHub repo name override (default: slugified project name)
|
|
24
|
+
--template-url <url> Override template repository URL for this run
|
|
25
|
+
-h, --help Show help
|
|
16
26
|
|
|
17
27
|
Examples:
|
|
18
28
|
code-factory my-app
|
|
19
29
|
code-factory "Youth Reg Response Platform" ~/Software_Projects
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
CODE_FACTORY_TEMPLATE_URL Override template repository URL
|
|
30
|
+
code-factory my-app ~/Software_Projects --github --private
|
|
31
|
+
code-factory my-app --github --owner my-org --repo my-org/my-app
|
|
23
32
|
`);
|
|
24
33
|
process.exit(exitCode);
|
|
25
34
|
}
|
|
26
35
|
|
|
27
|
-
function run(cmd, args) {
|
|
28
|
-
execFileSync(cmd, args, { stdio: "inherit" });
|
|
36
|
+
function run(cmd, args, cwd) {
|
|
37
|
+
execFileSync(cmd, args, { stdio: "inherit", cwd });
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
function runCapture(cmd, args, cwd) {
|
|
41
|
+
return execFileSync(cmd, args, {
|
|
42
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
43
|
+
cwd,
|
|
44
|
+
encoding: "utf8"
|
|
45
|
+
}).trim();
|
|
35
46
|
}
|
|
36
47
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
function slugify(name) {
|
|
49
|
+
return name
|
|
50
|
+
.toLowerCase()
|
|
51
|
+
.trim()
|
|
52
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
53
|
+
.replace(/^-+|-+$/g, "")
|
|
54
|
+
.replace(/-{2,}/g, "-")
|
|
55
|
+
.slice(0, 80);
|
|
40
56
|
}
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
function parseArgs(rawArgs) {
|
|
59
|
+
const options = {
|
|
60
|
+
github: false,
|
|
61
|
+
visibility: "private",
|
|
62
|
+
owner: null,
|
|
63
|
+
repo: null,
|
|
64
|
+
templateUrl: DEFAULT_TEMPLATE_URL
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
let args = [...rawArgs];
|
|
68
|
+
if (args[0] === "new") {
|
|
69
|
+
args = args.slice(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const positional = [];
|
|
73
|
+
|
|
74
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
75
|
+
const arg = args[index];
|
|
76
|
+
|
|
77
|
+
if (arg === "--github") {
|
|
78
|
+
options.github = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (arg === "--public") {
|
|
83
|
+
options.visibility = "public";
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (arg === "--private") {
|
|
88
|
+
options.visibility = "private";
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (arg === "--owner") {
|
|
93
|
+
options.owner = args[index + 1] ?? null;
|
|
94
|
+
index += 1;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (arg === "--repo") {
|
|
99
|
+
options.repo = args[index + 1] ?? null;
|
|
100
|
+
index += 1;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (arg === "--template-url") {
|
|
105
|
+
options.templateUrl = args[index + 1] ?? DEFAULT_TEMPLATE_URL;
|
|
106
|
+
index += 1;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (arg === "-h" || arg === "--help") {
|
|
111
|
+
usage(0);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (arg.startsWith("--")) {
|
|
115
|
+
console.error(`Unknown option: ${arg}`);
|
|
116
|
+
usage(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
positional.push(arg);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (positional.length < 1 || positional.length > 2) {
|
|
123
|
+
usage(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
projectName: positional[0],
|
|
128
|
+
destinationDir: positional[1] ?? process.cwd(),
|
|
129
|
+
options
|
|
130
|
+
};
|
|
44
131
|
}
|
|
45
132
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
133
|
+
function deriveGitHubRepo(projectName, options) {
|
|
134
|
+
if (options.repo && options.repo.includes("/")) {
|
|
135
|
+
const [owner, repo] = options.repo.split("/");
|
|
136
|
+
return { owner, repo };
|
|
137
|
+
}
|
|
49
138
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
139
|
+
const repo = options.repo ?? slugify(projectName);
|
|
140
|
+
if (!repo) {
|
|
141
|
+
throw new Error("Could not derive GitHub repository name from project name.");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const owner = options.owner ?? runCapture("gh", ["api", "user", "-q", ".login"]);
|
|
145
|
+
if (!owner) {
|
|
146
|
+
throw new Error("Unable to determine GitHub owner. Pass --owner.");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { owner, repo };
|
|
53
150
|
}
|
|
54
151
|
|
|
55
|
-
|
|
56
|
-
|
|
152
|
+
function createProject(projectName, destinationDir, options) {
|
|
153
|
+
const targetPath = resolve(destinationDir, projectName);
|
|
154
|
+
|
|
155
|
+
if (existsSync(targetPath)) {
|
|
156
|
+
throw new Error(`Target already exists: ${targetPath}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
run("git", ["clone", options.templateUrl, targetPath]);
|
|
57
160
|
rmSync(resolve(targetPath, ".git"), { recursive: true, force: true });
|
|
58
161
|
run("git", ["-C", targetPath, "init", "-q"]);
|
|
59
162
|
run("git", ["-C", targetPath, "add", "."]);
|
|
60
163
|
run("git", ["-C", targetPath, "commit", "-q", "-m", "Initial commit from Code Factory template"]);
|
|
61
164
|
|
|
165
|
+
if (options.github) {
|
|
166
|
+
const { owner, repo } = deriveGitHubRepo(projectName, options);
|
|
167
|
+
const fullRepo = `${owner}/${repo}`;
|
|
168
|
+
const visibilityFlag = options.visibility === "public" ? "--public" : "--private";
|
|
169
|
+
|
|
170
|
+
run("gh", ["repo", "create", fullRepo, visibilityFlag, "--source", targetPath, "--remote", "origin", "--push"]);
|
|
171
|
+
console.log(`GitHub repo created: https://github.com/${fullRepo}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
62
174
|
console.log("Your new Code Factory project is ready!");
|
|
63
175
|
console.log(`Path: ${targetPath}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const rawArgs = process.argv.slice(2);
|
|
180
|
+
if (rawArgs.length === 0) {
|
|
181
|
+
usage(0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const { projectName, destinationDir, options } = parseArgs(rawArgs);
|
|
185
|
+
createProject(projectName, destinationDir, options);
|
|
64
186
|
} catch (error) {
|
|
65
187
|
const message = error instanceof Error ? error.message : String(error);
|
|
66
188
|
console.error(`code-factory failed: ${message}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jnyross/code-factory",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Bootstrap new repos from the Code Factory template.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,11 +24,22 @@
|
|
|
24
24
|
"code-factory": "bin/code-factory.mjs",
|
|
25
25
|
"new-project": "bin/code-factory.mjs"
|
|
26
26
|
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"harness:risk-tier": "node scripts/control-plane/risk-tier.mjs",
|
|
29
|
+
"harness:legal-chat:smoke": "node scripts/control-plane/harness-smoke.mjs",
|
|
30
|
+
"harness:ui:capture-browser-evidence": "node scripts/control-plane/capture-browser-evidence.mjs",
|
|
31
|
+
"harness:ui:verify-browser-evidence": "node scripts/control-plane/verify-browser-evidence.mjs",
|
|
32
|
+
"harness:ui:pre-pr": "npm run harness:ui:capture-browser-evidence && npm run harness:ui:verify-browser-evidence",
|
|
33
|
+
"harness:weekly-metrics": "node scripts/control-plane/weekly-metrics.mjs"
|
|
34
|
+
},
|
|
27
35
|
"files": [
|
|
28
36
|
"bin",
|
|
29
37
|
"README.md",
|
|
30
38
|
"LICENSE"
|
|
31
39
|
],
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"yaml": "^2.8.1"
|
|
42
|
+
},
|
|
32
43
|
"engines": {
|
|
33
44
|
"node": ">=18"
|
|
34
45
|
},
|