chainlesschain 0.37.8 → 0.37.10

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.
Files changed (59) hide show
  1. package/README.md +403 -8
  2. package/bin/chainlesschain.js +4 -0
  3. package/package.json +7 -2
  4. package/src/commands/agent.js +30 -0
  5. package/src/commands/ask.js +114 -0
  6. package/src/commands/audit.js +286 -0
  7. package/src/commands/auth.js +387 -0
  8. package/src/commands/browse.js +184 -0
  9. package/src/commands/chat.js +35 -0
  10. package/src/commands/db.js +152 -0
  11. package/src/commands/did.js +376 -0
  12. package/src/commands/encrypt.js +233 -0
  13. package/src/commands/export.js +125 -0
  14. package/src/commands/git.js +215 -0
  15. package/src/commands/import.js +259 -0
  16. package/src/commands/instinct.js +202 -0
  17. package/src/commands/llm.js +288 -0
  18. package/src/commands/mcp.js +302 -0
  19. package/src/commands/memory.js +282 -0
  20. package/src/commands/note.js +489 -0
  21. package/src/commands/org.js +505 -0
  22. package/src/commands/p2p.js +274 -0
  23. package/src/commands/plugin.js +398 -0
  24. package/src/commands/search.js +237 -0
  25. package/src/commands/session.js +238 -0
  26. package/src/commands/skill.js +479 -0
  27. package/src/commands/sync.js +249 -0
  28. package/src/commands/tokens.js +214 -0
  29. package/src/commands/wallet.js +416 -0
  30. package/src/index.js +65 -0
  31. package/src/lib/audit-logger.js +364 -0
  32. package/src/lib/bm25-search.js +322 -0
  33. package/src/lib/browser-automation.js +216 -0
  34. package/src/lib/crypto-manager.js +246 -0
  35. package/src/lib/did-manager.js +270 -0
  36. package/src/lib/ensure-utf8.js +59 -0
  37. package/src/lib/git-integration.js +220 -0
  38. package/src/lib/instinct-manager.js +190 -0
  39. package/src/lib/knowledge-exporter.js +302 -0
  40. package/src/lib/knowledge-importer.js +293 -0
  41. package/src/lib/llm-providers.js +325 -0
  42. package/src/lib/mcp-client.js +413 -0
  43. package/src/lib/memory-manager.js +211 -0
  44. package/src/lib/note-versioning.js +244 -0
  45. package/src/lib/org-manager.js +424 -0
  46. package/src/lib/p2p-manager.js +317 -0
  47. package/src/lib/pdf-parser.js +96 -0
  48. package/src/lib/permission-engine.js +374 -0
  49. package/src/lib/plan-mode.js +333 -0
  50. package/src/lib/platform.js +15 -0
  51. package/src/lib/plugin-manager.js +312 -0
  52. package/src/lib/response-cache.js +156 -0
  53. package/src/lib/session-manager.js +189 -0
  54. package/src/lib/sync-manager.js +347 -0
  55. package/src/lib/token-tracker.js +200 -0
  56. package/src/lib/wallet-manager.js +348 -0
  57. package/src/repl/agent-repl.js +912 -0
  58. package/src/repl/chat-repl.js +262 -0
  59. package/src/runtime/bootstrap.js +159 -0
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Knowledge export commands
3
+ * chainlesschain export markdown|site --output <dir>
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import ora from "ora";
8
+ import { resolve } from "path";
9
+ import { logger } from "../lib/logger.js";
10
+ import { bootstrap, shutdown } from "../runtime/bootstrap.js";
11
+ import { exportToMarkdown, exportToSite } from "../lib/knowledge-exporter.js";
12
+
13
+ export function registerExportCommand(program) {
14
+ const exp = program
15
+ .command("export")
16
+ .description("Export knowledge base to external formats");
17
+
18
+ // export markdown
19
+ exp
20
+ .command("markdown")
21
+ .description("Export notes as markdown files")
22
+ .requiredOption("-o, --output <dir>", "Output directory")
23
+ .option("--category <category>", "Filter by category")
24
+ .option("--tag <tag>", "Filter by tag")
25
+ .option("-n, --limit <n>", "Max notes to export")
26
+ .option("--json", "Output as JSON")
27
+ .action(async (options) => {
28
+ try {
29
+ const outputDir = resolve(options.output);
30
+ const spinner = ora("Exporting to markdown...").start();
31
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
32
+ if (!ctx.db) {
33
+ spinner.fail("Database not available");
34
+ process.exit(1);
35
+ }
36
+
37
+ const db = ctx.db.getDatabase();
38
+ const exported = exportToMarkdown(db, outputDir, {
39
+ category: options.category,
40
+ tag: options.tag,
41
+ limit: options.limit ? parseInt(options.limit) : undefined,
42
+ });
43
+ spinner.stop();
44
+
45
+ if (options.json) {
46
+ console.log(
47
+ JSON.stringify(
48
+ { count: exported.length, output: outputDir, files: exported },
49
+ null,
50
+ 2,
51
+ ),
52
+ );
53
+ } else if (exported.length === 0) {
54
+ logger.info("No notes to export");
55
+ } else {
56
+ logger.success(
57
+ `Exported ${chalk.cyan(exported.length)} notes to ${chalk.gray(outputDir)}`,
58
+ );
59
+ for (const f of exported.slice(0, 10)) {
60
+ logger.log(` ${chalk.gray(f.path)}`);
61
+ }
62
+ if (exported.length > 10) {
63
+ logger.log(chalk.gray(` ... and ${exported.length - 10} more`));
64
+ }
65
+ }
66
+
67
+ await shutdown();
68
+ } catch (err) {
69
+ logger.error(`Export failed: ${err.message}`);
70
+ process.exit(1);
71
+ }
72
+ });
73
+
74
+ // export site
75
+ exp
76
+ .command("site")
77
+ .description("Export notes as a static HTML website")
78
+ .requiredOption("-o, --output <dir>", "Output directory")
79
+ .option("--title <title>", "Site title", "ChainlessChain Knowledge Base")
80
+ .option("--category <category>", "Filter by category")
81
+ .option("--tag <tag>", "Filter by tag")
82
+ .option("--json", "Output as JSON")
83
+ .action(async (options) => {
84
+ try {
85
+ const outputDir = resolve(options.output);
86
+ const spinner = ora("Generating static site...").start();
87
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
88
+ if (!ctx.db) {
89
+ spinner.fail("Database not available");
90
+ process.exit(1);
91
+ }
92
+
93
+ const db = ctx.db.getDatabase();
94
+ const exported = exportToSite(db, outputDir, {
95
+ title: options.title,
96
+ category: options.category,
97
+ tag: options.tag,
98
+ });
99
+ spinner.stop();
100
+
101
+ if (options.json) {
102
+ console.log(
103
+ JSON.stringify(
104
+ { count: exported.length, output: outputDir, files: exported },
105
+ null,
106
+ 2,
107
+ ),
108
+ );
109
+ } else {
110
+ logger.success(
111
+ `Generated static site with ${chalk.cyan(exported.length)} pages`,
112
+ );
113
+ logger.log(` ${chalk.gray("Output:")} ${outputDir}`);
114
+ logger.log(
115
+ ` ${chalk.gray("Files:")} index.html, style.css, ${exported.length} note pages`,
116
+ );
117
+ }
118
+
119
+ await shutdown();
120
+ } catch (err) {
121
+ logger.error(`Export failed: ${err.message}`);
122
+ process.exit(1);
123
+ }
124
+ });
125
+ }
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Git integration commands
3
+ * chainlesschain git status|auto-commit|hooks|history-analyze
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import { resolve } from "path";
8
+ import { logger } from "../lib/logger.js";
9
+ import {
10
+ gitStatus,
11
+ gitAutoCommit,
12
+ gitLog,
13
+ gitHistoryAnalyze,
14
+ gitInit,
15
+ isGitRepo,
16
+ } from "../lib/git-integration.js";
17
+
18
+ export function registerGitCommand(program) {
19
+ const git = program
20
+ .command("git")
21
+ .description("Git integration for knowledge base versioning");
22
+
23
+ // git status
24
+ git
25
+ .command("status")
26
+ .description("Show git status of the knowledge base directory")
27
+ .option("-d, --dir <dir>", "Target directory", ".")
28
+ .option("--json", "Output as JSON")
29
+ .action(async (options) => {
30
+ try {
31
+ const dir = resolve(options.dir);
32
+ const status = gitStatus(dir);
33
+
34
+ if (options.json) {
35
+ console.log(JSON.stringify(status, null, 2));
36
+ return;
37
+ }
38
+
39
+ if (!status.isRepo) {
40
+ logger.info(
41
+ `Not a git repository: ${dir}\n Run ${chalk.cyan("chainlesschain git init")} to initialize.`,
42
+ );
43
+ return;
44
+ }
45
+
46
+ logger.log(chalk.bold(`Branch: ${chalk.cyan(status.branch)}`));
47
+
48
+ if (status.clean) {
49
+ logger.log(chalk.green("Working tree clean"));
50
+ } else {
51
+ logger.log(`\n${status.files.length} changed file(s):\n`);
52
+ for (const f of status.files) {
53
+ const statusColor =
54
+ f.status === "M"
55
+ ? chalk.yellow
56
+ : f.status === "A" || f.status === "??"
57
+ ? chalk.green
58
+ : f.status === "D"
59
+ ? chalk.red
60
+ : chalk.white;
61
+ logger.log(` ${statusColor(f.status.padEnd(3))} ${f.file}`);
62
+ }
63
+ }
64
+ } catch (err) {
65
+ logger.error(`Git status failed: ${err.message}`);
66
+ process.exit(1);
67
+ }
68
+ });
69
+
70
+ // git init
71
+ git
72
+ .command("init")
73
+ .description("Initialize a git repository")
74
+ .option("-d, --dir <dir>", "Target directory", ".")
75
+ .action(async (options) => {
76
+ try {
77
+ const dir = resolve(options.dir);
78
+ const result = gitInit(dir);
79
+ if (result.initialized) {
80
+ logger.success(`Initialized git repository in ${chalk.gray(dir)}`);
81
+ } else {
82
+ logger.info(result.message);
83
+ }
84
+ } catch (err) {
85
+ logger.error(`Git init failed: ${err.message}`);
86
+ process.exit(1);
87
+ }
88
+ });
89
+
90
+ // git auto-commit
91
+ git
92
+ .command("auto-commit")
93
+ .description("Auto-commit all changes with a generated message")
94
+ .option("-d, --dir <dir>", "Target directory", ".")
95
+ .option("-m, --message <msg>", "Custom commit message")
96
+ .option("--json", "Output as JSON")
97
+ .action(async (options) => {
98
+ try {
99
+ const dir = resolve(options.dir);
100
+ const result = gitAutoCommit(dir, options.message);
101
+
102
+ if (options.json) {
103
+ console.log(JSON.stringify(result, null, 2));
104
+ return;
105
+ }
106
+
107
+ if (!result.committed) {
108
+ logger.info(result.message);
109
+ } else {
110
+ logger.success(
111
+ `Committed ${chalk.cyan(result.hash)}: ${result.message}`,
112
+ );
113
+ logger.log(chalk.gray(` ${result.filesChanged} file(s) changed`));
114
+ }
115
+ } catch (err) {
116
+ logger.error(`Auto-commit failed: ${err.message}`);
117
+ process.exit(1);
118
+ }
119
+ });
120
+
121
+ // git hooks
122
+ git
123
+ .command("hooks")
124
+ .description("Install git hooks for the knowledge base")
125
+ .option("-d, --dir <dir>", "Target directory", ".")
126
+ .option("--install", "Install hooks")
127
+ .action(async (options) => {
128
+ try {
129
+ const dir = resolve(options.dir);
130
+
131
+ if (!isGitRepo(dir)) {
132
+ logger.error("Not a git repository. Run 'git init' first.");
133
+ process.exit(1);
134
+ }
135
+
136
+ if (options.install) {
137
+ const { installHooks } = await import("../lib/git-integration.js");
138
+ const result = installHooks(dir);
139
+ logger.success(
140
+ `Installed ${chalk.cyan(result.hook)} hook at ${chalk.gray(result.path)}`,
141
+ );
142
+ } else {
143
+ logger.info(`Use ${chalk.cyan("--install")} to install git hooks`);
144
+ }
145
+ } catch (err) {
146
+ logger.error(`Hooks failed: ${err.message}`);
147
+ process.exit(1);
148
+ }
149
+ });
150
+
151
+ // git history-analyze
152
+ git
153
+ .command("history-analyze")
154
+ .description("Analyze repository history and statistics")
155
+ .option("-d, --dir <dir>", "Target directory", ".")
156
+ .option("-n, --limit <n>", "Recent commits to show", "10")
157
+ .option("--json", "Output as JSON")
158
+ .action(async (options) => {
159
+ try {
160
+ const dir = resolve(options.dir);
161
+
162
+ if (!isGitRepo(dir)) {
163
+ logger.error("Not a git repository");
164
+ process.exit(1);
165
+ }
166
+
167
+ const analysis = gitHistoryAnalyze(dir);
168
+ const recentLog = gitLog(dir, parseInt(options.limit));
169
+
170
+ if (options.json) {
171
+ console.log(
172
+ JSON.stringify({ ...analysis, recentCommits: recentLog }, null, 2),
173
+ );
174
+ return;
175
+ }
176
+
177
+ logger.log(chalk.bold("Repository Analysis\n"));
178
+ logger.log(
179
+ ` ${chalk.gray("Total commits:")} ${analysis.totalCommits}`,
180
+ );
181
+ logger.log(
182
+ ` ${chalk.gray("Tracked files:")} ${analysis.trackedFiles}`,
183
+ );
184
+ if (analysis.firstCommit) {
185
+ logger.log(
186
+ ` ${chalk.gray("First commit:")} ${analysis.firstCommit}`,
187
+ );
188
+ }
189
+ if (analysis.lastCommit) {
190
+ logger.log(` ${chalk.gray("Last commit:")} ${analysis.lastCommit}`);
191
+ }
192
+
193
+ if (analysis.contributors.length > 0) {
194
+ logger.log(chalk.bold("\nContributors:\n"));
195
+ for (const c of analysis.contributors) {
196
+ logger.log(
197
+ ` ${chalk.cyan(c.commits.toString().padStart(4))} ${c.author}`,
198
+ );
199
+ }
200
+ }
201
+
202
+ if (recentLog.length > 0) {
203
+ logger.log(chalk.bold("\nRecent Commits:\n"));
204
+ for (const c of recentLog) {
205
+ logger.log(
206
+ ` ${chalk.yellow(c.shortHash)} ${chalk.white(c.subject)} ${chalk.gray(c.date)}`,
207
+ );
208
+ }
209
+ }
210
+ } catch (err) {
211
+ logger.error(`Analysis failed: ${err.message}`);
212
+ process.exit(1);
213
+ }
214
+ });
215
+ }
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Knowledge import commands
3
+ * chainlesschain import markdown|evernote|notion|pdf <path>
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import ora from "ora";
8
+ import { existsSync, statSync } from "fs";
9
+ import { resolve } from "path";
10
+ import { logger } from "../lib/logger.js";
11
+ import { bootstrap, shutdown } from "../runtime/bootstrap.js";
12
+ import {
13
+ importMarkdownDir,
14
+ importEnexFile,
15
+ importNotionDir,
16
+ } from "../lib/knowledge-importer.js";
17
+
18
+ export function registerImportCommand(program) {
19
+ const imp = program
20
+ .command("import")
21
+ .description("Import knowledge from external sources");
22
+
23
+ // import markdown
24
+ imp
25
+ .command("markdown")
26
+ .description("Import markdown files from a directory")
27
+ .argument("<dir>", "Directory containing .md files")
28
+ .option("--json", "Output as JSON")
29
+ .action(async (dir, options) => {
30
+ try {
31
+ const absDir = resolve(dir);
32
+ if (!existsSync(absDir) || !statSync(absDir).isDirectory()) {
33
+ logger.error(`Directory not found: ${absDir}`);
34
+ process.exit(1);
35
+ }
36
+
37
+ const spinner = ora("Importing markdown files...").start();
38
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
39
+ if (!ctx.db) {
40
+ spinner.fail("Database not available");
41
+ process.exit(1);
42
+ }
43
+
44
+ const db = ctx.db.getDatabase();
45
+ const imported = importMarkdownDir(db, absDir);
46
+ spinner.stop();
47
+
48
+ if (options.json) {
49
+ console.log(
50
+ JSON.stringify(
51
+ { count: imported.length, notes: imported },
52
+ null,
53
+ 2,
54
+ ),
55
+ );
56
+ } else if (imported.length === 0) {
57
+ logger.info("No .md files found in the directory");
58
+ } else {
59
+ logger.success(
60
+ `Imported ${chalk.cyan(imported.length)} markdown notes`,
61
+ );
62
+ for (const n of imported.slice(0, 10)) {
63
+ logger.log(
64
+ ` ${chalk.gray(n.id.slice(0, 8))} ${chalk.white(n.title)} ${chalk.gray(n.source || "")}`,
65
+ );
66
+ }
67
+ if (imported.length > 10) {
68
+ logger.log(chalk.gray(` ... and ${imported.length - 10} more`));
69
+ }
70
+ }
71
+
72
+ await shutdown();
73
+ } catch (err) {
74
+ logger.error(`Import failed: ${err.message}`);
75
+ process.exit(1);
76
+ }
77
+ });
78
+
79
+ // import evernote
80
+ imp
81
+ .command("evernote")
82
+ .description("Import from Evernote ENEX export file")
83
+ .argument("<file>", "Path to .enex file")
84
+ .option("--json", "Output as JSON")
85
+ .action(async (file, options) => {
86
+ try {
87
+ const absFile = resolve(file);
88
+ if (!existsSync(absFile)) {
89
+ logger.error(`File not found: ${absFile}`);
90
+ process.exit(1);
91
+ }
92
+
93
+ const spinner = ora("Importing Evernote notes...").start();
94
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
95
+ if (!ctx.db) {
96
+ spinner.fail("Database not available");
97
+ process.exit(1);
98
+ }
99
+
100
+ const db = ctx.db.getDatabase();
101
+ const imported = importEnexFile(db, absFile);
102
+ spinner.stop();
103
+
104
+ if (options.json) {
105
+ console.log(
106
+ JSON.stringify(
107
+ { count: imported.length, notes: imported },
108
+ null,
109
+ 2,
110
+ ),
111
+ );
112
+ } else if (imported.length === 0) {
113
+ logger.info("No notes found in the ENEX file");
114
+ } else {
115
+ logger.success(
116
+ `Imported ${chalk.cyan(imported.length)} Evernote notes`,
117
+ );
118
+ for (const n of imported.slice(0, 10)) {
119
+ const tags =
120
+ n.tags.length > 0 ? chalk.gray(` [${n.tags.join(", ")}]`) : "";
121
+ logger.log(
122
+ ` ${chalk.gray(n.id.slice(0, 8))} ${chalk.white(n.title)}${tags}`,
123
+ );
124
+ }
125
+ if (imported.length > 10) {
126
+ logger.log(chalk.gray(` ... and ${imported.length - 10} more`));
127
+ }
128
+ }
129
+
130
+ await shutdown();
131
+ } catch (err) {
132
+ logger.error(`Import failed: ${err.message}`);
133
+ process.exit(1);
134
+ }
135
+ });
136
+
137
+ // import notion
138
+ imp
139
+ .command("notion")
140
+ .description("Import from Notion export directory")
141
+ .argument("<dir>", "Notion export directory")
142
+ .option("--json", "Output as JSON")
143
+ .action(async (dir, options) => {
144
+ try {
145
+ const absDir = resolve(dir);
146
+ if (!existsSync(absDir) || !statSync(absDir).isDirectory()) {
147
+ logger.error(`Directory not found: ${absDir}`);
148
+ process.exit(1);
149
+ }
150
+
151
+ const spinner = ora("Importing Notion pages...").start();
152
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
153
+ if (!ctx.db) {
154
+ spinner.fail("Database not available");
155
+ process.exit(1);
156
+ }
157
+
158
+ const db = ctx.db.getDatabase();
159
+ const imported = importNotionDir(db, absDir);
160
+ spinner.stop();
161
+
162
+ if (options.json) {
163
+ console.log(
164
+ JSON.stringify(
165
+ { count: imported.length, notes: imported },
166
+ null,
167
+ 2,
168
+ ),
169
+ );
170
+ } else if (imported.length === 0) {
171
+ logger.info("No markdown pages found in Notion export");
172
+ } else {
173
+ logger.success(
174
+ `Imported ${chalk.cyan(imported.length)} Notion pages`,
175
+ );
176
+ for (const n of imported.slice(0, 10)) {
177
+ const tags =
178
+ n.tags.length > 0 ? chalk.gray(` [${n.tags.join(", ")}]`) : "";
179
+ logger.log(
180
+ ` ${chalk.gray(n.id.slice(0, 8))} ${chalk.white(n.title)}${tags}`,
181
+ );
182
+ }
183
+ if (imported.length > 10) {
184
+ logger.log(chalk.gray(` ... and ${imported.length - 10} more`));
185
+ }
186
+ }
187
+
188
+ await shutdown();
189
+ } catch (err) {
190
+ logger.error(`Import failed: ${err.message}`);
191
+ process.exit(1);
192
+ }
193
+ });
194
+
195
+ // import pdf
196
+ imp
197
+ .command("pdf")
198
+ .description("Import text from a PDF file")
199
+ .argument("<file>", "Path to .pdf file")
200
+ .option("--json", "Output as JSON")
201
+ .action(async (file, options) => {
202
+ try {
203
+ const absFile = resolve(file);
204
+ if (!existsSync(absFile)) {
205
+ logger.error(`File not found: ${absFile}`);
206
+ process.exit(1);
207
+ }
208
+
209
+ const spinner = ora("Extracting text from PDF...").start();
210
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
211
+ if (!ctx.db) {
212
+ spinner.fail("Database not available");
213
+ process.exit(1);
214
+ }
215
+
216
+ const db = ctx.db.getDatabase();
217
+
218
+ // Lazy-load pdf-parser to keep it optional
219
+ const { parsePdfText } = await import("../lib/pdf-parser.js");
220
+ const { insertNote, ensureNotesTable } =
221
+ await import("../lib/knowledge-importer.js");
222
+ ensureNotesTable(db);
223
+
224
+ const parsed = await parsePdfText(absFile);
225
+ spinner.stop();
226
+
227
+ if (!parsed.content || parsed.content.trim().length === 0) {
228
+ logger.info("No text could be extracted from the PDF");
229
+ await shutdown();
230
+ return;
231
+ }
232
+
233
+ const note = insertNote(db, {
234
+ title: parsed.title,
235
+ content: parsed.content,
236
+ tags: ["pdf"],
237
+ category: "pdf",
238
+ });
239
+
240
+ if (options.json) {
241
+ console.log(JSON.stringify(note, null, 2));
242
+ } else {
243
+ logger.success(`Imported PDF as note: ${chalk.cyan(parsed.title)}`);
244
+ logger.log(` ${chalk.gray("ID:")} ${note.id.slice(0, 8)}`);
245
+ logger.log(
246
+ ` ${chalk.gray("Length:")} ${parsed.content.length} chars`,
247
+ );
248
+ if (parsed.pages) {
249
+ logger.log(` ${chalk.gray("Pages:")} ${parsed.pages}`);
250
+ }
251
+ }
252
+
253
+ await shutdown();
254
+ } catch (err) {
255
+ logger.error(`PDF import failed: ${err.message}`);
256
+ process.exit(1);
257
+ }
258
+ });
259
+ }