lean-spec 0.2.5-dev.20251120070726 → 0.2.5-dev.20251120093243

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.
@@ -6984,7 +6984,7 @@ function detectPackageManager(baseDir = process.cwd()) {
6984
6984
 
6985
6985
  // src/commands/ui.ts
6986
6986
  function uiCommand() {
6987
- return new Command("ui").description("Start local web UI for spec management").option("-s, --specs <dir>", "Specs directory (auto-detected if not specified)").option("-p, --port <port>", "Port to run on", "3000").option("--no-open", "Don't open browser automatically").option("--dev", "Run in development mode (only works in LeanSpec monorepo)").option("--dry-run", "Show what would run without executing").action(async (options) => {
6987
+ return new Command("ui").description("Start local web UI for spec management").option("-s, --specs <dir>", "Specs directory (auto-detected if not specified)").option("-p, --port <port>", "Port to run on", "3000").option("--no-open", "Don't open browser automatically").option("--multi-project", "Enable multi-project mode").option("--add-project <path>", "Add a project to multi-project registry").option("--discover <path>", "Discover LeanSpec projects in directory tree").option("--dev", "Run in development mode (only works in LeanSpec monorepo)").option("--dry-run", "Show what would run without executing").action(async (options) => {
6988
6988
  try {
6989
6989
  await startUi(options);
6990
6990
  } catch (error) {
@@ -7000,17 +7000,28 @@ async function startUi(options) {
7000
7000
  throw new Error(`Invalid port: ${options.port}`);
7001
7001
  }
7002
7002
  const cwd = process.cwd();
7003
- let specsDir;
7004
- if (options.specs) {
7005
- specsDir = resolve(cwd, options.specs);
7003
+ const specsMode = options.multiProject ? "multi-project" : "filesystem";
7004
+ let specsDir = "";
7005
+ if (!options.multiProject) {
7006
+ if (options.specs) {
7007
+ specsDir = resolve(cwd, options.specs);
7008
+ } else {
7009
+ const config = await loadConfig(cwd);
7010
+ specsDir = join(cwd, config.specsDir);
7011
+ }
7012
+ if (!existsSync(specsDir)) {
7013
+ console.error(chalk18.red(`\u2717 Specs directory not found: ${specsDir}`));
7014
+ console.log(chalk18.dim("\nRun `lean-spec init` to initialize LeanSpec in this directory."));
7015
+ throw new Error(`Specs directory not found: ${specsDir}`);
7016
+ }
7006
7017
  } else {
7007
- const config = await loadConfig(cwd);
7008
- specsDir = join(cwd, config.specsDir);
7009
- }
7010
- if (!existsSync(specsDir)) {
7011
- console.error(chalk18.red(`\u2717 Specs directory not found: ${specsDir}`));
7012
- console.log(chalk18.dim("\nRun `lean-spec init` to initialize LeanSpec in this directory."));
7013
- throw new Error(`Specs directory not found: ${specsDir}`);
7018
+ console.log(chalk18.cyan("\u2192 Multi-project mode enabled"));
7019
+ if (options.addProject) {
7020
+ console.log(chalk18.dim(` Adding project: ${options.addProject}`));
7021
+ }
7022
+ if (options.discover) {
7023
+ console.log(chalk18.dim(` Will discover projects in: ${options.discover}`));
7024
+ }
7014
7025
  }
7015
7026
  if (options.dev) {
7016
7027
  const isLeanSpecMonorepo = checkIsLeanSpecMonorepo(cwd);
@@ -7020,9 +7031,9 @@ async function startUi(options) {
7020
7031
  throw new Error("Not in LeanSpec monorepo");
7021
7032
  }
7022
7033
  const localUiDir = join(cwd, "packages/ui");
7023
- return runLocalWeb(localUiDir, specsDir, options);
7034
+ return runLocalWeb(localUiDir, specsDir, specsMode, options);
7024
7035
  }
7025
- return runPublishedUI(cwd, specsDir, options);
7036
+ return runPublishedUI(cwd, specsDir, specsMode, options);
7026
7037
  }
7027
7038
  function checkIsLeanSpecMonorepo(cwd) {
7028
7039
  const localUiDir = join(cwd, "packages/ui");
@@ -7037,14 +7048,14 @@ function checkIsLeanSpecMonorepo(cwd) {
7037
7048
  return false;
7038
7049
  }
7039
7050
  }
7040
- async function runLocalWeb(uiDir, specsDir, options) {
7051
+ async function runLocalWeb(uiDir, specsDir, specsMode, options) {
7041
7052
  console.log(chalk18.dim("\u2192 Detected LeanSpec monorepo, using local ui package\n"));
7042
7053
  const repoRoot = resolve(uiDir, "..", "..");
7043
7054
  const packageManager = detectPackageManager(repoRoot);
7044
7055
  if (options.dryRun) {
7045
7056
  console.log(chalk18.cyan("Would run:"));
7046
7057
  console.log(chalk18.dim(` cd ${uiDir}`));
7047
- console.log(chalk18.dim(` SPECS_MODE=filesystem SPECS_DIR=${specsDir} PORT=${options.port} ${packageManager} run dev`));
7058
+ console.log(chalk18.dim(` SPECS_MODE=${specsMode} SPECS_DIR=${specsDir} PORT=${options.port} ${packageManager} run dev`));
7048
7059
  if (options.open) {
7049
7060
  console.log(chalk18.dim(` open http://localhost:${options.port}`));
7050
7061
  }
@@ -7053,8 +7064,8 @@ async function runLocalWeb(uiDir, specsDir, options) {
7053
7064
  const spinner = ora("Starting web UI...").start();
7054
7065
  const env = {
7055
7066
  ...process.env,
7056
- SPECS_MODE: "filesystem",
7057
- SPECS_DIR: specsDir,
7067
+ SPECS_MODE: specsMode,
7068
+ SPECS_DIR: specsDir || "",
7058
7069
  PORT: options.port
7059
7070
  };
7060
7071
  const child = spawn(packageManager, ["run", "dev"], {
@@ -7068,7 +7079,55 @@ async function runLocalWeb(uiDir, specsDir, options) {
7068
7079
  console.log(chalk18.green(`
7069
7080
  \u2728 LeanSpec UI: http://localhost:${options.port}
7070
7081
  `));
7071
- console.log(chalk18.dim("Press Ctrl+C to stop\n"));
7082
+ if (options.multiProject) {
7083
+ console.log(chalk18.cyan("Multi-project mode is active"));
7084
+ if (options.addProject) {
7085
+ try {
7086
+ const res = await fetch(`http://localhost:${options.port}/api/projects`, {
7087
+ method: "POST",
7088
+ headers: { "Content-Type": "application/json" },
7089
+ body: JSON.stringify({ path: options.addProject })
7090
+ });
7091
+ if (res.ok) {
7092
+ console.log(chalk18.green(` \u2713 Added project: ${options.addProject}`));
7093
+ } else {
7094
+ console.error(chalk18.red(` \u2717 Failed to add project: ${options.addProject}`));
7095
+ }
7096
+ } catch (e) {
7097
+ console.error(chalk18.red(` \u2717 Failed to connect to server for adding project`));
7098
+ }
7099
+ }
7100
+ if (options.discover) {
7101
+ console.log(chalk18.dim(` Discovering projects in: ${options.discover}...`));
7102
+ try {
7103
+ const res = await fetch(`http://localhost:${options.port}/api/local-projects/discover`, {
7104
+ method: "POST",
7105
+ headers: { "Content-Type": "application/json" },
7106
+ body: JSON.stringify({ path: options.discover })
7107
+ });
7108
+ if (res.ok) {
7109
+ const data = await res.json();
7110
+ const discovered = data.discovered || [];
7111
+ console.log(chalk18.green(` \u2713 Found ${discovered.length} projects`));
7112
+ for (const p of discovered) {
7113
+ const addRes = await fetch(`http://localhost:${options.port}/api/projects`, {
7114
+ method: "POST",
7115
+ headers: { "Content-Type": "application/json" },
7116
+ body: JSON.stringify({ path: p.path })
7117
+ });
7118
+ if (addRes.ok) {
7119
+ console.log(chalk18.dim(` + Added: ${p.name} (${p.path})`));
7120
+ }
7121
+ }
7122
+ } else {
7123
+ console.error(chalk18.red(` \u2717 Failed to discover projects`));
7124
+ }
7125
+ } catch (e) {
7126
+ console.error(chalk18.red(` \u2717 Failed to connect to server for discovery`));
7127
+ }
7128
+ }
7129
+ }
7130
+ console.log(chalk18.dim("\nPress Ctrl+C to stop\n"));
7072
7131
  if (options.open) {
7073
7132
  try {
7074
7133
  const openModule = await import('open');
@@ -7115,10 +7174,10 @@ Process exited with code ${code}`));
7115
7174
  process.exit(0);
7116
7175
  });
7117
7176
  }
7118
- async function runPublishedUI(cwd, specsDir, options) {
7177
+ async function runPublishedUI(cwd, specsDir, specsMode, options) {
7119
7178
  console.log(chalk18.dim("\u2192 Using published @leanspec/ui package\n"));
7120
7179
  const packageManager = detectPackageManager(cwd);
7121
- const { command, args, preview } = buildUiRunner(packageManager, specsDir, options.port, options.open);
7180
+ const { command, args, preview } = buildUiRunner(packageManager, specsDir, specsMode, options.port, options.open, options.multiProject);
7122
7181
  if (options.dryRun) {
7123
7182
  console.log(chalk18.cyan("Would run:"));
7124
7183
  console.log(chalk18.dim(` ${preview}`));
@@ -7165,8 +7224,14 @@ async function runPublishedUI(cwd, specsDir, options) {
7165
7224
  process.exit(1);
7166
7225
  });
7167
7226
  }
7168
- function buildUiRunner(packageManager, specsDir, port, openBrowser) {
7169
- const uiArgs = ["@leanspec/ui", "--specs", specsDir, "--port", port];
7227
+ function buildUiRunner(packageManager, specsDir, specsMode, port, openBrowser, multiProject) {
7228
+ const uiArgs = ["@leanspec/ui"];
7229
+ if (!multiProject) {
7230
+ uiArgs.push("--specs", specsDir);
7231
+ } else {
7232
+ uiArgs.push("--multi-project");
7233
+ }
7234
+ uiArgs.push("--port", port);
7170
7235
  if (!openBrowser) {
7171
7236
  uiArgs.push("--no-open");
7172
7237
  }
@@ -8348,53 +8413,83 @@ function registerResources(server) {
8348
8413
  server.registerResource(...specResource());
8349
8414
  server.registerResource(...statsResource());
8350
8415
  }
8351
- function createFeatureSpecPrompt() {
8416
+ function planProjectRoadmapPrompt() {
8352
8417
  return [
8353
- "create-feature-spec",
8418
+ "plan-project-roadmap",
8354
8419
  {
8355
- title: "Create Feature Spec",
8356
- description: "Guided workflow to create a new feature specification",
8420
+ title: "Plan Project Roadmap",
8421
+ description: "Interactive roadmap planning with phases, tasks, and dependencies",
8357
8422
  argsSchema: {
8358
- featureName: z.string(),
8359
- description: z.string().optional()
8423
+ goal: z.string()
8360
8424
  }
8361
8425
  },
8362
- ({ featureName, description }) => ({
8426
+ ({ goal }) => ({
8363
8427
  messages: [
8364
8428
  {
8365
8429
  role: "user",
8366
8430
  content: {
8367
8431
  type: "text",
8368
- text: `Create a new feature specification for: ${featureName}${description ? `
8432
+ text: `Plan a project roadmap for: ${goal}
8369
8433
 
8370
- Description: ${description}` : ""}
8434
+ 1. **Review Existing Work**: Analyze current specs using \`list\`/\`board\`, identify what's already planned/in-progress, assess how existing work relates to the new goal
8435
+ 2. **Break Down Goal**: Decompose the goal into logical phases or milestones
8436
+ 3. **Identify Tasks**: List key tasks and work items for each phase
8437
+ 4. **Map Dependencies**: Establish dependencies between tasks (what must be done first)
8438
+ 5. **Create Specs**: Create specification documents for major work items using the \`create\` tool
8439
+ 6. **Set Relationships**: Use \`link\` tool to establish \`depends_on\` and \`related\` relationships
8440
+ 7. **Timeline Estimation**: Provide realistic timeline based on task complexity and project velocity
8441
+ 8. **Risk Analysis**: Identify risks, unknowns, and mitigation strategies
8371
8442
 
8372
- Please create this spec with appropriate metadata (status, priority, tags) and include standard sections like Overview, Design, Plan, and Test.`
8443
+ Use the following tools to build the roadmap:
8444
+ - \`list\` / \`board\` / \`stats\` - Understand current project state
8445
+ - \`create\` - Create new specs for roadmap items
8446
+ - \`link\` - Establish dependencies between specs
8447
+ - \`update\` - Set priority and metadata
8448
+
8449
+ Provide a clear roadmap with:
8450
+ - Phases/milestones with descriptions
8451
+ - Key specs to create
8452
+ - Dependency relationships
8453
+ - Recommended execution order
8454
+ - Actionable next steps to implement this plan`
8373
8455
  }
8374
8456
  }
8375
8457
  ]
8376
8458
  })
8377
8459
  ];
8378
8460
  }
8379
- function findRelatedSpecsPrompt() {
8461
+
8462
+ // src/mcp/prompts/project-progress-overview.ts
8463
+ function projectProgressOverviewPrompt() {
8380
8464
  return [
8381
- "find-related-specs",
8465
+ "project-progress-overview",
8382
8466
  {
8383
- title: "Find Related Specs",
8384
- description: "Discover specifications related to a topic or feature",
8385
- argsSchema: {
8386
- topic: z.string()
8387
- }
8467
+ title: "Project Progress Overview",
8468
+ description: "Generate comprehensive project status report combining specs, git history, and metrics"
8388
8469
  },
8389
- ({ topic }) => ({
8470
+ () => ({
8390
8471
  messages: [
8391
8472
  {
8392
8473
  role: "user",
8393
8474
  content: {
8394
8475
  type: "text",
8395
- text: `Find all specifications related to: ${topic}
8476
+ text: `Analyze project progress and provide a comprehensive overview:
8396
8477
 
8397
- Please search for this topic and show me the dependencies between related specs.`
8478
+ 1. **Spec Analysis**: Review all specs using \`board\` and \`stats\`, group by status (planned/in-progress/complete), highlight any blockers or dependencies
8479
+ 2. **Recent Activity**: Examine git commit history (last 2 weeks), identify key changes and development patterns
8480
+ 3. **Current State**: Assess what's actively being worked on, what's completed, what's planned
8481
+ 4. **Velocity Metrics**: Calculate completion rates, average time in each status, and throughput trends
8482
+ 5. **Risk Assessment**: Identify stalled specs, missing dependencies, potential bottlenecks
8483
+ 6. **Next Steps**: Recommend priority actions based on current project state
8484
+
8485
+ Use the following tools to gather data:
8486
+ - \`board\` - Get Kanban view of specs by status
8487
+ - \`stats\` - Get project metrics
8488
+ - \`list\` - List specs with filters
8489
+ - \`deps\` - Analyze dependencies for critical specs
8490
+ - Terminal git commands - Analyze recent commit history
8491
+
8492
+ Provide a clear, actionable summary that helps understand project health and next steps.`
8398
8493
  }
8399
8494
  }
8400
8495
  ]
@@ -8409,16 +8504,18 @@ function updateSpecStatusPrompt() {
8409
8504
  description: "Quick workflow to update specification status",
8410
8505
  argsSchema: {
8411
8506
  specPath: z.string(),
8412
- newStatus: z.enum(["planned", "in-progress", "complete", "archived"])
8507
+ status: z.enum(["planned", "in-progress", "complete", "archived"])
8413
8508
  }
8414
8509
  },
8415
- ({ specPath, newStatus }) => ({
8510
+ ({ specPath, status }) => ({
8416
8511
  messages: [
8417
8512
  {
8418
8513
  role: "user",
8419
8514
  content: {
8420
8515
  type: "text",
8421
- text: `Update the status of spec "${specPath}" to "${newStatus}". Use the update tool to make this change.`
8516
+ text: `Update the status of spec "${specPath}" to "${status}".
8517
+
8518
+ Use the \`update\` tool: \`update <spec> --status ${status}\``
8422
8519
  }
8423
8520
  }
8424
8521
  ]
@@ -8428,8 +8525,8 @@ function updateSpecStatusPrompt() {
8428
8525
 
8429
8526
  // src/mcp/prompts/registry.ts
8430
8527
  function registerPrompts(server) {
8431
- server.registerPrompt(...createFeatureSpecPrompt());
8432
- server.registerPrompt(...findRelatedSpecsPrompt());
8528
+ server.registerPrompt(...projectProgressOverviewPrompt());
8529
+ server.registerPrompt(...planProjectRoadmapPrompt());
8433
8530
  server.registerPrompt(...updateSpecStatusPrompt());
8434
8531
  }
8435
8532
 
@@ -8465,5 +8562,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
8465
8562
  }
8466
8563
 
8467
8564
  export { analyzeCommand, archiveCommand, backfillCommand, boardCommand, checkCommand, compactCommand, createCommand, createMcpServer, depsCommand, filesCommand, ganttCommand, initCommand, linkCommand, listCommand, mcpCommand, migrateCommand, openCommand, searchCommand, splitCommand, statsCommand, templatesCommand, timelineCommand, tokensCommand, uiCommand, unlinkCommand, updateCommand, validateCommand, viewCommand };
8468
- //# sourceMappingURL=chunk-D5HD2K67.js.map
8469
- //# sourceMappingURL=chunk-D5HD2K67.js.map
8565
+ //# sourceMappingURL=chunk-5A7MJSNZ.js.map
8566
+ //# sourceMappingURL=chunk-5A7MJSNZ.js.map