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/src/worktrees.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, statSync } from "node:fs";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { git } from "./git.js";
|
|
5
|
+
import { physicalPath, requireNonEmptyString } from "./utils.js";
|
|
6
|
+
|
|
7
|
+
export function parseWorktreeListPorcelain(stdout) {
|
|
8
|
+
const entries = [];
|
|
9
|
+
let current = null;
|
|
10
|
+
for (const line of String(stdout || "").split(/\r?\n/u)) {
|
|
11
|
+
if (!line) continue;
|
|
12
|
+
if (line.startsWith("worktree ")) {
|
|
13
|
+
if (current) entries.push(current);
|
|
14
|
+
current = { path: line.slice("worktree ".length), branch: null, head: null, bare: false, detached: false };
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (!current) continue;
|
|
18
|
+
if (line.startsWith("HEAD ")) current.head = line.slice("HEAD ".length);
|
|
19
|
+
if (line.startsWith("branch refs/heads/")) current.branch = line.slice("branch refs/heads/".length);
|
|
20
|
+
if (line === "bare") current.bare = true;
|
|
21
|
+
if (line === "detached") current.detached = true;
|
|
22
|
+
}
|
|
23
|
+
if (current) entries.push(current);
|
|
24
|
+
return entries;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function checkWorktreeIdentity(repo, worktree, expected = {}, options = {}) {
|
|
28
|
+
const physicalWorktree = physicalPath(worktree);
|
|
29
|
+
if (!existsSync(physicalWorktree) || !statSync(physicalWorktree).isDirectory()) {
|
|
30
|
+
return { ok: false, reason: "missing-worktree", worktree: physicalWorktree };
|
|
31
|
+
}
|
|
32
|
+
const result = git(repo, ["worktree", "list", "--porcelain"], options);
|
|
33
|
+
if (!result.ok) return { ok: false, reason: result.stderr || "git worktree list failed", worktree: physicalWorktree };
|
|
34
|
+
const entries = parseWorktreeListPorcelain(result.stdout);
|
|
35
|
+
const entry = entries.find((item) => physicalPath(item.path) === physicalWorktree) || null;
|
|
36
|
+
if (!entry) return { ok: false, reason: "not-a-registered-worktree", worktree: physicalWorktree };
|
|
37
|
+
if (expected.branch && entry.branch !== expected.branch) {
|
|
38
|
+
return { ok: false, reason: `branch-mismatch:${entry.branch || "detached"}`, worktree: physicalWorktree, entry };
|
|
39
|
+
}
|
|
40
|
+
return { ok: true, worktree: physicalWorktree, entry };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function deriveExpectedWorktreePath(repo, branch) {
|
|
44
|
+
const branchName = requireNonEmptyString(branch, "branch");
|
|
45
|
+
const root = resolve(repo, ".opencode", "worktrees");
|
|
46
|
+
const slug = branchName.replace(/[\\/]+/gu, "-");
|
|
47
|
+
const candidate = resolve(root, slug);
|
|
48
|
+
if (!existsSync(candidate)) return candidate;
|
|
49
|
+
const identity = checkWorktreeIdentity(repo, candidate, { branch: branchName });
|
|
50
|
+
if (identity.ok) return candidate;
|
|
51
|
+
return resolve(root, `${slug}-${shortHash(branchName)}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function shortHash(value) {
|
|
55
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 8);
|
|
56
|
+
}
|