openwriter 0.6.0 → 0.6.1

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,19 +1,136 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import { execSync } from 'child_process';
4
5
  import { fileURLToPath } from 'url';
5
6
  const __filename = fileURLToPath(import.meta.url);
6
7
  const __dirname = path.dirname(__filename);
8
+ function log(msg) {
9
+ console.error(msg);
10
+ }
11
+ function isGloballyInstalled() {
12
+ try {
13
+ const result = execSync('openwriter --version', {
14
+ stdio: ['pipe', 'pipe', 'pipe'],
15
+ timeout: 10000,
16
+ });
17
+ return true;
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ function installGlobally() {
24
+ log('\n② Installing openwriter globally...');
25
+ // Try without sudo first (works on Windows, nvm, Homebrew, volta, etc.)
26
+ try {
27
+ execSync('npm install -g openwriter', { stdio: 'inherit', timeout: 120000 });
28
+ log(' ✓ Installed globally');
29
+ return true;
30
+ }
31
+ catch {
32
+ // Likely permission error on macOS/Linux system Node
33
+ }
34
+ // Try with sudo on non-Windows
35
+ if (process.platform !== 'win32') {
36
+ log(' Retrying with sudo...');
37
+ try {
38
+ execSync('sudo npm install -g openwriter', { stdio: 'inherit', timeout: 120000 });
39
+ log(' ✓ Installed globally (sudo)');
40
+ return true;
41
+ }
42
+ catch {
43
+ // sudo failed too
44
+ }
45
+ }
46
+ log(' ✗ Could not install globally. Run manually:');
47
+ log(' npm install -g openwriter');
48
+ return false;
49
+ }
50
+ function isMcpConfigured() {
51
+ const claudeJson = path.join(os.homedir(), '.claude.json');
52
+ if (!fs.existsSync(claudeJson))
53
+ return false;
54
+ try {
55
+ const content = JSON.parse(fs.readFileSync(claudeJson, 'utf-8'));
56
+ return !!content?.mcpServers?.openwriter;
57
+ }
58
+ catch {
59
+ return false;
60
+ }
61
+ }
62
+ function configureMcp() {
63
+ log('\n③ Configuring MCP server for Claude Code...');
64
+ // Try using claude CLI
65
+ try {
66
+ execSync('claude mcp add -s user openwriter -- openwriter --no-open', {
67
+ stdio: 'inherit',
68
+ timeout: 15000,
69
+ });
70
+ log(' ✓ MCP server configured');
71
+ return true;
72
+ }
73
+ catch {
74
+ // claude CLI not available or failed
75
+ }
76
+ // Fallback: edit ~/.claude.json directly
77
+ log(' claude CLI not found — writing config directly...');
78
+ const claudeJson = path.join(os.homedir(), '.claude.json');
79
+ try {
80
+ let config = {};
81
+ if (fs.existsSync(claudeJson)) {
82
+ config = JSON.parse(fs.readFileSync(claudeJson, 'utf-8'));
83
+ }
84
+ if (!config.mcpServers)
85
+ config.mcpServers = {};
86
+ // Add openwriter as first entry (Claude Code loads sequentially)
87
+ const existing = config.mcpServers;
88
+ config.mcpServers = {
89
+ openwriter: { command: 'openwriter', args: ['--no-open'] },
90
+ ...existing,
91
+ };
92
+ fs.writeFileSync(claudeJson, JSON.stringify(config, null, 2), 'utf-8');
93
+ log(` ✓ MCP server added to ${claudeJson}`);
94
+ return true;
95
+ }
96
+ catch (err) {
97
+ log(` ✗ Could not configure MCP server. Add manually:`);
98
+ log(' claude mcp add -s user openwriter -- openwriter --no-open');
99
+ return false;
100
+ }
101
+ }
7
102
  export function installSkill() {
103
+ // Step 1: Copy SKILL.md
104
+ log('① Installing OpenWriter skill...');
8
105
  const source = path.join(__dirname, '../../skill/SKILL.md');
9
106
  const targetDir = path.join(os.homedir(), '.claude', 'skills', 'openwriter');
10
107
  const target = path.join(targetDir, 'SKILL.md');
11
108
  if (!fs.existsSync(source)) {
12
- console.error(`Error: SKILL.md not found at ${source}`);
109
+ log(` Error: SKILL.md not found at ${source}`);
13
110
  process.exit(1);
14
111
  }
15
112
  fs.mkdirSync(targetDir, { recursive: true });
16
113
  fs.copyFileSync(source, target);
17
- console.error(`Installed OpenWriter skill to ${target}`);
114
+ log(` Skill installed to ${target}`);
115
+ // Step 2: Global install (skip if already installed)
116
+ const alreadyInstalled = isGloballyInstalled();
117
+ if (alreadyInstalled) {
118
+ log('\n② openwriter already installed globally — skipping');
119
+ }
120
+ else {
121
+ if (!installGlobally()) {
122
+ process.exit(1);
123
+ }
124
+ }
125
+ // Step 3: Configure MCP server (skip if already configured)
126
+ if (isMcpConfigured()) {
127
+ log('\n③ MCP server already configured — skipping');
128
+ }
129
+ else {
130
+ configureMcp();
131
+ }
132
+ // Done
133
+ log('\n✓ OpenWriter is ready!');
134
+ log(' Restart Claude Code, then type /openwriter to start writing.\n');
18
135
  process.exit(0);
19
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openwriter",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "The open-source writing surface for AI agents. Markdown-native editor with pending change review — your agent writes, you accept or reject.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/skill/SKILL.md CHANGED
@@ -36,30 +36,31 @@ Check whether the `openwriter` MCP tools are available (e.g. `read_pad`, `write_
36
36
 
37
37
  ### MCP tools ARE available (ready to use)
38
38
 
39
- The user already has OpenWriter configured — either they ran `npx openwriter install-skill` (which installed this skill) and added the MCP server, or they set it up manually. You're good to go.
39
+ The user already has OpenWriter configured. You're good to go.
40
40
 
41
41
  **First action:** Share the browser URL:
42
42
  > OpenWriter is at **http://localhost:5050** — open it in your browser to see and review changes.
43
43
 
44
44
  Skip to [Writing Strategy](#writing-strategy) below.
45
45
 
46
- ### MCP tools are NOT available (skill-first install)
46
+ ### MCP tools are NOT available (needs setup)
47
47
 
48
- The user installed this skill from a directory but hasn't set up the MCP server yet. OpenWriter needs an MCP server to provide the 31 editing tools.
48
+ The user has this skill but hasn't set up the MCP server yet. One command does everything:
49
49
 
50
- **Step 1:** Tell the user to install globally and add the MCP server:
50
+ ```bash
51
+ npx openwriter install-skill
52
+ ```
53
+
54
+ This installs openwriter globally, configures the MCP server for Claude Code, and copies this skill — all in one step. After it finishes, the user just needs to restart their Claude Code session.
55
+
56
+ **Fallback (if the command above fails):** Do it manually:
51
57
 
52
58
  ```bash
53
- # Install globally for instant startup (no npx resolution delay)
54
59
  npm install -g openwriter
55
-
56
- # Add the OpenWriter MCP server to Claude Code
57
60
  claude mcp add -s user openwriter -- openwriter --no-open
58
61
  ```
59
62
 
60
- Then restart the Claude Code session. The 57 MCP tools become available on next launch.
61
-
62
- **Step 2 (if the user can't run the command above):** Edit `~/.claude.json` directly. Add `openwriter` as the **first entry** in the `mcpServers` object — MCP servers load sequentially, so first in config = first to load:
63
+ If `claude mcp add` can't run (e.g. nested session error), edit `~/.claude.json` directly. Add `openwriter` as the **first entry** in `mcpServers`:
63
64
 
64
65
  ```json
65
66
  {
@@ -72,14 +73,10 @@ Then restart the Claude Code session. The 57 MCP tools become available on next
72
73
  }
73
74
  ```
74
75
 
75
- **Why first?** Claude Code loads MCP servers sequentially in config order. If `openwriter` is last, it waits for every other server to finish connecting first. Putting it first makes it available instantly.
76
-
77
- After editing, tell the user:
76
+ After setup, tell the user:
78
77
  1. Restart your Claude Code session (MCP servers load on startup)
79
78
  2. Open http://localhost:5050 in your browser
80
79
 
81
- **Note:** You cannot run `claude mcp add` from inside a session (nested session error). That's why we edit the JSON directly when configuring from within Claude Code. Also, `claude mcp add` appends to the end — always verify the entry is first after adding.
82
-
83
80
  ## Document Identity: Titles vs DocIds
84
81
 
85
82
  Every document has an immutable **docId** (8-char hex, e.g. `a1b2c3d4`) in its YAML frontmatter. Titles are for human communication and agent reasoning. DocIds are for agent action.