@braingrid/cli 0.2.55 → 0.2.56
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/CHANGELOG.md +8 -0
- package/README.md +7 -12
- package/dist/cli.js +37 -331
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.56] - 2026-03-03
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **`/build` converted from command to skill** — moved `.claude/commands/build.md` to `.claude/skills/build/skill.md` with skill-style frontmatter; setup handler now auto-detects and removes the deprecated command file on install, init, and update paths
|
|
15
|
+
- **Removed `braingrid requirement build` and `braingrid requirement breakdown` CLI commands** — build workflow now handled entirely by the `/build` skill; `/specify` and `/save-requirement` updated to reference the skill
|
|
16
|
+
- **Updated E2E and unit tests** — setup tests now expect 5 skill directories and validate deprecated file cleanup
|
|
17
|
+
|
|
10
18
|
## [0.2.55] - 2026-02-26
|
|
11
19
|
|
|
12
20
|
### Fixed
|
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ npm install -g @braingrid/cli
|
|
|
46
46
|
braingrid setup claude-code
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Installs slash commands (`/specify`, `/
|
|
49
|
+
Installs slash commands (`/specify`, `/build`), BrainGrid skill, and status line showing your project/requirement/task context in real-time.
|
|
50
50
|
|
|
51
51
|
[→ Full Claude Code setup guide](./.braingrid/README.md)
|
|
52
52
|
|
|
@@ -71,11 +71,8 @@ braingrid init
|
|
|
71
71
|
# 2. Create a requirement with AI refinement
|
|
72
72
|
braingrid specify --prompt "Add user authentication"
|
|
73
73
|
|
|
74
|
-
# 3.
|
|
75
|
-
braingrid requirement
|
|
76
|
-
|
|
77
|
-
# 4. Build requirement with all tasks (markdown with full content)
|
|
78
|
-
braingrid requirement build REQ-1
|
|
74
|
+
# 3. Show requirement with all tasks (markdown with full content)
|
|
75
|
+
braingrid requirement show REQ-1 --format markdown
|
|
79
76
|
```
|
|
80
77
|
|
|
81
78
|
---
|
|
@@ -145,15 +142,15 @@ braingrid setup cursor --dry-run
|
|
|
145
142
|
**What gets installed:**
|
|
146
143
|
|
|
147
144
|
- **Claude Code integration:**
|
|
148
|
-
- Commands in `.claude/commands/` (specify,
|
|
149
|
-
- Skills in `.claude/skills
|
|
145
|
+
- Commands in `.claude/commands/` (specify, save-requirement)
|
|
146
|
+
- Skills in `.claude/skills/` (braingrid-cli, build, frontend-design, ux)
|
|
150
147
|
- Status line script at `.claude/statusline.sh`
|
|
151
148
|
- Hook script at `.claude/hooks/sync-braingrid-task.sh` for task status sync
|
|
152
149
|
- Settings in `.claude/settings.json` (configures status line and hooks)
|
|
153
150
|
- Content injected into `CLAUDE.md` (or creates it if it doesn't exist)
|
|
154
151
|
|
|
155
152
|
- **Cursor integration:**
|
|
156
|
-
- Commands in `.cursor/commands/` (specify,
|
|
153
|
+
- Commands in `.cursor/commands/` (specify, build, save-requirement)
|
|
157
154
|
- Rules in `.cursor/rules/`
|
|
158
155
|
- Content injected into `AGENTS.md` (or creates it if it doesn't exist)
|
|
159
156
|
|
|
@@ -216,8 +213,6 @@ braingrid requirement create --name "Name" [--content "Description"] [--assigned
|
|
|
216
213
|
braingrid requirement show [id]
|
|
217
214
|
braingrid requirement update [id] [--status IDEA|PLANNED|IN_PROGRESS|REVIEW|COMPLETED|CANCELLED] [--name "New Name"] [--content "markdown"]
|
|
218
215
|
braingrid requirement delete [id] [--force]
|
|
219
|
-
braingrid requirement breakdown [id] [--format markdown|json|xml]
|
|
220
|
-
braingrid requirement build [id] [--format markdown|json|xml]
|
|
221
216
|
braingrid requirement create-branch [id] [--name <branch-name>] [--base <branch>]
|
|
222
217
|
braingrid requirement review [id] [--pr <number>]
|
|
223
218
|
|
|
@@ -335,7 +330,7 @@ eval "$(braingrid completion zsh)"
|
|
|
335
330
|
### What Gets Completed
|
|
336
331
|
|
|
337
332
|
- **Commands**: `login`, `logout`, `project`, `requirement`, `task`, etc.
|
|
338
|
-
- **Subcommands**: `list`, `show`, `create`, `update`, `delete`, `
|
|
333
|
+
- **Subcommands**: `list`, `show`, `create`, `update`, `delete`, `create-branch`, `review`, `summary`, `specify`, `tag`
|
|
339
334
|
- **Options**: `--help`, `--format`, `--status`, `--project`, `--requirement`
|
|
340
335
|
- **Values**: Status values (`IDEA`, `PLANNED`, `IN_PROGRESS`, etc.), format options (`table`, `json`, `xml`, `markdown`)
|
|
341
336
|
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
|
|
16
16
|
// src/cli.ts
|
|
17
17
|
import { existsSync } from "fs";
|
|
18
|
-
import { resolve } from "path";
|
|
18
|
+
import { resolve as resolve2 } from "path";
|
|
19
19
|
import { fileURLToPath } from "url";
|
|
20
20
|
import { config } from "dotenv";
|
|
21
21
|
import { createRequire } from "module";
|
|
@@ -63,8 +63,6 @@ function createCompletion() {
|
|
|
63
63
|
});
|
|
64
64
|
completion.on("requirement", ({ reply }) => {
|
|
65
65
|
reply([
|
|
66
|
-
"breakdown",
|
|
67
|
-
"build",
|
|
68
66
|
"create",
|
|
69
67
|
"create-branch",
|
|
70
68
|
"delete",
|
|
@@ -155,7 +153,7 @@ var DEFAULT_OPTIONS = {
|
|
|
155
153
|
// No-op by default
|
|
156
154
|
};
|
|
157
155
|
async function sleep(ms) {
|
|
158
|
-
return new Promise((
|
|
156
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
159
157
|
}
|
|
160
158
|
function calculateDelay(attempt, initialDelay, maxDelay, backoffMultiplier) {
|
|
161
159
|
const exponentialDelay = initialDelay * backoffMultiplier ** (attempt - 1);
|
|
@@ -228,7 +226,7 @@ async function axiosWithRetry(config2, options) {
|
|
|
228
226
|
|
|
229
227
|
// src/build-config.ts
|
|
230
228
|
var BUILD_ENV = true ? "production" : process.env.NODE_ENV === "test" ? "development" : "production";
|
|
231
|
-
var CLI_VERSION = true ? "0.2.
|
|
229
|
+
var CLI_VERSION = true ? "0.2.56" : "0.0.0-test";
|
|
232
230
|
var PRODUCTION_CONFIG = {
|
|
233
231
|
apiUrl: "https://app.braingrid.ai",
|
|
234
232
|
workosAuthUrl: "https://auth.braingrid.ai",
|
|
@@ -619,7 +617,7 @@ var OAuth2Handler = class {
|
|
|
619
617
|
* Start local HTTP server to handle OAuth callback
|
|
620
618
|
*/
|
|
621
619
|
async startCallbackServer(port) {
|
|
622
|
-
return new Promise((
|
|
620
|
+
return new Promise((resolve3) => {
|
|
623
621
|
this.server = createServer((req, res) => {
|
|
624
622
|
const url = new URL(req.url || "", `http://127.0.0.1:${port}`);
|
|
625
623
|
if (url.pathname === "/callback") {
|
|
@@ -722,7 +720,7 @@ var OAuth2Handler = class {
|
|
|
722
720
|
});
|
|
723
721
|
this.server.listen(port, "0.0.0.0", () => {
|
|
724
722
|
logger.debug(`Callback server listening on http://127.0.0.1:${port}`);
|
|
725
|
-
|
|
723
|
+
resolve3();
|
|
726
724
|
});
|
|
727
725
|
});
|
|
728
726
|
}
|
|
@@ -787,7 +785,7 @@ var OAuth2Handler = class {
|
|
|
787
785
|
* Wait for the authorization code from the callback
|
|
788
786
|
*/
|
|
789
787
|
async waitForAuthorizationCode(timeoutMs = 3e5) {
|
|
790
|
-
return new Promise((
|
|
788
|
+
return new Promise((resolve3, reject) => {
|
|
791
789
|
const startTime = Date.now();
|
|
792
790
|
const checkInterval = setInterval(() => {
|
|
793
791
|
if (this.abortController?.signal.aborted) {
|
|
@@ -802,7 +800,7 @@ var OAuth2Handler = class {
|
|
|
802
800
|
}
|
|
803
801
|
if (this.authorizationCode) {
|
|
804
802
|
clearInterval(checkInterval);
|
|
805
|
-
|
|
803
|
+
resolve3(this.authorizationCode);
|
|
806
804
|
return;
|
|
807
805
|
}
|
|
808
806
|
if (Date.now() - startTime > timeoutMs) {
|
|
@@ -2706,19 +2704,19 @@ function closeLogger() {
|
|
|
2706
2704
|
logFile = null;
|
|
2707
2705
|
}
|
|
2708
2706
|
function flushLogger() {
|
|
2709
|
-
return new Promise((
|
|
2707
|
+
return new Promise((resolve3) => {
|
|
2710
2708
|
if (!logStream) {
|
|
2711
|
-
|
|
2709
|
+
resolve3();
|
|
2712
2710
|
return;
|
|
2713
2711
|
}
|
|
2714
2712
|
if (logStream.writableLength === 0) {
|
|
2715
|
-
|
|
2713
|
+
resolve3();
|
|
2716
2714
|
return;
|
|
2717
2715
|
}
|
|
2718
|
-
const timeout = setTimeout(
|
|
2716
|
+
const timeout = setTimeout(resolve3, 1e3);
|
|
2719
2717
|
logStream.once("drain", () => {
|
|
2720
2718
|
clearTimeout(timeout);
|
|
2721
|
-
|
|
2719
|
+
resolve3();
|
|
2722
2720
|
});
|
|
2723
2721
|
});
|
|
2724
2722
|
}
|
|
@@ -2782,7 +2780,7 @@ async function withRetry(fn, retries = MAX_RETRIES, delay = INITIAL_RETRY_DELAY)
|
|
|
2782
2780
|
throw error;
|
|
2783
2781
|
}
|
|
2784
2782
|
log("WARN", "github-api", "retry", `attempt=${MAX_RETRIES - retries + 1} delay=${delay}ms`);
|
|
2785
|
-
await new Promise((
|
|
2783
|
+
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
2786
2784
|
return withRetry(fn, retries - 1, delay * 2);
|
|
2787
2785
|
}
|
|
2788
2786
|
}
|
|
@@ -3670,7 +3668,7 @@ async function waitWithSpinner(message, checkFn, intervalMs = 3e3, maxAttempts =
|
|
|
3670
3668
|
return true;
|
|
3671
3669
|
}
|
|
3672
3670
|
if (attempt < maxAttempts - 1) {
|
|
3673
|
-
await new Promise((
|
|
3671
|
+
await new Promise((resolve3) => setTimeout(resolve3, intervalMs));
|
|
3674
3672
|
}
|
|
3675
3673
|
}
|
|
3676
3674
|
if (spinnerInterval) clearInterval(spinnerInterval);
|
|
@@ -3976,6 +3974,18 @@ async function _handleSetup(config2, opts) {
|
|
|
3976
3974
|
if (readmeCopied) {
|
|
3977
3975
|
log("INFO", "setup", "readme", "path=.braingrid/README.md");
|
|
3978
3976
|
}
|
|
3977
|
+
if (config2.deprecatedFiles?.length) {
|
|
3978
|
+
for (const file of config2.deprecatedFiles) {
|
|
3979
|
+
try {
|
|
3980
|
+
if (await fileExists(file)) {
|
|
3981
|
+
await fs5.unlink(path7.resolve(file));
|
|
3982
|
+
log("INFO", "setup", "cleanup", `removed=${file}`);
|
|
3983
|
+
console.log(chalk10.dim(` Removed deprecated: ${file}`));
|
|
3984
|
+
}
|
|
3985
|
+
} catch (_error) {
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3979
3989
|
return {
|
|
3980
3990
|
success: true,
|
|
3981
3991
|
data: {
|
|
@@ -4008,12 +4018,14 @@ async function handleSetupClaudeCode(opts) {
|
|
|
4008
4018
|
sourceDirs: [
|
|
4009
4019
|
"claude-code/commands",
|
|
4010
4020
|
"claude-code/skills/braingrid-cli",
|
|
4021
|
+
"claude-code/skills/build",
|
|
4011
4022
|
"claude-code/skills/frontend-design",
|
|
4012
4023
|
"claude-code/skills/ux"
|
|
4013
4024
|
],
|
|
4014
4025
|
targetDirs: [
|
|
4015
4026
|
".claude/commands",
|
|
4016
4027
|
".claude/skills/braingrid-cli",
|
|
4028
|
+
".claude/skills/build",
|
|
4017
4029
|
".claude/skills/frontend-design",
|
|
4018
4030
|
".claude/skills/ux"
|
|
4019
4031
|
],
|
|
@@ -4021,13 +4033,15 @@ async function handleSetupClaudeCode(opts) {
|
|
|
4021
4033
|
{ label: "Commands", countMode: "files" },
|
|
4022
4034
|
{ label: "Skills", countMode: "single" },
|
|
4023
4035
|
{ label: "Skills", countMode: "single" },
|
|
4036
|
+
{ label: "Skills", countMode: "single" },
|
|
4024
4037
|
{ label: "Skills", countMode: "single" }
|
|
4025
4038
|
],
|
|
4026
4039
|
injection: {
|
|
4027
4040
|
sourceFile: "claude-code/CLAUDE.md",
|
|
4028
4041
|
targetFile: "CLAUDE.md"
|
|
4029
4042
|
},
|
|
4030
|
-
docsUrl: "https://docs.braingrid.ai/claude-code"
|
|
4043
|
+
docsUrl: "https://docs.braingrid.ai/claude-code",
|
|
4044
|
+
deprecatedFiles: [".claude/commands/build.md"]
|
|
4031
4045
|
};
|
|
4032
4046
|
try {
|
|
4033
4047
|
const setupResult = await _handleSetup(config2, opts);
|
|
@@ -5749,84 +5763,6 @@ function formatRequirementListXml(requirements, pagination) {
|
|
|
5749
5763
|
xml += "</requirements>\n";
|
|
5750
5764
|
return xml;
|
|
5751
5765
|
}
|
|
5752
|
-
function formatRequirementBreakdownMarkdown(requirementShortId, tasks) {
|
|
5753
|
-
let md = `# Breakdown: ${requirementShortId}
|
|
5754
|
-
|
|
5755
|
-
`;
|
|
5756
|
-
md += `Generated ${tasks.length} task${tasks.length !== 1 ? "s" : ""}
|
|
5757
|
-
|
|
5758
|
-
`;
|
|
5759
|
-
if (tasks.length === 0) {
|
|
5760
|
-
md += "_No tasks generated._\n";
|
|
5761
|
-
return md;
|
|
5762
|
-
}
|
|
5763
|
-
for (const task2 of tasks) {
|
|
5764
|
-
md += `## Task ${task2.number}: ${task2.title}
|
|
5765
|
-
|
|
5766
|
-
`;
|
|
5767
|
-
md += `- **Status:** ${task2.status}
|
|
5768
|
-
`;
|
|
5769
|
-
md += `- **ID:** ${task2.id}
|
|
5770
|
-
`;
|
|
5771
|
-
if (task2.blocked_by.length > 0) {
|
|
5772
|
-
md += `- **Blocked by:** ${task2.blocked_by.join(", ")}
|
|
5773
|
-
`;
|
|
5774
|
-
}
|
|
5775
|
-
if (task2.content) {
|
|
5776
|
-
md += `
|
|
5777
|
-
### Content
|
|
5778
|
-
|
|
5779
|
-
${task2.content}
|
|
5780
|
-
`;
|
|
5781
|
-
}
|
|
5782
|
-
md += "\n";
|
|
5783
|
-
}
|
|
5784
|
-
return md;
|
|
5785
|
-
}
|
|
5786
|
-
function formatRequirementBreakdownXml(requirementShortId, tasks) {
|
|
5787
|
-
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
5788
|
-
xml += "<breakdown>\n";
|
|
5789
|
-
xml += ` <requirement_short_id>${escapeXml(requirementShortId)}</requirement_short_id>
|
|
5790
|
-
`;
|
|
5791
|
-
xml += ` <task_count>${tasks.length}</task_count>
|
|
5792
|
-
`;
|
|
5793
|
-
xml += " <tasks>\n";
|
|
5794
|
-
for (const task2 of tasks) {
|
|
5795
|
-
xml += " <task>\n";
|
|
5796
|
-
xml += ` <number>${escapeXml(task2.number)}</number>
|
|
5797
|
-
`;
|
|
5798
|
-
xml += ` <title>${escapeXml(task2.title)}</title>
|
|
5799
|
-
`;
|
|
5800
|
-
xml += ` <status>${escapeXml(task2.status)}</status>
|
|
5801
|
-
`;
|
|
5802
|
-
xml += ` <id>${escapeXml(task2.id)}</id>
|
|
5803
|
-
`;
|
|
5804
|
-
if (task2.content) {
|
|
5805
|
-
xml += ` <content>${escapeXml(task2.content)}</content>
|
|
5806
|
-
`;
|
|
5807
|
-
}
|
|
5808
|
-
if (task2.blocked_by.length > 0) {
|
|
5809
|
-
xml += " <blocked_by>\n";
|
|
5810
|
-
for (const blocker of task2.blocked_by) {
|
|
5811
|
-
xml += ` <task>${escapeXml(blocker)}</task>
|
|
5812
|
-
`;
|
|
5813
|
-
}
|
|
5814
|
-
xml += " </blocked_by>\n";
|
|
5815
|
-
}
|
|
5816
|
-
if (task2.assigned_to) {
|
|
5817
|
-
xml += ` <assigned_to>${escapeXml(task2.assigned_to)}</assigned_to>
|
|
5818
|
-
`;
|
|
5819
|
-
}
|
|
5820
|
-
xml += ` <created_at>${escapeXml(task2.created_at)}</created_at>
|
|
5821
|
-
`;
|
|
5822
|
-
xml += ` <updated_at>${escapeXml(task2.updated_at)}</updated_at>
|
|
5823
|
-
`;
|
|
5824
|
-
xml += " </task>\n";
|
|
5825
|
-
}
|
|
5826
|
-
xml += " </tasks>\n";
|
|
5827
|
-
xml += "</breakdown>\n";
|
|
5828
|
-
return xml;
|
|
5829
|
-
}
|
|
5830
5766
|
function formatRequirementBuildMarkdown(requirement2, options) {
|
|
5831
5767
|
const statusEmoji = getRequirementStatusEmoji(requirement2.status);
|
|
5832
5768
|
let md = `# ${statusEmoji} ${requirement2.name}
|
|
@@ -5897,7 +5833,7 @@ ${requirement2.content}
|
|
|
5897
5833
|
`;
|
|
5898
5834
|
if (tasks.length === 0) {
|
|
5899
5835
|
md += "_No tasks available_\n\n";
|
|
5900
|
-
md += "\u{1F4A1}
|
|
5836
|
+
md += "\u{1F4A1} _No tasks have been created yet for this requirement._\n";
|
|
5901
5837
|
} else {
|
|
5902
5838
|
for (let i = 0; i < tasks.length; i++) {
|
|
5903
5839
|
const task2 = tasks[i];
|
|
@@ -5968,20 +5904,6 @@ ${task2.content}
|
|
|
5968
5904
|
}
|
|
5969
5905
|
return md;
|
|
5970
5906
|
}
|
|
5971
|
-
function formatRequirementBuildJson(requirement2) {
|
|
5972
|
-
const tasks = requirement2.tasks || [];
|
|
5973
|
-
if (tasks.length === 0) {
|
|
5974
|
-
return JSON.stringify(
|
|
5975
|
-
{
|
|
5976
|
-
...requirement2,
|
|
5977
|
-
_hint: "Run 'braingrid requirement breakdown [id]' to generate tasks using AI"
|
|
5978
|
-
},
|
|
5979
|
-
null,
|
|
5980
|
-
2
|
|
5981
|
-
);
|
|
5982
|
-
}
|
|
5983
|
-
return JSON.stringify(requirement2, null, 2);
|
|
5984
|
-
}
|
|
5985
5907
|
function formatRequirementBuildXml(requirement2) {
|
|
5986
5908
|
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
5987
5909
|
xml += "<requirement>\n";
|
|
@@ -6036,7 +5958,7 @@ function formatRequirementBuildXml(requirement2) {
|
|
|
6036
5958
|
xml += ` <tasks count="${tasks.length}">
|
|
6037
5959
|
`;
|
|
6038
5960
|
if (tasks.length === 0) {
|
|
6039
|
-
xml += ` <hint>
|
|
5961
|
+
xml += ` <hint>No tasks have been created yet for this requirement.</hint>
|
|
6040
5962
|
`;
|
|
6041
5963
|
} else {
|
|
6042
5964
|
for (const task2 of tasks) {
|
|
@@ -6887,12 +6809,6 @@ var RequirementService = class {
|
|
|
6887
6809
|
const headers = this.getHeaders();
|
|
6888
6810
|
await this.axios.delete(url, { headers });
|
|
6889
6811
|
}
|
|
6890
|
-
async breakdownRequirement(projectId, requirementId, data) {
|
|
6891
|
-
const url = `${this.baseUrl}/api/v1/projects/${projectId}/requirements/${requirementId}/breakdown`;
|
|
6892
|
-
const headers = this.getHeaders();
|
|
6893
|
-
const response = await this.axios.post(url, data, { headers });
|
|
6894
|
-
return response.data;
|
|
6895
|
-
}
|
|
6896
6812
|
async createGitBranch(projectId, requirementId, data) {
|
|
6897
6813
|
const url = `${this.baseUrl}/api/v1/projects/${projectId}/requirements/${requirementId}/create-git-branch`;
|
|
6898
6814
|
const headers = this.getHeaders();
|
|
@@ -6906,12 +6822,12 @@ var RequirementService = class {
|
|
|
6906
6822
|
headers,
|
|
6907
6823
|
responseType: "stream"
|
|
6908
6824
|
});
|
|
6909
|
-
return new Promise((
|
|
6825
|
+
return new Promise((resolve3, reject) => {
|
|
6910
6826
|
response.data.on("data", (chunk) => {
|
|
6911
6827
|
onChunk(chunk.toString());
|
|
6912
6828
|
});
|
|
6913
6829
|
response.data.on("end", () => {
|
|
6914
|
-
|
|
6830
|
+
resolve3();
|
|
6915
6831
|
});
|
|
6916
6832
|
response.data.on("error", (error) => {
|
|
6917
6833
|
reject(error);
|
|
@@ -7497,192 +7413,6 @@ async function handleRequirementDelete(opts) {
|
|
|
7497
7413
|
};
|
|
7498
7414
|
}
|
|
7499
7415
|
}
|
|
7500
|
-
async function handleRequirementBreakdown(opts) {
|
|
7501
|
-
let stop;
|
|
7502
|
-
try {
|
|
7503
|
-
const { requirementService, auth } = getServices3();
|
|
7504
|
-
const isAuthenticated = await auth.isAuthenticated();
|
|
7505
|
-
if (!isAuthenticated) {
|
|
7506
|
-
return {
|
|
7507
|
-
success: false,
|
|
7508
|
-
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7509
|
-
};
|
|
7510
|
-
}
|
|
7511
|
-
const format = opts.format || "table";
|
|
7512
|
-
if (!["table", "json", "xml", "markdown"].includes(format)) {
|
|
7513
|
-
return {
|
|
7514
|
-
success: false,
|
|
7515
|
-
message: chalk16.red(
|
|
7516
|
-
`\u274C Invalid format: ${format}. Supported formats: table, json, xml, markdown`
|
|
7517
|
-
)
|
|
7518
|
-
};
|
|
7519
|
-
}
|
|
7520
|
-
const requirementResult = await workspaceManager.getRequirement(opts.id);
|
|
7521
|
-
if (!requirementResult.success) {
|
|
7522
|
-
return {
|
|
7523
|
-
success: false,
|
|
7524
|
-
message: requirementResult.error
|
|
7525
|
-
};
|
|
7526
|
-
}
|
|
7527
|
-
const requirementId = requirementResult.requirementId;
|
|
7528
|
-
const workspace = await workspaceManager.getProject(opts.project);
|
|
7529
|
-
if (!workspace.success) {
|
|
7530
|
-
return {
|
|
7531
|
-
success: false,
|
|
7532
|
-
message: workspace.error
|
|
7533
|
-
};
|
|
7534
|
-
}
|
|
7535
|
-
const projectId = workspace.projectId;
|
|
7536
|
-
const normalizedId = normalizeRequirementId(requirementId);
|
|
7537
|
-
stop = showSpinner("Checking requirement...", chalk16.gray);
|
|
7538
|
-
const requirement2 = await requirementService.getProjectRequirement(projectId, normalizedId);
|
|
7539
|
-
stop();
|
|
7540
|
-
const taskCount = requirement2.tasks?.length ?? requirement2.task_progress?.total ?? 0;
|
|
7541
|
-
if (taskCount > 0) {
|
|
7542
|
-
const shortId = requirement2.short_id || normalizedId;
|
|
7543
|
-
return {
|
|
7544
|
-
success: false,
|
|
7545
|
-
message: chalk16.red(
|
|
7546
|
-
`\u274C Cannot breakdown requirement ${shortId} - it already has ${taskCount} task(s)
|
|
7547
|
-
|
|
7548
|
-
`
|
|
7549
|
-
) + "The breakdown command is only for initial task generation from requirements.\nOnce tasks exist, you should manage them individually.\n\n" + chalk16.bold("To add more tasks to this requirement:\n") + chalk16.cyan(` braingrid task create -r ${shortId} --title "Task title"
|
|
7550
|
-
|
|
7551
|
-
`) + chalk16.bold("To view existing tasks:\n") + chalk16.cyan(` braingrid task list -r ${shortId}`)
|
|
7552
|
-
};
|
|
7553
|
-
}
|
|
7554
|
-
stop = showSpinner("Breaking down requirement into tasks...");
|
|
7555
|
-
const response = await requirementService.breakdownRequirement(projectId, normalizedId, {});
|
|
7556
|
-
stop();
|
|
7557
|
-
let output;
|
|
7558
|
-
switch (format) {
|
|
7559
|
-
case "json": {
|
|
7560
|
-
output = JSON.stringify(response, null, 2);
|
|
7561
|
-
break;
|
|
7562
|
-
}
|
|
7563
|
-
case "xml": {
|
|
7564
|
-
output = formatRequirementBreakdownXml(response.requirement_short_id, response.tasks);
|
|
7565
|
-
break;
|
|
7566
|
-
}
|
|
7567
|
-
case "markdown": {
|
|
7568
|
-
output = formatRequirementBreakdownMarkdown(response.requirement_short_id, response.tasks);
|
|
7569
|
-
break;
|
|
7570
|
-
}
|
|
7571
|
-
default: {
|
|
7572
|
-
output = chalk16.green(
|
|
7573
|
-
`\u2705 Generated ${response.tasks.length} tasks for ${response.requirement_short_id}
|
|
7574
|
-
|
|
7575
|
-
`
|
|
7576
|
-
);
|
|
7577
|
-
output += "Number Status Title\n";
|
|
7578
|
-
output += `${"\u2500".repeat(80)}
|
|
7579
|
-
`;
|
|
7580
|
-
for (const task2 of response.tasks) {
|
|
7581
|
-
const statusEmoji = getRequirementStatusEmoji(task2.status);
|
|
7582
|
-
const number = task2.number.padEnd(6);
|
|
7583
|
-
const statusText = task2.status.padEnd(12);
|
|
7584
|
-
const title = task2.title.substring(0, 55);
|
|
7585
|
-
output += `${number} ${statusEmoji} ${statusText} ${title}
|
|
7586
|
-
`;
|
|
7587
|
-
}
|
|
7588
|
-
break;
|
|
7589
|
-
}
|
|
7590
|
-
}
|
|
7591
|
-
return {
|
|
7592
|
-
success: true,
|
|
7593
|
-
message: output,
|
|
7594
|
-
data: response
|
|
7595
|
-
};
|
|
7596
|
-
} catch (error) {
|
|
7597
|
-
stop?.();
|
|
7598
|
-
if (error && typeof error === "object" && "response" in error) {
|
|
7599
|
-
return {
|
|
7600
|
-
success: false,
|
|
7601
|
-
message: handleError(
|
|
7602
|
-
error,
|
|
7603
|
-
`breaking down requirement ${opts.id || "unknown"}`
|
|
7604
|
-
)
|
|
7605
|
-
};
|
|
7606
|
-
}
|
|
7607
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7608
|
-
return {
|
|
7609
|
-
success: false,
|
|
7610
|
-
message: chalk16.red(`\u274C Error breaking down requirement: ${errorMessage}`)
|
|
7611
|
-
};
|
|
7612
|
-
}
|
|
7613
|
-
}
|
|
7614
|
-
async function handleRequirementBuild(opts) {
|
|
7615
|
-
let stopSpinner = null;
|
|
7616
|
-
try {
|
|
7617
|
-
const { requirementService, auth, config: config2 } = getServices3();
|
|
7618
|
-
const isAuthenticated = await auth.isAuthenticated();
|
|
7619
|
-
if (!isAuthenticated) {
|
|
7620
|
-
return {
|
|
7621
|
-
success: false,
|
|
7622
|
-
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7623
|
-
};
|
|
7624
|
-
}
|
|
7625
|
-
const format = opts.format || "markdown";
|
|
7626
|
-
if (!["markdown", "json", "xml"].includes(format)) {
|
|
7627
|
-
return {
|
|
7628
|
-
success: false,
|
|
7629
|
-
message: chalk16.red("\u274C Invalid format. Must be one of: markdown, json, xml")
|
|
7630
|
-
};
|
|
7631
|
-
}
|
|
7632
|
-
const requirementResult = await workspaceManager.getRequirement(opts.id);
|
|
7633
|
-
if (!requirementResult.success) {
|
|
7634
|
-
return {
|
|
7635
|
-
success: false,
|
|
7636
|
-
message: requirementResult.error
|
|
7637
|
-
};
|
|
7638
|
-
}
|
|
7639
|
-
const requirementId = requirementResult.requirementId;
|
|
7640
|
-
const workspace = await workspaceManager.getProject(opts.project);
|
|
7641
|
-
if (!workspace.success) {
|
|
7642
|
-
return {
|
|
7643
|
-
success: false,
|
|
7644
|
-
message: workspace.error
|
|
7645
|
-
};
|
|
7646
|
-
}
|
|
7647
|
-
const projectId = workspace.projectId;
|
|
7648
|
-
const normalizedId = normalizeRequirementId(requirementId);
|
|
7649
|
-
stopSpinner = showSpinner("Building requirement with tasks", chalk16.gray);
|
|
7650
|
-
const requirement2 = await requirementService.getProjectRequirement(projectId, normalizedId);
|
|
7651
|
-
stopSpinner();
|
|
7652
|
-
stopSpinner = null;
|
|
7653
|
-
let output;
|
|
7654
|
-
switch (format) {
|
|
7655
|
-
case "json": {
|
|
7656
|
-
output = formatRequirementBuildJson(requirement2);
|
|
7657
|
-
break;
|
|
7658
|
-
}
|
|
7659
|
-
case "xml": {
|
|
7660
|
-
output = formatRequirementBuildXml(requirement2);
|
|
7661
|
-
break;
|
|
7662
|
-
}
|
|
7663
|
-
default: {
|
|
7664
|
-
output = formatRequirementBuildMarkdown(requirement2, {
|
|
7665
|
-
apiUrl: config2.apiUrl,
|
|
7666
|
-
projectShortId: projectId
|
|
7667
|
-
});
|
|
7668
|
-
break;
|
|
7669
|
-
}
|
|
7670
|
-
}
|
|
7671
|
-
return {
|
|
7672
|
-
success: true,
|
|
7673
|
-
message: output,
|
|
7674
|
-
data: requirement2
|
|
7675
|
-
};
|
|
7676
|
-
} catch (error) {
|
|
7677
|
-
if (stopSpinner) {
|
|
7678
|
-
stopSpinner();
|
|
7679
|
-
}
|
|
7680
|
-
return {
|
|
7681
|
-
success: false,
|
|
7682
|
-
message: formatError(error, getResourceContext("requirement", opts.id || "unknown"))
|
|
7683
|
-
};
|
|
7684
|
-
}
|
|
7685
|
-
}
|
|
7686
7416
|
function slugify(text) {
|
|
7687
7417
|
return text.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").substring(0, 40);
|
|
7688
7418
|
}
|
|
@@ -9042,9 +8772,9 @@ function formatTaskSpecifyMarkdown(response, _apiUrl) {
|
|
|
9042
8772
|
|
|
9043
8773
|
// src/cli.ts
|
|
9044
8774
|
var __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
9045
|
-
var packageRoot =
|
|
8775
|
+
var packageRoot = resolve2(__dirname, "..");
|
|
9046
8776
|
var envFile = process.env.NODE_ENV ? `.env.${process.env.NODE_ENV}` : ".env";
|
|
9047
|
-
var envPath =
|
|
8777
|
+
var envPath = resolve2(packageRoot, envFile);
|
|
9048
8778
|
if (existsSync(envPath)) {
|
|
9049
8779
|
config({ path: envPath, quiet: true });
|
|
9050
8780
|
}
|
|
@@ -9210,30 +8940,6 @@ requirement.command("delete [id]").description("Delete a requirement (auto-detec
|
|
|
9210
8940
|
process.exit(1);
|
|
9211
8941
|
}
|
|
9212
8942
|
});
|
|
9213
|
-
requirement.command("breakdown [id]").description(
|
|
9214
|
-
"Break down a requirement into actionable tasks using AI (auto-detects ID from git branch if not provided)"
|
|
9215
|
-
).option(
|
|
9216
|
-
"-p, --project <id>",
|
|
9217
|
-
"project ID (auto-detects from .braingrid/project.json if not provided)"
|
|
9218
|
-
).option("--format <format>", "output format (table, json, xml, markdown)", "markdown").action(async (id, opts) => {
|
|
9219
|
-
const result = await handleRequirementBreakdown({ ...opts, id });
|
|
9220
|
-
console.log(result.message);
|
|
9221
|
-
if (!result.success) {
|
|
9222
|
-
process.exit(1);
|
|
9223
|
-
}
|
|
9224
|
-
});
|
|
9225
|
-
requirement.command("build [id]").description(
|
|
9226
|
-
"Build requirement with all tasks in specified format (auto-detects ID from git branch if not provided)"
|
|
9227
|
-
).option(
|
|
9228
|
-
"-p, --project <id>",
|
|
9229
|
-
"project ID (auto-detects from .braingrid/project.json if not provided)"
|
|
9230
|
-
).option("--format <format>", "output format (markdown, json, xml)", "markdown").action(async (id, opts) => {
|
|
9231
|
-
const result = await handleRequirementBuild({ ...opts, id });
|
|
9232
|
-
console.log(result.message);
|
|
9233
|
-
if (!result.success) {
|
|
9234
|
-
process.exit(1);
|
|
9235
|
-
}
|
|
9236
|
-
});
|
|
9237
8943
|
requirement.command("create-branch [id]").description(
|
|
9238
8944
|
"Create a GitHub branch for a requirement (auto-detects ID from git branch if not provided)"
|
|
9239
8945
|
).option(
|