@fledge/workflow 0.6.3 → 0.7.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/scripts/brief.js +116 -51
- package/package.json +2 -2
- package/skills/brief/SKILL.md +60 -32
package/dist/scripts/brief.js
CHANGED
|
@@ -554,7 +554,6 @@ const string$1 = (params) => {
|
|
|
554
554
|
const regex = params ? `[\\s\\S]{${params?.minimum ?? 0},${params?.maximum ?? ""}}` : `[\\s\\S]*`;
|
|
555
555
|
return new RegExp(`^${regex}$`);
|
|
556
556
|
};
|
|
557
|
-
const boolean$1 = /^(?:true|false)$/i;
|
|
558
557
|
const lowercase = /^[^A-Z]*$/;
|
|
559
558
|
const uppercase = /^[^a-z]*$/;
|
|
560
559
|
//#endregion
|
|
@@ -1166,24 +1165,6 @@ const $ZodJWT = /* @__PURE__ */ $constructor("$ZodJWT", (inst, def) => {
|
|
|
1166
1165
|
});
|
|
1167
1166
|
};
|
|
1168
1167
|
});
|
|
1169
|
-
const $ZodBoolean = /* @__PURE__ */ $constructor("$ZodBoolean", (inst, def) => {
|
|
1170
|
-
$ZodType.init(inst, def);
|
|
1171
|
-
inst._zod.pattern = boolean$1;
|
|
1172
|
-
inst._zod.parse = (payload, _ctx) => {
|
|
1173
|
-
if (def.coerce) try {
|
|
1174
|
-
payload.value = Boolean(payload.value);
|
|
1175
|
-
} catch (_) {}
|
|
1176
|
-
const input = payload.value;
|
|
1177
|
-
if (typeof input === "boolean") return payload;
|
|
1178
|
-
payload.issues.push({
|
|
1179
|
-
expected: "boolean",
|
|
1180
|
-
code: "invalid_type",
|
|
1181
|
-
input,
|
|
1182
|
-
inst
|
|
1183
|
-
});
|
|
1184
|
-
return payload;
|
|
1185
|
-
};
|
|
1186
|
-
});
|
|
1187
1168
|
const $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => {
|
|
1188
1169
|
$ZodType.init(inst, def);
|
|
1189
1170
|
inst._zod.parse = (payload) => payload;
|
|
@@ -2146,13 +2127,6 @@ function _isoDuration(Class, params) {
|
|
|
2146
2127
|
});
|
|
2147
2128
|
}
|
|
2148
2129
|
/* @__NO_SIDE_EFFECTS__ */
|
|
2149
|
-
function _boolean(Class, params) {
|
|
2150
|
-
return new Class({
|
|
2151
|
-
type: "boolean",
|
|
2152
|
-
...normalizeParams(params)
|
|
2153
|
-
});
|
|
2154
|
-
}
|
|
2155
|
-
/* @__NO_SIDE_EFFECTS__ */
|
|
2156
2130
|
function _unknown(Class) {
|
|
2157
2131
|
return new Class({ type: "unknown" });
|
|
2158
2132
|
}
|
|
@@ -3336,14 +3310,6 @@ const ZodJWT = /* @__PURE__ */ $constructor("ZodJWT", (inst, def) => {
|
|
|
3336
3310
|
$ZodJWT.init(inst, def);
|
|
3337
3311
|
ZodStringFormat.init(inst, def);
|
|
3338
3312
|
});
|
|
3339
|
-
const ZodBoolean = /* @__PURE__ */ $constructor("ZodBoolean", (inst, def) => {
|
|
3340
|
-
$ZodBoolean.init(inst, def);
|
|
3341
|
-
ZodType.init(inst, def);
|
|
3342
|
-
inst._zod.processJSONSchema = (ctx, json, params) => booleanProcessor(inst, ctx, json, params);
|
|
3343
|
-
});
|
|
3344
|
-
function boolean(params) {
|
|
3345
|
-
return /* @__PURE__ */ _boolean(ZodBoolean, params);
|
|
3346
|
-
}
|
|
3347
3313
|
const ZodUnknown = /* @__PURE__ */ $constructor("ZodUnknown", (inst, def) => {
|
|
3348
3314
|
$ZodUnknown.init(inst, def);
|
|
3349
3315
|
ZodType.init(inst, def);
|
|
@@ -3650,8 +3616,10 @@ function superRefine(fn) {
|
|
|
3650
3616
|
//#region ../cli/dist/schemas/brief.js
|
|
3651
3617
|
const briefStatus = _enum([
|
|
3652
3618
|
"draft",
|
|
3619
|
+
"ready",
|
|
3653
3620
|
"active",
|
|
3654
|
-
"completed"
|
|
3621
|
+
"completed",
|
|
3622
|
+
"cancelled"
|
|
3655
3623
|
]);
|
|
3656
3624
|
const briefFrontmatter = object({
|
|
3657
3625
|
name: string().min(1),
|
|
@@ -3663,20 +3631,42 @@ const briefFrontmatter = object({
|
|
|
3663
3631
|
/**
|
|
3664
3632
|
* Valid state transitions for a brief.
|
|
3665
3633
|
* Each key maps to the set of states it can transition to.
|
|
3634
|
+
*
|
|
3635
|
+
* draft → ready: brief skill marks the brief as designed and enriched
|
|
3636
|
+
* ready → active: build skill begins building
|
|
3637
|
+
* ready → draft: brief needs rework
|
|
3638
|
+
* active → completed: build skill finishes all tasks
|
|
3639
|
+
* draft/ready/active → cancelled: feature is dropped
|
|
3666
3640
|
*/
|
|
3667
3641
|
const briefTransitions = {
|
|
3668
|
-
draft: ["
|
|
3669
|
-
|
|
3670
|
-
|
|
3642
|
+
draft: ["ready", "cancelled"],
|
|
3643
|
+
ready: [
|
|
3644
|
+
"active",
|
|
3645
|
+
"draft",
|
|
3646
|
+
"cancelled"
|
|
3647
|
+
],
|
|
3648
|
+
active: ["completed", "cancelled"],
|
|
3649
|
+
completed: [],
|
|
3650
|
+
cancelled: []
|
|
3671
3651
|
};
|
|
3652
|
+
//#endregion
|
|
3653
|
+
//#region ../cli/dist/schemas/tasks.js
|
|
3654
|
+
const taskStatus = _enum([
|
|
3655
|
+
"pending",
|
|
3656
|
+
"active",
|
|
3657
|
+
"completed",
|
|
3658
|
+
"skipped"
|
|
3659
|
+
]);
|
|
3672
3660
|
const tasksFrontmatter = object({ tasks: array(object({
|
|
3673
3661
|
name: string().min(1),
|
|
3674
3662
|
group: string().min(1).optional(),
|
|
3675
|
-
|
|
3663
|
+
status: taskStatus.default("pending")
|
|
3676
3664
|
})) });
|
|
3677
3665
|
//#endregion
|
|
3678
3666
|
//#region ../cli/dist/brief.js
|
|
3679
|
-
const
|
|
3667
|
+
const FLEDGE_DIRECTORY = ".fledge";
|
|
3668
|
+
const BRIEFS_DIRECTORY = path.join(FLEDGE_DIRECTORY, "briefs");
|
|
3669
|
+
path.join(FLEDGE_DIRECTORY, "project.md");
|
|
3680
3670
|
/**
|
|
3681
3671
|
* Creates a BriefContext from a project directory path or falls back to `cwd()`.
|
|
3682
3672
|
*
|
|
@@ -3726,6 +3716,16 @@ function getTasksFile(context, name) {
|
|
|
3726
3716
|
return path.join(getBriefDirectory(context, name), "tasks.md");
|
|
3727
3717
|
}
|
|
3728
3718
|
/**
|
|
3719
|
+
* Returns the absolute path to a brief's `spec.md` file.
|
|
3720
|
+
*
|
|
3721
|
+
* @param context - The brief context.
|
|
3722
|
+
* @param name - The brief name.
|
|
3723
|
+
* @returns The absolute path to `spec.md`.
|
|
3724
|
+
*/
|
|
3725
|
+
function getSpecFile(context, name) {
|
|
3726
|
+
return path.join(getBriefDirectory(context, name), "spec.md");
|
|
3727
|
+
}
|
|
3728
|
+
/**
|
|
3729
3729
|
* Checks whether a brief directory exists.
|
|
3730
3730
|
*
|
|
3731
3731
|
* @param context - The brief context.
|
|
@@ -3815,6 +3815,35 @@ const projectDirectory = {
|
|
|
3815
3815
|
description: "Project root directory. Overrides cwd() for locating .fledge/briefs/."
|
|
3816
3816
|
};
|
|
3817
3817
|
//#endregion
|
|
3818
|
+
//#region ../cli/dist/commands/brief/cancel.js
|
|
3819
|
+
var cancel_default = defineCommand({
|
|
3820
|
+
meta: {
|
|
3821
|
+
name: "cancel",
|
|
3822
|
+
description: "Cancel a brief"
|
|
3823
|
+
},
|
|
3824
|
+
args: {
|
|
3825
|
+
name: {
|
|
3826
|
+
type: "positional",
|
|
3827
|
+
required: true,
|
|
3828
|
+
description: "The name of the brief to cancel"
|
|
3829
|
+
},
|
|
3830
|
+
projectDirectory
|
|
3831
|
+
},
|
|
3832
|
+
run(context) {
|
|
3833
|
+
const ctx = createBriefContext(context.args.projectDirectory);
|
|
3834
|
+
const { name } = context.args;
|
|
3835
|
+
if (!briefExists(ctx, name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(ctx, name)}"`);
|
|
3836
|
+
const brief = readBriefFrontmatter(ctx, name);
|
|
3837
|
+
validateTransition(brief.status, "cancelled");
|
|
3838
|
+
updateBriefFrontmatter(ctx, name, {
|
|
3839
|
+
...brief,
|
|
3840
|
+
status: "cancelled",
|
|
3841
|
+
updated: formatDate()
|
|
3842
|
+
});
|
|
3843
|
+
stdout.write(`Brief "${name}" has been cancelled\n`);
|
|
3844
|
+
}
|
|
3845
|
+
});
|
|
3846
|
+
//#endregion
|
|
3818
3847
|
//#region ../cli/dist/commands/brief/complete.js
|
|
3819
3848
|
var complete_default = defineCommand({
|
|
3820
3849
|
meta: {
|
|
@@ -3836,10 +3865,10 @@ var complete_default = defineCommand({
|
|
|
3836
3865
|
const brief = readBriefFrontmatter(ctx, name);
|
|
3837
3866
|
validateTransition(brief.status, "completed");
|
|
3838
3867
|
if (!brief.summary) throw new Error("Brief must have a summary before completing. Add a \"summary\" field to the brief.md frontmatter.");
|
|
3839
|
-
const incomplete = readTasksFrontmatter(ctx, name).tasks.filter((task) =>
|
|
3868
|
+
const incomplete = readTasksFrontmatter(ctx, name).tasks.filter((task) => task.status !== "completed" && task.status !== "skipped");
|
|
3840
3869
|
if (incomplete.length > 0) {
|
|
3841
|
-
const names = incomplete.map((task) => ` - ${task.name}`).join("\n");
|
|
3842
|
-
throw new Error(`Cannot complete brief with ${incomplete.length}
|
|
3870
|
+
const names = incomplete.map((task) => ` - ${task.name} [${task.status}]`).join("\n");
|
|
3871
|
+
throw new Error(`Cannot complete brief with ${incomplete.length} unfinished task(s):\n${names}`);
|
|
3843
3872
|
}
|
|
3844
3873
|
updateBriefFrontmatter(ctx, name, {
|
|
3845
3874
|
...brief,
|
|
@@ -3878,6 +3907,7 @@ var create_default = defineCommand({
|
|
|
3878
3907
|
updated: date
|
|
3879
3908
|
}));
|
|
3880
3909
|
fs.writeFileSync(getTasksFile(ctx, name), writeFrontmatter({ tasks: [] }));
|
|
3910
|
+
fs.writeFileSync(getSpecFile(ctx, name), "");
|
|
3881
3911
|
stdout.write(`Created brief "${name}" at "${directory}"\n`);
|
|
3882
3912
|
}
|
|
3883
3913
|
});
|
|
@@ -3892,7 +3922,7 @@ var list_default = defineCommand({
|
|
|
3892
3922
|
status: {
|
|
3893
3923
|
type: "string",
|
|
3894
3924
|
required: false,
|
|
3895
|
-
description: "Filter by brief status (draft, active, completed)"
|
|
3925
|
+
description: "Filter by brief status (draft, ready, active, completed, cancelled)"
|
|
3896
3926
|
},
|
|
3897
3927
|
projectDirectory
|
|
3898
3928
|
},
|
|
@@ -3906,7 +3936,7 @@ var list_default = defineCommand({
|
|
|
3906
3936
|
let briefs = names.map((name) => {
|
|
3907
3937
|
const brief = readBriefFrontmatter(ctx, name);
|
|
3908
3938
|
const { tasks } = readTasksFrontmatter(ctx, name);
|
|
3909
|
-
const done = tasks.filter((task) => task.
|
|
3939
|
+
const done = tasks.filter((task) => task.status === "completed" || task.status === "skipped").length;
|
|
3910
3940
|
return {
|
|
3911
3941
|
...brief,
|
|
3912
3942
|
tasksDone: done,
|
|
@@ -3934,6 +3964,35 @@ var list_default = defineCommand({
|
|
|
3934
3964
|
}
|
|
3935
3965
|
});
|
|
3936
3966
|
//#endregion
|
|
3967
|
+
//#region ../cli/dist/commands/brief/ready.js
|
|
3968
|
+
var ready_default = defineCommand({
|
|
3969
|
+
meta: {
|
|
3970
|
+
name: "ready",
|
|
3971
|
+
description: "Transition a brief from draft to ready"
|
|
3972
|
+
},
|
|
3973
|
+
args: {
|
|
3974
|
+
name: {
|
|
3975
|
+
type: "positional",
|
|
3976
|
+
required: true,
|
|
3977
|
+
description: "The name of the brief to mark as ready"
|
|
3978
|
+
},
|
|
3979
|
+
projectDirectory
|
|
3980
|
+
},
|
|
3981
|
+
run(context) {
|
|
3982
|
+
const ctx = createBriefContext(context.args.projectDirectory);
|
|
3983
|
+
const { name } = context.args;
|
|
3984
|
+
if (!briefExists(ctx, name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(ctx, name)}"`);
|
|
3985
|
+
const brief = readBriefFrontmatter(ctx, name);
|
|
3986
|
+
validateTransition(brief.status, "ready");
|
|
3987
|
+
updateBriefFrontmatter(ctx, name, {
|
|
3988
|
+
...brief,
|
|
3989
|
+
status: "ready",
|
|
3990
|
+
updated: formatDate()
|
|
3991
|
+
});
|
|
3992
|
+
stdout.write(`Brief "${name}" is ready for implementation\n`);
|
|
3993
|
+
}
|
|
3994
|
+
});
|
|
3995
|
+
//#endregion
|
|
3937
3996
|
//#region ../cli/dist/commands/brief/schema.js
|
|
3938
3997
|
var schema_default = defineCommand({
|
|
3939
3998
|
meta: {
|
|
@@ -3953,7 +4012,7 @@ var schema_default = defineCommand({
|
|
|
3953
4012
|
var start_default = defineCommand({
|
|
3954
4013
|
meta: {
|
|
3955
4014
|
name: "start",
|
|
3956
|
-
description: "Transition a brief from
|
|
4015
|
+
description: "Transition a brief from ready to active"
|
|
3957
4016
|
},
|
|
3958
4017
|
args: {
|
|
3959
4018
|
name: {
|
|
@@ -3969,7 +4028,6 @@ var start_default = defineCommand({
|
|
|
3969
4028
|
if (!briefExists(ctx, name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(ctx, name)}"`);
|
|
3970
4029
|
const brief = readBriefFrontmatter(ctx, name);
|
|
3971
4030
|
validateTransition(brief.status, "active");
|
|
3972
|
-
if (readTasksFrontmatter(ctx, name).tasks.length === 0) throw new Error("Brief must have at least one task before starting");
|
|
3973
4031
|
updateBriefFrontmatter(ctx, name, {
|
|
3974
4032
|
...brief,
|
|
3975
4033
|
status: "active",
|
|
@@ -3997,13 +4055,18 @@ function groupTasks(tasks) {
|
|
|
3997
4055
|
return groups;
|
|
3998
4056
|
}
|
|
3999
4057
|
/**
|
|
4000
|
-
*
|
|
4058
|
+
* Maps a task status to a display indicator.
|
|
4001
4059
|
*
|
|
4002
4060
|
* @param task - The task to format.
|
|
4003
|
-
* @returns The formatted task string.
|
|
4061
|
+
* @returns The formatted task string with status indicator.
|
|
4004
4062
|
*/
|
|
4005
4063
|
function formatTask(task) {
|
|
4006
|
-
return ` ${
|
|
4064
|
+
return ` ${{
|
|
4065
|
+
pending: "[ ]",
|
|
4066
|
+
active: "[~]",
|
|
4067
|
+
completed: "[x]",
|
|
4068
|
+
skipped: "[-]"
|
|
4069
|
+
}[task.status] ?? "[ ]"} ${task.name}`;
|
|
4007
4070
|
}
|
|
4008
4071
|
//#endregion
|
|
4009
4072
|
//#region ../cli/dist/run-brief.js
|
|
@@ -4013,9 +4076,11 @@ runMain(defineCommand({
|
|
|
4013
4076
|
description: "Manage feature briefs"
|
|
4014
4077
|
},
|
|
4015
4078
|
subCommands: {
|
|
4079
|
+
cancel: cancel_default,
|
|
4016
4080
|
complete: complete_default,
|
|
4017
4081
|
create: create_default,
|
|
4018
4082
|
list: list_default,
|
|
4083
|
+
ready: ready_default,
|
|
4019
4084
|
schema: schema_default,
|
|
4020
4085
|
start: start_default,
|
|
4021
4086
|
status: defineCommand({
|
|
@@ -4037,7 +4102,7 @@ runMain(defineCommand({
|
|
|
4037
4102
|
if (!briefExists(ctx, name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(ctx, name)}"`);
|
|
4038
4103
|
const brief = readBriefFrontmatter(ctx, name);
|
|
4039
4104
|
const { tasks } = readTasksFrontmatter(ctx, name);
|
|
4040
|
-
const done = tasks.filter((task) => task.
|
|
4105
|
+
const done = tasks.filter((task) => task.status === "completed" || task.status === "skipped").length;
|
|
4041
4106
|
const lines = [];
|
|
4042
4107
|
lines.push(`${name} [${brief.status}] ${done}/${tasks.length} tasks done`);
|
|
4043
4108
|
lines.push("");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fledge/workflow",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.1",
|
|
5
5
|
"author": "René Schapka",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"files": [
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"node": ">=24"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@fledge/cli": "^0.
|
|
18
|
+
"@fledge/cli": "^0.11.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@antfu/eslint-config": "7.7.3",
|
package/skills/brief/SKILL.md
CHANGED
|
@@ -1,20 +1,43 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fledge-brief
|
|
3
3
|
description: >-
|
|
4
|
-
Guide feature brief creation and lifecycle. Plan new features, create or update feature briefs,
|
|
4
|
+
Guide feature brief creation and lifecycle. Plan new features, create or update feature briefs, enrich with project knowledge, or complete a feature.
|
|
5
5
|
Invoked directly via /fledge-brief, not auto-triggered.
|
|
6
6
|
metadata:
|
|
7
7
|
type: workflow
|
|
8
|
-
allowed-tools: Bash(node *scripts/brief.js
|
|
8
|
+
allowed-tools: Bash(node *scripts/brief.js*)
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## Lifecycle overview
|
|
12
|
+
|
|
13
|
+
The brief skill owns two workflow phases: **Brief** (capture what to build) and **Enrich** (connect to project knowledge). See [docs/workflow.md](../../docs/workflow.md) for the full workflow.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Brief → Enrich → [Build] → [Verify] → [Complete]
|
|
17
|
+
└── this skill ──┘ └── build skill (future) ──────────┘
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**States this skill manages:** `draft → ready`
|
|
21
|
+
**States managed by the build skill:** `ready → active → completed`
|
|
22
|
+
|
|
23
|
+
A brief directory contains three files:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
.fledge/briefs/<name>/
|
|
27
|
+
brief.md what, why, scope, design decisions (product-level)
|
|
28
|
+
spec.md data models, APIs, external services (technical context)
|
|
29
|
+
tasks.md tasks (empty until build phase)
|
|
30
|
+
```
|
|
31
|
+
|
|
11
32
|
## Available scripts
|
|
12
33
|
|
|
13
34
|
**Important:** Run all scripts with `node` (e.g. `node scripts/brief.js list`). Always pass `--project-dir` pointing to the project root.
|
|
14
35
|
|
|
15
36
|
- **`node scripts/brief.js create <name> --project-dir <path>`** -- Create a new brief with stub files
|
|
16
|
-
- **`node scripts/brief.js
|
|
37
|
+
- **`node scripts/brief.js ready <name> --project-dir <path>`** -- Transition a brief from draft to ready
|
|
38
|
+
- **`node scripts/brief.js start <name> --project-dir <path>`** -- Transition a brief from ready to active
|
|
17
39
|
- **`node scripts/brief.js complete <name> --project-dir <path>`** -- Transition a brief from active to completed
|
|
40
|
+
- **`node scripts/brief.js cancel <name> --project-dir <path>`** -- Cancel a brief
|
|
18
41
|
- **`node scripts/brief.js status <name> --project-dir <path>`** -- Show status and task progress
|
|
19
42
|
- **`node scripts/brief.js list [--status <status>] --project-dir <path>`** -- List all briefs with progress and summary
|
|
20
43
|
- **`node scripts/brief.js validate <name> --project-dir <path>`** -- Validate brief files against schemas
|
|
@@ -25,8 +48,8 @@ allowed-tools: Bash(node *scripts/brief.js *)
|
|
|
25
48
|
Ask what the user wants to do, or infer from context. Present these options:
|
|
26
49
|
|
|
27
50
|
1. **New brief** -- plan a new feature from scratch. Proceed to Step 1.
|
|
28
|
-
2. **Continue a brief** -- pick up an existing brief. Proceed to Step
|
|
29
|
-
3. **Complete a brief** -- wrap up a finished feature. Proceed to Step
|
|
51
|
+
2. **Continue a brief** -- pick up an existing brief. Proceed to Step 5.
|
|
52
|
+
3. **Complete a brief** -- wrap up a finished feature. Proceed to Step 6.
|
|
30
53
|
|
|
31
54
|
If unclear, run `node scripts/brief.js list --project-dir <path>` to show current briefs and ask.
|
|
32
55
|
|
|
@@ -60,65 +83,70 @@ Write the brief content into `brief.md`. The frontmatter is managed by the scrip
|
|
|
60
83
|
- **Scope**: what is included and what is explicitly excluded
|
|
61
84
|
- **Design decisions**: key choices made during this conversation, with reasoning
|
|
62
85
|
|
|
63
|
-
Keep it concise. The brief is a reference for
|
|
86
|
+
Keep it concise. The brief is a reference for building, not a specification document. If something is obvious from the code, do not repeat it here.
|
|
64
87
|
|
|
65
88
|
Proceed to Step 3.
|
|
66
89
|
|
|
67
90
|
---
|
|
68
91
|
|
|
69
|
-
## Step 3:
|
|
92
|
+
## Step 3: Enrich with project knowledge
|
|
70
93
|
|
|
71
|
-
|
|
94
|
+
Connect the brief to the technical reality of the project. Start by reading `.fledge/project.md` to understand the project landscape. If this file does not exist, suggest running `fledge init` first.
|
|
72
95
|
|
|
73
|
-
|
|
74
|
-
- **Specific enough** that someone (or an agent) can start without further clarification
|
|
75
|
-
- **Grouped** by concern when the feature spans multiple areas (e.g. backend, frontend, data)
|
|
96
|
+
Walk through each section of `project.md` with the brief in mind:
|
|
76
97
|
|
|
77
|
-
|
|
98
|
+
1. **Domain**: which concepts from the glossary does this feature involve? Are there new domain terms that need defining?
|
|
99
|
+
2. **Data models**: which existing models does the feature interact with? Are new models or fields needed?
|
|
100
|
+
3. **APIs**: which existing endpoints are relevant? Are new endpoints needed? Do they follow the documented conventions?
|
|
101
|
+
4. **External services**: does this feature involve any third-party integrations?
|
|
102
|
+
5. **Conventions**: are there project-specific patterns the build should follow?
|
|
103
|
+
6. **Stack**: which technology skills are available to guide the build?
|
|
104
|
+
|
|
105
|
+
Write the relevant findings into `spec.md`. The spec grounds the product-level brief in the project's actual structure. It does **not** include tasks or technology-specific guidance (those come from the build skill and technology skills).
|
|
106
|
+
|
|
107
|
+
**Gap detection**: if you discover aspects of the project that are not reflected in `project.md` (an undocumented endpoint, a model that is missing, a domain concept that should be in the glossary), note these gaps and suggest updates to `project.md`. Keeping project knowledge current is part of enrichment.
|
|
108
|
+
|
|
109
|
+
Proceed to Step 4.
|
|
78
110
|
|
|
79
|
-
```yaml
|
|
80
|
-
---
|
|
81
|
-
tasks:
|
|
82
|
-
- name: <clear, actionable task name>
|
|
83
|
-
group: <area>
|
|
84
|
-
done: false
|
|
85
111
|
---
|
|
86
|
-
```
|
|
87
112
|
|
|
88
|
-
|
|
113
|
+
## Step 4: Mark as ready
|
|
114
|
+
|
|
115
|
+
Run `node scripts/brief.js validate <name> --project-dir <path>` to confirm the brief is valid.
|
|
89
116
|
|
|
90
|
-
|
|
117
|
+
Present the complete brief (`brief.md`) and spec (`spec.md`) to the user for review.
|
|
91
118
|
|
|
92
|
-
|
|
119
|
+
Once approved, run `node scripts/brief.js ready <name> --project-dir <path>` to transition to ready. The brief is now ready for the build skill to pick up.
|
|
93
120
|
|
|
94
121
|
---
|
|
95
122
|
|
|
96
|
-
## Step
|
|
123
|
+
## Step 5: Continue a brief
|
|
97
124
|
|
|
98
125
|
Run `node scripts/brief.js list --project-dir <path>` to show all briefs. If the user does not specify which brief, ask them to pick one.
|
|
99
126
|
|
|
100
|
-
Run `node scripts/brief.js status <name> --project-dir <path>` to show progress. Read the brief and tasks files to understand the full context.
|
|
127
|
+
Run `node scripts/brief.js status <name> --project-dir <path>` to show progress. Read the brief, spec, and tasks files to understand the full context.
|
|
101
128
|
|
|
102
129
|
From here, the user may want to:
|
|
103
|
-
- **Discuss
|
|
104
|
-
- **Update
|
|
105
|
-
- **Revise the brief** -- update scope or design decisions based on what was learned
|
|
130
|
+
- **Discuss the brief** -- talk through scope or design decisions
|
|
131
|
+
- **Update the spec** -- add or revise technical context
|
|
132
|
+
- **Revise the brief** -- update scope or design decisions based on what was learned
|
|
133
|
+
- **Send back to draft** -- if a ready brief needs significant rework, update the status back to draft
|
|
106
134
|
|
|
107
|
-
When updating
|
|
135
|
+
When updating files, run `node scripts/brief.js validate <name> --project-dir <path>` afterward to confirm validity.
|
|
108
136
|
|
|
109
137
|
---
|
|
110
138
|
|
|
111
|
-
## Step
|
|
139
|
+
## Step 6: Complete a brief
|
|
112
140
|
|
|
113
141
|
Run `node scripts/brief.js status <name> --project-dir <path>` to verify all tasks are done.
|
|
114
142
|
|
|
115
143
|
If there are incomplete tasks, ask the user whether to:
|
|
116
|
-
1. Mark remaining tasks as
|
|
117
|
-
2.
|
|
144
|
+
1. Mark remaining tasks as completed (if they were completed outside this conversation)
|
|
145
|
+
2. Mark tasks as skipped (if they are no longer needed)
|
|
118
146
|
3. Continue working on them first
|
|
119
147
|
|
|
120
148
|
Write a summary into the `brief.md` frontmatter `summary` field. The summary should be one to two sentences capturing:
|
|
121
149
|
- What was built
|
|
122
150
|
- Key decisions or patterns established that future features should know about
|
|
123
151
|
|
|
124
|
-
Run `node scripts/brief.js complete <name> --project-dir <path>` to transition to completed. The script validates that all tasks are done and the summary is present.
|
|
152
|
+
Run `node scripts/brief.js complete <name> --project-dir <path>` to transition to completed. The script validates that all tasks are done or skipped and the summary is present.
|