specrails-hub 1.7.0 → 1.8.0
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/package.json
CHANGED
|
@@ -8,9 +8,7 @@ const child_process_1 = require("child_process");
|
|
|
8
8
|
const readline_1 = require("readline");
|
|
9
9
|
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
10
10
|
const db_1 = require("./db");
|
|
11
|
-
const
|
|
12
|
-
'You can help answer questions about the codebase, explain SpecRails concepts, and suggest commands to run. ' +
|
|
13
|
-
'When you want to suggest a SpecRails command for the user to execute, wrap it in a command block like this: ' +
|
|
11
|
+
const COMMAND_INSTRUCTION = 'When you want to suggest a SpecRails command for the user to execute, wrap it in a command block like this: ' +
|
|
14
12
|
':::command\n/sr:implement #42\n::: ' +
|
|
15
13
|
'The user will be prompted to confirm before the command runs.';
|
|
16
14
|
function claudeOnPath() {
|
|
@@ -51,15 +49,60 @@ class ChatManager {
|
|
|
51
49
|
_emittedProposals;
|
|
52
50
|
_abortingConversations;
|
|
53
51
|
_cwd;
|
|
54
|
-
|
|
52
|
+
_projectName;
|
|
53
|
+
constructor(broadcast, db, cwd, projectName) {
|
|
55
54
|
this._broadcast = broadcast;
|
|
56
55
|
this._db = db;
|
|
57
56
|
this._cwd = cwd;
|
|
57
|
+
this._projectName = projectName;
|
|
58
58
|
this._activeProcesses = new Map();
|
|
59
59
|
this._buffers = new Map();
|
|
60
60
|
this._emittedProposals = new Map();
|
|
61
61
|
this._abortingConversations = new Set();
|
|
62
62
|
}
|
|
63
|
+
_buildSystemPrompt() {
|
|
64
|
+
const name = this._projectName ?? 'this project';
|
|
65
|
+
let contextSection = '';
|
|
66
|
+
try {
|
|
67
|
+
const stats = (0, db_1.getStats)(this._db);
|
|
68
|
+
const { jobs: recentJobs } = (0, db_1.listJobs)(this._db, { limit: 5 });
|
|
69
|
+
// Active job (running or queued at top)
|
|
70
|
+
const activeJob = recentJobs.find((j) => j.status === 'running' || j.status === 'queued');
|
|
71
|
+
const activeLine = activeJob
|
|
72
|
+
? `**${activeJob.status.toUpperCase()}**: \`${activeJob.command}\``
|
|
73
|
+
: 'No job currently running.';
|
|
74
|
+
// Recent terminal jobs
|
|
75
|
+
const terminalJobs = recentJobs.filter((j) => j.status === 'completed' || j.status === 'failed' || j.status === 'canceled');
|
|
76
|
+
const jobLines = terminalJobs.map((j) => {
|
|
77
|
+
const status = j.status === 'completed' ? '✓' : j.status === 'failed' ? '✗' : '○';
|
|
78
|
+
const dur = j.duration_ms != null ? `${Math.round(j.duration_ms / 1000)}s` : '—';
|
|
79
|
+
const cost = j.total_cost_usd != null ? `$${j.total_cost_usd.toFixed(3)}` : '—';
|
|
80
|
+
const cmd = j.command.length > 60 ? j.command.slice(0, 57) + '...' : j.command;
|
|
81
|
+
return `- ${status} \`${cmd}\` | ${dur} | ${cost}`;
|
|
82
|
+
});
|
|
83
|
+
const successRate = stats.totalJobs > 0
|
|
84
|
+
? Math.round(((stats.totalJobs - (recentJobs.filter((j) => j.status === 'failed').length)) / stats.totalJobs) * 100)
|
|
85
|
+
: null;
|
|
86
|
+
contextSection =
|
|
87
|
+
`\n\n## Current Dashboard Context\n\n` +
|
|
88
|
+
`### Active Job\n${activeLine}\n\n` +
|
|
89
|
+
(jobLines.length > 0 ? `### Recent Jobs\n${jobLines.join('\n')}\n\n` : '') +
|
|
90
|
+
`### Project Stats\n` +
|
|
91
|
+
`- Total jobs: ${stats.totalJobs}\n` +
|
|
92
|
+
`- Jobs today: ${stats.jobsToday}\n` +
|
|
93
|
+
(successRate != null ? `- Overall success rate: ${successRate}%\n` : '') +
|
|
94
|
+
`- Total cost: $${stats.totalCostUsd.toFixed(3)}\n` +
|
|
95
|
+
`- Cost today: $${stats.costToday.toFixed(3)}`;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Context is best-effort; fall back gracefully
|
|
99
|
+
}
|
|
100
|
+
return (`You are a project assistant for the "${name}" specrails project with full access to this repository via Claude Code. ` +
|
|
101
|
+
`You can help answer questions about the codebase, explain SpecRails concepts, and suggest commands to run.` +
|
|
102
|
+
contextSection +
|
|
103
|
+
`\n\n` +
|
|
104
|
+
COMMAND_INSTRUCTION);
|
|
105
|
+
}
|
|
63
106
|
isActive(conversationId) {
|
|
64
107
|
return this._activeProcesses.has(conversationId);
|
|
65
108
|
}
|
|
@@ -86,13 +129,14 @@ class ChatManager {
|
|
|
86
129
|
const isFirstTurn = conversation.session_id === null;
|
|
87
130
|
// Persist user message
|
|
88
131
|
(0, db_1.addMessage)(this._db, { conversation_id: conversationId, role: 'user', content: userText });
|
|
89
|
-
// Build spawn args
|
|
132
|
+
// Build spawn args with contextual system prompt
|
|
133
|
+
const systemPrompt = this._buildSystemPrompt();
|
|
90
134
|
const args = [
|
|
91
135
|
'--model', conversation.model,
|
|
92
136
|
'--dangerously-skip-permissions',
|
|
93
137
|
'--output-format', 'stream-json',
|
|
94
138
|
'--verbose',
|
|
95
|
-
'--system-prompt',
|
|
139
|
+
'--system-prompt', systemPrompt,
|
|
96
140
|
'-p', userText,
|
|
97
141
|
];
|
|
98
142
|
if (conversation.session_id) {
|
|
@@ -83,7 +83,7 @@ class ProjectRegistry {
|
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
85
|
const queueManager = new queue_manager_1.QueueManager(boundBroadcast, db, undefined, project.path);
|
|
86
|
-
const chatManager = new chat_manager_1.ChatManager(boundBroadcast, db, project.path);
|
|
86
|
+
const chatManager = new chat_manager_1.ChatManager(boundBroadcast, db, project.path, project.name);
|
|
87
87
|
const setupManager = new setup_manager_1.SetupManager(boundBroadcast, (pid, sid) => (0, hub_db_1.setProjectSetupSession)(this._hubDb, pid, sid), (pid) => (0, hub_db_1.clearProjectSetupSession)(this._hubDb, pid));
|
|
88
88
|
const proposalManager = new proposal_manager_1.ProposalManager(boundBroadcast, db, project.path);
|
|
89
89
|
// Load commands for this project
|