@useguano/create 0.1.5 → 0.1.7
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/index.js +36 -14
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @useguano/create: scaffold a project dir that runs Guano via npm scripts,
|
|
3
|
-
// install it, and connect Claude
|
|
3
|
+
// install it, and connect Claude — all in this one command, so what
|
|
4
4
|
// is left afterwards is `cd my-site && npm run dev`. Zero dependencies —
|
|
5
5
|
// prompts with node:readline.
|
|
6
6
|
//
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
//
|
|
10
10
|
// The MCP connect runs through the just-installed package's own
|
|
11
11
|
// `guano connect --offline`: it mints a PENDING API token into <dir>/data,
|
|
12
|
-
// writes the two agent permissions there, and writes
|
|
12
|
+
// writes the two agent permissions there, and writes the config of every
|
|
13
|
+
// Claude client on the machine (Claude Code and Claude Desktop).
|
|
13
14
|
// The server binds the token to the first admin at /admin setup, and the
|
|
14
15
|
// setup form shows the permissions instead of asking them again.
|
|
15
16
|
import { spawnSync } from 'node:child_process'
|
|
@@ -43,7 +44,7 @@ if (!dir) {
|
|
|
43
44
|
dir = rl ? (await ask(`Project folder (${DEFAULT_DIR}): `)).trim() || DEFAULT_DIR : DEFAULT_DIR
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
// Connect Claude
|
|
47
|
+
// Connect Claude? Flags decide non-interactively (CI never asks and
|
|
47
48
|
// never connects); a terminal asks, default yes.
|
|
48
49
|
let connect = false
|
|
49
50
|
let policyFlags = []
|
|
@@ -52,7 +53,7 @@ if (flag('--no-connect')) {
|
|
|
52
53
|
} else if (flag('--connect') || flag('--main') || flag('--publish')) {
|
|
53
54
|
connect = true
|
|
54
55
|
} else if (rl) {
|
|
55
|
-
connect = await yes('Connect Claude Desktop to this site over MCP?', true)
|
|
56
|
+
connect = await yes('Connect Claude (Code and Desktop) to this site over MCP?', true)
|
|
56
57
|
}
|
|
57
58
|
if (connect) {
|
|
58
59
|
if (flag('--main')) policyFlags.push('--main')
|
|
@@ -74,6 +75,20 @@ const name =
|
|
|
74
75
|
.replace(/[^a-z0-9-]+/g, '-')
|
|
75
76
|
.replace(/^-+|-+$/g, '') || DEFAULT_DIR
|
|
76
77
|
|
|
78
|
+
/**
|
|
79
|
+
* The same thing, as a NAME rather than a package slug.
|
|
80
|
+
*
|
|
81
|
+
* `acme-landing` is what npm needs and what the folder is called; "Acme
|
|
82
|
+
* Landing" is what belongs in a field labelled Project name. The setup screen
|
|
83
|
+
* prefills it and the human can change it there — this is a head start, not a
|
|
84
|
+
* decision.
|
|
85
|
+
*/
|
|
86
|
+
const displayName = name
|
|
87
|
+
.split('-')
|
|
88
|
+
.filter(Boolean)
|
|
89
|
+
.map((w) => w[0].toUpperCase() + w.slice(1))
|
|
90
|
+
.join(' ')
|
|
91
|
+
|
|
77
92
|
await mkdir(target, { recursive: true })
|
|
78
93
|
if ((await readdir(target)).length) {
|
|
79
94
|
rl?.close()
|
|
@@ -95,9 +110,10 @@ if ((await readdir(target)).length) {
|
|
|
95
110
|
* 4174 is still preferred, so a first project looks exactly as documented.
|
|
96
111
|
*/
|
|
97
112
|
async function pickPort(first = 4174, tries = 40) {
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
113
|
+
// One port per site: the preview is served by the same server under
|
|
114
|
+
// /_guano/preview, so there is no second port to keep free above it.
|
|
115
|
+
for (let port = first; port < first + tries; port += 1) {
|
|
116
|
+
if (await free(port)) return port
|
|
101
117
|
}
|
|
102
118
|
return first
|
|
103
119
|
}
|
|
@@ -147,6 +163,12 @@ export default {}
|
|
|
147
163
|
# must not share one, or whichever starts first answers for both and the
|
|
148
164
|
# other's MCP token is refused with a 401.
|
|
149
165
|
PORT=${port}
|
|
166
|
+
|
|
167
|
+
# What you called this project in the terminal. It prefills the Project name
|
|
168
|
+
# on the setup screen, so nobody is asked to name the same thing twice. Only
|
|
169
|
+
# read before the first admin account exists; edit the name in the editor
|
|
170
|
+
# after that.
|
|
171
|
+
GUANO_PROJECT_NAME=${displayName}
|
|
150
172
|
`,
|
|
151
173
|
|
|
152
174
|
'.env.example': `PORT=${port}
|
|
@@ -176,14 +198,14 @@ site. Your published site is served at http://localhost:${port}/.
|
|
|
176
198
|
This project runs on port ${port} (set in \`.env\`). Each Guano site on a
|
|
177
199
|
machine needs its own, so two of them never answer for one address.
|
|
178
200
|
|
|
179
|
-
If you said yes to Claude
|
|
201
|
+
If you said yes to Claude when scaffolding, it is already connected:
|
|
180
202
|
the agent token is bound to the admin account you create, and the permissions
|
|
181
203
|
you chose (edit the live project, publish) show on the setup form and stay
|
|
182
204
|
editable in Settings → MCP → Agent permissions.
|
|
183
205
|
|
|
184
206
|
- \`npm run start\` — production server (\`NODE_ENV=production\`)
|
|
185
207
|
- \`npm run build\` — export the site as static files to \`./dist-site\`
|
|
186
|
-
- \`npm run connect\` — connect Claude
|
|
208
|
+
- \`npm run connect\` — connect Claude later, or from another machine (writes Claude Code's \`~/.claude.json\` and Claude Desktop's config; quit Desktop first; \`--main\` / \`--publish\` grant the agent permissions, or it asks)
|
|
187
209
|
- everything lives in \`./data\` — **backing up = copying that directory**
|
|
188
210
|
|
|
189
211
|
See the @useguano/guano package README for deploy notes and environment variables.
|
|
@@ -205,7 +227,7 @@ if (install) {
|
|
|
205
227
|
if (!installed) console.error('\nnpm install failed — run it yourself in the project folder.')
|
|
206
228
|
}
|
|
207
229
|
|
|
208
|
-
// ---- connect Claude
|
|
230
|
+
// ---- connect Claude, through the installed package ----
|
|
209
231
|
// GUANO_CREATE_LOCAL_PKG is a dev-only escape hatch: with --no-install there
|
|
210
232
|
// is no node_modules, so a repo checkout names its own packages/guano here.
|
|
211
233
|
let connected = false
|
|
@@ -214,7 +236,7 @@ if (connect) {
|
|
|
214
236
|
? join(target, 'node_modules', '@useguano', 'guano')
|
|
215
237
|
: process.env.GUANO_CREATE_LOCAL_PKG
|
|
216
238
|
if (!pkgDir) {
|
|
217
|
-
console.error('\nClaude
|
|
239
|
+
console.error('\nClaude not connected: guano is not installed yet — run `npm run connect` inside the project later.')
|
|
218
240
|
} else {
|
|
219
241
|
try {
|
|
220
242
|
// the package resolves its data dir from this env at import time
|
|
@@ -224,7 +246,7 @@ if (connect) {
|
|
|
224
246
|
const { main } = await import(pathToFileURL(join(pkgDir, 'mcp', 'connect.mjs')).href)
|
|
225
247
|
const connectArgs = [...policyFlags, '--offline', '--yes']
|
|
226
248
|
if (opt('--config')) connectArgs.push('--config', resolve(opt('--config')))
|
|
227
|
-
console.log('\nConnecting Claude
|
|
249
|
+
console.log('\nConnecting Claude…')
|
|
228
250
|
await main(connectArgs, {
|
|
229
251
|
whenClaudeRunning: async () => {
|
|
230
252
|
if (!rl) return 'skip'
|
|
@@ -236,7 +258,7 @@ if (connect) {
|
|
|
236
258
|
})
|
|
237
259
|
connected = true
|
|
238
260
|
} catch (e) {
|
|
239
|
-
console.error(`\n✗ ${e?.message ?? e}\n Claude
|
|
261
|
+
console.error(`\n✗ ${e?.message ?? e}\n Claude not connected — run \`npm run connect\` inside the project later.`)
|
|
240
262
|
}
|
|
241
263
|
}
|
|
242
264
|
}
|
|
@@ -246,4 +268,4 @@ console.log(`
|
|
|
246
268
|
Next:
|
|
247
269
|
cd ${dir}${installed || !install ? '' : '\n npm install'}
|
|
248
270
|
npm run dev → http://localhost:4174/admin (create your admin account)
|
|
249
|
-
${connected ? '\nThen open Claude
|
|
271
|
+
${connected ? '\nThen open Claude — guano is connected; the agent token is bound to the admin you create.\n' : ''}`)
|