toon-memory 1.4.0 ā 1.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.
- package/README.md +165 -38
- package/bin/toon-memory.js +0 -0
- package/dist/cli/setup.js +146 -5
- package/mcp/server.js +115 -4
- package/package.json +1 -1
- package/src/cli/setup.ts +350 -15
- package/src/mcp/server.ts +337 -6
package/src/cli/setup.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, cpSync, unlinkSync } from "fs"
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, cpSync, unlinkSync, readdirSync, statSync } from "fs"
|
|
2
2
|
import { basename, dirname, join } from "path"
|
|
3
3
|
import { fileURLToPath } from "url"
|
|
4
4
|
import { execSync } from "child_process"
|
|
5
5
|
import { createInterface } from "readline"
|
|
6
6
|
import { createRequire } from "module"
|
|
7
|
+
import { gzipSync, gunzipSync } from "zlib"
|
|
7
8
|
|
|
8
9
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
9
10
|
const projectRoot = process.cwd()
|
|
@@ -12,10 +13,15 @@ const projectRoot = process.cwd()
|
|
|
12
13
|
const sourceDir = join(__dirname, "..", "..", "src")
|
|
13
14
|
const HOME = process.env.HOME || process.env.USERPROFILE || "~"
|
|
14
15
|
|
|
16
|
+
/** Supported AI coding agent configuration */
|
|
15
17
|
interface Agent {
|
|
18
|
+
/** Agent identifier (e.g., "opencode", "vscode/copilot") */
|
|
16
19
|
name: string
|
|
20
|
+
/** Global config file path (e.g., ~/.config/opencode/opencode.json) */
|
|
17
21
|
global?: string
|
|
22
|
+
/** Local (project-level) config file path (e.g., .opencode/opencode.json) */
|
|
18
23
|
local?: string
|
|
24
|
+
/** JSON key where MCP servers are stored */
|
|
19
25
|
mcpKey: string
|
|
20
26
|
}
|
|
21
27
|
|
|
@@ -27,7 +33,22 @@ try {
|
|
|
27
33
|
execSync("npm install @toon-format/toon", { cwd: projectRoot, stdio: "inherit" })
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Detect all supported AI coding agents on the system.
|
|
38
|
+
*
|
|
39
|
+
* Scans for configuration files in both global (~/.config/) and local
|
|
40
|
+
* (.opencode/, .vscode/, etc.) locations.
|
|
41
|
+
*
|
|
42
|
+
* @returns Array of detected agent configurations
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const agents = detectAgents()
|
|
47
|
+
* for (const agent of agents) {
|
|
48
|
+
* console.log(`${agent.name}: ${agent.local || "not found"}`)
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
31
52
|
function detectAgents(): Agent[] {
|
|
32
53
|
const agents: Agent[] = []
|
|
33
54
|
|
|
@@ -94,7 +115,17 @@ function detectAgents(): Agent[] {
|
|
|
94
115
|
return agents
|
|
95
116
|
}
|
|
96
117
|
|
|
97
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Install custom tools and memory directory for OpenCode.
|
|
120
|
+
*
|
|
121
|
+
* Creates `.opencode/tools/` and `.opencode/memory/` directories,
|
|
122
|
+
* copies `memory.ts` tool, and creates initial `data.toon` if needed.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```bash
|
|
126
|
+
* npx toon-memory init # Calls installOpenCodeTools()
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
98
129
|
function installOpenCodeTools(): void {
|
|
99
130
|
const toolsDir = join(projectRoot, ".opencode", "tools")
|
|
100
131
|
const memoryDir = join(projectRoot, ".opencode", "memory")
|
|
@@ -112,7 +143,17 @@ function installOpenCodeTools(): void {
|
|
|
112
143
|
}
|
|
113
144
|
}
|
|
114
145
|
|
|
115
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Add `.opencode/memory/` to `.gitignore` if not already present.
|
|
148
|
+
*
|
|
149
|
+
* Creates `.gitignore` if it doesn't exist, or appends the memory
|
|
150
|
+
* exclusion pattern to the existing file.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* ensureGitignore() // Adds .opencode/memory/ to .gitignore
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
116
157
|
function ensureGitignore(): void {
|
|
117
158
|
const gitignorePath = join(projectRoot, ".gitignore")
|
|
118
159
|
const entry = ".opencode/memory/"
|
|
@@ -130,7 +171,20 @@ function ensureGitignore(): void {
|
|
|
130
171
|
}
|
|
131
172
|
}
|
|
132
173
|
|
|
133
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Install MCP server configuration for an agent.
|
|
176
|
+
*
|
|
177
|
+
* Adds the `toon-memory` MCP server entry to the agent's config file.
|
|
178
|
+
*
|
|
179
|
+
* @param agent - Agent configuration with config path and MCP key
|
|
180
|
+
* @param scope - "global" or "local" installation scope
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* const agent = { name: "opencode", local: ".opencode/opencode.json", mcpKey: "mcp" }
|
|
185
|
+
* installMCPConfig(agent, "local")
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
134
188
|
function installMCPConfig(agent: Agent, scope: string): void {
|
|
135
189
|
const configPath = scope === "global" ? agent.global : agent.local
|
|
136
190
|
|
|
@@ -154,16 +208,34 @@ function installMCPConfig(agent: Agent, scope: string): void {
|
|
|
154
208
|
const mcpKey = agent.mcpKey || "mcpServers"
|
|
155
209
|
if (!config[mcpKey]) config[mcpKey] = {}
|
|
156
210
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
211
|
+
// OpenCode uses a different format than other agents
|
|
212
|
+
if (agent.name === "opencode") {
|
|
213
|
+
config[mcpKey]["toon-memory"] = {
|
|
214
|
+
enabled: true,
|
|
215
|
+
type: "local",
|
|
216
|
+
command: ["npx", "-y", "toon-memory", "mcp"]
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
config[mcpKey]["toon-memory"] = {
|
|
220
|
+
command: "npx",
|
|
221
|
+
args: ["-y", "toon-memory", "mcp"]
|
|
222
|
+
}
|
|
160
223
|
}
|
|
161
224
|
|
|
162
225
|
writeFileSync(configPath, JSON.stringify(config, null, 2))
|
|
163
226
|
console.log(` MCP server added to ${configPath}`)
|
|
164
227
|
}
|
|
165
228
|
|
|
166
|
-
|
|
229
|
+
/**
|
|
230
|
+
* Uninstall toon-memory from all detected agents.
|
|
231
|
+
*
|
|
232
|
+
* Removes MCP server configurations and custom tools from all agents.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```bash
|
|
236
|
+
* npx toon-memory uninstall
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
167
239
|
function uninstall(): void {
|
|
168
240
|
console.log("\nš§ toon-memory uninstaller\n")
|
|
169
241
|
|
|
@@ -198,7 +270,20 @@ function uninstall(): void {
|
|
|
198
270
|
console.log("\nā
toon-memory uninstalled from all agents\n")
|
|
199
271
|
}
|
|
200
272
|
|
|
201
|
-
|
|
273
|
+
/**
|
|
274
|
+
* Initialize toon-memory for all detected agents (non-interactive).
|
|
275
|
+
*
|
|
276
|
+
* Installs MCP server configs, creates memory directory, and
|
|
277
|
+
* updates `.gitignore`.
|
|
278
|
+
*
|
|
279
|
+
* @param scope - "local" (default) or "global" installation scope
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```bash
|
|
283
|
+
* npx toon-memory init # Local install
|
|
284
|
+
* npx toon-memory init global # Global install
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
202
287
|
function init(scope: string = "local"): void {
|
|
203
288
|
console.log("\nš§ toon-memory init\n")
|
|
204
289
|
|
|
@@ -220,7 +305,16 @@ function init(scope: string = "local"): void {
|
|
|
220
305
|
console.log("Done! Restart your agent to use memory tools.\n")
|
|
221
306
|
}
|
|
222
307
|
|
|
223
|
-
|
|
308
|
+
/**
|
|
309
|
+
* Show toon-memory installation status.
|
|
310
|
+
*
|
|
311
|
+
* Displays version, memory entry count, and agent configuration status.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```bash
|
|
315
|
+
* npx toon-memory status
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
224
318
|
function status(): void {
|
|
225
319
|
console.log("\nš§ toon-memory status\n")
|
|
226
320
|
|
|
@@ -272,7 +366,16 @@ function status(): void {
|
|
|
272
366
|
console.log("")
|
|
273
367
|
}
|
|
274
368
|
|
|
275
|
-
|
|
369
|
+
/**
|
|
370
|
+
* Upgrade toon-memory to the latest version.
|
|
371
|
+
*
|
|
372
|
+
* Checks npm registry for updates and installs if available.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```bash
|
|
376
|
+
* npx toon-memory upgrade
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
276
379
|
function upgrade(): void {
|
|
277
380
|
console.log("\nš§ toon-memory upgrade\n")
|
|
278
381
|
|
|
@@ -291,7 +394,16 @@ function upgrade(): void {
|
|
|
291
394
|
}
|
|
292
395
|
}
|
|
293
396
|
|
|
294
|
-
|
|
397
|
+
/**
|
|
398
|
+
* Display memory statistics.
|
|
399
|
+
*
|
|
400
|
+
* Shows entry counts by category, last update date, and file size.
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* ```bash
|
|
404
|
+
* npx toon-memory stats
|
|
405
|
+
* ```
|
|
406
|
+
*/
|
|
295
407
|
function stats(): void {
|
|
296
408
|
console.log("\nš§ toon-memory stats\n")
|
|
297
409
|
|
|
@@ -333,7 +445,17 @@ function stats(): void {
|
|
|
333
445
|
console.log("")
|
|
334
446
|
}
|
|
335
447
|
|
|
336
|
-
|
|
448
|
+
/**
|
|
449
|
+
* Export memory to JSON format.
|
|
450
|
+
*
|
|
451
|
+
* Creates a `toon-memory-export.json` file with all entries
|
|
452
|
+
* for backup or transfer to another project.
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```bash
|
|
456
|
+
* npx toon-memory export
|
|
457
|
+
* ```
|
|
458
|
+
*/
|
|
337
459
|
function exportMemory(): void {
|
|
338
460
|
console.log("\nš§ toon-memory export\n")
|
|
339
461
|
|
|
@@ -374,7 +496,18 @@ function exportMemory(): void {
|
|
|
374
496
|
console.log(` ${outputPath}\n`)
|
|
375
497
|
}
|
|
376
498
|
|
|
377
|
-
|
|
499
|
+
/**
|
|
500
|
+
* Import memory from JSON file.
|
|
501
|
+
*
|
|
502
|
+
* Imports entries from a JSON export, skipping duplicates based on key.
|
|
503
|
+
*
|
|
504
|
+
* @param file - Path to JSON file (relative or absolute)
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```bash
|
|
508
|
+
* npx toon-memory import toon-memory-export.json
|
|
509
|
+
* ```
|
|
510
|
+
*/
|
|
378
511
|
function importMemory(): void {
|
|
379
512
|
console.log("\nš§ toon-memory import\n")
|
|
380
513
|
|
|
@@ -446,6 +579,203 @@ function importMemory(): void {
|
|
|
446
579
|
console.log(`Skipped ${importData.entries.length - newEntries.length} duplicates\n`)
|
|
447
580
|
}
|
|
448
581
|
|
|
582
|
+
/** Watch mode options */
|
|
583
|
+
interface WatchOptions {
|
|
584
|
+
/** Backup interval in minutes (default: 5) */
|
|
585
|
+
interval: number
|
|
586
|
+
/** Maximum number of backups to keep (0 = unlimited) */
|
|
587
|
+
maxBackups: number
|
|
588
|
+
/** Enable gzip compression for backups */
|
|
589
|
+
compress: boolean
|
|
590
|
+
/** Enable file logging */
|
|
591
|
+
logFile: boolean
|
|
592
|
+
/** Log file path */
|
|
593
|
+
logPath: string
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/** Parse watch CLI arguments into WatchOptions */
|
|
597
|
+
function parseWatchOptions(args: string[]): WatchOptions {
|
|
598
|
+
const opts: WatchOptions = {
|
|
599
|
+
interval: 5,
|
|
600
|
+
maxBackups: 10,
|
|
601
|
+
compress: false,
|
|
602
|
+
logFile: false,
|
|
603
|
+
logPath: ""
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
for (let i = 1; i < args.length; i++) {
|
|
607
|
+
const arg = args[i]
|
|
608
|
+
if (arg === "--compress" || arg === "-c") {
|
|
609
|
+
opts.compress = true
|
|
610
|
+
} else if (arg === "--log" || arg === "-l") {
|
|
611
|
+
opts.logFile = true
|
|
612
|
+
opts.logPath = args[++i] || join(projectRoot, ".opencode", "memory", "watch.log")
|
|
613
|
+
} else if (arg === "--max-backups" || arg === "-m") {
|
|
614
|
+
opts.maxBackups = parseInt(args[++i]) || 10
|
|
615
|
+
} else if (!arg.startsWith("-")) {
|
|
616
|
+
opts.interval = parseInt(arg) || 5
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return opts
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/** Write a line to the watch log file */
|
|
624
|
+
function writeWatchLog(logPath: string, message: string): void {
|
|
625
|
+
if (!logPath) return
|
|
626
|
+
const timestamp = new Date().toISOString()
|
|
627
|
+
const logLine = `[${timestamp}] ${message}\n`
|
|
628
|
+
writeFileSync(logPath, logLine, { flag: "a" })
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/** Get list of backup files sorted by creation time (oldest first) */
|
|
632
|
+
function getBackupFiles(backupDir: string): string[] {
|
|
633
|
+
if (!existsSync(backupDir)) return []
|
|
634
|
+
|
|
635
|
+
return readdirSync(backupDir)
|
|
636
|
+
.filter(f => f.startsWith("backup-") && (f.endsWith(".toon") || f.endsWith(".gz")))
|
|
637
|
+
.map(f => join(backupDir, f))
|
|
638
|
+
.sort((a, b) => statSync(a).mtimeMs - statSync(b).mtimeMs)
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** Remove oldest backups if we exceed maxBackups */
|
|
642
|
+
function pruneBackups(backupDir: string, maxBackups: number): number {
|
|
643
|
+
if (maxBackups <= 0) return 0
|
|
644
|
+
|
|
645
|
+
const files = getBackupFiles(backupDir)
|
|
646
|
+
const excess = files.length - maxBackups
|
|
647
|
+
|
|
648
|
+
if (excess <= 0) return 0
|
|
649
|
+
|
|
650
|
+
for (let i = 0; i < excess; i++) {
|
|
651
|
+
unlinkSync(files[i])
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return excess
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/** Compress content with gzip */
|
|
658
|
+
function compressData(data: string): Buffer {
|
|
659
|
+
return gzipSync(Buffer.from(data, "utf-8"))
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/** Decompress gzip data to string */
|
|
663
|
+
function decompressData(data: Buffer): string {
|
|
664
|
+
return gunzipSync(data).toString("utf-8")
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Watch mode - backup memory every N minutes
|
|
669
|
+
*
|
|
670
|
+
* @example
|
|
671
|
+
* ```bash
|
|
672
|
+
* npx toon-memory watch # Default: 5 min interval, 10 max backups
|
|
673
|
+
* npx toon-memory watch 10 # 10 minute interval
|
|
674
|
+
* npx toon-memory watch -c # Enable compression
|
|
675
|
+
* npx toon-memory watch -l # Enable file logging
|
|
676
|
+
* npx toon-memory watch -m 20 # Keep max 20 backups
|
|
677
|
+
* npx toon-memory watch 15 -c -l -m 5 # All options
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
680
|
+
function watch(): void {
|
|
681
|
+
console.log("\nš§ toon-memory watch\n")
|
|
682
|
+
|
|
683
|
+
const memoryFile = join(projectRoot, ".opencode", "memory", "data.toon")
|
|
684
|
+
const backupDir = join(projectRoot, ".opencode", "memory", "backups")
|
|
685
|
+
|
|
686
|
+
if (!existsSync(memoryFile)) {
|
|
687
|
+
console.log("Memory not initialized. Run 'npx toon-memory init' first.\n")
|
|
688
|
+
return
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (!existsSync(backupDir)) mkdirSync(backupDir, { recursive: true })
|
|
692
|
+
|
|
693
|
+
const opts = parseWatchOptions(args)
|
|
694
|
+
|
|
695
|
+
if (opts.logFile) {
|
|
696
|
+
const logDir = dirname(opts.logPath)
|
|
697
|
+
if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true })
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
console.log(`Watching memory file every ${opts.interval} minutes...`)
|
|
701
|
+
console.log(`Max backups: ${opts.maxBackups === 0 ? "unlimited" : opts.maxBackups}`)
|
|
702
|
+
console.log(`Compression: ${opts.compress ? "enabled" : "disabled"}`)
|
|
703
|
+
console.log(`Logging: ${opts.logFile ? `enabled (${opts.logPath})` : "disabled"}`)
|
|
704
|
+
console.log(`Press Ctrl+C to stop\n`)
|
|
705
|
+
|
|
706
|
+
let lastContent = readFileSync(memoryFile, "utf-8")
|
|
707
|
+
let lastHash = hashContent(lastContent)
|
|
708
|
+
let backupCount = 0
|
|
709
|
+
|
|
710
|
+
if (opts.logFile) writeWatchLog(opts.logPath, "Watch started")
|
|
711
|
+
|
|
712
|
+
const backup = () => {
|
|
713
|
+
try {
|
|
714
|
+
const currentContent = readFileSync(memoryFile, "utf-8")
|
|
715
|
+
const currentHash = hashContent(currentContent)
|
|
716
|
+
|
|
717
|
+
if (currentHash !== lastHash) {
|
|
718
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-")
|
|
719
|
+
const ext = opts.compress ? ".toon.gz" : ".toon"
|
|
720
|
+
const backupFile = join(backupDir, `backup-${timestamp}${ext}`)
|
|
721
|
+
|
|
722
|
+
if (opts.compress) {
|
|
723
|
+
writeFileSync(backupFile, compressData(currentContent))
|
|
724
|
+
} else {
|
|
725
|
+
writeFileSync(backupFile, currentContent)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
backupCount++
|
|
729
|
+
console.log(`š¦ Backup #${backupCount} created: ${timestamp}`)
|
|
730
|
+
if (opts.logFile) writeWatchLog(opts.logPath, `Backup #${backupCount}: ${timestamp}`)
|
|
731
|
+
|
|
732
|
+
lastContent = currentContent
|
|
733
|
+
lastHash = currentHash
|
|
734
|
+
|
|
735
|
+
const pruned = pruneBackups(backupDir, opts.maxBackups)
|
|
736
|
+
if (pruned > 0) {
|
|
737
|
+
console.log(`šļø Pruned ${pruned} old backup(s)`)
|
|
738
|
+
if (opts.logFile) writeWatchLog(opts.logPath, `Pruned ${pruned} old backup(s)`)
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
} catch (err) {
|
|
742
|
+
const msg = `Error creating backup: ${(err as Error).message}`
|
|
743
|
+
console.error(`ā ${msg}`)
|
|
744
|
+
if (opts.logFile) writeWatchLog(opts.logPath, msg)
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/** Simple hash for change detection (not cryptographic) */
|
|
749
|
+
function hashContent(content: string): string {
|
|
750
|
+
let hash = 0
|
|
751
|
+
for (let i = 0; i < content.length; i++) {
|
|
752
|
+
const char = content.charCodeAt(i)
|
|
753
|
+
hash = ((hash << 5) - hash) + char
|
|
754
|
+
hash = hash & hash
|
|
755
|
+
}
|
|
756
|
+
return hash.toString(36)
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Initial backup
|
|
760
|
+
backup()
|
|
761
|
+
|
|
762
|
+
// Set interval
|
|
763
|
+
const interval = setInterval(backup, opts.interval * 60 * 1000)
|
|
764
|
+
|
|
765
|
+
// Handle Ctrl+C
|
|
766
|
+
process.on("SIGINT", () => {
|
|
767
|
+
clearInterval(interval)
|
|
768
|
+
console.log(`\nā
Watch stopped. ${backupCount} backups created.\n`)
|
|
769
|
+
if (opts.logFile) writeWatchLog(opts.logPath, `Watch stopped. ${backupCount} backups created.`)
|
|
770
|
+
process.exit(0)
|
|
771
|
+
})
|
|
772
|
+
|
|
773
|
+
process.on("SIGTERM", () => {
|
|
774
|
+
clearInterval(interval)
|
|
775
|
+
process.exit(0)
|
|
776
|
+
})
|
|
777
|
+
}
|
|
778
|
+
|
|
449
779
|
// Main
|
|
450
780
|
const args = process.argv.slice(2)
|
|
451
781
|
|
|
@@ -484,6 +814,11 @@ if (args[0] === "import") {
|
|
|
484
814
|
process.exit(0)
|
|
485
815
|
}
|
|
486
816
|
|
|
817
|
+
if (args[0] === "watch") {
|
|
818
|
+
watch()
|
|
819
|
+
process.exit(0)
|
|
820
|
+
}
|
|
821
|
+
|
|
487
822
|
const agents = detectAgents()
|
|
488
823
|
console.log("\nš§ toon-memory installer\n")
|
|
489
824
|
|