@our2ndbrain/cli 1.1.3

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 (47) hide show
  1. package/.obsidian/.2ndbrain-manifest.json +8 -0
  2. package/.obsidian/app.json +6 -0
  3. package/.obsidian/appearance.json +1 -0
  4. package/.obsidian/community-plugins.json +4 -0
  5. package/.obsidian/core-plugins.json +33 -0
  6. package/.obsidian/graph.json +22 -0
  7. package/.obsidian/plugins/calendar/data.json +10 -0
  8. package/.obsidian/plugins/calendar/main.js +4459 -0
  9. package/.obsidian/plugins/calendar/manifest.json +10 -0
  10. package/.obsidian/plugins/obsidian-custom-attachment-location/data.json +32 -0
  11. package/.obsidian/plugins/obsidian-custom-attachment-location/main.js +575 -0
  12. package/.obsidian/plugins/obsidian-custom-attachment-location/manifest.json +11 -0
  13. package/.obsidian/plugins/obsidian-custom-attachment-location/styles.css +1 -0
  14. package/.obsidian/plugins/obsidian-git/data.json +62 -0
  15. package/.obsidian/plugins/obsidian-git/main.js +426 -0
  16. package/.obsidian/plugins/obsidian-git/manifest.json +10 -0
  17. package/.obsidian/plugins/obsidian-git/obsidian_askpass.sh +23 -0
  18. package/.obsidian/plugins/obsidian-git/styles.css +629 -0
  19. package/.obsidian/plugins/obsidian-tasks-plugin/main.js +504 -0
  20. package/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
  21. package/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
  22. package/.obsidian/types.json +28 -0
  23. package/00_Dashboard/01_All_Tasks.md +118 -0
  24. package/00_Dashboard/09_All_Done.md +42 -0
  25. package/10_Inbox/Agents/Journal.md +1 -0
  26. package/99_System/Scripts/init_member.sh +108 -0
  27. package/99_System/Templates/tpl_daily_note.md +13 -0
  28. package/99_System/Templates/tpl_member_done.md +32 -0
  29. package/99_System/Templates/tpl_member_tasks.md +97 -0
  30. package/AGENTS.md +193 -0
  31. package/CHANGELOG.md +67 -0
  32. package/CLAUDE.md +153 -0
  33. package/LICENSE +201 -0
  34. package/README.md +636 -0
  35. package/bin/2ndbrain.js +117 -0
  36. package/package.json +56 -0
  37. package/src/commands/completion.js +198 -0
  38. package/src/commands/init.js +308 -0
  39. package/src/commands/member.js +123 -0
  40. package/src/commands/remove.js +88 -0
  41. package/src/commands/update.js +507 -0
  42. package/src/index.js +17 -0
  43. package/src/lib/config.js +112 -0
  44. package/src/lib/diff.js +222 -0
  45. package/src/lib/files.js +340 -0
  46. package/src/lib/obsidian.js +366 -0
  47. package/src/lib/prompt.js +182 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * 2ndBrain CLI - Member Command
3
+ *
4
+ * Initialize a new member directory with personal dashboard.
5
+ */
6
+
7
+ const path = require('path');
8
+ const fs = require('fs-extra');
9
+ const { is2ndBrainProject } = require('../lib/config');
10
+ const { createFile, ensureDirs } = require('../lib/files');
11
+
12
+ /**
13
+ * Template placeholders to replace
14
+ */
15
+ const PLACEHOLDER = '{{MEMBER_NAME}}';
16
+
17
+ /**
18
+ * Member files configuration
19
+ */
20
+ const MEMBER_FILES = [
21
+ { template: 'tpl_member_tasks.md', output: '01_Tasks.md' },
22
+ { template: 'tpl_member_done.md', output: '09_Done.md' },
23
+ ];
24
+
25
+ /**
26
+ * Initialize a new member directory
27
+ * @param {string} memberName - Member name
28
+ * @param {string} targetPath - Target project path
29
+ * @param {Object} options - Command options
30
+ * @param {boolean} [options.force] - Force overwrite existing member
31
+ * @param {boolean} [options.noConfig] - Skip Obsidian config update
32
+ * @param {Object} log - Logger object
33
+ */
34
+ async function member(memberName, targetPath, options, log) {
35
+ const resolvedPath = path.resolve(targetPath);
36
+ const memberDir = path.join(resolvedPath, '10_Inbox', memberName);
37
+ const templateDir = path.join(resolvedPath, '99_System/Templates');
38
+ const obsidianConfig = path.join(resolvedPath, '.obsidian/daily-notes.json');
39
+
40
+ log.info('');
41
+ log.info('🧠 2ndBrain Member Init');
42
+ log.info('========================');
43
+ log.info(`Member name: ${memberName}`);
44
+ log.info(`Member directory: 10_Inbox/${memberName}`);
45
+ log.info('');
46
+
47
+ // Validate project
48
+ if (!is2ndBrainProject(resolvedPath)) {
49
+ throw new Error(
50
+ 'Target directory is not a 2ndBrain project. Run "2ndbrain init" first.'
51
+ );
52
+ }
53
+
54
+ // Check if member directory exists
55
+ if (await fs.pathExists(memberDir)) {
56
+ const contents = await fs.readdir(memberDir);
57
+ if (contents.length > 0 && !options.force) {
58
+ throw new Error(
59
+ `Member directory 10_Inbox/${memberName} already exists. Use --force to overwrite.`
60
+ );
61
+ }
62
+ if (options.force) {
63
+ log.warn(`Overwriting existing member directory...`);
64
+ }
65
+ }
66
+
67
+ // Step 1: Create member directory
68
+ log.info('📁 Creating member directory...');
69
+ await ensureDirs([`10_Inbox/${memberName}`], resolvedPath);
70
+ log.success(` + 10_Inbox/${memberName}/`);
71
+
72
+ // Step 2: Copy and process template files
73
+ log.info('📋 Creating member files...');
74
+ for (const { template, output } of MEMBER_FILES) {
75
+ const templatePath = path.join(templateDir, template);
76
+ const outputPath = path.join(memberDir, output);
77
+
78
+ if (!await fs.pathExists(templatePath)) {
79
+ log.error(` ! ${template} (template not found)`);
80
+ continue;
81
+ }
82
+
83
+ // Read template and replace placeholder
84
+ let content = await fs.readFile(templatePath, 'utf8');
85
+ content = content.replace(new RegExp(PLACEHOLDER, 'g'), memberName);
86
+
87
+ // Write output file
88
+ await createFile(outputPath, content);
89
+ log.success(` + 10_Inbox/${memberName}/${output}`);
90
+ }
91
+
92
+ // Step 3: Update Obsidian daily-notes config (unless --no-config)
93
+ if (options.config !== false) {
94
+ log.info('⚙️ Configuring Obsidian daily-notes...');
95
+ await fs.ensureDir(path.join(resolvedPath, '.obsidian'));
96
+
97
+ const config = {
98
+ folder: `10_Inbox/${memberName}`,
99
+ autorun: true,
100
+ template: '99_System/Templates/tpl_daily_note',
101
+ };
102
+
103
+ await createFile(obsidianConfig, JSON.stringify(config, null, 2));
104
+ log.success(' + .obsidian/daily-notes.json');
105
+ }
106
+
107
+ // Summary
108
+ log.info('');
109
+ log.info('========================');
110
+ log.success('🎉 Member init complete!');
111
+ log.info('');
112
+ log.info('Created files:');
113
+ log.info(` - 10_Inbox/${memberName}/01_Tasks.md (personal dashboard)`);
114
+ log.info(` - 10_Inbox/${memberName}/09_Done.md (done records)`);
115
+ log.info('');
116
+ log.info('Next steps:');
117
+ log.info(' 1. Open this vault with Obsidian');
118
+ log.info(` 2. New daily notes will auto-save to 10_Inbox/${memberName}/`);
119
+ log.info(' 3. Start recording your first task!');
120
+ log.info('');
121
+ }
122
+
123
+ module.exports = member;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * 2ndBrain CLI - Remove Command
3
+ *
4
+ * Remove framework files while preserving user data.
5
+ */
6
+
7
+ const path = require('path');
8
+ const {
9
+ FRAMEWORK_FILES,
10
+ FRAMEWORK_DIRS,
11
+ is2ndBrainProject,
12
+ } = require('../lib/config');
13
+ const { removeFiles, removeEmptyDirs } = require('../lib/files');
14
+
15
+ /**
16
+ * Remove framework files from a 2ndBrain project
17
+ * @param {string} targetPath - Target directory path
18
+ * @param {Object} options - Command options
19
+ * @param {boolean} [options.dryRun] - Dry run mode
20
+ * @param {boolean} [options.force] - Force removal without confirmation
21
+ * @param {Function} log - Logger function
22
+ */
23
+ async function remove(targetPath, options, log) {
24
+ const resolvedPath = path.resolve(targetPath);
25
+
26
+ log.info(`Removing 2ndBrain framework from: ${resolvedPath}`);
27
+
28
+ // Check if target is a 2ndBrain project
29
+ if (!is2ndBrainProject(resolvedPath)) {
30
+ throw new Error(
31
+ 'Target directory is not a 2ndBrain project.'
32
+ );
33
+ }
34
+
35
+ if (options.dryRun) {
36
+ log.warn('[DRY RUN] No files will be removed.');
37
+ log.info('');
38
+ log.info('Files that would be removed:');
39
+ for (const file of FRAMEWORK_FILES) {
40
+ log.info(` ${file}`);
41
+ }
42
+ log.info('');
43
+ log.info('Directories that would be cleaned:');
44
+ for (const dir of FRAMEWORK_DIRS) {
45
+ log.info(` ${dir}/`);
46
+ }
47
+ return;
48
+ }
49
+
50
+ if (!options.force) {
51
+ log.warn('This will remove all framework files.');
52
+ log.warn('User data in 20_Areas/, 30_Projects/, 40_Resources/, 90_Archives/ will be preserved.');
53
+ log.warn('Use --force to skip this warning.');
54
+ throw new Error('Aborted. Use --force to confirm removal.');
55
+ }
56
+
57
+ // Remove framework files
58
+ log.info('Removing framework files...');
59
+ const fileResult = await removeFiles(FRAMEWORK_FILES, resolvedPath, (file, action, reason) => {
60
+ if (action === 'remove') {
61
+ log.success(` - ${file}`);
62
+ } else if (action === 'skip') {
63
+ log.warn(` ~ ${file} (skipped: ${reason})`);
64
+ } else if (action === 'error') {
65
+ log.error(` ! ${file} (error: ${reason})`);
66
+ }
67
+ });
68
+
69
+ // Remove empty framework directories
70
+ log.info('Cleaning empty directories...');
71
+ const removedDirs = await removeEmptyDirs(FRAMEWORK_DIRS, resolvedPath, (dir, action) => {
72
+ if (action === 'remove') {
73
+ log.success(` - ${dir}/`);
74
+ }
75
+ });
76
+
77
+ // Summary
78
+ log.info('');
79
+ log.success('2ndBrain framework removed successfully!');
80
+ log.info(` Removed: ${fileResult.removed.length} files, ${removedDirs.length} directories`);
81
+ if (fileResult.skipped.length > 0) {
82
+ log.warn(` Skipped: ${fileResult.skipped.length} files`);
83
+ }
84
+ log.info('');
85
+ log.info('User data directories have been preserved.');
86
+ }
87
+
88
+ module.exports = remove;