beflow 0.1.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 +121 -0
- package/config.example.json +68 -0
- package/config.schema.json +413 -0
- package/package.json +72 -0
- package/src/agent/acpx.ts +197 -0
- package/src/agent/driver.ts +38 -0
- package/src/agent/events.ts +228 -0
- package/src/agent/issuefence.ts +42 -0
- package/src/agent/report.ts +44 -0
- package/src/cli.ts +910 -0
- package/src/config/load.ts +45 -0
- package/src/config/persist.ts +58 -0
- package/src/config/schema.ts +181 -0
- package/src/config/store.ts +119 -0
- package/src/core/accept.ts +25 -0
- package/src/core/continuation.ts +57 -0
- package/src/core/deadletter.ts +55 -0
- package/src/core/decision.ts +8 -0
- package/src/core/doctor.ts +223 -0
- package/src/core/drift.ts +59 -0
- package/src/core/gc.ts +223 -0
- package/src/core/inputquality.ts +30 -0
- package/src/core/issuetemplate.ts +175 -0
- package/src/core/mcp.ts +191 -0
- package/src/core/newissue.ts +343 -0
- package/src/core/notify.ts +151 -0
- package/src/core/prompts.ts +165 -0
- package/src/core/qualitygate.ts +70 -0
- package/src/core/queue.ts +40 -0
- package/src/core/review.ts +266 -0
- package/src/core/run.ts +1075 -0
- package/src/core/runstore.ts +144 -0
- package/src/core/runsview.ts +111 -0
- package/src/core/setup.ts +203 -0
- package/src/core/sla.ts +39 -0
- package/src/core/template.ts +65 -0
- package/src/core/watch.ts +825 -0
- package/src/core/worktree.ts +74 -0
- package/src/core/writeback.ts +88 -0
- package/src/index.ts +154 -0
- package/src/model/types.ts +35 -0
- package/src/prompts/defaults/continuation.md +9 -0
- package/src/prompts/defaults/implement.md +13 -0
- package/src/prompts/defaults/issue-enrich.md +30 -0
- package/src/prompts/defaults/issues/bug.md +35 -0
- package/src/prompts/defaults/issues/feature.md +24 -0
- package/src/prompts/defaults/issues/generic.md +16 -0
- package/src/prompts/defaults/issues/spike.md +24 -0
- package/src/prompts/defaults/report.md +20 -0
- package/src/prompts/defaults/review.md +34 -0
- package/src/prompts/defaults/spec.md +11 -0
- package/src/prompts/defaults/task.md +6 -0
- package/src/prompts/defaults/triage.md +11 -0
- package/src/prompts/text-modules.d.ts +4 -0
- package/src/resolve/jobkind.ts +11 -0
- package/src/resolve/metadata.ts +103 -0
- package/src/resolve/precedence.ts +104 -0
- package/src/trackers/factory.ts +17 -0
- package/src/trackers/linear/adapter.ts +416 -0
- package/src/trackers/linear/client.ts +264 -0
- package/src/trackers/linear/map.ts +113 -0
- package/src/trackers/linear/types.ts +44 -0
- package/src/trackers/marker.ts +20 -0
- package/src/trackers/plane/adapter.ts +754 -0
- package/src/trackers/plane/client.ts +302 -0
- package/src/trackers/plane/map.ts +168 -0
- package/src/trackers/plane/types.ts +134 -0
- package/src/trackers/tracker.ts +135 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { spawn } from "bun";
|
|
5
|
+
|
|
6
|
+
export interface ExecResult {
|
|
7
|
+
code: number;
|
|
8
|
+
stdout: string;
|
|
9
|
+
stderr: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type Exec = (cmd: string, args: string[]) => Promise<ExecResult>;
|
|
13
|
+
|
|
14
|
+
export function sanitizeKey(key: string): string {
|
|
15
|
+
return key
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
18
|
+
.replace(/^-+|-+$/g, "");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Expand a leading `~` / `~/` to the home directory; other paths pass through. */
|
|
22
|
+
export function expandHome(path: string): string {
|
|
23
|
+
if (path === "~") {
|
|
24
|
+
return homedir();
|
|
25
|
+
}
|
|
26
|
+
if (path.startsWith("~/")) {
|
|
27
|
+
return join(homedir(), path.slice(2));
|
|
28
|
+
}
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Resolve the worktree base dir: the configured value (~-expanded) or the default. */
|
|
33
|
+
export function resolveWorktreeDir(configured?: string): string {
|
|
34
|
+
return configured !== undefined ? expandHome(configured) : join(homedir(), ".beflow", "worktrees");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function worktreePath(baseDir: string, key: string): string {
|
|
38
|
+
return join(baseDir, sanitizeKey(key));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function runGit(repoPath: string, args: string[], exec: Exec): Promise<ExecResult> {
|
|
42
|
+
const fullArgs = ["-C", repoPath, ...args];
|
|
43
|
+
const result = await exec("git", fullArgs);
|
|
44
|
+
if (result.code !== 0) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`beflow: git ${fullArgs.join(" ")} failed (exit ${String(result.code)}): ${result.stderr.trim()}`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function createWorktree(repoPath: string, key: string, exec: Exec, baseDir: string): Promise<string> {
|
|
53
|
+
const wtPath = worktreePath(baseDir, key);
|
|
54
|
+
const branch = `beflow/${sanitizeKey(key)}`;
|
|
55
|
+
// -B (create-or-reset) rather than -b: createWorktree only runs on the
|
|
56
|
+
// Non-resume path, so any leftover beflow/<key> branch is a stale orphan
|
|
57
|
+
// From an interrupted run and must be reset, not collided with.
|
|
58
|
+
await runGit(repoPath, ["worktree", "add", "-B", branch, wtPath], exec);
|
|
59
|
+
return wtPath;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function removeWorktree(repoPath: string, wtPath: string, exec: Exec): Promise<void> {
|
|
63
|
+
await runGit(repoPath, ["worktree", "remove", wtPath, "--force"], exec);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function bunExec(cmd: string, args: string[]): Promise<ExecResult> {
|
|
67
|
+
const proc = spawn([cmd, ...args], { stderr: "pipe", stdout: "pipe" });
|
|
68
|
+
const [stdout, stderr, code] = await Promise.all([
|
|
69
|
+
new Response(proc.stdout).text(),
|
|
70
|
+
new Response(proc.stderr).text(),
|
|
71
|
+
proc.exited,
|
|
72
|
+
]);
|
|
73
|
+
return { code, stderr, stdout };
|
|
74
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Report } from "../agent/report.ts";
|
|
2
|
+
import type { Issue, JobKind } from "../model/types.ts";
|
|
3
|
+
import type { Tracker } from "../trackers/tracker.ts";
|
|
4
|
+
|
|
5
|
+
const BLOCKED_LABEL = "blocked";
|
|
6
|
+
const FAILED_LABEL = "failed";
|
|
7
|
+
const TRIAGED_LABEL = "triaged";
|
|
8
|
+
const NEEDS_INPUT_STATE = "Needs Input";
|
|
9
|
+
|
|
10
|
+
export interface WritebackResult {
|
|
11
|
+
movedTo?: string;
|
|
12
|
+
labeled?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DONE_STATES: Record<JobKind, string> = {
|
|
16
|
+
implement: "In Review",
|
|
17
|
+
spec: "Todo",
|
|
18
|
+
triage: "Backlog",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function defaultDoneState(jobKind: JobKind): string {
|
|
22
|
+
return DONE_STATES[jobKind];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function buildCommentBody(report: Report, telemetry?: string): string {
|
|
26
|
+
const parts = [report.summary];
|
|
27
|
+
if (report.notes !== undefined && report.notes.trim() !== "") {
|
|
28
|
+
parts.push(report.notes);
|
|
29
|
+
}
|
|
30
|
+
if (report.questions !== undefined && report.questions.length > 0) {
|
|
31
|
+
const list = report.questions.map((q) => `- ${q}`).join("\n");
|
|
32
|
+
parts.push(`Questions:\n${list}`);
|
|
33
|
+
}
|
|
34
|
+
if (telemetry !== undefined && telemetry.trim() !== "") {
|
|
35
|
+
parts.push(telemetry);
|
|
36
|
+
}
|
|
37
|
+
return parts.join("\n\n");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function applyReport(
|
|
41
|
+
tracker: Tracker,
|
|
42
|
+
issue: Issue,
|
|
43
|
+
report: Report,
|
|
44
|
+
jobKind: JobKind,
|
|
45
|
+
telemetry?: string,
|
|
46
|
+
): Promise<WritebackResult> {
|
|
47
|
+
if (report.prUrl !== undefined && report.prUrl !== "") {
|
|
48
|
+
await tracker.linkPR(issue, report.prUrl);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await tracker.comment(issue, buildCommentBody(report, telemetry));
|
|
52
|
+
|
|
53
|
+
const result: WritebackResult = {};
|
|
54
|
+
|
|
55
|
+
switch (report.status) {
|
|
56
|
+
case "done": {
|
|
57
|
+
const target = defaultDoneState(jobKind);
|
|
58
|
+
await tracker.updateState(issue, target);
|
|
59
|
+
result.movedTo = target;
|
|
60
|
+
if (jobKind === "triage") {
|
|
61
|
+
await tracker.addProperty(issue, TRIAGED_LABEL);
|
|
62
|
+
result.labeled = TRIAGED_LABEL;
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case "needs_input": {
|
|
67
|
+
await tracker.updateState(issue, NEEDS_INPUT_STATE);
|
|
68
|
+
result.movedTo = NEEDS_INPUT_STATE;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case "blocked": {
|
|
72
|
+
await tracker.addProperty(issue, BLOCKED_LABEL);
|
|
73
|
+
await tracker.updateState(issue, NEEDS_INPUT_STATE);
|
|
74
|
+
result.labeled = BLOCKED_LABEL;
|
|
75
|
+
result.movedTo = NEEDS_INPUT_STATE;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case "failed": {
|
|
79
|
+
await tracker.addProperty(issue, FAILED_LABEL);
|
|
80
|
+
await tracker.updateState(issue, NEEDS_INPUT_STATE);
|
|
81
|
+
result.labeled = FAILED_LABEL;
|
|
82
|
+
result.movedTo = NEEDS_INPUT_STATE;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result;
|
|
88
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export type { StateGroup, RunMode, JobKind, Issue, IssueMeta, Resolved } from "./model/types.ts";
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
configSchema,
|
|
5
|
+
fileSchema,
|
|
6
|
+
registrySchema,
|
|
7
|
+
projectSchema,
|
|
8
|
+
agentConfigSchema,
|
|
9
|
+
type Config,
|
|
10
|
+
type ConfigFile,
|
|
11
|
+
type Registry,
|
|
12
|
+
type Project,
|
|
13
|
+
type AgentConfig,
|
|
14
|
+
} from "./config/schema.ts";
|
|
15
|
+
|
|
16
|
+
export { loadConfig, loadRegistry } from "./config/load.ts";
|
|
17
|
+
|
|
18
|
+
export { parseIssueMeta } from "./resolve/metadata.ts";
|
|
19
|
+
|
|
20
|
+
export { autoDetectJobKind } from "./resolve/jobkind.ts";
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
cascade,
|
|
24
|
+
resolve,
|
|
25
|
+
resolveAgent,
|
|
26
|
+
resolveRunMode,
|
|
27
|
+
resolveRepo,
|
|
28
|
+
resolveJobKind,
|
|
29
|
+
type ResolveInputs,
|
|
30
|
+
} from "./resolve/precedence.ts";
|
|
31
|
+
|
|
32
|
+
export type {
|
|
33
|
+
Tracker,
|
|
34
|
+
QueueFilter,
|
|
35
|
+
IntakeItem,
|
|
36
|
+
BoardTemplate,
|
|
37
|
+
EnsureBoardResult,
|
|
38
|
+
Comment,
|
|
39
|
+
} from "./trackers/tracker.ts";
|
|
40
|
+
|
|
41
|
+
export { BEFLOW_MARKER, withMarker, hasMarker, stripMarker } from "./trackers/marker.ts";
|
|
42
|
+
|
|
43
|
+
export { PlaneTracker, createPlaneTracker, type PlaneTrackerOptions } from "./trackers/plane/adapter.ts";
|
|
44
|
+
|
|
45
|
+
export { PlaneClient, type PlaneClientOptions } from "./trackers/plane/client.ts";
|
|
46
|
+
|
|
47
|
+
export { LinearTracker, createLinearTracker, type LinearTrackerOptions } from "./trackers/linear/adapter.ts";
|
|
48
|
+
|
|
49
|
+
export { LinearSdkGateway, type LinearGateway, type ListIssuesQuery } from "./trackers/linear/client.ts";
|
|
50
|
+
|
|
51
|
+
export { createTracker } from "./trackers/factory.ts";
|
|
52
|
+
|
|
53
|
+
export { extractReport, type Report, type ReportStatus } from "./agent/report.ts";
|
|
54
|
+
|
|
55
|
+
export { parseAcpLine, reduceAcpStream, type AcpStreamResult, type AcpToolCall } from "./agent/events.ts";
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
AcpxDriver,
|
|
59
|
+
BunProcessRunner,
|
|
60
|
+
buildAcpxArgs,
|
|
61
|
+
buildCancelArgs,
|
|
62
|
+
type ProcessRunner,
|
|
63
|
+
type SpawnedProcess,
|
|
64
|
+
} from "./agent/acpx.ts";
|
|
65
|
+
|
|
66
|
+
export type { AgentDriver, AgentRunResult, RunOptions } from "./agent/driver.ts";
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
PROMPT_NAMES,
|
|
70
|
+
buildPromptContext,
|
|
71
|
+
defaultPromptResolveDeps,
|
|
72
|
+
loadPromptSet,
|
|
73
|
+
renderContract,
|
|
74
|
+
renderTask,
|
|
75
|
+
renderTemplate,
|
|
76
|
+
type PromptName,
|
|
77
|
+
type PromptResolveDeps,
|
|
78
|
+
type PromptSet,
|
|
79
|
+
} from "./core/prompts.ts";
|
|
80
|
+
|
|
81
|
+
export { applyReport, defaultDoneState, type WritebackResult } from "./core/writeback.ts";
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
createWorktree,
|
|
85
|
+
removeWorktree,
|
|
86
|
+
worktreePath,
|
|
87
|
+
sanitizeKey,
|
|
88
|
+
bunExec,
|
|
89
|
+
type Exec,
|
|
90
|
+
type ExecResult,
|
|
91
|
+
} from "./core/worktree.ts";
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
resolveRun,
|
|
95
|
+
runIssue,
|
|
96
|
+
runOpen,
|
|
97
|
+
runSupervised,
|
|
98
|
+
defaultOpenIssue,
|
|
99
|
+
type ResolvedRun,
|
|
100
|
+
type RunIssueDeps,
|
|
101
|
+
type RunOpenDeps,
|
|
102
|
+
type RunResult,
|
|
103
|
+
type RunSupervisedDeps,
|
|
104
|
+
type SupervisedResult,
|
|
105
|
+
type OpenIssue,
|
|
106
|
+
type OpenLaunch,
|
|
107
|
+
type Logger,
|
|
108
|
+
type LaunchInteractive,
|
|
109
|
+
type InteractiveLaunch,
|
|
110
|
+
type AskOutcome,
|
|
111
|
+
type OutcomeAnswer,
|
|
112
|
+
} from "./core/run.ts";
|
|
113
|
+
|
|
114
|
+
export { beflowBoardTemplate } from "./core/template.ts";
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
assembleContinuation,
|
|
118
|
+
renderContinuation,
|
|
119
|
+
type AssembleOptions,
|
|
120
|
+
type ContinuationContext,
|
|
121
|
+
} from "./core/continuation.ts";
|
|
122
|
+
|
|
123
|
+
export {
|
|
124
|
+
loadRecord,
|
|
125
|
+
saveRecord,
|
|
126
|
+
deleteRecord,
|
|
127
|
+
resolveRunsDir,
|
|
128
|
+
systemClock,
|
|
129
|
+
nodeRunStoreFs,
|
|
130
|
+
reportSchema,
|
|
131
|
+
runRecordSchema,
|
|
132
|
+
type RunRecord,
|
|
133
|
+
type RunStoreFs,
|
|
134
|
+
type Clock,
|
|
135
|
+
} from "./core/runstore.ts";
|
|
136
|
+
|
|
137
|
+
export { setupProject, type SetupDeps } from "./core/setup.ts";
|
|
138
|
+
|
|
139
|
+
export { queueView, type QueueRow, type QueueDeps, type QueueOptions } from "./core/queue.ts";
|
|
140
|
+
|
|
141
|
+
export { acceptIntake, type AcceptDeps } from "./core/accept.ts";
|
|
142
|
+
|
|
143
|
+
export { doctor, type DoctorCheck, type DoctorDeps, type CheckLevel } from "./core/doctor.ts";
|
|
144
|
+
|
|
145
|
+
export {
|
|
146
|
+
watch,
|
|
147
|
+
watchTick,
|
|
148
|
+
type WatchDeps,
|
|
149
|
+
type WatchControl,
|
|
150
|
+
type WatchAction,
|
|
151
|
+
type WatchTickResult,
|
|
152
|
+
} from "./core/watch.ts";
|
|
153
|
+
|
|
154
|
+
export { runCli, defaultCliDeps, type CliDeps, type WatchRunner, type Ping } from "./cli.ts";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type StateGroup = "backlog" | "unstarted" | "started" | "completed" | "cancelled";
|
|
2
|
+
|
|
3
|
+
export type RunMode = "autonomous" | "supervised";
|
|
4
|
+
|
|
5
|
+
export type JobKind = "triage" | "spec" | "implement";
|
|
6
|
+
|
|
7
|
+
export interface IssueMeta {
|
|
8
|
+
agent?: string;
|
|
9
|
+
repo?: string;
|
|
10
|
+
runMode?: RunMode;
|
|
11
|
+
jobKind?: JobKind;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface Issue {
|
|
15
|
+
id: string;
|
|
16
|
+
key: string;
|
|
17
|
+
title: string;
|
|
18
|
+
body: string;
|
|
19
|
+
type?: string;
|
|
20
|
+
state: { name: string; group: StateGroup };
|
|
21
|
+
labels: string[];
|
|
22
|
+
areas: string[];
|
|
23
|
+
priority?: string;
|
|
24
|
+
parentId?: string;
|
|
25
|
+
meta: IssueMeta;
|
|
26
|
+
archived?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Resolved {
|
|
30
|
+
agent: string;
|
|
31
|
+
jobKind: JobKind;
|
|
32
|
+
repo: string;
|
|
33
|
+
repoPath: string;
|
|
34
|
+
runMode: RunMode;
|
|
35
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
This work item is returning to you for continuation — read the context below before continuing.
|
|
2
|
+
|
|
3
|
+
Prior outcome: {{prior_report}}
|
|
4
|
+
Open PR: {{pr_url}}
|
|
5
|
+
|
|
6
|
+
New input since your last update:
|
|
7
|
+
{{review_comments}}
|
|
8
|
+
|
|
9
|
+
Address the input above. If a pull request is already open for this item, UPDATE it — do not open a new one. Then finish the work and emit your report block as usual.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
You are in IMPLEMENT. Do the work in the repository at your working directory and ship it as a reviewable pull request.
|
|
2
|
+
|
|
3
|
+
Work through it:
|
|
4
|
+
|
|
5
|
+
- If your environment provides a to-do/task tool, use it: break the work into a checklist before you start and keep it updated as you go, marking each item done the moment it is, so no step is dropped or forgotten.
|
|
6
|
+
- Read and obey that repo's CLAUDE.md — it carries the architecture, conventions, and Definition of Done. The repo holds the bar; follow its existing patterns for structure, naming, error handling, and tests.
|
|
7
|
+
- Make the change, and back EVERY behavioral change with a test. New behavior without a test does not count as done.
|
|
8
|
+
- Run the repo's own test and lint commands and paste their REAL output into the report `summary`/`notes`. Fabricating or paraphrasing results is forbidden — paste what actually ran.
|
|
9
|
+
- ONLY when the gate is green — tests and lint pass on real output — and every item on your to-do list is marked complete (nothing left pending or in progress), commit, push, and open a pull request with `gh`, then put the PR URL in `prUrl`.
|
|
10
|
+
|
|
11
|
+
If the task is ambiguous, or turns out far larger than described, STOP: do not guess and do not half-do it. Return status `needs_input` with the specific decisions you need in `questions`.
|
|
12
|
+
|
|
13
|
+
When you are continuing a returning item (a continuation block precedes this task), read the prior PR and the new input, address it, and UPDATE the existing pull request — do not open a new one.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
You are authoring a new work item by investigating the repository it belongs to.
|
|
2
|
+
|
|
3
|
+
Investigate the repository READ-ONLY: only read and search files. Do NOT modify, create, or delete any file, and do NOT run commands that change the working tree — beflow-issue-enrich-readonly-author is a strictly observational pass.
|
|
4
|
+
|
|
5
|
+
Here is what the human supplied through the new-issue form.
|
|
6
|
+
|
|
7
|
+
Title: {{title}}
|
|
8
|
+
|
|
9
|
+
Answers:
|
|
10
|
+
{{answers}}
|
|
11
|
+
|
|
12
|
+
Draft body assembled from the template:
|
|
13
|
+
{{draft}}
|
|
14
|
+
|
|
15
|
+
The desired body format for this kind of issue:
|
|
16
|
+
{{format}}
|
|
17
|
+
|
|
18
|
+
Use the answers, the draft, and the desired format together with what you learn from reading the codebase to write one complete, specific, actionable issue. Ground every claim in files you actually read; reference concrete paths and symbols where they help an implementer.
|
|
19
|
+
|
|
20
|
+
Output ONLY a single fenced code block whose info string is exactly `beflow-issue`, containing one JSON object. Include at least `body`. Optionally include a refined `title`, and suggested `type`, `priority` (one of "urgent", "high", "medium", "low", "none"), and `labels` (an array of strings). Emit nothing after the block.
|
|
21
|
+
|
|
22
|
+
```beflow-issue
|
|
23
|
+
{
|
|
24
|
+
"body": "the full issue body in Markdown",
|
|
25
|
+
"title": "an optional refined title",
|
|
26
|
+
"type": "an optional suggested type",
|
|
27
|
+
"priority": "an optional suggested priority",
|
|
28
|
+
"labels": ["an optional suggested label"]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bug
|
|
3
|
+
description: A reproducible defect — steps, expected vs actual
|
|
4
|
+
type: Bug
|
|
5
|
+
jobKind: implement
|
|
6
|
+
priority: high
|
|
7
|
+
enrich: false
|
|
8
|
+
title: "{{summary}}"
|
|
9
|
+
questions:
|
|
10
|
+
- { key: summary, label: One-line summary, type: text, required: true }
|
|
11
|
+
- { key: steps, label: Steps to reproduce, type: longtext, required: true }
|
|
12
|
+
- { key: expected, label: Expected behaviour, type: text }
|
|
13
|
+
- { key: actual, label: Actual behaviour, type: text }
|
|
14
|
+
- { key: severity, label: Severity, type: options, options: [low, medium, high, critical] }
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Summary
|
|
18
|
+
|
|
19
|
+
{{summary}}
|
|
20
|
+
|
|
21
|
+
## Steps to reproduce
|
|
22
|
+
|
|
23
|
+
{{steps}}
|
|
24
|
+
|
|
25
|
+
## Expected
|
|
26
|
+
|
|
27
|
+
{{expected}}
|
|
28
|
+
|
|
29
|
+
## Actual
|
|
30
|
+
|
|
31
|
+
{{actual}}
|
|
32
|
+
|
|
33
|
+
## Severity
|
|
34
|
+
|
|
35
|
+
{{severity}}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feature
|
|
3
|
+
description: A new capability — motivation + acceptance criteria
|
|
4
|
+
type: Feature
|
|
5
|
+
jobKind: spec
|
|
6
|
+
enrich: false
|
|
7
|
+
title: "{{summary}}"
|
|
8
|
+
questions:
|
|
9
|
+
- { key: summary, label: One-line summary, type: text, required: true }
|
|
10
|
+
- { key: motivation, label: Why — problem / value, type: longtext, required: true }
|
|
11
|
+
- { key: criteria, label: Acceptance criteria, type: longtext }
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Summary
|
|
15
|
+
|
|
16
|
+
{{summary}}
|
|
17
|
+
|
|
18
|
+
## Motivation
|
|
19
|
+
|
|
20
|
+
{{motivation}}
|
|
21
|
+
|
|
22
|
+
## Acceptance criteria
|
|
23
|
+
|
|
24
|
+
{{criteria}}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: generic
|
|
3
|
+
description: A blank issue — summary plus free-form context
|
|
4
|
+
enrich: false
|
|
5
|
+
questions:
|
|
6
|
+
- { key: summary, label: One-line summary, type: text, required: true }
|
|
7
|
+
- { key: context, label: Context / details, type: longtext }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
{{summary}}
|
|
13
|
+
|
|
14
|
+
## Context
|
|
15
|
+
|
|
16
|
+
{{context}}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spike
|
|
3
|
+
description: A time-boxed investigation — a question to answer
|
|
4
|
+
type: Spike
|
|
5
|
+
jobKind: triage
|
|
6
|
+
enrich: false
|
|
7
|
+
title: "{{summary}}"
|
|
8
|
+
questions:
|
|
9
|
+
- { key: summary, label: Question / goal, type: text, required: true }
|
|
10
|
+
- { key: context, label: Context / background, type: longtext }
|
|
11
|
+
- { key: outcome, label: Expected outcome / deliverable, type: text }
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Question
|
|
15
|
+
|
|
16
|
+
{{summary}}
|
|
17
|
+
|
|
18
|
+
## Context
|
|
19
|
+
|
|
20
|
+
{{context}}
|
|
21
|
+
|
|
22
|
+
## Expected outcome
|
|
23
|
+
|
|
24
|
+
{{outcome}}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
End your FINAL message with a fenced code block whose info string is exactly `beflow-report`, containing a single JSON object:
|
|
2
|
+
|
|
3
|
+
```beflow-report
|
|
4
|
+
{
|
|
5
|
+
"status": "done" | "needs_input" | "blocked" | "failed",
|
|
6
|
+
"summary": "one or two sentences on what you did",
|
|
7
|
+
"prUrl": "https://… (only when you opened a PR)",
|
|
8
|
+
"questions": ["…"],
|
|
9
|
+
"notes": "extra detail (required when status is failed)"
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Status meanings:
|
|
14
|
+
|
|
15
|
+
- "done" — the work is complete; if it produced code, a PR is open and its URL is in `prUrl`.
|
|
16
|
+
- "needs_input" — you cannot proceed without a human decision; put the exact decisions you need in `questions`.
|
|
17
|
+
- "blocked" — you are waiting on an external dependency (another party, an upstream change, access you don't have); explain in `notes`.
|
|
18
|
+
- "failed" — you could not complete the work; explain why in `notes`.
|
|
19
|
+
|
|
20
|
+
Emit exactly one such block, as the last thing in your final message.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
You are in REVIEW. A pull request has been opened for work item {{key}} ("{{title}}") in the repository at your working directory. Read the PR and report what you find — you are a REVIEWER, not an author.
|
|
2
|
+
|
|
3
|
+
Work through it:
|
|
4
|
+
|
|
5
|
+
- Inspect the changes with `gh pr diff` in the current working directory. Read the surrounding code as needed to judge each change in context.
|
|
6
|
+
- Focus on correctness and bugs, security issues, missing or weak test coverage, and anything that would block a merge. Be specific: point at the file and, where you can, the line.
|
|
7
|
+
- This is a {{type}} work item; weigh the changes against what it set out to do.
|
|
8
|
+
|
|
9
|
+
You must NOT merge, push, commit, or change anything — no `gh pr merge`, no edits to the working tree, no board changes. Your only output is the review block below.
|
|
10
|
+
|
|
11
|
+
End your FINAL message with a fenced code block whose info string is exactly `beflow-review`, containing a single JSON object:
|
|
12
|
+
|
|
13
|
+
```beflow-review
|
|
14
|
+
{
|
|
15
|
+
"summary": "one or two sentences on the overall state of the PR",
|
|
16
|
+
"findings": [
|
|
17
|
+
{
|
|
18
|
+
"severity": "blocker" | "major" | "minor" | "nit",
|
|
19
|
+
"comment": "what is wrong and what to do about it",
|
|
20
|
+
"file": "path/to/file (optional)",
|
|
21
|
+
"line": 0
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Severity meanings:
|
|
28
|
+
|
|
29
|
+
- "blocker" — must be fixed before merge (correctness, security, data loss).
|
|
30
|
+
- "major" — a real problem that should be addressed.
|
|
31
|
+
- "minor" — a small issue worth fixing.
|
|
32
|
+
- "nit" — a stylistic or cosmetic suggestion.
|
|
33
|
+
|
|
34
|
+
Emit exactly one such block, as the last thing in your final message. An empty `findings` array is valid when the PR looks clean.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
You are in SPEC. Produce an implementation plan for a structured item — detailed enough that an implementer can execute it and a human can approve or redirect it. This is design ONLY — do NOT implement anything and do NOT open a PR.
|
|
2
|
+
|
|
3
|
+
Work through it:
|
|
4
|
+
|
|
5
|
+
- Explore the codebase first: the data model, the surface this touches, existing patterns to follow, and the test conventions. The more you understand, the sharper the plan.
|
|
6
|
+
- Separate the work into what is new, what is modified, and what is affected downstream.
|
|
7
|
+
- Choose ONE opinionated approach. If there are real alternatives, weigh them briefly, then commit to one and say why.
|
|
8
|
+
- List the specific files and functions to touch, in dependency order.
|
|
9
|
+
- Define acceptance criteria, and call out migration, rollout, and risk: can it ship behind a flag, is the migration reversible, what breaks if a deploy half-lands.
|
|
10
|
+
|
|
11
|
+
Report status `done` when the plan is ready for review; report `needs_input` when a human decision is required first. beflow decides where the item moves.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
You are in TRIAGE. Take a raw, unstructured item and turn it into a crisp, structured one a human can act on at a glance. This is investigation and organization ONLY — do NOT write code, do NOT open a PR.
|
|
2
|
+
|
|
3
|
+
Work through it:
|
|
4
|
+
|
|
5
|
+
- Restate the real problem in one line. Strip the noise; say what the customer or author actually wants.
|
|
6
|
+
- For a bug: locate the relevant code, establish a concrete reproduction (steps, or note that you could not reproduce), and name the likely root cause down to file and line.
|
|
7
|
+
- For a feature: identify the affected areas of the code (data model, surface, UI), note what already exists to build on, and give a rough effort and risk read.
|
|
8
|
+
- Write tight, testable acceptance criteria — the conditions under which this is "done".
|
|
9
|
+
- Flag open questions, but only genuine human decisions (scope, priority, business calls); resolve the technical ones yourself.
|
|
10
|
+
|
|
11
|
+
Decide your business questions yourself only where they are technical; surface the rest. Report status `done` when triage is complete and the item is ready to schedule; report `needs_input` when a human decision is required first. beflow decides where the item moves.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { JobKind, StateGroup } from "../model/types.ts";
|
|
2
|
+
|
|
3
|
+
export function autoDetectJobKind(type: string | undefined, stateGroup: StateGroup): JobKind {
|
|
4
|
+
if (type === "Spike") {
|
|
5
|
+
return "triage";
|
|
6
|
+
}
|
|
7
|
+
if (stateGroup === "backlog") {
|
|
8
|
+
return "spec";
|
|
9
|
+
}
|
|
10
|
+
return "implement";
|
|
11
|
+
}
|