@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.
- package/.obsidian/.2ndbrain-manifest.json +8 -0
- package/.obsidian/app.json +6 -0
- package/.obsidian/appearance.json +1 -0
- package/.obsidian/community-plugins.json +4 -0
- package/.obsidian/core-plugins.json +33 -0
- package/.obsidian/graph.json +22 -0
- package/.obsidian/plugins/calendar/data.json +10 -0
- package/.obsidian/plugins/calendar/main.js +4459 -0
- package/.obsidian/plugins/calendar/manifest.json +10 -0
- package/.obsidian/plugins/obsidian-custom-attachment-location/data.json +32 -0
- package/.obsidian/plugins/obsidian-custom-attachment-location/main.js +575 -0
- package/.obsidian/plugins/obsidian-custom-attachment-location/manifest.json +11 -0
- package/.obsidian/plugins/obsidian-custom-attachment-location/styles.css +1 -0
- package/.obsidian/plugins/obsidian-git/data.json +62 -0
- package/.obsidian/plugins/obsidian-git/main.js +426 -0
- package/.obsidian/plugins/obsidian-git/manifest.json +10 -0
- package/.obsidian/plugins/obsidian-git/obsidian_askpass.sh +23 -0
- package/.obsidian/plugins/obsidian-git/styles.css +629 -0
- package/.obsidian/plugins/obsidian-tasks-plugin/main.js +504 -0
- package/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
- package/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
- package/.obsidian/types.json +28 -0
- package/00_Dashboard/01_All_Tasks.md +118 -0
- package/00_Dashboard/09_All_Done.md +42 -0
- package/10_Inbox/Agents/Journal.md +1 -0
- package/99_System/Scripts/init_member.sh +108 -0
- package/99_System/Templates/tpl_daily_note.md +13 -0
- package/99_System/Templates/tpl_member_done.md +32 -0
- package/99_System/Templates/tpl_member_tasks.md +97 -0
- package/AGENTS.md +193 -0
- package/CHANGELOG.md +67 -0
- package/CLAUDE.md +153 -0
- package/LICENSE +201 -0
- package/README.md +636 -0
- package/bin/2ndbrain.js +117 -0
- package/package.json +56 -0
- package/src/commands/completion.js +198 -0
- package/src/commands/init.js +308 -0
- package/src/commands/member.js +123 -0
- package/src/commands/remove.js +88 -0
- package/src/commands/update.js +507 -0
- package/src/index.js +17 -0
- package/src/lib/config.js +112 -0
- package/src/lib/diff.js +222 -0
- package/src/lib/files.js +340 -0
- package/src/lib/obsidian.js +366 -0
- 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;
|