amorphous-cli 0.1.5 → 0.1.6

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
@@ -3,5 +3,5 @@
3
3
  Build abstracts on Amorphous. An abstract can be anything. Your own agent builds it. Other people open it and write to it.
4
4
 
5
5
  ```bash
6
- npx amorphous-cli setup
6
+ npx amorphous-cli@latest setup
7
7
  ```
package/index.mjs CHANGED
@@ -2,13 +2,18 @@
2
2
  import { loadDotEnv } from './lib/config.mjs'
3
3
  import { runCommand } from './lib/commands.mjs'
4
4
  import { ensureLocalHttpsTrust } from './lib/tls.mjs'
5
+ import { startUpdateCheck } from './lib/update-check.mjs'
5
6
 
6
7
  ensureLocalHttpsTrust()
7
8
  loadDotEnv()
8
9
 
10
+ const update = startUpdateCheck({ argv: process.argv.slice(2) })
11
+
9
12
  try {
10
13
  await runCommand(process.argv.slice(2))
14
+ await update.finish()
11
15
  } catch (e) {
12
16
  console.error(e instanceof Error ? e.message : e)
17
+ await update.finish()
13
18
  process.exit(1)
14
19
  }
@@ -1,10 +1,11 @@
1
1
  import { createHash } from 'node:crypto'
2
- import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
2
+ import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
3
3
  import { dirname, join } from 'node:path'
4
4
  import { fileURLToPath } from 'node:url'
5
5
  import { homedir } from 'node:os'
6
+ import { CLI_VERSION } from './version.mjs'
6
7
 
7
- export const CONTRACT_VERSION = 1
8
+ export const CONTRACT_VERSION = 2
8
9
 
9
10
  const skillRoot = join(dirname(fileURLToPath(import.meta.url)), '../skill/amorphous')
10
11
 
@@ -75,9 +76,12 @@ export function userSkillDirs(home = homedir()) {
75
76
 
76
77
  export function installUserSkill(home = homedir()) {
77
78
  const dirs = userSkillDirs(home)
79
+ const stamp = `${CLI_VERSION}\n`
78
80
  for (const dest of dirs) {
81
+ rmSync(dest, { recursive: true, force: true })
79
82
  mkdirSync(dest, { recursive: true })
80
83
  cpSync(skillRoot, dest, { recursive: true })
84
+ writeFileSync(join(dest, '.amorphous-version'), stamp)
81
85
  }
82
86
  return dirs
83
87
  }
@@ -1,5 +1,5 @@
1
1
  import assert from 'node:assert/strict'
2
- import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
2
+ import { existsSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
3
3
  import { tmpdir } from 'node:os'
4
4
  import { join } from 'node:path'
5
5
  import test from 'node:test'
@@ -10,6 +10,7 @@ import {
10
10
  syncProjectContract,
11
11
  writeStarter,
12
12
  } from './agent-contract.mjs'
13
+ import { CLI_VERSION } from './version.mjs'
13
14
 
14
15
  test('setup copies the skill, and an edited contract is left alone', () => {
15
16
  const home = mkdtempSync(join(tmpdir(), 'amorphous-skill-'))
@@ -17,6 +18,12 @@ test('setup copies the skill, and an edited contract is left alone', () => {
17
18
  assert.equal(dirs.length, 2)
18
19
  assert.match(readFileSync(join(dirs[0], 'SKILL.md'), 'utf8'), /build abstracts/)
19
20
  assert.match(readFileSync(join(dirs[1], 'assets/app.js'), 'utf8'), /amorphous\.me/)
21
+ assert.equal(readFileSync(join(dirs[0], '.amorphous-version'), 'utf8'), `${CLI_VERSION}\n`)
22
+ assert.equal(readFileSync(join(dirs[1], '.amorphous-version'), 'utf8'), `${CLI_VERSION}\n`)
23
+
24
+ writeFileSync(join(dirs[1], 'stray.txt'), 'gone')
25
+ installUserSkill(home)
26
+ assert.equal(existsSync(join(dirs[1], 'stray.txt')), false)
20
27
 
21
28
  const project = mkdtempSync(join(tmpdir(), 'amorphous-page-'))
22
29
  assert.equal(syncProjectContract(project), 'wrote')
@@ -29,7 +36,23 @@ test('setup copies the skill, and an edited contract is left alone', () => {
29
36
  assert.equal(syncProjectContract(project), 'kept')
30
37
  assert.match(readFileSync(join(project, 'AGENTS.md'), 'utf8'), /Edited\./)
31
38
 
32
- writeFileSync(join(project, 'AGENTS.md'), projectContract().replace('amorphous-contract: 1', 'amorphous-contract: 0'))
39
+ const older = projectContract().replace(
40
+ `amorphous-contract: ${CONTRACT_VERSION}`,
41
+ 'amorphous-contract: 0',
42
+ )
43
+ writeFileSync(join(project, 'AGENTS.md'), older)
33
44
  assert.equal(syncProjectContract(project), 'refreshed')
34
- assert.match(readFileSync(join(project, 'AGENTS.md'), 'utf8'), /amorphous-contract: 1/)
45
+ assert.match(
46
+ readFileSync(join(project, 'AGENTS.md'), 'utf8'),
47
+ new RegExp(`amorphous-contract: ${CONTRACT_VERSION}`),
48
+ )
49
+
50
+ writeFileSync(
51
+ join(project, 'AGENTS.md'),
52
+ projectContract().replace(
53
+ `amorphous-contract: ${CONTRACT_VERSION}`,
54
+ `amorphous-contract: ${CONTRACT_VERSION + 1}`,
55
+ ),
56
+ )
57
+ assert.equal(syncProjectContract(project), 'kept')
35
58
  })
@@ -0,0 +1,188 @@
1
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
2
+ import { homedir } from 'node:os'
3
+ import { join } from 'node:path'
4
+ import { randomBytes } from 'node:crypto'
5
+ import { ALIASES } from './help.mjs'
6
+ import { userSkillDirs } from './agent-contract.mjs'
7
+ import { CLI_VERSION } from './version.mjs'
8
+
9
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000
10
+ const NUDGE = 'Run: npx amorphous-cli@latest setup\n'
11
+
12
+ function parseVersion(v) {
13
+ if (typeof v !== 'string') return null
14
+ const m = v.match(/^(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?(?:\+.*)?$/)
15
+ if (!m) return null
16
+ return {
17
+ major: Number(m[1]),
18
+ minor: Number(m[2]),
19
+ patch: Number(m[3]),
20
+ prerelease: m[4] ?? null,
21
+ }
22
+ }
23
+
24
+ export function compareVersions(a, b) {
25
+ const va = parseVersion(a)
26
+ const vb = parseVersion(b)
27
+ if (!va || !vb) return null
28
+ if (va.major !== vb.major) return va.major < vb.major ? -1 : 1
29
+ if (va.minor !== vb.minor) return va.minor < vb.minor ? -1 : 1
30
+ if (va.patch !== vb.patch) return va.patch < vb.patch ? -1 : 1
31
+ if (va.prerelease && vb.prerelease) return 0
32
+ if (va.prerelease && !vb.prerelease) return -1
33
+ if (!va.prerelease && vb.prerelease) return 1
34
+ return 0
35
+ }
36
+
37
+ function resolveCommand(argv) {
38
+ const raw = argv[0]
39
+ if (!raw) return ''
40
+ return ALIASES[raw] || raw
41
+ }
42
+
43
+ export function shouldCheck(argv, env = process.env) {
44
+ if ('CI' in env) return false
45
+ if (env.AMORPHOUS_NO_UPDATE_CHECK === '1') return false
46
+ if (!argv.length) return false
47
+ if (argv[0] === 'help') return false
48
+ if (argv.includes('-h') || argv.includes('--help')) return false
49
+ if (resolveCommand(argv) === 'mcp') return false
50
+ return true
51
+ }
52
+
53
+ function existsSkill(path) {
54
+ try {
55
+ readFileSync(path)
56
+ return true
57
+ } catch {
58
+ return false
59
+ }
60
+ }
61
+
62
+ export function skillStale(home, version) {
63
+ const dirs = userSkillDirs(home)
64
+ for (const dir of dirs) {
65
+ const skillPath = join(dir, 'SKILL.md')
66
+ if (!existsSkill(skillPath)) continue
67
+ const stampPath = join(dir, '.amorphous-version')
68
+ let stamp
69
+ try {
70
+ stamp = readFileSync(stampPath, 'utf8').trim()
71
+ } catch {
72
+ return true
73
+ }
74
+ const cmp = compareVersions(stamp, version)
75
+ if (cmp === null) continue
76
+ if (cmp < 0) return true
77
+ }
78
+ return false
79
+ }
80
+
81
+ function cachePath(home) {
82
+ return join(home, '.config', 'amorphous', 'update-check.json')
83
+ }
84
+
85
+ function readCache(home) {
86
+ try {
87
+ const data = JSON.parse(readFileSync(cachePath(home), 'utf8'))
88
+ if (!data?.checkedAt) return null
89
+ return data
90
+ } catch {
91
+ return null
92
+ }
93
+ }
94
+
95
+ function cacheFresh(cache, now) {
96
+ if (!cache?.checkedAt) return false
97
+ const t = Date.parse(cache.checkedAt)
98
+ if (Number.isNaN(t)) return false
99
+ return now() - t < CACHE_TTL_MS
100
+ }
101
+
102
+ function writeCache(home, data) {
103
+ const dir = join(home, '.config', 'amorphous')
104
+ mkdirSync(dir, { recursive: true })
105
+ const dest = cachePath(home)
106
+ const tmp = join(dir, `.update-check.${process.pid}.${randomBytes(4).toString('hex')}.tmp`)
107
+ writeFileSync(tmp, JSON.stringify(data) + '\n')
108
+ renameSync(tmp, dest)
109
+ }
110
+
111
+ function defaultRegistry(env) {
112
+ const raw = env.npm_config_registry || 'https://registry.npmjs.org'
113
+ return raw.replace(/\/$/, '')
114
+ }
115
+
116
+ export function defaultFetchLatest(env = process.env) {
117
+ const registry = defaultRegistry(env)
118
+ return fetch(`${registry}/amorphous-cli/latest`, {
119
+ signal: AbortSignal.timeout(1500),
120
+ }).then(async (res) => {
121
+ if (!res.ok) throw new Error('registry')
122
+ const body = await res.json()
123
+ if (typeof body?.version !== 'string') throw new Error('registry')
124
+ return body.version
125
+ })
126
+ }
127
+
128
+ function needsNudge({ cmd, version, latest, home }) {
129
+ if (latest && compareVersions(version, latest) === -1) return true
130
+ if (cmd !== 'setup' && skillStale(home, version)) return true
131
+ return false
132
+ }
133
+
134
+ export function startUpdateCheck({
135
+ argv,
136
+ env = process.env,
137
+ home = homedir(),
138
+ version = CLI_VERSION,
139
+ now = () => Date.now(),
140
+ fetchLatest = () => defaultFetchLatest(env),
141
+ stderr = process.stderr,
142
+ } = {}) {
143
+ let printed = false
144
+ let fetchPromise = null
145
+ let cache = null
146
+ let stale = false
147
+
148
+ if (shouldCheck(argv, env)) {
149
+ cache = readCache(home)
150
+ stale = !cacheFresh(cache, now)
151
+ if (stale) {
152
+ fetchPromise = fetchLatest().catch(() => null)
153
+ }
154
+ }
155
+
156
+ return {
157
+ async finish() {
158
+ try {
159
+ if (!shouldCheck(argv, env)) return
160
+ const cmd = resolveCommand(argv)
161
+ let latest = typeof cache?.latest === 'string' ? cache.latest : null
162
+
163
+ if (stale) {
164
+ const fetched = fetchPromise ? await fetchPromise : null
165
+ const next = { checkedAt: new Date(now()).toISOString() }
166
+ if (fetched) {
167
+ next.latest = fetched
168
+ latest = fetched
169
+ } else if (latest) {
170
+ next.latest = latest
171
+ }
172
+ try {
173
+ writeCache(home, next)
174
+ } catch {
175
+ /* a read-only home must not change the command's exit code */
176
+ }
177
+ }
178
+
179
+ if (!printed && needsNudge({ cmd, version, latest, home })) {
180
+ printed = true
181
+ stderr.write(NUDGE)
182
+ }
183
+ } catch {
184
+ /* a failed lookup must not change the command's exit code */
185
+ }
186
+ },
187
+ }
188
+ }
@@ -0,0 +1,270 @@
1
+ import assert from 'node:assert/strict'
2
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
3
+ import { mkdtempSync } from 'node:fs'
4
+ import { tmpdir } from 'node:os'
5
+ import { join } from 'node:path'
6
+ import { spawnSync } from 'node:child_process'
7
+ import test from 'node:test'
8
+ import { userSkillDirs } from './agent-contract.mjs'
9
+ import { CLI_VERSION } from './version.mjs'
10
+ import {
11
+ compareVersions,
12
+ shouldCheck,
13
+ skillStale,
14
+ startUpdateCheck,
15
+ } from './update-check.mjs'
16
+
17
+ const NUDGE = 'Run: npx amorphous-cli@latest setup\n'
18
+
19
+ test('compareVersions', () => {
20
+ assert.equal(compareVersions('1.0.0', '1.0.0'), 0)
21
+ assert.equal(compareVersions('1.0.0', '1.0.1'), -1)
22
+ assert.equal(compareVersions('1.1.0', '1.0.9'), 1)
23
+ assert.equal(compareVersions('2.0.0', '1.9.9'), 1)
24
+ assert.equal(compareVersions('1.0.0-beta', '1.0.0'), -1)
25
+ assert.equal(compareVersions('1.0.0-beta', '1.0.0-rc'), 0)
26
+ assert.equal(compareVersions('nope', '1.0.0'), null)
27
+ })
28
+
29
+ function cacheFile(home) {
30
+ return join(home, '.config', 'amorphous', 'update-check.json')
31
+ }
32
+
33
+ function writeCache(home, data) {
34
+ const dir = join(home, '.config', 'amorphous')
35
+ mkdirSync(dir, { recursive: true })
36
+ writeFileSync(cacheFile(home), JSON.stringify(data) + '\n')
37
+ }
38
+
39
+ test('fresh cache nudges when cached latest is newer', async () => {
40
+ const home = mkdtempSync(join(tmpdir(), 'amorphous-uc-'))
41
+ const now = () => Date.parse('2026-01-01T12:00:00.000Z')
42
+ writeCache(home, { checkedAt: '2026-01-01T10:00:00.000Z', latest: '9.9.9' })
43
+ let fetchCalls = 0
44
+ let err = ''
45
+ const update = startUpdateCheck({
46
+ argv: ['list'],
47
+ env: {},
48
+ home,
49
+ version: '0.1.0',
50
+ now,
51
+ fetchLatest: async () => {
52
+ fetchCalls++
53
+ return '9.9.9'
54
+ },
55
+ stderr: { write: (s) => {
56
+ err += s
57
+ } },
58
+ })
59
+ await update.finish()
60
+ assert.equal(fetchCalls, 0)
61
+ assert.equal(err, NUDGE)
62
+ })
63
+
64
+ test('stale cache fetches and rewrites cache', async () => {
65
+ const home = mkdtempSync(join(tmpdir(), 'amorphous-uc-'))
66
+ const t0 = Date.parse('2026-01-01T00:00:00.000Z')
67
+ let nowMs = t0
68
+ writeCache(home, { checkedAt: '2025-01-01T00:00:00.000Z', latest: '0.1.0' })
69
+ let fetchCalls = 0
70
+ const update = startUpdateCheck({
71
+ argv: ['setup'],
72
+ env: {},
73
+ home,
74
+ version: CLI_VERSION,
75
+ now: () => nowMs,
76
+ fetchLatest: async () => {
77
+ fetchCalls++
78
+ return CLI_VERSION
79
+ },
80
+ stderr: { write: () => {} },
81
+ })
82
+ nowMs = t0 + 1000
83
+ await update.finish()
84
+ assert.equal(fetchCalls, 1)
85
+ const cache = JSON.parse(readFileSync(cacheFile(home), 'utf8'))
86
+ assert.equal(cache.latest, CLI_VERSION)
87
+ assert.ok(cache.checkedAt)
88
+ })
89
+
90
+ test('fetch failure keeps old latest and prints nothing', async () => {
91
+ const home = mkdtempSync(join(tmpdir(), 'amorphous-uc-'))
92
+ writeCache(home, { checkedAt: '2025-01-01T00:00:00.000Z', latest: '0.1.0' })
93
+ let err = ''
94
+ const update = startUpdateCheck({
95
+ argv: ['setup'],
96
+ env: {},
97
+ home,
98
+ version: CLI_VERSION,
99
+ now: () => Date.parse('2026-01-02T00:00:00.000Z'),
100
+ fetchLatest: async () => {
101
+ throw new Error('offline')
102
+ },
103
+ stderr: { write: (s) => {
104
+ err += s
105
+ } },
106
+ })
107
+ await update.finish()
108
+ assert.equal(err, '')
109
+ const cache = JSON.parse(readFileSync(cacheFile(home), 'utf8'))
110
+ assert.equal(cache.latest, '0.1.0')
111
+ assert.ok(cache.checkedAt)
112
+ })
113
+
114
+ test('corrupt cache triggers fetch', async () => {
115
+ const home = mkdtempSync(join(tmpdir(), 'amorphous-uc-'))
116
+ const dir = join(home, '.config', 'amorphous')
117
+ mkdirSync(dir, { recursive: true })
118
+ writeFileSync(cacheFile(home), '{not json')
119
+ let fetchCalls = 0
120
+ const update = startUpdateCheck({
121
+ argv: ['setup'],
122
+ env: {},
123
+ home,
124
+ version: CLI_VERSION,
125
+ now: () => Date.now(),
126
+ fetchLatest: async () => {
127
+ fetchCalls++
128
+ return CLI_VERSION
129
+ },
130
+ stderr: { write: () => {} },
131
+ })
132
+ await update.finish()
133
+ assert.equal(fetchCalls, 1)
134
+ })
135
+
136
+ test('equal or newer running version stays quiet', async () => {
137
+ const home = mkdtempSync(join(tmpdir(), 'amorphous-uc-'))
138
+ writeCache(home, { checkedAt: new Date().toISOString(), latest: CLI_VERSION })
139
+ let err = ''
140
+ const update = startUpdateCheck({
141
+ argv: ['list'],
142
+ env: {},
143
+ home,
144
+ version: CLI_VERSION,
145
+ now: () => Date.now(),
146
+ fetchLatest: async () => '0.0.1',
147
+ stderr: { write: (s) => {
148
+ err += s
149
+ } },
150
+ })
151
+ await update.finish()
152
+ assert.equal(err, '')
153
+
154
+ writeCache(home, { checkedAt: new Date().toISOString(), latest: '0.1.0' })
155
+ err = ''
156
+ const update2 = startUpdateCheck({
157
+ argv: ['list'],
158
+ env: {},
159
+ home,
160
+ version: CLI_VERSION,
161
+ now: () => Date.now(),
162
+ fetchLatest: async () => '0.1.0',
163
+ stderr: { write: (s) => {
164
+ err += s
165
+ } },
166
+ })
167
+ await update2.finish()
168
+ assert.equal(err, '')
169
+ })
170
+
171
+ test('shouldCheck', () => {
172
+ const off = {}
173
+ assert.equal(shouldCheck([], off), false)
174
+ assert.equal(shouldCheck(['help'], off), false)
175
+ assert.equal(shouldCheck(['list', '--help'], off), false)
176
+ assert.equal(shouldCheck(['login', '-h'], off), false)
177
+ assert.equal(shouldCheck(['mcp'], off), false)
178
+ assert.equal(shouldCheck(['mcp', 'whatever'], off), false)
179
+ assert.equal(shouldCheck(['setup'], { CI: '1' }), false)
180
+ assert.equal(shouldCheck(['setup'], { AMORPHOUS_NO_UPDATE_CHECK: '1' }), false)
181
+ assert.equal(shouldCheck(['setup'], off), true)
182
+ assert.equal(shouldCheck(['grant'], off), true)
183
+ })
184
+
185
+ test('skillStale', () => {
186
+ const home = mkdtempSync(join(tmpdir(), 'amorphous-skill-stale-'))
187
+ assert.equal(skillStale(home, CLI_VERSION), false)
188
+
189
+ const [d0, d1] = userSkillDirs(home)
190
+ mkdirSync(d0, { recursive: true })
191
+ writeFileSync(join(d0, 'SKILL.md'), 'skill')
192
+ assert.equal(skillStale(home, CLI_VERSION), true)
193
+
194
+ writeFileSync(join(d0, '.amorphous-version'), '0.1.0\n')
195
+ assert.equal(skillStale(home, CLI_VERSION), true)
196
+
197
+ writeFileSync(join(d0, '.amorphous-version'), `${CLI_VERSION}\n`)
198
+ assert.equal(skillStale(home, CLI_VERSION), false)
199
+
200
+ writeFileSync(join(d0, '.amorphous-version'), 'not-a-version\n')
201
+ assert.equal(skillStale(home, CLI_VERSION), false)
202
+
203
+ mkdirSync(d1, { recursive: true })
204
+ writeFileSync(join(d1, 'SKILL.md'), 'skill')
205
+ writeFileSync(join(d1, '.amorphous-version'), '9.9.9\n')
206
+ assert.equal(skillStale(home, CLI_VERSION), false)
207
+
208
+ writeFileSync(join(d1, '.amorphous-version'), '0.1.0\n')
209
+ assert.equal(skillStale(home, CLI_VERSION), true)
210
+ })
211
+
212
+ test('setup skips skill stamp nudge when registry matches', async () => {
213
+ const home = mkdtempSync(join(tmpdir(), 'amorphous-uc-'))
214
+ const [d0] = userSkillDirs(home)
215
+ mkdirSync(d0, { recursive: true })
216
+ writeFileSync(join(d0, 'SKILL.md'), 'skill')
217
+ writeCache(home, { checkedAt: new Date().toISOString(), latest: CLI_VERSION })
218
+ let err = ''
219
+ const update = startUpdateCheck({
220
+ argv: ['setup'],
221
+ env: {},
222
+ home,
223
+ version: CLI_VERSION,
224
+ now: () => Date.now(),
225
+ fetchLatest: async () => CLI_VERSION,
226
+ stderr: { write: (s) => {
227
+ err += s
228
+ } },
229
+ })
230
+ await update.finish()
231
+ assert.equal(err, '')
232
+ })
233
+
234
+ test('non-setup nudges on stale skill when registry matches', async () => {
235
+ const home = mkdtempSync(join(tmpdir(), 'amorphous-uc-'))
236
+ const [d0] = userSkillDirs(home)
237
+ mkdirSync(d0, { recursive: true })
238
+ writeFileSync(join(d0, 'SKILL.md'), 'skill')
239
+ writeCache(home, { checkedAt: new Date().toISOString(), latest: CLI_VERSION })
240
+ let err = ''
241
+ const update = startUpdateCheck({
242
+ argv: ['list'],
243
+ env: {},
244
+ home,
245
+ version: CLI_VERSION,
246
+ now: () => Date.now(),
247
+ fetchLatest: async () => CLI_VERSION,
248
+ stderr: { write: (s) => {
249
+ err += s
250
+ } },
251
+ })
252
+ await update.finish()
253
+ assert.equal(err, NUDGE)
254
+ })
255
+
256
+ test('integration help skips update check file', () => {
257
+ const home = mkdtempSync(join(tmpdir(), 'amorphous-int-'))
258
+ const repoRoot = join(import.meta.dirname, '../..')
259
+ const env = { ...process.env, HOME: home, USERPROFILE: home }
260
+ delete env.AMORPHOUS_NO_UPDATE_CHECK
261
+ delete env.CI
262
+ const r = spawnSync(process.execPath, ['cli/index.mjs', '--help'], {
263
+ cwd: repoRoot,
264
+ env,
265
+ encoding: 'utf8',
266
+ })
267
+ assert.equal(r.status, 0)
268
+ assert.ok(!r.stderr.includes('Run: npx amorphous-cli@latest setup'))
269
+ assert.throws(() => readFileSync(cacheFile(home)), { code: 'ENOENT' })
270
+ })
@@ -0,0 +1,3 @@
1
+ import { readFileSync } from 'node:fs'
2
+
3
+ export const CLI_VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version
package/mcp.mjs CHANGED
@@ -9,6 +9,7 @@ import { fileURLToPath } from 'node:url'
9
9
  import { McpServer } from '@modelcontextprotocol/server'
10
10
  import { serveStdio } from '@modelcontextprotocol/server/stdio'
11
11
  import { z } from 'zod'
12
+ import { CLI_VERSION } from './lib/version.mjs'
12
13
 
13
14
  const cliPath = join(dirname(fileURLToPath(import.meta.url)), 'index.mjs')
14
15
 
@@ -49,7 +50,7 @@ async function call(args) {
49
50
  }
50
51
 
51
52
  await serveStdio(() => {
52
- const server = new McpServer({ name: 'amorphous', version: '0.1.5' })
53
+ const server = new McpServer({ name: 'amorphous', version: CLI_VERSION })
53
54
 
54
55
  server.registerTool(
55
56
  'list',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amorphous-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "mcpName": "io.github.amorphous-one/amorphous",
5
5
  "description": "Build abstracts on Amorphous. An abstract can be anything.",
6
6
  "type": "module",