pneuma-skills 0.3.0 → 0.5.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/README.md CHANGED
@@ -126,7 +126,7 @@ pneuma-skills/
126
126
  │ ├── StreamingText.tsx # Streaming response display
127
127
  │ ├── ActivityIndicator.tsx # Thinking/tool progress indicator
128
128
  │ ├── PermissionBanner.tsx # Tool permission approval UI
129
- │ └── StatusBar.tsx # Connection status + session info
129
+ │ └── TopBar.tsx # Tab navigation + connection status
130
130
  ├── skill/
131
131
  │ └── doc/SKILL.md # Doc Mode skill prompt for Claude Code
132
132
  ├── docs/adr/ # Architecture Decision Records
package/bin/pneuma.ts CHANGED
@@ -41,6 +41,22 @@ function saveSession(workspace: string, session: PersistedSession): void {
41
41
  writeFileSync(join(dir, "session.json"), JSON.stringify(session, null, 2));
42
42
  }
43
43
 
44
+ function loadHistory(workspace: string): unknown[] {
45
+ try {
46
+ const content = readFileSync(join(workspace, ".pneuma", "history.json"), "utf-8");
47
+ const data = JSON.parse(content);
48
+ return Array.isArray(data) ? data : [];
49
+ } catch {
50
+ return [];
51
+ }
52
+ }
53
+
54
+ function saveHistory(workspace: string, history: unknown[]): void {
55
+ const dir = join(workspace, ".pneuma");
56
+ mkdirSync(dir, { recursive: true });
57
+ writeFileSync(join(dir, "history.json"), JSON.stringify(history));
58
+ }
59
+
44
60
  // ── CLI arg parsing ──────────────────────────────────────────────────────────
45
61
 
46
62
  function parseArgs(argv: string[]) {
@@ -78,7 +94,29 @@ function ask(question: string): Promise<string> {
78
94
 
79
95
  // ── Main ─────────────────────────────────────────────────────────────────────
80
96
 
97
+ function checkBunVersion() {
98
+ const MIN_BUN = "1.3.5"; // Required for Bun.spawn terminal (PTY) support
99
+ const current = typeof Bun !== "undefined" ? Bun.version : null;
100
+ if (!current) {
101
+ console.warn("[pneuma] Warning: Not running under Bun. Pneuma requires Bun >= " + MIN_BUN);
102
+ return;
103
+ }
104
+ const [curMajor, curMinor, curPatch] = current.split(".").map(Number);
105
+ const [minMajor, minMinor, minPatch] = MIN_BUN.split(".").map(Number);
106
+ const ok =
107
+ curMajor > minMajor ||
108
+ (curMajor === minMajor && curMinor > minMinor) ||
109
+ (curMajor === minMajor && curMinor === minMinor && curPatch >= minPatch);
110
+ if (!ok) {
111
+ console.warn(
112
+ `[pneuma] Warning: Bun ${current} detected, but >= ${MIN_BUN} is required.` +
113
+ ` Terminal features may not work. Run \`bun upgrade\` to update.`
114
+ );
115
+ }
116
+ }
117
+
81
118
  async function main() {
119
+ checkBunVersion();
82
120
  const { mode, workspace, port, noOpen } = parseArgs(process.argv);
83
121
 
84
122
  if (!mode || mode !== "doc") {
@@ -186,8 +224,42 @@ async function main() {
186
224
 
187
225
  console.log(`[pneuma] CLI session started: ${session.sessionId}`);
188
226
 
189
- // If resume fails (CLI exits quickly), clear cliSessionId from persistence
227
+ // Auto-greeting for fresh sessions sends a hidden prompt so Claude greets the user
228
+ if (!resuming) {
229
+ const greeting = "The user just opened the Pneuma document editor workspace. Briefly greet them and let them know you're ready to help edit and create documents. Keep it to 1-2 sentences.";
230
+ wsBridge.injectGreeting(session.sessionId, greeting);
231
+ console.log("[pneuma] Sent auto-greeting for fresh session");
232
+ }
233
+
234
+ // Load persisted message history into WsBridge
235
+ const savedHistory = loadHistory(workspace);
236
+ if (savedHistory.length > 0) {
237
+ wsBridge.loadMessageHistory(session.sessionId, savedHistory as any);
238
+ console.log(`[pneuma] Restored ${savedHistory.length} messages from history`);
239
+ }
240
+
241
+ // Periodically persist message history (debounced — every 5s)
242
+ const historyInterval = setInterval(() => {
243
+ const history = wsBridge.getMessageHistory(session.sessionId);
244
+ if (history.length > 0) {
245
+ saveHistory(workspace, history);
246
+ }
247
+ }, 5_000);
248
+
249
+ // Handle CLI exit: surface errors + clear stale resume state
190
250
  launcher.onSessionExited((exitedId, exitCode) => {
251
+ // Broadcast CLI errors to browser
252
+ if (exitCode !== 0 && exitCode !== 143 /* SIGTERM = normal shutdown */) {
253
+ let errorMsg: string;
254
+ if (exitCode === 127) {
255
+ errorMsg = "Claude Code CLI not found. Please install it: https://docs.anthropic.com/claude-code";
256
+ } else {
257
+ errorMsg = `Claude Code exited unexpectedly (code ${exitCode}). Check CLI installation and subscription status.`;
258
+ }
259
+ wsBridge.broadcastToSession(exitedId, { type: "error", message: errorMsg });
260
+ }
261
+
262
+ // If resume fails (CLI exits quickly), clear cliSessionId from persistence
191
263
  if (exitedId === session.sessionId && resuming) {
192
264
  const info = launcher.getSession(exitedId);
193
265
  if (info && !info.cliSessionId) {
@@ -261,6 +333,13 @@ async function main() {
261
333
  // Graceful shutdown
262
334
  const shutdown = async () => {
263
335
  console.log("\n[pneuma] Shutting down...");
336
+ clearInterval(historyInterval);
337
+ // Final history save
338
+ const history = wsBridge.getMessageHistory(session.sessionId);
339
+ if (history.length > 0) {
340
+ saveHistory(workspace, history);
341
+ console.log(`[pneuma] Saved ${history.length} messages to history`);
342
+ }
264
343
  viteProc?.kill();
265
344
  await launcher.killAll();
266
345
  server.stop(true);