specsmd 0.1.23 → 0.1.25

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.
@@ -0,0 +1,78 @@
1
+ const { renderHeaderLines, truncate } = require('./components/header');
2
+ const { renderStatsLines } = require('./components/stats-strip');
3
+ const { renderErrorLines } = require('./components/error-banner');
4
+ const { renderHelpLines } = require('./components/help-footer');
5
+ const { renderRunsViewLines } = require('./views/runs-view');
6
+ const { renderOverviewViewLines } = require('./views/overview-view');
7
+
8
+ function normalizeWidth(width) {
9
+ if (!Number.isFinite(width)) {
10
+ return 120;
11
+ }
12
+
13
+ return Math.max(40, Math.min(Math.floor(width), 180));
14
+ }
15
+
16
+ function buildDashboardLines(params) {
17
+ const {
18
+ snapshot,
19
+ error,
20
+ flow,
21
+ workspacePath,
22
+ view,
23
+ runFilter,
24
+ watchEnabled,
25
+ watchStatus,
26
+ showHelp,
27
+ lastRefreshAt,
28
+ width
29
+ } = params;
30
+
31
+ const safeWidth = normalizeWidth(width);
32
+ const lines = [];
33
+
34
+ lines.push(...renderHeaderLines({
35
+ snapshot,
36
+ flow,
37
+ workspacePath,
38
+ view,
39
+ runFilter,
40
+ watchEnabled,
41
+ watchStatus,
42
+ lastRefreshAt,
43
+ width: safeWidth
44
+ }));
45
+
46
+ if (snapshot) {
47
+ lines.push(...renderStatsLines(snapshot, safeWidth));
48
+ lines.push('');
49
+ }
50
+
51
+ if (error) {
52
+ lines.push(...renderErrorLines(error, safeWidth, watchEnabled));
53
+ lines.push('');
54
+ }
55
+
56
+ if (!snapshot) {
57
+ lines.push(truncate('No snapshot available yet. Waiting for refresh...', safeWidth));
58
+ } else if (view === 'overview') {
59
+ lines.push(...renderOverviewViewLines(snapshot, safeWidth));
60
+ } else {
61
+ lines.push(...renderRunsViewLines(snapshot, runFilter, safeWidth));
62
+ }
63
+
64
+ lines.push('');
65
+ lines.push(...renderHelpLines(showHelp, safeWidth));
66
+
67
+ return lines;
68
+ }
69
+
70
+ function formatDashboardText(params) {
71
+ return buildDashboardLines(params).join('\n');
72
+ }
73
+
74
+ module.exports = {
75
+ buildDashboardLines,
76
+ formatDashboardText,
77
+ normalizeWidth
78
+ };
@@ -0,0 +1,30 @@
1
+ function createInitialUIState() {
2
+ return {
3
+ view: 'runs',
4
+ runFilter: 'all',
5
+ showHelp: true
6
+ };
7
+ }
8
+
9
+ function cycleView(current) {
10
+ if (current === 'runs') {
11
+ return 'overview';
12
+ }
13
+ return 'runs';
14
+ }
15
+
16
+ function cycleRunFilter(current) {
17
+ if (current === 'all') {
18
+ return 'active';
19
+ }
20
+ if (current === 'active') {
21
+ return 'completed';
22
+ }
23
+ return 'all';
24
+ }
25
+
26
+ module.exports = {
27
+ createInitialUIState,
28
+ cycleView,
29
+ cycleRunFilter
30
+ };
@@ -0,0 +1,61 @@
1
+ const { truncate } = require('../components/header');
2
+
3
+ const STANDARD_TYPES = [
4
+ 'constitution',
5
+ 'tech-stack',
6
+ 'coding-standards',
7
+ 'testing-standards',
8
+ 'system-architecture'
9
+ ];
10
+
11
+ function renderOverviewViewLines(snapshot, width) {
12
+ const lines = ['Overview'];
13
+
14
+ if (!snapshot?.initialized) {
15
+ lines.push('FIRE project folder exists but state.yaml is missing.');
16
+ lines.push('Run initialization and the overview will appear automatically.');
17
+ return lines.map((line) => truncate(line, width));
18
+ }
19
+
20
+ const project = snapshot.project || {};
21
+ const workspace = snapshot.workspace || {};
22
+
23
+ lines.push(`Project: ${project.name || 'Unknown'} | FIRE version: ${project.fireVersion || snapshot.version || '0.0.0'}`);
24
+ lines.push(`Workspace: ${workspace.type || 'unknown'} / ${workspace.structure || 'unknown'} | autonomy: ${workspace.autonomyBias || 'unknown'} | run scope pref: ${workspace.runScopePreference || 'unknown'}`);
25
+ lines.push('');
26
+
27
+ lines.push('Intent Summary');
28
+ lines.push(` total: ${snapshot.stats.totalIntents} | completed: ${snapshot.stats.completedIntents} | in_progress: ${snapshot.stats.inProgressIntents} | pending: ${snapshot.stats.pendingIntents} | blocked: ${snapshot.stats.blockedIntents}`);
29
+ lines.push('');
30
+
31
+ lines.push('Work Item Summary');
32
+ lines.push(` total: ${snapshot.stats.totalWorkItems} | completed: ${snapshot.stats.completedWorkItems} | in_progress: ${snapshot.stats.inProgressWorkItems} | pending: ${snapshot.stats.pendingWorkItems} | blocked: ${snapshot.stats.blockedWorkItems}`);
33
+ lines.push('');
34
+
35
+ const standardSet = new Set((snapshot.standards || []).map((item) => item.type));
36
+ lines.push('Standards');
37
+ for (const type of STANDARD_TYPES) {
38
+ const marker = standardSet.has(type) ? '[x]' : '[ ]';
39
+ lines.push(` ${marker} ${type}.md`);
40
+ }
41
+
42
+ lines.push('');
43
+ lines.push('Top Intents');
44
+
45
+ const intents = (snapshot.intents || []).slice(0, 6);
46
+ if (intents.length === 0) {
47
+ lines.push(' - none');
48
+ } else {
49
+ for (const intent of intents) {
50
+ const totalWorkItems = (intent.workItems || []).length;
51
+ const completedWorkItems = (intent.workItems || []).filter((item) => item.status === 'completed').length;
52
+ lines.push(` - ${intent.id}: ${intent.status} (${completedWorkItems}/${totalWorkItems} work items completed)`);
53
+ }
54
+ }
55
+
56
+ return lines.map((line) => truncate(line, width));
57
+ }
58
+
59
+ module.exports = {
60
+ renderOverviewViewLines
61
+ };
@@ -0,0 +1,98 @@
1
+ const { truncate } = require('../components/header');
2
+
3
+ function safeArray(value) {
4
+ return Array.isArray(value) ? value : [];
5
+ }
6
+
7
+ function formatRunProgress(run) {
8
+ const workItems = safeArray(run.workItems);
9
+ const total = workItems.length;
10
+ const completed = workItems.filter((item) => item.status === 'completed').length;
11
+ const inProgress = workItems.filter((item) => item.status === 'in_progress').length;
12
+ return `${completed}/${total} completed${inProgress > 0 ? `, ${inProgress} in_progress` : ''}`;
13
+ }
14
+
15
+ function renderActiveRunLines(activeRuns, width) {
16
+ const lines = ['Active Runs'];
17
+ if (!activeRuns || activeRuns.length === 0) {
18
+ lines.push(' - none');
19
+ return lines.map((line) => truncate(line, width));
20
+ }
21
+
22
+ for (const run of activeRuns) {
23
+ const currentItem = run.currentItem || 'n/a';
24
+ const artifacts = [
25
+ run.hasPlan ? 'plan' : null,
26
+ run.hasWalkthrough ? 'walkthrough' : null,
27
+ run.hasTestReport ? 'test-report' : null
28
+ ].filter(Boolean).join(', ') || 'no artifacts yet';
29
+
30
+ lines.push(` - ${run.id} [${run.scope}] current: ${currentItem}`);
31
+ lines.push(` progress: ${formatRunProgress(run)} | artifacts: ${artifacts}`);
32
+ }
33
+
34
+ return lines.map((line) => truncate(line, width));
35
+ }
36
+
37
+ function renderPendingQueueLines(pendingItems, width) {
38
+ const lines = ['Pending Queue'];
39
+ if (!pendingItems || pendingItems.length === 0) {
40
+ lines.push(' - none');
41
+ return lines.map((line) => truncate(line, width));
42
+ }
43
+
44
+ for (const item of pendingItems.slice(0, 12)) {
45
+ const deps = item.dependencies && item.dependencies.length > 0
46
+ ? ` deps:${item.dependencies.join(',')}`
47
+ : '';
48
+ lines.push(` - ${item.id} (${item.mode}/${item.complexity}) in ${item.intentTitle}${deps}`);
49
+ }
50
+
51
+ if (pendingItems.length > 12) {
52
+ lines.push(` ... ${pendingItems.length - 12} more pending items`);
53
+ }
54
+
55
+ return lines.map((line) => truncate(line, width));
56
+ }
57
+
58
+ function renderCompletedRunLines(completedRuns, width) {
59
+ const lines = ['Recent Completed Runs'];
60
+ if (!completedRuns || completedRuns.length === 0) {
61
+ lines.push(' - none');
62
+ return lines.map((line) => truncate(line, width));
63
+ }
64
+
65
+ for (const run of completedRuns.slice(0, 5)) {
66
+ const completedAt = run.completedAt || 'unknown';
67
+ lines.push(` - ${run.id} [${run.scope}] completed: ${completedAt} | ${formatRunProgress(run)}`);
68
+ }
69
+
70
+ return lines.map((line) => truncate(line, width));
71
+ }
72
+
73
+ function renderRunsViewLines(snapshot, runFilter, width) {
74
+ const lines = [];
75
+
76
+ if (!snapshot?.initialized) {
77
+ lines.push('FIRE detected, but state.yaml is not initialized yet.');
78
+ lines.push('Initialize FIRE project context, then the dashboard will auto-populate.');
79
+ return lines.map((line) => truncate(line, width));
80
+ }
81
+
82
+ if (runFilter !== 'completed') {
83
+ lines.push(...renderActiveRunLines(snapshot.activeRuns, width));
84
+ lines.push('');
85
+ lines.push(...renderPendingQueueLines(snapshot.pendingItems, width));
86
+ lines.push('');
87
+ }
88
+
89
+ if (runFilter !== 'active') {
90
+ lines.push(...renderCompletedRunLines(snapshot.completedRuns, width));
91
+ }
92
+
93
+ return lines.map((line) => truncate(line, width));
94
+ }
95
+
96
+ module.exports = {
97
+ renderRunsViewLines
98
+ };
@@ -62,17 +62,6 @@ class CodexInstaller extends ToolInstaller {
62
62
  await fs.ensureDir(skillDir);
63
63
  await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillContent, 'utf8');
64
64
 
65
- // Write agents/openai.yaml
66
- const agentsDir = path.join(skillDir, 'agents');
67
- await fs.ensureDir(agentsDir);
68
- const openaiYaml = [
69
- 'interface:',
70
- ` display_name: "specsmd ${commandName}"`,
71
- ` short_description: "${description || 'specsmd agent'}"`,
72
- ` default_prompt: "Use $${skillName} to start spec-driven development"`
73
- ].join('\n');
74
- await fs.writeFile(path.join(agentsDir, 'openai.yaml'), openaiYaml, 'utf8');
75
-
76
65
  installedFiles.push(skillName);
77
66
  } catch (err) {
78
67
  console.log(theme.warning(` Failed to install ${cmdFile}: ${err.message}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specsmd",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "Multi-agent orchestration system for AI-native software development. Delivers AI-DLC, Agile, and custom SDLC flows as markdown-based agent systems.",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {
@@ -41,17 +41,20 @@
41
41
  ],
42
42
  "dependencies": {
43
43
  "chalk": "^4.1.2",
44
+ "chokidar": "^4.0.3",
44
45
  "commander": "^11.1.0",
45
46
  "figlet": "^1.9.4",
46
47
  "fs-extra": "^11.1.1",
47
48
  "gradient-string": "^2.0.2",
49
+ "ink": "^5.2.1",
48
50
  "js-yaml": "^4.1.0",
49
51
  "mixpanel": "^0.18.0",
50
52
  "oh-my-logo": "^0.4.0",
51
- "prompts": "^2.4.2"
53
+ "prompts": "^2.4.2",
54
+ "react": "^18.3.1"
52
55
  },
53
56
  "engines": {
54
- "node": ">=14.0.0"
57
+ "node": ">=20.0.0"
55
58
  },
56
59
  "devDependencies": {
57
60
  "@types/js-yaml": "^4.0.9",