lean-spec 0.2.5-dev.20251124070920 → 0.2.5-dev.20251125010225
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/{chunk-LV7XEQ4D.js → chunk-RTEGSMVL.js} +272 -46
- package/dist/chunk-RTEGSMVL.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -2
- package/templates/detailed/AGENTS.md +113 -0
- package/templates/detailed/README.md +28 -0
- package/templates/detailed/files/DESIGN.md +43 -0
- package/templates/detailed/files/PLAN.md +59 -0
- package/templates/detailed/files/README.md +30 -0
- package/templates/detailed/files/TEST.md +71 -0
- package/templates/standard/AGENTS.md +113 -0
- package/templates/standard/README.md +4 -2
- package/dist/chunk-LV7XEQ4D.js.map +0 -1
- package/templates/_shared/agents-components/core-rules-base-additions.md +0 -4
- package/templates/_shared/agents-components/core-rules-enterprise-additions.md +0 -4
- package/templates/_shared/agents-components/core-rules-shared.md +0 -1
- package/templates/_shared/agents-components/discovery-commands-enterprise-additions.md +0 -6
- package/templates/_shared/agents-components/discovery-commands-minimal-additions.md +0 -0
- package/templates/_shared/agents-components/discovery-commands-shared.md +0 -8
- package/templates/_shared/agents-components/discovery-commands-standard-additions.md +0 -3
- package/templates/_shared/agents-components/enterprise-approval.md +0 -12
- package/templates/_shared/agents-components/enterprise-compliance.md +0 -12
- package/templates/_shared/agents-components/enterprise-when-required.md +0 -13
- package/templates/_shared/agents-components/essential-commands-enterprise-additions.md +0 -29
- package/templates/_shared/agents-components/essential-commands-minimal-additions.md +0 -1
- package/templates/_shared/agents-components/essential-commands-shared.md +0 -15
- package/templates/_shared/agents-components/essential-commands-standard-additions.md +0 -18
- package/templates/_shared/agents-components/frontmatter-enterprise.md +0 -33
- package/templates/_shared/agents-components/frontmatter-minimal.md +0 -18
- package/templates/_shared/agents-components/frontmatter-standard.md +0 -23
- package/templates/_shared/agents-components/quality-standards-enterprise-additions.md +0 -4
- package/templates/_shared/agents-components/quality-standards-minimal-additions.md +0 -3
- package/templates/_shared/agents-components/quality-standards-shared.md +0 -6
- package/templates/_shared/agents-components/status-update-triggers.md +0 -14
- package/templates/_shared/agents-components/when-to-use-enterprise.md +0 -11
- package/templates/_shared/agents-components/when-to-use-minimal.md +0 -9
- package/templates/_shared/agents-components/when-to-use-standard.md +0 -9
- package/templates/_shared/agents-components/workflow-enterprise.md +0 -13
- package/templates/_shared/agents-components/workflow-standard-detailed.md +0 -12
- package/templates/_shared/agents-components/workflow-standard.md +0 -10
- package/templates/_shared/agents-template.hbs +0 -43
- package/templates/enterprise/README.md +0 -25
- package/templates/enterprise/agents-config.json +0 -16
- package/templates/enterprise/files/AGENTS.md +0 -198
- package/templates/enterprise/spec-template.md +0 -80
- package/templates/minimal/README.md +0 -18
- package/templates/minimal/agents-config.json +0 -13
- package/templates/minimal/config.json +0 -15
- package/templates/minimal/files/AGENTS.md +0 -118
- package/templates/minimal/spec-template.md +0 -25
- package/templates/standard/agents-config.json +0 -13
- package/templates/standard/files/AGENTS.md +0 -144
- /package/templates/{enterprise → detailed}/config.json +0 -0
- /package/templates/standard/{spec-template.md → files/README.md} +0 -0
|
@@ -550,7 +550,7 @@ async function withSpinner(text, fn, options) {
|
|
|
550
550
|
|
|
551
551
|
// src/commands/check.ts
|
|
552
552
|
function checkCommand() {
|
|
553
|
-
return new Command("check").description("Check for sequence conflicts").option("-q, --quiet", "Brief output").action(async (options) => {
|
|
553
|
+
return new Command("check").description("Check for sequence conflicts").option("-q, --quiet", "Brief output").option("--json", "Output as JSON").action(async (options) => {
|
|
554
554
|
const hasNoConflicts = await checkSpecs(options);
|
|
555
555
|
process.exit(hasNoConflicts ? 0 : 1);
|
|
556
556
|
});
|
|
@@ -578,10 +578,25 @@ async function checkSpecs(options = {}) {
|
|
|
578
578
|
const conflicts = Array.from(sequenceMap.entries()).filter(([_, paths]) => paths.length > 1).sort(([a], [b]) => a - b);
|
|
579
579
|
if (conflicts.length === 0) {
|
|
580
580
|
if (!options.quiet && !options.silent) {
|
|
581
|
-
|
|
581
|
+
if (options.json) {
|
|
582
|
+
console.log(JSON.stringify({ conflicts: [], hasConflicts: false }, null, 2));
|
|
583
|
+
} else {
|
|
584
|
+
console.log(chalk19.green("\u2713 No sequence conflicts detected"));
|
|
585
|
+
}
|
|
582
586
|
}
|
|
583
587
|
return true;
|
|
584
588
|
}
|
|
589
|
+
if (options.json) {
|
|
590
|
+
const jsonOutput = {
|
|
591
|
+
hasConflicts: true,
|
|
592
|
+
conflicts: conflicts.map(([seq, paths]) => ({
|
|
593
|
+
sequence: seq,
|
|
594
|
+
specs: paths
|
|
595
|
+
}))
|
|
596
|
+
};
|
|
597
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
585
600
|
if (!options.silent) {
|
|
586
601
|
if (!options.quiet) {
|
|
587
602
|
console.log("");
|
|
@@ -698,13 +713,33 @@ async function createSpec(name, options = {}) {
|
|
|
698
713
|
} else {
|
|
699
714
|
templateName = config.template || "spec-template.md";
|
|
700
715
|
}
|
|
701
|
-
|
|
716
|
+
let templatePath = path15.join(templatesDir, templateName);
|
|
717
|
+
try {
|
|
718
|
+
await fs9.access(templatePath);
|
|
719
|
+
} catch {
|
|
720
|
+
const legacyPath = path15.join(templatesDir, "spec-template.md");
|
|
721
|
+
try {
|
|
722
|
+
await fs9.access(legacyPath);
|
|
723
|
+
templatePath = legacyPath;
|
|
724
|
+
templateName = "spec-template.md";
|
|
725
|
+
} catch {
|
|
726
|
+
const readmePath = path15.join(templatesDir, "README.md");
|
|
727
|
+
try {
|
|
728
|
+
await fs9.access(readmePath);
|
|
729
|
+
templatePath = readmePath;
|
|
730
|
+
templateName = "README.md";
|
|
731
|
+
} catch {
|
|
732
|
+
throw new Error(`Template not found: ${templatePath}. Run: lean-spec init`);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
702
736
|
let content;
|
|
737
|
+
let varContext;
|
|
703
738
|
try {
|
|
704
739
|
const template = await fs9.readFile(templatePath, "utf-8");
|
|
705
740
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
706
741
|
const title = options.title || name;
|
|
707
|
-
|
|
742
|
+
varContext = await buildVariableContext(config, { name: title, date });
|
|
708
743
|
content = resolveVariables(template, varContext);
|
|
709
744
|
const parsed = matter2(content, {
|
|
710
745
|
engines: {
|
|
@@ -746,8 +781,29 @@ ${options.description}`
|
|
|
746
781
|
throw new Error(`Template not found: ${templatePath}. Run: lean-spec init`);
|
|
747
782
|
}
|
|
748
783
|
await fs9.writeFile(specFile, content, "utf-8");
|
|
749
|
-
|
|
750
|
-
|
|
784
|
+
try {
|
|
785
|
+
const templateFiles = await fs9.readdir(templatesDir);
|
|
786
|
+
const additionalFiles = templateFiles.filter(
|
|
787
|
+
(f) => f.endsWith(".md") && f !== templateName && f !== "spec-template.md" && f !== config.structure.defaultFile
|
|
788
|
+
);
|
|
789
|
+
if (additionalFiles.length > 0) {
|
|
790
|
+
for (const file of additionalFiles) {
|
|
791
|
+
const srcPath = path15.join(templatesDir, file);
|
|
792
|
+
const destPath = path15.join(specDir, file);
|
|
793
|
+
let fileContent = await fs9.readFile(srcPath, "utf-8");
|
|
794
|
+
fileContent = resolveVariables(fileContent, varContext);
|
|
795
|
+
await fs9.writeFile(destPath, fileContent, "utf-8");
|
|
796
|
+
}
|
|
797
|
+
console.log(chalk19.green(`\u2713 Created: ${sanitizeUserInput(specDir)}/`));
|
|
798
|
+
console.log(chalk19.gray(` Files: ${config.structure.defaultFile}, ${additionalFiles.join(", ")}`));
|
|
799
|
+
} else {
|
|
800
|
+
console.log(chalk19.green(`\u2713 Created: ${sanitizeUserInput(specDir)}/`));
|
|
801
|
+
console.log(chalk19.gray(` Edit: ${sanitizeUserInput(specFile)}`));
|
|
802
|
+
}
|
|
803
|
+
} catch (error) {
|
|
804
|
+
console.log(chalk19.green(`\u2713 Created: ${sanitizeUserInput(specDir)}/`));
|
|
805
|
+
console.log(chalk19.gray(` Edit: ${sanitizeUserInput(specFile)}`));
|
|
806
|
+
}
|
|
751
807
|
await autoCheckIfEnabled();
|
|
752
808
|
}
|
|
753
809
|
function archiveCommand() {
|
|
@@ -867,7 +923,7 @@ function getPriorityEmoji(priority) {
|
|
|
867
923
|
|
|
868
924
|
// src/commands/list.ts
|
|
869
925
|
function listCommand() {
|
|
870
|
-
return new Command("list").description("List all specs").option("--archived", "Include archived specs").option("--status <status>", "Filter by status (planned, in-progress, complete, archived)").option("--tag <tag...>", "Filter by tag (can specify multiple)").option("--priority <priority>", "Filter by priority (low, medium, high, critical)").option("--assignee <name>", "Filter by assignee").option("--field <name=value...>", "Filter by custom field (can specify multiple)").option("--sort <field>", "Sort by field (id, created, name, status, priority)", "id").option("--order <order>", "Sort order (asc, desc)", "desc").action(async (options) => {
|
|
926
|
+
return new Command("list").description("List all specs").option("--archived", "Include archived specs").option("--status <status>", "Filter by status (planned, in-progress, complete, archived)").option("--tag <tag...>", "Filter by tag (can specify multiple)").option("--priority <priority>", "Filter by priority (low, medium, high, critical)").option("--assignee <name>", "Filter by assignee").option("--field <name=value...>", "Filter by custom field (can specify multiple)").option("--sort <field>", "Sort by field (id, created, name, status, priority)", "id").option("--order <order>", "Sort order (asc, desc)", "desc").option("--json", "Output as JSON").action(async (options) => {
|
|
871
927
|
const customFields = parseCustomFieldOptions(options.field);
|
|
872
928
|
const listOptions = {
|
|
873
929
|
showArchived: options.archived,
|
|
@@ -877,7 +933,8 @@ function listCommand() {
|
|
|
877
933
|
assignee: options.assignee,
|
|
878
934
|
customFields: Object.keys(customFields).length > 0 ? customFields : void 0,
|
|
879
935
|
sortBy: options.sort || "id",
|
|
880
|
-
sortOrder: options.order || "desc"
|
|
936
|
+
sortOrder: options.order || "desc",
|
|
937
|
+
json: options.json
|
|
881
938
|
};
|
|
882
939
|
await listSpecs(listOptions);
|
|
883
940
|
});
|
|
@@ -913,7 +970,30 @@ async function listSpecs(options = {}) {
|
|
|
913
970
|
})
|
|
914
971
|
);
|
|
915
972
|
if (specs.length === 0) {
|
|
916
|
-
|
|
973
|
+
if (options.json) {
|
|
974
|
+
console.log(JSON.stringify({ specs: [], total: 0 }, null, 2));
|
|
975
|
+
} else {
|
|
976
|
+
console.log(chalk19.dim("No specs found."));
|
|
977
|
+
}
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
if (options.json) {
|
|
981
|
+
const jsonOutput = {
|
|
982
|
+
specs: specs.map((spec) => ({
|
|
983
|
+
path: spec.path,
|
|
984
|
+
name: spec.name,
|
|
985
|
+
status: spec.frontmatter.status,
|
|
986
|
+
priority: spec.frontmatter.priority,
|
|
987
|
+
tags: spec.frontmatter.tags,
|
|
988
|
+
assignee: spec.frontmatter.assignee,
|
|
989
|
+
created: spec.frontmatter.created,
|
|
990
|
+
completed: spec.frontmatter.completed,
|
|
991
|
+
subFiles: spec.subFiles?.length || 0
|
|
992
|
+
})),
|
|
993
|
+
total: specs.length,
|
|
994
|
+
filter: options
|
|
995
|
+
};
|
|
996
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
917
997
|
return;
|
|
918
998
|
}
|
|
919
999
|
console.log(chalk19.bold.cyan("\u{1F4C4} Spec List"));
|
|
@@ -1458,13 +1538,14 @@ function fileExistsInGit(filePath) {
|
|
|
1458
1538
|
|
|
1459
1539
|
// src/commands/backfill.ts
|
|
1460
1540
|
function backfillCommand() {
|
|
1461
|
-
return new Command("backfill").description("Backfill timestamps from git history").argument("[specs...]", "Specific specs to backfill (optional)").option("--dry-run", "Show what would be updated without making changes").option("--force", "Overwrite existing timestamp values").option("--assignee", "Include assignee from first commit author").option("--transitions", "Include full status transition history").option("--all", "Include all optional fields (assignee + transitions)").action(async (specs, options) => {
|
|
1541
|
+
return new Command("backfill").description("Backfill timestamps from git history").argument("[specs...]", "Specific specs to backfill (optional)").option("--dry-run", "Show what would be updated without making changes").option("--force", "Overwrite existing timestamp values").option("--assignee", "Include assignee from first commit author").option("--transitions", "Include full status transition history").option("--all", "Include all optional fields (assignee + transitions)").option("--json", "Output as JSON").action(async (specs, options) => {
|
|
1462
1542
|
await backfillTimestamps({
|
|
1463
1543
|
dryRun: options.dryRun,
|
|
1464
1544
|
force: options.force,
|
|
1465
1545
|
includeAssignee: options.assignee || options.all,
|
|
1466
1546
|
includeTransitions: options.transitions || options.all,
|
|
1467
|
-
specs: specs && specs.length > 0 ? specs : void 0
|
|
1547
|
+
specs: specs && specs.length > 0 ? specs : void 0,
|
|
1548
|
+
json: options.json
|
|
1468
1549
|
});
|
|
1469
1550
|
});
|
|
1470
1551
|
}
|
|
@@ -1830,7 +1911,7 @@ async function detectExistingSystemPrompts(cwd) {
|
|
|
1830
1911
|
async function handleExistingFiles(action, existingFiles, templateDir, cwd, variables = {}) {
|
|
1831
1912
|
for (const file of existingFiles) {
|
|
1832
1913
|
const filePath = path15.join(cwd, file);
|
|
1833
|
-
const templateFilePath = path15.join(templateDir,
|
|
1914
|
+
const templateFilePath = path15.join(templateDir, file);
|
|
1834
1915
|
try {
|
|
1835
1916
|
await fs9.access(templateFilePath);
|
|
1836
1917
|
} catch {
|
|
@@ -2003,7 +2084,7 @@ var __dirname = path15.dirname(fileURLToPath(import.meta.url));
|
|
|
2003
2084
|
var TEMPLATES_DIR = path15.join(__dirname, "..", "templates");
|
|
2004
2085
|
var EXAMPLES_DIR = path15.join(TEMPLATES_DIR, "examples");
|
|
2005
2086
|
function initCommand() {
|
|
2006
|
-
return new Command("init").description("Initialize LeanSpec in current directory").option("-y, --yes", "Skip prompts and use defaults (quick start with standard template)").option("--example [name]", "Scaffold an example project for tutorials (interactive if no name provided)").option("--name <dirname>", "Custom directory name for example project").option("--list", "List available example projects").action(async (options) => {
|
|
2087
|
+
return new Command("init").description("Initialize LeanSpec in current directory").option("-y, --yes", "Skip prompts and use defaults (quick start with standard template)").option("--template <name>", "Use specific template (standard or detailed)").option("--example [name]", "Scaffold an example project for tutorials (interactive if no name provided)").option("--name <dirname>", "Custom directory name for example project").option("--list", "List available example projects").action(async (options) => {
|
|
2007
2088
|
if (options.list) {
|
|
2008
2089
|
await listExamples();
|
|
2009
2090
|
return;
|
|
@@ -2012,10 +2093,10 @@ function initCommand() {
|
|
|
2012
2093
|
await scaffoldExample(options.example, options.name);
|
|
2013
2094
|
return;
|
|
2014
2095
|
}
|
|
2015
|
-
await initProject(options.yes);
|
|
2096
|
+
await initProject(options.yes, options.template);
|
|
2016
2097
|
});
|
|
2017
2098
|
}
|
|
2018
|
-
async function initProject(skipPrompts = false) {
|
|
2099
|
+
async function initProject(skipPrompts = false, templateOption) {
|
|
2019
2100
|
const cwd = process.cwd();
|
|
2020
2101
|
try {
|
|
2021
2102
|
await fs9.access(path15.join(cwd, ".lean-spec", "config.json"));
|
|
@@ -2028,11 +2109,11 @@ async function initProject(skipPrompts = false) {
|
|
|
2028
2109
|
console.log(chalk19.green("Welcome to LeanSpec!"));
|
|
2029
2110
|
console.log("");
|
|
2030
2111
|
let setupMode = "quick";
|
|
2031
|
-
let templateName = "standard";
|
|
2112
|
+
let templateName = templateOption || "standard";
|
|
2032
2113
|
if (skipPrompts) {
|
|
2033
2114
|
console.log(chalk19.gray("Using defaults: quick start with standard template"));
|
|
2034
2115
|
console.log("");
|
|
2035
|
-
} else {
|
|
2116
|
+
} else if (!templateOption) {
|
|
2036
2117
|
setupMode = await select({
|
|
2037
2118
|
message: "How would you like to set up?",
|
|
2038
2119
|
choices: [
|
|
@@ -2044,7 +2125,7 @@ async function initProject(skipPrompts = false) {
|
|
|
2044
2125
|
{
|
|
2045
2126
|
name: "Choose template",
|
|
2046
2127
|
value: "template",
|
|
2047
|
-
description: "Pick from:
|
|
2128
|
+
description: "Pick from: standard, detailed"
|
|
2048
2129
|
}
|
|
2049
2130
|
// TODO: Re-enable when custom setup mode is implemented
|
|
2050
2131
|
// {
|
|
@@ -2058,17 +2139,27 @@ async function initProject(skipPrompts = false) {
|
|
|
2058
2139
|
templateName = await select({
|
|
2059
2140
|
message: "Select template:",
|
|
2060
2141
|
choices: [
|
|
2061
|
-
{ name: "
|
|
2062
|
-
{ name: "standard", value: "standard", description: "Recommended - includes AGENTS.md" },
|
|
2142
|
+
{ name: "standard", value: "standard", description: "Recommended - single-file specs with AGENTS.md" },
|
|
2063
2143
|
{
|
|
2064
|
-
name: "
|
|
2065
|
-
value: "
|
|
2066
|
-
description: "
|
|
2144
|
+
name: "detailed",
|
|
2145
|
+
value: "detailed",
|
|
2146
|
+
description: "Complex specs with sub-spec structure (DESIGN, PLAN, TEST)"
|
|
2067
2147
|
}
|
|
2068
2148
|
]
|
|
2069
2149
|
});
|
|
2070
2150
|
}
|
|
2071
2151
|
}
|
|
2152
|
+
if (templateName === "minimal") {
|
|
2153
|
+
console.log(chalk19.yellow('\u26A0 The "minimal" template has been removed.'));
|
|
2154
|
+
console.log(chalk19.gray(' Using "standard" template instead (same lightweight approach).'));
|
|
2155
|
+
console.log("");
|
|
2156
|
+
templateName = "standard";
|
|
2157
|
+
} else if (templateName === "enterprise") {
|
|
2158
|
+
console.log(chalk19.yellow('\u26A0 The "enterprise" template has been renamed to "detailed".'));
|
|
2159
|
+
console.log(chalk19.gray(' Using "detailed" template (sub-spec structure for complex specs).'));
|
|
2160
|
+
console.log("");
|
|
2161
|
+
templateName = "detailed";
|
|
2162
|
+
}
|
|
2072
2163
|
const templateDir = path15.join(TEMPLATES_DIR, templateName);
|
|
2073
2164
|
const templateConfigPath = path15.join(templateDir, "config.json");
|
|
2074
2165
|
let templateConfig;
|
|
@@ -2133,19 +2224,35 @@ async function initProject(skipPrompts = false) {
|
|
|
2133
2224
|
console.error(chalk19.red("Error creating templates directory:"), error);
|
|
2134
2225
|
process.exit(1);
|
|
2135
2226
|
}
|
|
2136
|
-
const
|
|
2137
|
-
const targetSpecPath = path15.join(templatesDir, "spec-template.md");
|
|
2227
|
+
const templateFilesDir = path15.join(templateDir, "files");
|
|
2138
2228
|
try {
|
|
2139
|
-
await fs9.
|
|
2140
|
-
|
|
2229
|
+
const files = await fs9.readdir(templateFilesDir);
|
|
2230
|
+
if (templateName === "standard") {
|
|
2231
|
+
const readmePath = path15.join(templateFilesDir, "README.md");
|
|
2232
|
+
const targetSpecPath = path15.join(templatesDir, "spec-template.md");
|
|
2233
|
+
await fs9.copyFile(readmePath, targetSpecPath);
|
|
2234
|
+
console.log(chalk19.green("\u2713 Created .lean-spec/templates/spec-template.md"));
|
|
2235
|
+
templateConfig.template = "spec-template.md";
|
|
2236
|
+
templateConfig.templates = {
|
|
2237
|
+
default: "spec-template.md"
|
|
2238
|
+
};
|
|
2239
|
+
} else if (templateName === "detailed") {
|
|
2240
|
+
for (const file of files) {
|
|
2241
|
+
const srcPath = path15.join(templateFilesDir, file);
|
|
2242
|
+
const destPath = path15.join(templatesDir, file);
|
|
2243
|
+
await fs9.copyFile(srcPath, destPath);
|
|
2244
|
+
}
|
|
2245
|
+
console.log(chalk19.green(`\u2713 Created .lean-spec/templates/ with ${files.length} files`));
|
|
2246
|
+
console.log(chalk19.gray(` Files: ${files.join(", ")}`));
|
|
2247
|
+
templateConfig.template = "README.md";
|
|
2248
|
+
templateConfig.templates = {
|
|
2249
|
+
default: "README.md"
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2141
2252
|
} catch (error) {
|
|
2142
|
-
console.error(chalk19.red("Error copying template:"), error);
|
|
2253
|
+
console.error(chalk19.red("Error copying template files:"), error);
|
|
2143
2254
|
process.exit(1);
|
|
2144
2255
|
}
|
|
2145
|
-
templateConfig.template = "spec-template.md";
|
|
2146
|
-
templateConfig.templates = {
|
|
2147
|
-
default: "spec-template.md"
|
|
2148
|
-
};
|
|
2149
2256
|
await saveConfig(templateConfig, cwd);
|
|
2150
2257
|
console.log(chalk19.green("\u2713 Created .lean-spec/config.json"));
|
|
2151
2258
|
const existingFiles = await detectExistingSystemPrompts(cwd);
|
|
@@ -2191,9 +2298,26 @@ async function initProject(skipPrompts = false) {
|
|
|
2191
2298
|
}
|
|
2192
2299
|
}
|
|
2193
2300
|
const projectName = await getProjectName2(cwd);
|
|
2301
|
+
if (!skipFiles.includes("AGENTS.md")) {
|
|
2302
|
+
const agentsSourcePath = path15.join(templateDir, "AGENTS.md");
|
|
2303
|
+
const agentsTargetPath = path15.join(cwd, "AGENTS.md");
|
|
2304
|
+
try {
|
|
2305
|
+
let agentsContent = await fs9.readFile(agentsSourcePath, "utf-8");
|
|
2306
|
+
agentsContent = agentsContent.replace(/\{project_name\}/g, projectName);
|
|
2307
|
+
await fs9.writeFile(agentsTargetPath, agentsContent, "utf-8");
|
|
2308
|
+
console.log(chalk19.green("\u2713 Created AGENTS.md"));
|
|
2309
|
+
} catch (error) {
|
|
2310
|
+
console.error(chalk19.red("Error copying AGENTS.md:"), error);
|
|
2311
|
+
process.exit(1);
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2194
2314
|
const filesDir = path15.join(templateDir, "files");
|
|
2195
2315
|
try {
|
|
2196
|
-
await
|
|
2316
|
+
const filesToCopy = await fs9.readdir(filesDir);
|
|
2317
|
+
const hasOtherFiles = filesToCopy.some((f) => !f.match(/\.(md)$/i) || !["README.md", "DESIGN.md", "PLAN.md", "TEST.md"].includes(f));
|
|
2318
|
+
if (hasOtherFiles) {
|
|
2319
|
+
await copyDirectory(filesDir, cwd, [...skipFiles, "README.md", "DESIGN.md", "PLAN.md", "TEST.md"], { project_name: projectName });
|
|
2320
|
+
}
|
|
2197
2321
|
console.log(chalk19.green("\u2713 Initialized project structure"));
|
|
2198
2322
|
} catch (error) {
|
|
2199
2323
|
console.error(chalk19.red("Error copying template files:"), error);
|
|
@@ -3648,7 +3772,7 @@ function filesCommand(specPath, options = {}) {
|
|
|
3648
3772
|
if (typeof specPath === "string") {
|
|
3649
3773
|
return showFiles(specPath, options);
|
|
3650
3774
|
}
|
|
3651
|
-
return new Command("files").description("List files in a spec").argument("<spec>", "Spec to list files for").option("--type <type>", "Filter by type: docs, assets").option("--tree", "Show tree structure").action(async (target, opts) => {
|
|
3775
|
+
return new Command("files").description("List files in a spec").argument("<spec>", "Spec to list files for").option("--type <type>", "Filter by type: docs, assets").option("--tree", "Show tree structure").option("--json", "Output as JSON").action(async (target, opts) => {
|
|
3652
3776
|
await showFiles(target, opts);
|
|
3653
3777
|
});
|
|
3654
3778
|
}
|
|
@@ -3666,6 +3790,31 @@ async function showFiles(specPath, options = {}) {
|
|
|
3666
3790
|
throw new Error(`Could not load spec: ${sanitizeUserInput(specPath)}`);
|
|
3667
3791
|
}
|
|
3668
3792
|
const subFiles = await loadSubFiles(spec.fullPath);
|
|
3793
|
+
if (options.json) {
|
|
3794
|
+
const readmeStat2 = await fs9.stat(spec.filePath);
|
|
3795
|
+
const readmeContent2 = await fs9.readFile(spec.filePath, "utf-8");
|
|
3796
|
+
const readmeTokens2 = await countTokens({ content: readmeContent2 });
|
|
3797
|
+
const jsonOutput = {
|
|
3798
|
+
spec: spec.name,
|
|
3799
|
+
path: spec.fullPath,
|
|
3800
|
+
files: [
|
|
3801
|
+
{
|
|
3802
|
+
name: "README.md",
|
|
3803
|
+
type: "required",
|
|
3804
|
+
size: readmeStat2.size,
|
|
3805
|
+
tokens: readmeTokens2.total
|
|
3806
|
+
},
|
|
3807
|
+
...subFiles.map((f) => ({
|
|
3808
|
+
name: f.name,
|
|
3809
|
+
type: f.type,
|
|
3810
|
+
size: f.size
|
|
3811
|
+
}))
|
|
3812
|
+
],
|
|
3813
|
+
total: subFiles.length + 1
|
|
3814
|
+
};
|
|
3815
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
3816
|
+
return;
|
|
3817
|
+
}
|
|
3669
3818
|
console.log("");
|
|
3670
3819
|
console.log(chalk19.cyan(`\u{1F4C4} Files in ${sanitizeUserInput(spec.name)}`));
|
|
3671
3820
|
console.log("");
|
|
@@ -4530,13 +4679,13 @@ Validating ${specs.length} specs...
|
|
|
4530
4679
|
|
|
4531
4680
|
// src/commands/validate.ts
|
|
4532
4681
|
function validateCommand() {
|
|
4533
|
-
return new Command("validate").description("Validate specs for quality issues").argument("[specs...]", "Specific specs to validate (optional)").option("--max-lines <number>", "Custom line limit (default: 400)", parseInt).option("--verbose", "Show passing specs").option("--quiet", "Suppress warnings, only show errors").option("--format <format>", "Output format: default, json, compact", "default").option("--rule <rule>", "Filter by specific rule name (e.g., max-lines, frontmatter)").option("--warnings-only", "Treat all issues as warnings, never fail (useful for CI pre-release checks)").action(async (specs, options) => {
|
|
4682
|
+
return new Command("validate").description("Validate specs for quality issues").argument("[specs...]", "Specific specs to validate (optional)").option("--max-lines <number>", "Custom line limit (default: 400)", parseInt).option("--verbose", "Show passing specs").option("--quiet", "Suppress warnings, only show errors").option("--format <format>", "Output format: default, json, compact", "default").option("--json", "Output as JSON (shorthand for --format json)").option("--rule <rule>", "Filter by specific rule name (e.g., max-lines, frontmatter)").option("--warnings-only", "Treat all issues as warnings, never fail (useful for CI pre-release checks)").action(async (specs, options) => {
|
|
4534
4683
|
const passed = await validateSpecs({
|
|
4535
4684
|
maxLines: options.maxLines,
|
|
4536
4685
|
specs: specs && specs.length > 0 ? specs : void 0,
|
|
4537
4686
|
verbose: options.verbose,
|
|
4538
4687
|
quiet: options.quiet,
|
|
4539
|
-
format: options.format,
|
|
4688
|
+
format: options.json ? "json" : options.format,
|
|
4540
4689
|
rule: options.rule,
|
|
4541
4690
|
warningsOnly: options.warningsOnly
|
|
4542
4691
|
});
|
|
@@ -5011,7 +5160,7 @@ function calculateVelocityMetrics(specs) {
|
|
|
5011
5160
|
|
|
5012
5161
|
// src/commands/board.ts
|
|
5013
5162
|
function boardCommand() {
|
|
5014
|
-
return new Command("board").description("Show Kanban-style board view with project completion summary").option("--complete", "Include complete specs (default: hidden)").option("--simple", "Hide completion summary (kanban only)").option("--completion-only", "Show only completion summary (no kanban)").option("--tag <tag>", "Filter by tag").option("--assignee <name>", "Filter by assignee").action(async (options) => {
|
|
5163
|
+
return new Command("board").description("Show Kanban-style board view with project completion summary").option("--complete", "Include complete specs (default: hidden)").option("--simple", "Hide completion summary (kanban only)").option("--completion-only", "Show only completion summary (no kanban)").option("--tag <tag>", "Filter by tag").option("--assignee <name>", "Filter by assignee").option("--json", "Output as JSON").action(async (options) => {
|
|
5015
5164
|
await showBoard(options);
|
|
5016
5165
|
});
|
|
5017
5166
|
}
|
|
@@ -5032,7 +5181,11 @@ async function showBoard(options) {
|
|
|
5032
5181
|
})
|
|
5033
5182
|
);
|
|
5034
5183
|
if (specs.length === 0) {
|
|
5035
|
-
|
|
5184
|
+
if (options.json) {
|
|
5185
|
+
console.log(JSON.stringify({ columns: {}, total: 0 }, null, 2));
|
|
5186
|
+
} else {
|
|
5187
|
+
console.log(chalk19.dim("No specs found."));
|
|
5188
|
+
}
|
|
5036
5189
|
return;
|
|
5037
5190
|
}
|
|
5038
5191
|
const columns = {
|
|
@@ -5047,6 +5200,29 @@ async function showBoard(options) {
|
|
|
5047
5200
|
columns[status].push(spec);
|
|
5048
5201
|
}
|
|
5049
5202
|
}
|
|
5203
|
+
if (options.json) {
|
|
5204
|
+
const completionMetrics = calculateCompletion(specs);
|
|
5205
|
+
const velocityMetrics = calculateVelocityMetrics(specs);
|
|
5206
|
+
const jsonOutput = {
|
|
5207
|
+
columns: {
|
|
5208
|
+
planned: columns.planned.map((s) => ({ path: s.path, priority: s.frontmatter.priority, assignee: s.frontmatter.assignee, tags: s.frontmatter.tags })),
|
|
5209
|
+
"in-progress": columns["in-progress"].map((s) => ({ path: s.path, priority: s.frontmatter.priority, assignee: s.frontmatter.assignee, tags: s.frontmatter.tags })),
|
|
5210
|
+
complete: columns.complete.map((s) => ({ path: s.path, priority: s.frontmatter.priority, assignee: s.frontmatter.assignee, tags: s.frontmatter.tags }))
|
|
5211
|
+
},
|
|
5212
|
+
summary: {
|
|
5213
|
+
total: completionMetrics.totalSpecs,
|
|
5214
|
+
active: completionMetrics.activeSpecs,
|
|
5215
|
+
complete: completionMetrics.completeSpecs,
|
|
5216
|
+
completionRate: completionMetrics.score,
|
|
5217
|
+
velocity: {
|
|
5218
|
+
avgCycleTime: velocityMetrics.cycleTime.average,
|
|
5219
|
+
throughputPerWeek: velocityMetrics.throughput.perWeek / 7 * 7
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
5222
|
+
};
|
|
5223
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
5224
|
+
return;
|
|
5225
|
+
}
|
|
5050
5226
|
console.log(chalk19.bold.cyan("\u{1F4CB} Spec Kanban Board"));
|
|
5051
5227
|
if (options.tag || options.assignee) {
|
|
5052
5228
|
const filterParts = [];
|
|
@@ -5672,14 +5848,15 @@ async function showStats(options) {
|
|
|
5672
5848
|
}
|
|
5673
5849
|
}
|
|
5674
5850
|
function searchCommand() {
|
|
5675
|
-
return new Command("search").description("Full-text search with metadata filters").argument("<query>", "Search query").option("--status <status>", "Filter by status").option("--tag <tag>", "Filter by tag").option("--priority <priority>", "Filter by priority").option("--assignee <name>", "Filter by assignee").option("--field <name=value...>", "Filter by custom field (can specify multiple)").action(async (query, options) => {
|
|
5851
|
+
return new Command("search").description("Full-text search with metadata filters").argument("<query>", "Search query").option("--status <status>", "Filter by status").option("--tag <tag>", "Filter by tag").option("--priority <priority>", "Filter by priority").option("--assignee <name>", "Filter by assignee").option("--field <name=value...>", "Filter by custom field (can specify multiple)").option("--json", "Output as JSON").action(async (query, options) => {
|
|
5676
5852
|
const customFields = parseCustomFieldOptions(options.field);
|
|
5677
5853
|
await performSearch(query, {
|
|
5678
5854
|
status: options.status,
|
|
5679
5855
|
tag: options.tag,
|
|
5680
5856
|
priority: options.priority,
|
|
5681
5857
|
assignee: options.assignee,
|
|
5682
|
-
customFields: Object.keys(customFields).length > 0 ? customFields : void 0
|
|
5858
|
+
customFields: Object.keys(customFields).length > 0 ? customFields : void 0,
|
|
5859
|
+
json: options.json
|
|
5683
5860
|
});
|
|
5684
5861
|
});
|
|
5685
5862
|
}
|
|
@@ -5718,6 +5895,25 @@ async function performSearch(query, options) {
|
|
|
5718
5895
|
contextLength: 80
|
|
5719
5896
|
});
|
|
5720
5897
|
const { results, metadata } = searchResult;
|
|
5898
|
+
if (options.json) {
|
|
5899
|
+
const jsonOutput = {
|
|
5900
|
+
query,
|
|
5901
|
+
results: results.map((r) => ({
|
|
5902
|
+
spec: r.spec.path,
|
|
5903
|
+
score: r.score,
|
|
5904
|
+
totalMatches: r.totalMatches,
|
|
5905
|
+
matches: r.matches.map((m) => ({
|
|
5906
|
+
field: m.field,
|
|
5907
|
+
text: m.text,
|
|
5908
|
+
lineNumber: m.lineNumber
|
|
5909
|
+
}))
|
|
5910
|
+
})),
|
|
5911
|
+
metadata,
|
|
5912
|
+
filters: filter
|
|
5913
|
+
};
|
|
5914
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
5915
|
+
return;
|
|
5916
|
+
}
|
|
5721
5917
|
if (results.length === 0) {
|
|
5722
5918
|
console.log("");
|
|
5723
5919
|
console.log(chalk19.yellow(`\u{1F50D} No specs found matching "${sanitizeUserInput(query)}"`));
|
|
@@ -5958,7 +6154,7 @@ function displayChain(node, level) {
|
|
|
5958
6154
|
}
|
|
5959
6155
|
}
|
|
5960
6156
|
function timelineCommand() {
|
|
5961
|
-
return new Command("timeline").description("Show creation/completion over time").option("--days <n>", "Show last N days (default: 30)", parseInt).option("--by-tag", "Group by tag").option("--by-assignee", "Group by assignee").action(async (options) => {
|
|
6157
|
+
return new Command("timeline").description("Show creation/completion over time").option("--days <n>", "Show last N days (default: 30)", parseInt).option("--by-tag", "Group by tag").option("--by-assignee", "Group by assignee").option("--json", "Output as JSON").action(async (options) => {
|
|
5962
6158
|
await showTimeline(options);
|
|
5963
6159
|
});
|
|
5964
6160
|
}
|
|
@@ -5997,6 +6193,16 @@ async function showTimeline(options) {
|
|
|
5997
6193
|
}
|
|
5998
6194
|
}
|
|
5999
6195
|
}
|
|
6196
|
+
if (options.json) {
|
|
6197
|
+
const jsonOutput = {
|
|
6198
|
+
days,
|
|
6199
|
+
createdByDate,
|
|
6200
|
+
completedByDate,
|
|
6201
|
+
createdByMonth
|
|
6202
|
+
};
|
|
6203
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
6204
|
+
return;
|
|
6205
|
+
}
|
|
6000
6206
|
console.log(chalk19.bold.cyan("\u{1F4C8} Spec Timeline"));
|
|
6001
6207
|
console.log("");
|
|
6002
6208
|
const allDates = /* @__PURE__ */ new Set([...Object.keys(createdByDate), ...Object.keys(completedByDate)]);
|
|
@@ -6135,7 +6341,7 @@ var PRIORITY_CONFIG3 = {
|
|
|
6135
6341
|
low: { emoji: "\u{1F7E2}", label: "LOW", colorFn: chalk19.green }
|
|
6136
6342
|
};
|
|
6137
6343
|
function ganttCommand() {
|
|
6138
|
-
return new Command("gantt").description("Show timeline with dependencies").option("--weeks <n>", "Show N weeks (default: 4)", parseInt).option("--show-complete", "Include completed specs").option("--critical-path", "Highlight critical path").action(async (options) => {
|
|
6344
|
+
return new Command("gantt").description("Show timeline with dependencies").option("--weeks <n>", "Show N weeks (default: 4)", parseInt).option("--show-complete", "Include completed specs").option("--critical-path", "Highlight critical path").option("--json", "Output as JSON").action(async (options) => {
|
|
6139
6345
|
await showGantt(options);
|
|
6140
6346
|
});
|
|
6141
6347
|
}
|
|
@@ -6160,8 +6366,28 @@ async function showGantt(options) {
|
|
|
6160
6366
|
return spec.frontmatter.status !== "archived";
|
|
6161
6367
|
});
|
|
6162
6368
|
if (relevantSpecs.length === 0) {
|
|
6163
|
-
|
|
6164
|
-
|
|
6369
|
+
if (options.json) {
|
|
6370
|
+
console.log(JSON.stringify({ specs: [], weeks }, null, 2));
|
|
6371
|
+
} else {
|
|
6372
|
+
console.log(chalk19.dim("No active specs found."));
|
|
6373
|
+
console.log(chalk19.dim("Tip: Use --show-complete to include completed specs."));
|
|
6374
|
+
}
|
|
6375
|
+
return;
|
|
6376
|
+
}
|
|
6377
|
+
if (options.json) {
|
|
6378
|
+
const jsonOutput = {
|
|
6379
|
+
weeks,
|
|
6380
|
+
specs: relevantSpecs.map((spec) => ({
|
|
6381
|
+
path: spec.path,
|
|
6382
|
+
status: spec.frontmatter.status,
|
|
6383
|
+
priority: spec.frontmatter.priority,
|
|
6384
|
+
created: spec.frontmatter.created,
|
|
6385
|
+
completed: spec.frontmatter.completed,
|
|
6386
|
+
due: spec.frontmatter.due,
|
|
6387
|
+
dependsOn: spec.frontmatter.depends_on
|
|
6388
|
+
}))
|
|
6389
|
+
};
|
|
6390
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
6165
6391
|
return;
|
|
6166
6392
|
}
|
|
6167
6393
|
const groupedSpecs = {
|
|
@@ -8808,5 +9034,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
8808
9034
|
}
|
|
8809
9035
|
|
|
8810
9036
|
export { analyzeCommand, archiveCommand, backfillCommand, boardCommand, checkCommand, compactCommand, createCommand, createMcpServer, depsCommand, examplesCommand, filesCommand, ganttCommand, initCommand, linkCommand, listCommand, mcpCommand, migrateCommand, openCommand, searchCommand, splitCommand, statsCommand, templatesCommand, timelineCommand, tokensCommand, uiCommand, unlinkCommand, updateCommand, validateCommand, viewCommand };
|
|
8811
|
-
//# sourceMappingURL=chunk-
|
|
8812
|
-
//# sourceMappingURL=chunk-
|
|
9037
|
+
//# sourceMappingURL=chunk-RTEGSMVL.js.map
|
|
9038
|
+
//# sourceMappingURL=chunk-RTEGSMVL.js.map
|