@fledge/workflow 0.5.0 → 0.6.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 +123 -80
- package/package.json +2 -2
- package/skills/brief/SKILL.md +17 -26
package/dist/scripts/brief.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { i as runMain, n as writeFrontmatter, r as defineCommand, t as parseFrontmatter } from "./frontmatter-BUnjSmTA.js";
|
|
3
|
-
import { cwd,
|
|
3
|
+
import { cwd, stdout } from "node:process";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
Object.freeze({ status: "aborted" });
|
|
@@ -3679,90 +3679,98 @@ const tasksFrontmatter = object({ tasks: array(object({
|
|
|
3679
3679
|
//#region ../cli/dist/brief.js
|
|
3680
3680
|
const BRIEFS_DIRECTORY = path.join(".fledge", "briefs");
|
|
3681
3681
|
/**
|
|
3682
|
-
*
|
|
3683
|
-
* Uses the `FLEDGE_PROJECT_DIR` environment variable if set, otherwise falls back to `cwd()`.
|
|
3682
|
+
* Creates a BriefContext from a project directory path or falls back to `cwd()`.
|
|
3684
3683
|
*
|
|
3685
|
-
* @
|
|
3684
|
+
* @param projectDirectory - Optional project root override.
|
|
3685
|
+
* @returns A BriefContext with the resolved project root.
|
|
3686
3686
|
*/
|
|
3687
|
-
function
|
|
3688
|
-
return
|
|
3687
|
+
function createBriefContext(projectDirectory) {
|
|
3688
|
+
return { projectRoot: projectDirectory ? path.resolve(projectDirectory) : cwd() };
|
|
3689
3689
|
}
|
|
3690
3690
|
/**
|
|
3691
|
-
* Returns the absolute path to the briefs directory for the
|
|
3691
|
+
* Returns the absolute path to the briefs directory for the given context.
|
|
3692
3692
|
*
|
|
3693
|
+
* @param context - The brief context.
|
|
3693
3694
|
* @returns The absolute path to `.fledge/briefs/` in the project root.
|
|
3694
3695
|
*/
|
|
3695
|
-
function getBriefsDirectory() {
|
|
3696
|
-
return path.join(
|
|
3696
|
+
function getBriefsDirectory(context) {
|
|
3697
|
+
return path.join(context.projectRoot, BRIEFS_DIRECTORY);
|
|
3697
3698
|
}
|
|
3698
3699
|
/**
|
|
3699
3700
|
* Returns the absolute path to a specific brief's directory.
|
|
3700
3701
|
*
|
|
3702
|
+
* @param context - The brief context.
|
|
3701
3703
|
* @param name - The brief name (used as directory name).
|
|
3702
3704
|
* @returns The absolute path to the brief directory.
|
|
3703
3705
|
*/
|
|
3704
|
-
function getBriefDirectory(name) {
|
|
3705
|
-
return path.join(getBriefsDirectory(), name);
|
|
3706
|
+
function getBriefDirectory(context, name) {
|
|
3707
|
+
return path.join(getBriefsDirectory(context), name);
|
|
3706
3708
|
}
|
|
3707
3709
|
/**
|
|
3708
3710
|
* Returns the absolute path to a brief's `brief.md` file.
|
|
3709
3711
|
*
|
|
3712
|
+
* @param context - The brief context.
|
|
3710
3713
|
* @param name - The brief name.
|
|
3711
3714
|
* @returns The absolute path to `brief.md`.
|
|
3712
3715
|
*/
|
|
3713
|
-
function getBriefFile(name) {
|
|
3714
|
-
return path.join(getBriefDirectory(name), "brief.md");
|
|
3716
|
+
function getBriefFile(context, name) {
|
|
3717
|
+
return path.join(getBriefDirectory(context, name), "brief.md");
|
|
3715
3718
|
}
|
|
3716
3719
|
/**
|
|
3717
3720
|
* Returns the absolute path to a brief's `tasks.md` file.
|
|
3718
3721
|
*
|
|
3722
|
+
* @param context - The brief context.
|
|
3719
3723
|
* @param name - The brief name.
|
|
3720
3724
|
* @returns The absolute path to `tasks.md`.
|
|
3721
3725
|
*/
|
|
3722
|
-
function getTasksFile(name) {
|
|
3723
|
-
return path.join(getBriefDirectory(name), "tasks.md");
|
|
3726
|
+
function getTasksFile(context, name) {
|
|
3727
|
+
return path.join(getBriefDirectory(context, name), "tasks.md");
|
|
3724
3728
|
}
|
|
3725
3729
|
/**
|
|
3726
3730
|
* Checks whether a brief directory exists.
|
|
3727
3731
|
*
|
|
3732
|
+
* @param context - The brief context.
|
|
3728
3733
|
* @param name - The brief name.
|
|
3729
3734
|
* @returns `true` if the brief directory exists.
|
|
3730
3735
|
*/
|
|
3731
|
-
function briefExists(name) {
|
|
3732
|
-
return fs.existsSync(getBriefDirectory(name));
|
|
3736
|
+
function briefExists(context, name) {
|
|
3737
|
+
return fs.existsSync(getBriefDirectory(context, name));
|
|
3733
3738
|
}
|
|
3734
3739
|
/**
|
|
3735
3740
|
* Reads and validates the frontmatter of a brief's `brief.md` file.
|
|
3736
3741
|
*
|
|
3742
|
+
* @param context - The brief context.
|
|
3737
3743
|
* @param name - The brief name.
|
|
3738
3744
|
* @returns The validated brief frontmatter.
|
|
3739
3745
|
* @throws If the file does not exist or frontmatter fails validation.
|
|
3740
3746
|
*/
|
|
3741
|
-
function readBriefFrontmatter(name) {
|
|
3742
|
-
const content = fs.readFileSync(getBriefFile(name), "utf8");
|
|
3747
|
+
function readBriefFrontmatter(context, name) {
|
|
3748
|
+
const content = fs.readFileSync(getBriefFile(context, name), "utf8");
|
|
3743
3749
|
return briefFrontmatter.parse(parseFrontmatter(content));
|
|
3744
3750
|
}
|
|
3745
3751
|
/**
|
|
3746
3752
|
* Reads and validates the frontmatter of a brief's `tasks.md` file.
|
|
3747
3753
|
*
|
|
3754
|
+
* @param context - The brief context.
|
|
3748
3755
|
* @param name - The brief name.
|
|
3749
3756
|
* @returns The validated tasks frontmatter.
|
|
3750
3757
|
* @throws If the file does not exist or frontmatter fails validation.
|
|
3751
3758
|
*/
|
|
3752
|
-
function readTasksFrontmatter(name) {
|
|
3753
|
-
const content = fs.readFileSync(getTasksFile(name), "utf8");
|
|
3759
|
+
function readTasksFrontmatter(context, name) {
|
|
3760
|
+
const content = fs.readFileSync(getTasksFile(context, name), "utf8");
|
|
3754
3761
|
return tasksFrontmatter.parse(parseFrontmatter(content));
|
|
3755
3762
|
}
|
|
3756
3763
|
/**
|
|
3757
3764
|
* Updates the frontmatter of a brief's `brief.md` file while preserving its body content.
|
|
3758
3765
|
*
|
|
3766
|
+
* @param context - The brief context.
|
|
3759
3767
|
* @param name - The brief name.
|
|
3760
3768
|
* @param data - The new frontmatter data to write.
|
|
3761
3769
|
*/
|
|
3762
|
-
function updateBriefFrontmatter(name, data) {
|
|
3763
|
-
const
|
|
3764
|
-
const content = fs.readFileSync(
|
|
3765
|
-
fs.writeFileSync(
|
|
3770
|
+
function updateBriefFrontmatter(context, name, data) {
|
|
3771
|
+
const file = getBriefFile(context, name);
|
|
3772
|
+
const content = fs.readFileSync(file, "utf8");
|
|
3773
|
+
fs.writeFileSync(file, writeFrontmatter(data, content));
|
|
3766
3774
|
}
|
|
3767
3775
|
/**
|
|
3768
3776
|
* Validates that a state transition is allowed and throws a descriptive error if not.
|
|
@@ -3789,37 +3797,52 @@ function formatDate() {
|
|
|
3789
3797
|
/**
|
|
3790
3798
|
* Lists all brief directories in the project.
|
|
3791
3799
|
*
|
|
3800
|
+
* @param context - The brief context.
|
|
3792
3801
|
* @returns An array of brief names (directory names).
|
|
3793
3802
|
*/
|
|
3794
|
-
function listBriefs() {
|
|
3795
|
-
const directory = getBriefsDirectory();
|
|
3803
|
+
function listBriefs(context) {
|
|
3804
|
+
const directory = getBriefsDirectory(context);
|
|
3796
3805
|
if (!fs.existsSync(directory)) return [];
|
|
3797
3806
|
return fs.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
3798
3807
|
}
|
|
3799
3808
|
//#endregion
|
|
3809
|
+
//#region ../cli/dist/commands/brief/shared.js
|
|
3810
|
+
/**
|
|
3811
|
+
* Shared argument definition for the `--project-dir` flag used by all brief subcommands.
|
|
3812
|
+
*/
|
|
3813
|
+
const projectDirectory = {
|
|
3814
|
+
required: false,
|
|
3815
|
+
alias: "project-dir",
|
|
3816
|
+
description: "Project root directory. Overrides cwd() for locating .fledge/briefs/."
|
|
3817
|
+
};
|
|
3818
|
+
//#endregion
|
|
3800
3819
|
//#region ../cli/dist/commands/brief/complete.js
|
|
3801
3820
|
var complete_default = defineCommand({
|
|
3802
3821
|
meta: {
|
|
3803
3822
|
name: "complete",
|
|
3804
3823
|
description: "Transition a brief from active to completed"
|
|
3805
3824
|
},
|
|
3806
|
-
args: {
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3825
|
+
args: {
|
|
3826
|
+
name: {
|
|
3827
|
+
type: "positional",
|
|
3828
|
+
required: true,
|
|
3829
|
+
description: "The name of the brief to complete"
|
|
3830
|
+
},
|
|
3831
|
+
projectDirectory
|
|
3832
|
+
},
|
|
3811
3833
|
run(context) {
|
|
3834
|
+
const ctx = createBriefContext(context.args.projectDirectory);
|
|
3812
3835
|
const { name } = context.args;
|
|
3813
|
-
if (!briefExists(name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(name)}"`);
|
|
3814
|
-
const brief = readBriefFrontmatter(name);
|
|
3836
|
+
if (!briefExists(ctx, name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(ctx, name)}"`);
|
|
3837
|
+
const brief = readBriefFrontmatter(ctx, name);
|
|
3815
3838
|
validateTransition(brief.status, "completed");
|
|
3816
3839
|
if (!brief.summary) throw new Error("Brief must have a summary before completing. Add a \"summary\" field to the brief.md frontmatter.");
|
|
3817
|
-
const incomplete = readTasksFrontmatter(name).tasks.filter((task) => !task.done);
|
|
3840
|
+
const incomplete = readTasksFrontmatter(ctx, name).tasks.filter((task) => !task.done);
|
|
3818
3841
|
if (incomplete.length > 0) {
|
|
3819
3842
|
const names = incomplete.map((task) => ` - ${task.name}`).join("\n");
|
|
3820
3843
|
throw new Error(`Cannot complete brief with ${incomplete.length} incomplete task(s):\n${names}`);
|
|
3821
3844
|
}
|
|
3822
|
-
updateBriefFrontmatter(name, {
|
|
3845
|
+
updateBriefFrontmatter(ctx, name, {
|
|
3823
3846
|
...brief,
|
|
3824
3847
|
status: "completed",
|
|
3825
3848
|
updated: formatDate()
|
|
@@ -3834,24 +3857,28 @@ var create_default = defineCommand({
|
|
|
3834
3857
|
name: "create",
|
|
3835
3858
|
description: "Create a new brief with stub files"
|
|
3836
3859
|
},
|
|
3837
|
-
args: {
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3860
|
+
args: {
|
|
3861
|
+
name: {
|
|
3862
|
+
type: "positional",
|
|
3863
|
+
required: true,
|
|
3864
|
+
description: "The name of the brief to create (used as directory name)"
|
|
3865
|
+
},
|
|
3866
|
+
projectDirectory
|
|
3867
|
+
},
|
|
3842
3868
|
run(context) {
|
|
3869
|
+
const ctx = createBriefContext(context.args.projectDirectory);
|
|
3843
3870
|
const { name } = context.args;
|
|
3844
|
-
if (briefExists(name)) throw new Error(`Brief "${name}" already exists at "${getBriefDirectory(name)}"`);
|
|
3845
|
-
const directory = getBriefDirectory(name);
|
|
3871
|
+
if (briefExists(ctx, name)) throw new Error(`Brief "${name}" already exists at "${getBriefDirectory(ctx, name)}"`);
|
|
3872
|
+
const directory = getBriefDirectory(ctx, name);
|
|
3846
3873
|
fs.mkdirSync(directory, { recursive: true });
|
|
3847
3874
|
const date = formatDate();
|
|
3848
|
-
fs.writeFileSync(getBriefFile(name), writeFrontmatter({
|
|
3875
|
+
fs.writeFileSync(getBriefFile(ctx, name), writeFrontmatter({
|
|
3849
3876
|
name,
|
|
3850
3877
|
status: "draft",
|
|
3851
3878
|
created: date,
|
|
3852
3879
|
updated: date
|
|
3853
3880
|
}));
|
|
3854
|
-
fs.writeFileSync(getTasksFile(name), writeFrontmatter({ tasks: [] }));
|
|
3881
|
+
fs.writeFileSync(getTasksFile(ctx, name), writeFrontmatter({ tasks: [] }));
|
|
3855
3882
|
stdout.write(`Created brief "${name}" at "${directory}"\n`);
|
|
3856
3883
|
}
|
|
3857
3884
|
});
|
|
@@ -3862,20 +3889,24 @@ var list_default = defineCommand({
|
|
|
3862
3889
|
name: "list",
|
|
3863
3890
|
description: "List all briefs with their status and progress"
|
|
3864
3891
|
},
|
|
3865
|
-
args: {
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3892
|
+
args: {
|
|
3893
|
+
status: {
|
|
3894
|
+
type: "string",
|
|
3895
|
+
required: false,
|
|
3896
|
+
description: "Filter by brief status (draft, active, completed)"
|
|
3897
|
+
},
|
|
3898
|
+
projectDirectory
|
|
3899
|
+
},
|
|
3870
3900
|
run(context) {
|
|
3871
|
-
const
|
|
3901
|
+
const ctx = createBriefContext(context.args.projectDirectory);
|
|
3902
|
+
const names = listBriefs(ctx);
|
|
3872
3903
|
if (names.length === 0) {
|
|
3873
3904
|
stdout.write("No briefs found\n");
|
|
3874
3905
|
return;
|
|
3875
3906
|
}
|
|
3876
3907
|
let briefs = names.map((name) => {
|
|
3877
|
-
const brief = readBriefFrontmatter(name);
|
|
3878
|
-
const { tasks } = readTasksFrontmatter(name);
|
|
3908
|
+
const brief = readBriefFrontmatter(ctx, name);
|
|
3909
|
+
const { tasks } = readTasksFrontmatter(ctx, name);
|
|
3879
3910
|
const done = tasks.filter((task) => task.done).length;
|
|
3880
3911
|
return {
|
|
3881
3912
|
...brief,
|
|
@@ -3925,18 +3956,22 @@ var start_default = defineCommand({
|
|
|
3925
3956
|
name: "start",
|
|
3926
3957
|
description: "Transition a brief from draft to active"
|
|
3927
3958
|
},
|
|
3928
|
-
args: {
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3959
|
+
args: {
|
|
3960
|
+
name: {
|
|
3961
|
+
type: "positional",
|
|
3962
|
+
required: true,
|
|
3963
|
+
description: "The name of the brief to start"
|
|
3964
|
+
},
|
|
3965
|
+
projectDirectory
|
|
3966
|
+
},
|
|
3933
3967
|
run(context) {
|
|
3968
|
+
const ctx = createBriefContext(context.args.projectDirectory);
|
|
3934
3969
|
const { name } = context.args;
|
|
3935
|
-
if (!briefExists(name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(name)}"`);
|
|
3936
|
-
const brief = readBriefFrontmatter(name);
|
|
3970
|
+
if (!briefExists(ctx, name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(ctx, name)}"`);
|
|
3971
|
+
const brief = readBriefFrontmatter(ctx, name);
|
|
3937
3972
|
validateTransition(brief.status, "active");
|
|
3938
|
-
if (readTasksFrontmatter(name).tasks.length === 0) throw new Error("Brief must have at least one task before starting");
|
|
3939
|
-
updateBriefFrontmatter(name, {
|
|
3973
|
+
if (readTasksFrontmatter(ctx, name).tasks.length === 0) throw new Error("Brief must have at least one task before starting");
|
|
3974
|
+
updateBriefFrontmatter(ctx, name, {
|
|
3940
3975
|
...brief,
|
|
3941
3976
|
status: "active",
|
|
3942
3977
|
updated: formatDate()
|
|
@@ -3989,24 +4024,28 @@ runMain(defineCommand({
|
|
|
3989
4024
|
name: "status",
|
|
3990
4025
|
description: "Show the status and task progress of a brief"
|
|
3991
4026
|
},
|
|
3992
|
-
args: {
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
4027
|
+
args: {
|
|
4028
|
+
name: {
|
|
4029
|
+
type: "positional",
|
|
4030
|
+
required: true,
|
|
4031
|
+
description: "The name of the brief to show status for"
|
|
4032
|
+
},
|
|
4033
|
+
projectDirectory
|
|
4034
|
+
},
|
|
3997
4035
|
run(context) {
|
|
4036
|
+
const ctx = createBriefContext(context.args.projectDirectory);
|
|
3998
4037
|
const { name } = context.args;
|
|
3999
|
-
if (!briefExists(name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(name)}"`);
|
|
4000
|
-
const brief = readBriefFrontmatter(name);
|
|
4001
|
-
const { tasks } = readTasksFrontmatter(name);
|
|
4038
|
+
if (!briefExists(ctx, name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(ctx, name)}"`);
|
|
4039
|
+
const brief = readBriefFrontmatter(ctx, name);
|
|
4040
|
+
const { tasks } = readTasksFrontmatter(ctx, name);
|
|
4002
4041
|
const done = tasks.filter((task) => task.done).length;
|
|
4003
4042
|
const lines = [];
|
|
4004
4043
|
lines.push(`${name} [${brief.status}] ${done}/${tasks.length} tasks done`);
|
|
4005
4044
|
lines.push("");
|
|
4006
4045
|
const groups = groupTasks(tasks);
|
|
4007
|
-
for (const [group,
|
|
4046
|
+
for (const [group, groupedTasks] of groups) {
|
|
4008
4047
|
if (group) lines.push(group);
|
|
4009
|
-
for (const task of
|
|
4048
|
+
for (const task of groupedTasks) lines.push(formatTask(task));
|
|
4010
4049
|
}
|
|
4011
4050
|
stdout.write(`${lines.join("\n")}\n`);
|
|
4012
4051
|
}
|
|
@@ -4016,19 +4055,23 @@ runMain(defineCommand({
|
|
|
4016
4055
|
name: "validate",
|
|
4017
4056
|
description: "Validate brief and tasks files against their schemas"
|
|
4018
4057
|
},
|
|
4019
|
-
args: {
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4058
|
+
args: {
|
|
4059
|
+
name: {
|
|
4060
|
+
type: "positional",
|
|
4061
|
+
required: true,
|
|
4062
|
+
description: "The name of the brief to validate"
|
|
4063
|
+
},
|
|
4064
|
+
projectDirectory
|
|
4065
|
+
},
|
|
4024
4066
|
run(context) {
|
|
4067
|
+
const ctx = createBriefContext(context.args.projectDirectory);
|
|
4025
4068
|
const { name } = context.args;
|
|
4026
|
-
if (!briefExists(name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(name)}"`);
|
|
4069
|
+
if (!briefExists(ctx, name)) throw new Error(`Brief "${name}" does not exist at "${getBriefDirectory(ctx, name)}"`);
|
|
4027
4070
|
const errors = [];
|
|
4028
|
-
const briefContent = fs.readFileSync(getBriefFile(name), "utf8");
|
|
4071
|
+
const briefContent = fs.readFileSync(getBriefFile(ctx, name), "utf8");
|
|
4029
4072
|
const briefResult = briefFrontmatter.safeParse(parseFrontmatter(briefContent));
|
|
4030
4073
|
if (!briefResult.success) for (const issue of briefResult.error.issues) errors.push(`brief.md: ${issue.path.join(".")}: ${issue.message}`);
|
|
4031
|
-
const tasksContent = fs.readFileSync(getTasksFile(name), "utf8");
|
|
4074
|
+
const tasksContent = fs.readFileSync(getTasksFile(ctx, name), "utf8");
|
|
4032
4075
|
const tasksResult = tasksFrontmatter.safeParse(parseFrontmatter(tasksContent));
|
|
4033
4076
|
if (!tasksResult.success) for (const issue of tasksResult.error.issues) errors.push(`tasks.md: ${issue.path.join(".")}: ${issue.message}`);
|
|
4034
4077
|
if (errors.length > 0) throw new Error(`Validation failed for "${name}":\n${errors.map((error) => ` - ${error}`).join("\n")}`);
|
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.6.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.9.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@antfu/eslint-config": "7.7.3",
|
package/skills/brief/SKILL.md
CHANGED
|
@@ -5,28 +5,19 @@ description: >-
|
|
|
5
5
|
Invoked directly via /fledge-brief, not auto-triggered.
|
|
6
6
|
metadata:
|
|
7
7
|
type: workflow
|
|
8
|
+
allowed-tools: Bash(scripts/brief.js *), Bash(node *)
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
## Setup
|
|
11
|
-
|
|
12
|
-
Before running any script, set the `FLEDGE_PROJECT_DIR` environment variable to the project root so scripts know where to find and create briefs:
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
export FLEDGE_PROJECT_DIR="$(pwd)"
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Set this once at the start of the conversation. All scripts below assume it is set.
|
|
19
|
-
|
|
20
11
|
## Available scripts
|
|
21
12
|
|
|
22
|
-
|
|
13
|
+
**Important:** All scripts have a shebang and are executable. Run them directly (e.g. `scripts/brief.js list`), never prefix with `node`. Always pass `--project-dir` pointing to the project root.
|
|
23
14
|
|
|
24
|
-
- **`scripts/brief.js create <name>`** -- Create a new brief with stub files
|
|
25
|
-
- **`scripts/brief.js start <name>`** -- Transition a brief from draft to active
|
|
26
|
-
- **`scripts/brief.js complete <name>`** -- Transition a brief from active to completed
|
|
27
|
-
- **`scripts/brief.js status <name>`** -- Show status and task progress
|
|
28
|
-
- **`scripts/brief.js list [--status <status>]
|
|
29
|
-
- **`scripts/brief.js validate <name>`** -- Validate brief files against schemas
|
|
15
|
+
- **`scripts/brief.js create <name> --project-dir <path>`** -- Create a new brief with stub files
|
|
16
|
+
- **`scripts/brief.js start <name> --project-dir <path>`** -- Transition a brief from draft to active
|
|
17
|
+
- **`scripts/brief.js complete <name> --project-dir <path>`** -- Transition a brief from active to completed
|
|
18
|
+
- **`scripts/brief.js status <name> --project-dir <path>`** -- Show status and task progress
|
|
19
|
+
- **`scripts/brief.js list [--status <status>] --project-dir <path>`** -- List all briefs with progress and summary
|
|
20
|
+
- **`scripts/brief.js validate <name> --project-dir <path>`** -- Validate brief files against schemas
|
|
30
21
|
- **`scripts/brief.js schema`** -- Output JSON Schema for brief and tasks frontmatter
|
|
31
22
|
|
|
32
23
|
## Step 0: Determine intent
|
|
@@ -37,7 +28,7 @@ Ask what the user wants to do, or infer from context. Present these options:
|
|
|
37
28
|
2. **Continue a brief** -- pick up an existing brief. Proceed to Step 4.
|
|
38
29
|
3. **Complete a brief** -- wrap up a finished feature. Proceed to Step 5.
|
|
39
30
|
|
|
40
|
-
If unclear, run `scripts/brief.js list
|
|
31
|
+
If unclear, run `scripts/brief.js list --project-dir <path>` to show current briefs and ask.
|
|
41
32
|
|
|
42
33
|
---
|
|
43
34
|
|
|
@@ -45,7 +36,7 @@ If unclear, run `scripts/brief.js list` to show current briefs and ask.
|
|
|
45
36
|
|
|
46
37
|
Before writing anything, build an understanding of what exists.
|
|
47
38
|
|
|
48
|
-
1. Run `scripts/brief.js list --status completed
|
|
39
|
+
1. Run `scripts/brief.js list --status completed --project-dir <path>` to read summaries of completed features. Note anything relevant to the new feature.
|
|
49
40
|
2. Ask the user what they want to build. Keep it conversational, not a form. Aim to understand:
|
|
50
41
|
- What is the user-facing change?
|
|
51
42
|
- Why does it matter?
|
|
@@ -60,7 +51,7 @@ Proceed to Step 2.
|
|
|
60
51
|
|
|
61
52
|
## Step 2: Draft the brief
|
|
62
53
|
|
|
63
|
-
Run `scripts/brief.js create <name>` to create the brief directory.
|
|
54
|
+
Run `scripts/brief.js create <name> --project-dir <path>` to create the brief directory.
|
|
64
55
|
|
|
65
56
|
Write the brief content into `brief.md`. The frontmatter is managed by the scripts. The markdown body should capture:
|
|
66
57
|
|
|
@@ -96,7 +87,7 @@ tasks:
|
|
|
96
87
|
|
|
97
88
|
Order tasks by dependency: tasks that others depend on come first within their group.
|
|
98
89
|
|
|
99
|
-
After writing tasks, run `scripts/brief.js validate <name>` to confirm the brief is valid, then run `scripts/brief.js start <name>` to transition to active.
|
|
90
|
+
After writing tasks, run `scripts/brief.js validate <name> --project-dir <path>` to confirm the brief is valid, then run `scripts/brief.js start <name> --project-dir <path>` to transition to active.
|
|
100
91
|
|
|
101
92
|
Present the complete brief and task list to the user for review before starting.
|
|
102
93
|
|
|
@@ -104,22 +95,22 @@ Present the complete brief and task list to the user for review before starting.
|
|
|
104
95
|
|
|
105
96
|
## Step 4: Continue a brief
|
|
106
97
|
|
|
107
|
-
Run `scripts/brief.js list
|
|
98
|
+
Run `scripts/brief.js list --project-dir <path>` to show all briefs. If the user does not specify which brief, ask them to pick one.
|
|
108
99
|
|
|
109
|
-
Run `scripts/brief.js status <name>` to show progress. Read the brief and tasks files to understand the full context.
|
|
100
|
+
Run `scripts/brief.js status <name> --project-dir <path>` to show progress. Read the brief and tasks files to understand the full context.
|
|
110
101
|
|
|
111
102
|
From here, the user may want to:
|
|
112
103
|
- **Discuss a task** -- talk through approach before implementing
|
|
113
104
|
- **Update tasks** -- mark tasks as done, add new tasks, reorder
|
|
114
105
|
- **Revise the brief** -- update scope or design decisions based on what was learned during implementation
|
|
115
106
|
|
|
116
|
-
When updating task status, modify the `tasks.md` frontmatter directly, then run `scripts/brief.js status <name>` to confirm the update.
|
|
107
|
+
When updating task status, modify the `tasks.md` frontmatter directly, then run `scripts/brief.js status <name> --project-dir <path>` to confirm the update.
|
|
117
108
|
|
|
118
109
|
---
|
|
119
110
|
|
|
120
111
|
## Step 5: Complete a brief
|
|
121
112
|
|
|
122
|
-
Run `scripts/brief.js status <name>` to verify all tasks are done.
|
|
113
|
+
Run `scripts/brief.js status <name> --project-dir <path>` to verify all tasks are done.
|
|
123
114
|
|
|
124
115
|
If there are incomplete tasks, ask the user whether to:
|
|
125
116
|
1. Mark remaining tasks as done (if they were completed outside this conversation)
|
|
@@ -130,4 +121,4 @@ Write a summary into the `brief.md` frontmatter `summary` field. The summary sho
|
|
|
130
121
|
- What was built
|
|
131
122
|
- Key decisions or patterns established that future features should know about
|
|
132
123
|
|
|
133
|
-
Run `scripts/brief.js complete <name>` to transition to completed. The script validates that all tasks are done and the summary is present.
|
|
124
|
+
Run `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.
|