@specforge/cli 0.2.8 → 0.2.9

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.
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,OAAO,EACL,aAAa,EAGd,MAAM,mBAAmB,CAAC;AA0D3B;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAoExE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkB5D"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,OAAO,EACL,aAAa,EAKd,MAAM,mBAAmB,CAAC;AAqD3B;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ExE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkB5D"}
@@ -11,29 +11,27 @@ function renderStatus(report, next, specificationId) {
11
11
  printBlank();
12
12
  console.log(colors.bold("Context"));
13
13
  console.log(colors.muted("\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
14
- console.log(`Project: ${report.project?.name ?? colors.muted("(unknown)")}`);
15
- const specEntry = specificationId ? report.specifications.find((s) => s.specification.id === specificationId) : report.specifications[0];
14
+ console.log(`Project: ${report?.project?.name ?? colors.muted("(unknown)")}`);
15
+ const specs = report?.specifications ?? [];
16
+ const specEntry = specificationId ? specs.find((s) => s.id === specificationId) : specs[0];
16
17
  if (specEntry) {
17
- const spec = specEntry.specification;
18
- const specStatus = spec.status;
19
- console.log(`Spec: ${spec.title}${specStatus ? colors.muted(` (${specStatus})`) : ""}`);
18
+ console.log(
19
+ `Spec: ${specEntry.title}${specEntry.status ? colors.muted(` (${specEntry.status})`) : ""}`
20
+ );
20
21
  printBlank();
21
22
  console.log(colors.bold("Progress"));
22
23
  console.log(colors.muted("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
23
- const pct = specEntry.totalTickets > 0 ? Math.round(specEntry.completedTickets / specEntry.totalTickets * 100) : 0;
24
- console.log(`Tickets: ${specEntry.completedTickets}/${specEntry.totalTickets} (${pct}%)`);
25
- console.log(`Epics: ${specEntry.completedEpics}/${specEntry.totalEpics}`);
24
+ const pct = typeof specEntry.progress === "number" ? Math.round(specEntry.progress) : 0;
25
+ console.log(`Progress: ${pct}%`);
26
+ if (typeof specEntry.ticketsRemaining === "number") {
27
+ console.log(`Remaining: ${specEntry.ticketsRemaining} ticket${specEntry.ticketsRemaining === 1 ? "" : "s"}`);
28
+ }
29
+ if (specEntry.blockerCount && specEntry.blockerCount > 0) {
30
+ console.log(`Blockers: ${specEntry.blockerCount}`);
31
+ }
26
32
  } else {
27
33
  console.log(colors.muted("Spec: (none active \u2014 run `specforge switch <id>`)"));
28
34
  }
29
- if (report.recentActivity && report.recentActivity.length > 0) {
30
- printBlank();
31
- console.log(colors.bold("Recent activity"));
32
- console.log(colors.muted("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
33
- for (const a of report.recentActivity.slice(0, 3)) {
34
- console.log(` ${a.action} \u2014 ${a.ticketTitle}`);
35
- }
36
- }
37
35
  if (next && next.items.length > 0) {
38
36
  printBlank();
39
37
  console.log(colors.bold("Next actionable"));
@@ -64,18 +62,20 @@ async function statusAction(options) {
64
62
  );
65
63
  }
66
64
  const spinner = ora({ text: "Fetching status...", color: "cyan" }).start();
65
+ let report;
66
+ let next;
67
67
  try {
68
68
  const client = new ApiClient({
69
69
  apiKey: config.apiKey,
70
70
  apiUrl: config.apiUrl,
71
71
  debug: config.debug
72
72
  });
73
- const report = await client.call("get_report", {
73
+ const response = await client.call("get_report", {
74
74
  type: "implementation",
75
75
  scope,
76
76
  scopeId
77
77
  });
78
- let next;
78
+ report = response?.report;
79
79
  if (config.specificationId) {
80
80
  try {
81
81
  next = await client.call("get_next_actionable_tickets", {
@@ -85,11 +85,6 @@ async function statusAction(options) {
85
85
  }
86
86
  }
87
87
  spinner.stop();
88
- if (options.json) {
89
- console.log(JSON.stringify({ report, nextActionable: next ?? null }, null, 2));
90
- } else {
91
- renderStatus(report, next, config.specificationId);
92
- }
93
88
  } catch (error) {
94
89
  spinner.fail("Failed to fetch status");
95
90
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -102,6 +97,11 @@ async function statusAction(options) {
102
97
  }
103
98
  throw new NetworkError(`Failed to fetch status: ${message}`);
104
99
  }
100
+ if (options.json) {
101
+ console.log(JSON.stringify({ report: report ?? null, nextActionable: next ?? null }, null, 2));
102
+ } else {
103
+ renderStatus(report, next, config.specificationId);
104
+ }
105
105
  }
106
106
  function registerStatusCommand(program) {
107
107
  program.command("status").description("Show active project/spec, implementation progress, and next actionable tickets").option("--json", "Output as JSON").addHelpText("after", `
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/cli/commands/status.ts"],"sourcesContent":["/**\n * Status Command\n *\n * Shows what this terminal is pointed at (project / spec from\n * `.specforge/config.json`) plus a live implementation report for the active\n * spec and the next actionable tickets. Uses only canonical tools —\n * `get_report` (type='implementation') + `get_next_actionable_tickets`.\n */\n\nimport { Command } from 'commander';\nimport ora from 'ora';\nimport { resolveConfig } from '../config/index.js';\nimport { withErrorHandler, CliError, NetworkError } from '../middleware/error-handler.js';\nimport { printBlank } from '../ui/output.js';\nimport { colors } from '../ui/colors.js';\nimport { ApiClient } from '../../client/api-client.js';\nimport type { ImplementationSummary } from '../../types/index.js';\nimport {\n StatusOptions,\n NextActionableResponse,\n formatTicketNumber,\n} from './status.types.js';\n\n/**\n * Render the human-facing status view.\n */\nfunction renderStatus(\n report: ImplementationSummary,\n next: NextActionableResponse | undefined,\n specificationId: string | null\n): void {\n printBlank();\n console.log(colors.bold('Context'));\n console.log(colors.muted('───────'));\n console.log(`Project: ${report.project?.name ?? colors.muted('(unknown)')}`);\n\n const specEntry = specificationId\n ? report.specifications.find((s) => s.specification.id === specificationId)\n : report.specifications[0];\n\n if (specEntry) {\n const spec = specEntry.specification;\n const specStatus = (spec as { status?: string }).status;\n console.log(`Spec: ${spec.title}${specStatus ? colors.muted(` (${specStatus})`) : ''}`);\n\n printBlank();\n console.log(colors.bold('Progress'));\n console.log(colors.muted('────────'));\n const pct = specEntry.totalTickets > 0\n ? Math.round((specEntry.completedTickets / specEntry.totalTickets) * 100)\n : 0;\n console.log(`Tickets: ${specEntry.completedTickets}/${specEntry.totalTickets} (${pct}%)`);\n console.log(`Epics: ${specEntry.completedEpics}/${specEntry.totalEpics}`);\n } else {\n console.log(colors.muted('Spec: (none active run `specforge switch <id>`)'));\n }\n\n if (report.recentActivity && report.recentActivity.length > 0) {\n printBlank();\n console.log(colors.bold('Recent activity'));\n console.log(colors.muted('───────────────'));\n for (const a of report.recentActivity.slice(0, 3)) {\n console.log(` ${a.action} ${a.ticketTitle}`);\n }\n }\n\n if (next && next.items.length > 0) {\n printBlank();\n console.log(colors.bold('Next actionable'));\n console.log(colors.muted('───────────────'));\n for (const t of next.items.slice(0, 5)) {\n const priority = t.priority ? colors.muted(` [${t.priority}]`) : '';\n console.log(` ${formatTicketNumber(t.ticketNumber)} ${t.title}${priority}`);\n }\n }\n\n printBlank();\n}\n\n/**\n * Status command action handler\n */\nexport async function statusAction(options: StatusOptions): Promise<void> {\n const config = resolveConfig();\n\n if (!config.apiKey) {\n throw new CliError(\n 'Not authenticated',\n 1,\n 'Run `specforge login` to authenticate first'\n );\n }\n\n const scope = config.specificationId ? 'specification' : 'project';\n const scopeId = config.specificationId ?? config.projectId;\n if (!scopeId) {\n throw new CliError(\n 'No active project or specification',\n 1,\n 'Run `specforge init` or `specforge switch <id>` first'\n );\n }\n\n const spinner = ora({ text: 'Fetching status...', color: 'cyan' }).start();\n\n try {\n const client = new ApiClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n const report = await client.call<ImplementationSummary>('get_report', {\n type: 'implementation',\n scope,\n scopeId,\n });\n\n let next: NextActionableResponse | undefined;\n if (config.specificationId) {\n try {\n next = await client.call<NextActionableResponse>('get_next_actionable_tickets', {\n specificationId: config.specificationId,\n });\n } catch {\n // Next-actionable is a best-effort enrichment — ignore failures.\n }\n }\n\n spinner.stop();\n\n if (options.json) {\n console.log(JSON.stringify({ report, nextActionable: next ?? null }, null, 2));\n } else {\n renderStatus(report, next, config.specificationId);\n }\n } catch (error) {\n spinner.fail('Failed to fetch status');\n\n const message = error instanceof Error ? error.message : 'Unknown error';\n if (message.includes('401') || message.includes('Unauthorized')) {\n throw new CliError(\n 'Authentication failed',\n 1,\n 'Your API key may be invalid. Run `specforge login` to re-authenticate'\n );\n }\n\n throw new NetworkError(`Failed to fetch status: ${message}`);\n }\n}\n\n/**\n * Register status command with Commander\n */\nexport function registerStatusCommand(program: Command): void {\n program\n .command('status')\n .description('Show active project/spec, implementation progress, and next actionable tickets')\n .option('--json', 'Output as JSON')\n .addHelpText('after', `\nExamples:\n $ specforge status # Active project/spec + progress + next tickets\n $ specforge status --json # Machine-readable output\n\nShows:\n - Context: active project and specification (from .specforge/config.json)\n - Progress: epic/ticket completion for the active spec\n - Next actionable: ready-to-work tickets\n\nUse 'specforge switch <id>' to change the active project or specification.\n`)\n .action(withErrorHandler(statusAction));\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/cli/commands/status.ts"],"sourcesContent":["/**\n * Status Command\n *\n * Shows what this terminal is pointed at (project / spec from\n * `.specforge/config.json`) plus a live implementation report for the active\n * spec and the next actionable tickets. Uses only canonical tools —\n * `get_report` (type='implementation') + `get_next_actionable_tickets`.\n */\n\nimport { Command } from 'commander';\nimport ora from 'ora';\nimport { resolveConfig } from '../config/index.js';\nimport { withErrorHandler, CliError, NetworkError } from '../middleware/error-handler.js';\nimport { printBlank } from '../ui/output.js';\nimport { colors } from '../ui/colors.js';\nimport { ApiClient } from '../../client/api-client.js';\nimport {\n StatusOptions,\n NextActionableResponse,\n GetReportResponse,\n ImplementationReport,\n formatTicketNumber,\n} from './status.types.js';\n\n/**\n * Render the human-facing status view.\n */\nfunction renderStatus(\n report: ImplementationReport | undefined,\n next: NextActionableResponse | undefined,\n specificationId: string | null\n): void {\n printBlank();\n console.log(colors.bold('Context'));\n console.log(colors.muted('───────'));\n console.log(`Project: ${report?.project?.name ?? colors.muted('(unknown)')}`);\n\n const specs = report?.specifications ?? [];\n const specEntry = specificationId\n ? specs.find((s) => s.id === specificationId)\n : specs[0];\n\n if (specEntry) {\n console.log(\n `Spec: ${specEntry.title}${specEntry.status ? colors.muted(` (${specEntry.status})`) : ''}`\n );\n\n printBlank();\n console.log(colors.bold('Progress'));\n console.log(colors.muted('────────'));\n const pct = typeof specEntry.progress === 'number' ? Math.round(specEntry.progress) : 0;\n console.log(`Progress: ${pct}%`);\n if (typeof specEntry.ticketsRemaining === 'number') {\n console.log(`Remaining: ${specEntry.ticketsRemaining} ticket${specEntry.ticketsRemaining === 1 ? '' : 's'}`);\n }\n if (specEntry.blockerCount && specEntry.blockerCount > 0) {\n console.log(`Blockers: ${specEntry.blockerCount}`);\n }\n } else {\n console.log(colors.muted('Spec: (none active run `specforge switch <id>`)'));\n }\n\n if (next && next.items.length > 0) {\n printBlank();\n console.log(colors.bold('Next actionable'));\n console.log(colors.muted('───────────────'));\n for (const t of next.items.slice(0, 5)) {\n const priority = t.priority ? colors.muted(` [${t.priority}]`) : '';\n console.log(` ${formatTicketNumber(t.ticketNumber)} ${t.title}${priority}`);\n }\n }\n\n printBlank();\n}\n\n/**\n * Status command action handler\n */\nexport async function statusAction(options: StatusOptions): Promise<void> {\n const config = resolveConfig();\n\n if (!config.apiKey) {\n throw new CliError(\n 'Not authenticated',\n 1,\n 'Run `specforge login` to authenticate first'\n );\n }\n\n const scope = config.specificationId ? 'specification' : 'project';\n const scopeId = config.specificationId ?? config.projectId;\n if (!scopeId) {\n throw new CliError(\n 'No active project or specification',\n 1,\n 'Run `specforge init` or `specforge switch <id>` first'\n );\n }\n\n const spinner = ora({ text: 'Fetching status...', color: 'cyan' }).start();\n\n let report: ImplementationReport | undefined;\n let next: NextActionableResponse | undefined;\n\n try {\n const client = new ApiClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n // `get_report` returns a metadata wrapper — { type, scope, scopeId, format,\n // report } — with the implementation report nested under `report` (NOT the\n // top-level body). The `/local` transport has no `{ data }` envelope to\n // unwrap here, so we reach into `.report` ourselves.\n const response = await client.call<GetReportResponse>('get_report', {\n type: 'implementation',\n scope,\n scopeId,\n });\n report = response?.report;\n\n if (config.specificationId) {\n try {\n next = await client.call<NextActionableResponse>('get_next_actionable_tickets', {\n specificationId: config.specificationId,\n });\n } catch {\n // Next-actionable is a best-effort enrichment — ignore failures.\n }\n }\n\n spinner.stop();\n } catch (error) {\n spinner.fail('Failed to fetch status');\n\n const message = error instanceof Error ? error.message : 'Unknown error';\n if (message.includes('401') || message.includes('Unauthorized')) {\n throw new CliError(\n 'Authentication failed',\n 1,\n 'Your API key may be invalid. Run `specforge login` to re-authenticate'\n );\n }\n\n throw new NetworkError(`Failed to fetch status: ${message}`);\n }\n\n // Render OUTSIDE the try: a rendering bug must surface as itself, never as a\n // mislabelled \"Failed to fetch status\" network error (the fetch already\n // succeeded by this point).\n if (options.json) {\n console.log(JSON.stringify({ report: report ?? null, nextActionable: next ?? null }, null, 2));\n } else {\n renderStatus(report, next, config.specificationId);\n }\n}\n\n/**\n * Register status command with Commander\n */\nexport function registerStatusCommand(program: Command): void {\n program\n .command('status')\n .description('Show active project/spec, implementation progress, and next actionable tickets')\n .option('--json', 'Output as JSON')\n .addHelpText('after', `\nExamples:\n $ specforge status # Active project/spec + progress + next tickets\n $ specforge status --json # Machine-readable output\n\nShows:\n - Context: active project and specification (from .specforge/config.json)\n - Progress: epic/ticket completion for the active spec\n - Next actionable: ready-to-work tickets\n\nUse 'specforge switch <id>' to change the active project or specification.\n`)\n .action(withErrorHandler(statusAction));\n}\n"],"mappings":"AAUA,OAAO,SAAS;AAChB,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB,UAAU,oBAAoB;AACzD,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B;AAAA,EAKE;AAAA,OACK;AAKP,SAAS,aACP,QACA,MACA,iBACM;AACN,aAAW;AACX,UAAQ,IAAI,OAAO,KAAK,SAAS,CAAC;AAClC,UAAQ,IAAI,OAAO,MAAM,4CAAS,CAAC;AACnC,UAAQ,IAAI,YAAY,QAAQ,SAAS,QAAQ,OAAO,MAAM,WAAW,CAAC,EAAE;AAE5E,QAAM,QAAQ,QAAQ,kBAAkB,CAAC;AACzC,QAAM,YAAY,kBACd,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,eAAe,IAC1C,MAAM,CAAC;AAEX,MAAI,WAAW;AACb,YAAQ;AAAA,MACN,YAAY,UAAU,KAAK,GAAG,UAAU,SAAS,OAAO,MAAM,KAAK,UAAU,MAAM,GAAG,IAAI,EAAE;AAAA,IAC9F;AAEA,eAAW;AACX,YAAQ,IAAI,OAAO,KAAK,UAAU,CAAC;AACnC,YAAQ,IAAI,OAAO,MAAM,kDAAU,CAAC;AACpC,UAAM,MAAM,OAAO,UAAU,aAAa,WAAW,KAAK,MAAM,UAAU,QAAQ,IAAI;AACtF,YAAQ,IAAI,cAAc,GAAG,GAAG;AAChC,QAAI,OAAO,UAAU,qBAAqB,UAAU;AAClD,cAAQ,IAAI,cAAc,UAAU,gBAAgB,UAAU,UAAU,qBAAqB,IAAI,KAAK,GAAG,EAAE;AAAA,IAC7G;AACA,QAAI,UAAU,gBAAgB,UAAU,eAAe,GAAG;AACxD,cAAQ,IAAI,cAAc,UAAU,YAAY,EAAE;AAAA,IACpD;AAAA,EACF,OAAO;AACL,YAAQ,IAAI,OAAO,MAAM,2DAAsD,CAAC;AAAA,EAClF;AAEA,MAAI,QAAQ,KAAK,MAAM,SAAS,GAAG;AACjC,eAAW;AACX,YAAQ,IAAI,OAAO,KAAK,iBAAiB,CAAC;AAC1C,YAAQ,IAAI,OAAO,MAAM,4FAAiB,CAAC;AAC3C,eAAW,KAAK,KAAK,MAAM,MAAM,GAAG,CAAC,GAAG;AACtC,YAAM,WAAW,EAAE,WAAW,OAAO,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI;AACjE,cAAQ,IAAI,KAAK,mBAAmB,EAAE,YAAY,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,EAAE;AAAA,IAC7E;AAAA,EACF;AAEA,aAAW;AACb;AAKA,eAAsB,aAAa,SAAuC;AACxE,QAAM,SAAS,cAAc;AAE7B,MAAI,CAAC,OAAO,QAAQ;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,OAAO,kBAAkB,kBAAkB;AACzD,QAAM,UAAU,OAAO,mBAAmB,OAAO;AACjD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,EAAE,MAAM,sBAAsB,OAAO,OAAO,CAAC,EAAE,MAAM;AAEzE,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,UAAM,SAAS,IAAI,UAAU;AAAA,MAC3B,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,IAChB,CAAC;AAMD,UAAM,WAAW,MAAM,OAAO,KAAwB,cAAc;AAAA,MAClE,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AACD,aAAS,UAAU;AAEnB,QAAI,OAAO,iBAAiB;AAC1B,UAAI;AACF,eAAO,MAAM,OAAO,KAA6B,+BAA+B;AAAA,UAC9E,iBAAiB,OAAO;AAAA,QAC1B,CAAC;AAAA,MACH,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,YAAQ,KAAK;AAAA,EACf,SAAS,OAAO;AACd,YAAQ,KAAK,wBAAwB;AAErC,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,QAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,cAAc,GAAG;AAC/D,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,IAAI,aAAa,2BAA2B,OAAO,EAAE;AAAA,EAC7D;AAKA,MAAI,QAAQ,MAAM;AAChB,YAAQ,IAAI,KAAK,UAAU,EAAE,QAAQ,UAAU,MAAM,gBAAgB,QAAQ,KAAK,GAAG,MAAM,CAAC,CAAC;AAAA,EAC/F,OAAO;AACL,iBAAa,QAAQ,MAAM,OAAO,eAAe;AAAA,EACnD;AACF;AAKO,SAAS,sBAAsB,SAAwB;AAC5D,UACG,QAAQ,QAAQ,EAChB,YAAY,gFAAgF,EAC5F,OAAO,UAAU,gBAAgB,EACjC,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAWzB,EACI,OAAO,iBAAiB,YAAY,CAAC;AAC1C;","names":[]}
@@ -30,6 +30,44 @@ export interface NextActionableResponse {
30
30
  items: NextActionableItem[];
31
31
  total: number;
32
32
  }
33
+ /**
34
+ * One spec's progress row inside the implementation report. The live
35
+ * `get_report` (type='implementation') returns FLAT spec rows — id/title/status
36
+ * at the top of each entry, plus progress metrics — NOT a nested
37
+ * `{ specification, completedTickets, … }` object (the stale
38
+ * `@specforge/report-types` shape). This local type mirrors the real wire.
39
+ */
40
+ export interface SpecProgressEntry {
41
+ id: string;
42
+ title: string;
43
+ status?: string;
44
+ /** Completion percentage, 0–100. */
45
+ progress?: number;
46
+ ticketsRemaining?: number;
47
+ estimatedCompletion?: string | null;
48
+ blockerCount?: number;
49
+ }
50
+ /**
51
+ * The `report` payload nested inside the get_report response wrapper.
52
+ */
53
+ export interface ImplementationReport {
54
+ project?: {
55
+ id: string;
56
+ name: string;
57
+ };
58
+ specifications?: SpecProgressEntry[];
59
+ }
60
+ /**
61
+ * The full get_report response — a metadata wrapper carrying the actual
62
+ * implementation report under `report`.
63
+ */
64
+ export interface GetReportResponse {
65
+ type?: string;
66
+ scope?: string;
67
+ scopeId?: string;
68
+ format?: string;
69
+ report?: ImplementationReport;
70
+ }
33
71
  /**
34
72
  * Format a ticket number with padding (e.g. 7 → "TKT-007").
35
73
  */
@@ -1 +1 @@
1
- {"version":3,"file":"status.types.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,EACpB,MAAM,SAAQ,EACd,OAAO,SAAI,GACV,MAAM,CAER"}
1
+ {"version":3,"file":"status.types.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,cAAc,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACtC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,EACpB,MAAM,SAAQ,EACd,OAAO,SAAI,GACV,MAAM,CAER"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/cli/commands/status.types.ts"],"sourcesContent":["/**\n * Status Command Types\n *\n * Post-M9: status shows the active project/spec (from config) plus a live\n * implementation report and the next actionable tickets. The pre-M9\n * working-context / session / dashboard / teams model was removed with its\n * backing ops (M9.4).\n */\n\n/**\n * Status command options from Commander\n */\nexport interface StatusOptions {\n /** Output as JSON */\n json?: boolean;\n}\n\n/**\n * A single next-actionable ticket (subset of get_next_actionable_tickets items).\n */\nexport interface NextActionableItem {\n id: string;\n ticketNumber: number;\n title: string;\n priority?: string;\n epicTitle?: string;\n}\n\n/**\n * Response shape of get_next_actionable_tickets.\n */\nexport interface NextActionableResponse {\n items: NextActionableItem[];\n total: number;\n}\n\n/**\n * Format a ticket number with padding (e.g. 7 → \"TKT-007\").\n */\nexport function formatTicketNumber(\n ticketNumber: number,\n prefix = 'TKT',\n padding = 3\n): string {\n return `${prefix}-${ticketNumber.toString().padStart(padding, '0')}`;\n}\n"],"mappings":"AAuCO,SAAS,mBACd,cACA,SAAS,OACT,UAAU,GACF;AACR,SAAO,GAAG,MAAM,IAAI,aAAa,SAAS,EAAE,SAAS,SAAS,GAAG,CAAC;AACpE;","names":[]}
1
+ {"version":3,"sources":["../../../src/cli/commands/status.types.ts"],"sourcesContent":["/**\n * Status Command Types\n *\n * Post-M9: status shows the active project/spec (from config) plus a live\n * implementation report and the next actionable tickets. The pre-M9\n * working-context / session / dashboard / teams model was removed with its\n * backing ops (M9.4).\n */\n\n/**\n * Status command options from Commander\n */\nexport interface StatusOptions {\n /** Output as JSON */\n json?: boolean;\n}\n\n/**\n * A single next-actionable ticket (subset of get_next_actionable_tickets items).\n */\nexport interface NextActionableItem {\n id: string;\n ticketNumber: number;\n title: string;\n priority?: string;\n epicTitle?: string;\n}\n\n/**\n * Response shape of get_next_actionable_tickets.\n */\nexport interface NextActionableResponse {\n items: NextActionableItem[];\n total: number;\n}\n\n/**\n * One spec's progress row inside the implementation report. The live\n * `get_report` (type='implementation') returns FLAT spec rows — id/title/status\n * at the top of each entry, plus progress metrics — NOT a nested\n * `{ specification, completedTickets, … }` object (the stale\n * `@specforge/report-types` shape). This local type mirrors the real wire.\n */\nexport interface SpecProgressEntry {\n id: string;\n title: string;\n status?: string;\n /** Completion percentage, 0–100. */\n progress?: number;\n ticketsRemaining?: number;\n estimatedCompletion?: string | null;\n blockerCount?: number;\n}\n\n/**\n * The `report` payload nested inside the get_report response wrapper.\n */\nexport interface ImplementationReport {\n project?: { id: string; name: string };\n specifications?: SpecProgressEntry[];\n}\n\n/**\n * The full get_report response — a metadata wrapper carrying the actual\n * implementation report under `report`.\n */\nexport interface GetReportResponse {\n type?: string;\n scope?: string;\n scopeId?: string;\n format?: string;\n report?: ImplementationReport;\n}\n\n/**\n * Format a ticket number with padding (e.g. 7 → \"TKT-007\").\n */\nexport function formatTicketNumber(\n ticketNumber: number,\n prefix = 'TKT',\n padding = 3\n): string {\n return `${prefix}-${ticketNumber.toString().padStart(padding, '0')}`;\n}\n"],"mappings":"AA6EO,SAAS,mBACd,cACA,SAAS,OACT,UAAU,GACF;AACR,SAAO,GAAG,MAAM,IAAI,aAAa,SAAS,EAAE,SAAS,SAAS,GAAG,CAAC;AACpE;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specforge/cli",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "MCP server for SpecForge - AI agent integration",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -61,7 +61,7 @@
61
61
  "@specforge/session-types",
62
62
  "@specforge/report-types"
63
63
  ],
64
- "gitHead": "e3abc5b580b5d0a8cd509b2e1f749d7b54fdec96",
64
+ "gitHead": "a6063a453ff8a71622a83cc0bb20a97517a3bde8",
65
65
  "scripts": {
66
66
  "build": "tsup && tsc --emitDeclarationOnly --outDir dist",
67
67
  "typecheck": "tsc --noEmit",