opencode-feature-factory 0.2.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/LICENSE +21 -0
- package/README.md +877 -0
- package/assets/agent/backend-builder.md +73 -0
- package/assets/agent/codebase-researcher.md +56 -0
- package/assets/agent/design-interpreter.md +37 -0
- package/assets/agent/frontend-builder.md +74 -0
- package/assets/agent/implementation-validator.md +73 -0
- package/assets/agent/security-reviewer.md +60 -0
- package/assets/agent/spec-writer.md +94 -0
- package/assets/agent/story-reader.md +38 -0
- package/assets/agent/story-writer.md +39 -0
- package/assets/agent/test-verifier.md +73 -0
- package/assets/agent/work-decomposer.md +102 -0
- package/assets/agent/work-reviewer.md +77 -0
- package/assets/command/feature.md +68 -0
- package/assets/skills/feature/SCHEMA.md +990 -0
- package/assets/skills/feature/SKILL.md +620 -0
- package/dist/tui.js +3641 -0
- package/package.json +65 -0
- package/src/cleanup-sweep-command.js +75 -0
- package/src/cleanup-sweep-eligibility.js +581 -0
- package/src/cleanup-sweep-output.js +139 -0
- package/src/cleanup-sweep-report.js +548 -0
- package/src/cleanup-sweep.js +546 -0
- package/src/cli-output.js +251 -0
- package/src/cli.js +1152 -0
- package/src/config.js +231 -0
- package/src/cost-attribution.js +327 -0
- package/src/cost-report.js +185 -0
- package/src/detached-log-supervisor.js +178 -0
- package/src/doctor.js +598 -0
- package/src/env-snapshot.js +211 -0
- package/src/factory-diagnostics.js +429 -0
- package/src/factory-paths.js +40 -0
- package/src/factory.js +4769 -0
- package/src/feature-command-payload.js +378 -0
- package/src/git.js +110 -0
- package/src/github.js +252 -0
- package/src/hardening/atomic-write.js +954 -0
- package/src/hardening/line-output.js +139 -0
- package/src/hardening/output-policy.js +365 -0
- package/src/hardening/process-verification.js +542 -0
- package/src/hardening/sensitive-data.js +341 -0
- package/src/hardening/terminal-encoding.js +224 -0
- package/src/plugin.js +246 -0
- package/src/post-pr-ci.js +754 -0
- package/src/process-evidence.js +1139 -0
- package/src/refs.js +144 -0
- package/src/run-state.js +2411 -0
- package/src/steering-conflicts.js +77 -0
- package/src/telemetry.js +419 -0
- package/src/tui-data.js +499 -0
- package/src/tui-rendering.js +148 -0
- package/src/utils.js +35 -0
- package/src/validate.js +1655 -0
- package/src/worktrees.js +56 -0
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-feature-factory",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Hybrid opencode plugin and scriptable feature factory workflow.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/plugin.js",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/jasoncarreira/opencode-feature-factory.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/jasoncarreira/opencode-feature-factory/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/jasoncarreira/opencode-feature-factory#readme",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./src/plugin.js",
|
|
17
|
+
"./server": "./src/plugin.js",
|
|
18
|
+
"./tui": "./dist/tui.js",
|
|
19
|
+
"./cli": "./src/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"feature-factory": "src/cli.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"assets",
|
|
26
|
+
"dist",
|
|
27
|
+
"src/**/*.js",
|
|
28
|
+
"README.md",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build:tui": "esbuild src/tui.jsx --bundle --platform=node --format=esm --jsx=automatic --jsx-import-source=@opentui/solid --packages=external --outfile=dist/tui.js",
|
|
33
|
+
"prepack": "npm run build:tui",
|
|
34
|
+
"doctor": "node src/cli.js doctor",
|
|
35
|
+
"doctor:local": "node src/cli.js doctor --local",
|
|
36
|
+
"test": "npm run test:unit",
|
|
37
|
+
"test:unit": "node --test test/*.test.js",
|
|
38
|
+
"smoke:pack": "node --test test/package-smoke.mjs",
|
|
39
|
+
"check": "npm run test:unit && npm run smoke:pack"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"opencode",
|
|
43
|
+
"plugin",
|
|
44
|
+
"agent",
|
|
45
|
+
"automation",
|
|
46
|
+
"workflow"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@opentelemetry/api": "^1.9.1",
|
|
50
|
+
"@opentui/solid": "^0.4.3",
|
|
51
|
+
"jsonc-parser": "^3.3.1",
|
|
52
|
+
"solid-js": "^1.9.10"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@babel/parser": "^7.29.7",
|
|
56
|
+
"esbuild": "^0.25.0"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=20"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"license": "MIT"
|
|
65
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { parseCleanupSweepDigest } from "./cleanup-sweep-report.js";
|
|
3
|
+
import { identitySegment, StructuredOutputError } from "./hardening/output-policy.js";
|
|
4
|
+
|
|
5
|
+
const ALLOWED_FLAGS = new Set(["--all", "--dry-run", "--digest", "--repo", "--json"]);
|
|
6
|
+
const VALUE_FLAGS = new Set(["--digest", "--repo"]);
|
|
7
|
+
|
|
8
|
+
export function parseCleanupSweepCommand(args, options = {}) {
|
|
9
|
+
if (!Array.isArray(args) || args.some((arg) => typeof arg !== "string")) {
|
|
10
|
+
throw commandError("factory cleanup sweep arguments must be an array of strings");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
const values = new Map();
|
|
15
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
16
|
+
const arg = args[index];
|
|
17
|
+
if (!ALLOWED_FLAGS.has(arg)) {
|
|
18
|
+
if (arg === "--force") throw commandError("factory cleanup --all does not support --force");
|
|
19
|
+
if (!arg.startsWith("-")) throw commandError("factory cleanup --all does not accept a run ID");
|
|
20
|
+
throw commandError("factory cleanup --all received an unsupported option");
|
|
21
|
+
}
|
|
22
|
+
if (seen.has(arg)) throw commandError(`factory cleanup --all does not allow repeated ${arg}`);
|
|
23
|
+
seen.add(arg);
|
|
24
|
+
if (!VALUE_FLAGS.has(arg)) continue;
|
|
25
|
+
|
|
26
|
+
const value = args[index + 1];
|
|
27
|
+
if (typeof value !== "string" || value.length === 0 || value.startsWith("-")) {
|
|
28
|
+
throw commandError(`factory cleanup --all requires a value for ${arg}`);
|
|
29
|
+
}
|
|
30
|
+
values.set(arg, value);
|
|
31
|
+
index += 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!seen.has("--all")) {
|
|
35
|
+
if (seen.has("--digest")) throw commandError("factory cleanup --digest requires --all");
|
|
36
|
+
throw commandError("factory cleanup sweep requires --all");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const dryRun = seen.has("--dry-run");
|
|
40
|
+
const hasDigest = seen.has("--digest");
|
|
41
|
+
if (dryRun === hasDigest) {
|
|
42
|
+
throw commandError("factory cleanup --all requires exactly one of --dry-run or --digest");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let digest;
|
|
46
|
+
if (hasDigest) {
|
|
47
|
+
digest = values.get("--digest");
|
|
48
|
+
try {
|
|
49
|
+
parseCleanupSweepDigest(digest);
|
|
50
|
+
} catch {
|
|
51
|
+
throw commandError("factory cleanup --all requires a valid cleanup digest");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const baseCwd = typeof options.cwd === "string" ? options.cwd : process.cwd();
|
|
56
|
+
const cwd = seen.has("--repo") ? resolve(baseCwd, values.get("--repo")) : baseCwd;
|
|
57
|
+
const command = { mode: dryRun ? "preview" : "execute", cwd, json: seen.has("--json") };
|
|
58
|
+
if (hasDigest) command.digest = digest;
|
|
59
|
+
return command;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function runCleanupSweepCommand(args, handlers = {}) {
|
|
63
|
+
const command = parseCleanupSweepCommand(args, { cwd: handlers.cwd });
|
|
64
|
+
const handler = command.mode === "preview" ? handlers.preview : handlers.execute;
|
|
65
|
+
if (typeof handler !== "function") throw new TypeError(`cleanup sweep ${command.mode} handler is required`);
|
|
66
|
+
const report = await handler(command);
|
|
67
|
+
if (!report || typeof report !== "object" || (report.exit_code !== 0 && report.exit_code !== 1)) {
|
|
68
|
+
throw new TypeError("cleanup sweep handler must return a report with exit_code 0 or 1");
|
|
69
|
+
}
|
|
70
|
+
return { report, exitCode: report.exit_code };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function commandError(message) {
|
|
74
|
+
return new StructuredOutputError(message, [identitySegment(message)]);
|
|
75
|
+
}
|