@our2ndbrain/cli 1.1.3 → 2026.4.5

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 (52) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/README.md +54 -582
  3. package/README_en.md +108 -0
  4. package/bin/2ndbrain.js +30 -1
  5. package/package.json +14 -11
  6. package/src/commands/check.js +199 -0
  7. package/src/commands/completion.js +35 -1
  8. package/src/commands/init.js +7 -4
  9. package/src/commands/member.js +3 -1
  10. package/src/commands/update.js +38 -12
  11. package/src/commands/watch.js +212 -0
  12. package/src/index.js +4 -0
  13. package/src/lib/config.js +46 -2
  14. package/src/lib/files.js +58 -19
  15. package/{.obsidian → template/.obsidian}/plugins/obsidian-git/obsidian_askpass.sh +0 -0
  16. package/{00_Dashboard → template/00_Dashboard}/01_All_Tasks.md +17 -15
  17. package/template/10_Inbox/.gitkeep +0 -0
  18. package/template/20_Areas/.gitkeep +0 -0
  19. package/template/30_Projects/.gitkeep +0 -0
  20. package/template/40_Resources/.gitkeep +0 -0
  21. package/template/90_Archives/.gitkeep +0 -0
  22. package/{99_System → template/99_System}/Scripts/init_member.sh +0 -0
  23. package/template/99_System/Templates/tpl_daily_note.md +30 -0
  24. package/{99_System → template/99_System}/Templates/tpl_member_tasks.md +11 -5
  25. package/template/99_System/Templates/tpl_member_todo.md +5 -0
  26. package/99_System/Templates/tpl_daily_note.md +0 -13
  27. package/AGENTS.md +0 -193
  28. package/CLAUDE.md +0 -153
  29. /package/{.obsidian → template/.obsidian}/.2ndbrain-manifest.json +0 -0
  30. /package/{.obsidian → template/.obsidian}/app.json +0 -0
  31. /package/{.obsidian → template/.obsidian}/appearance.json +0 -0
  32. /package/{.obsidian → template/.obsidian}/community-plugins.json +0 -0
  33. /package/{.obsidian → template/.obsidian}/core-plugins.json +0 -0
  34. /package/{.obsidian → template/.obsidian}/graph.json +0 -0
  35. /package/{.obsidian → template/.obsidian}/plugins/calendar/data.json +0 -0
  36. /package/{.obsidian → template/.obsidian}/plugins/calendar/main.js +0 -0
  37. /package/{.obsidian → template/.obsidian}/plugins/calendar/manifest.json +0 -0
  38. /package/{.obsidian → template/.obsidian}/plugins/obsidian-custom-attachment-location/data.json +0 -0
  39. /package/{.obsidian → template/.obsidian}/plugins/obsidian-custom-attachment-location/main.js +0 -0
  40. /package/{.obsidian → template/.obsidian}/plugins/obsidian-custom-attachment-location/manifest.json +0 -0
  41. /package/{.obsidian → template/.obsidian}/plugins/obsidian-custom-attachment-location/styles.css +0 -0
  42. /package/{.obsidian → template/.obsidian}/plugins/obsidian-git/data.json +0 -0
  43. /package/{.obsidian → template/.obsidian}/plugins/obsidian-git/main.js +0 -0
  44. /package/{.obsidian → template/.obsidian}/plugins/obsidian-git/manifest.json +0 -0
  45. /package/{.obsidian → template/.obsidian}/plugins/obsidian-git/styles.css +0 -0
  46. /package/{.obsidian → template/.obsidian}/plugins/obsidian-tasks-plugin/main.js +0 -0
  47. /package/{.obsidian → template/.obsidian}/plugins/obsidian-tasks-plugin/manifest.json +0 -0
  48. /package/{.obsidian → template/.obsidian}/plugins/obsidian-tasks-plugin/styles.css +0 -0
  49. /package/{.obsidian → template/.obsidian}/types.json +0 -0
  50. /package/{00_Dashboard → template/00_Dashboard}/09_All_Done.md +0 -0
  51. /package/{10_Inbox → template/10_Inbox}/Agents/Journal.md +0 -0
  52. /package/{99_System → template/99_System}/Templates/tpl_member_done.md +0 -0
package/README_en.md ADDED
@@ -0,0 +1,108 @@
1
+ # 🧠 2ndBrain
2
+
3
+ > A lightweight entrypoint for AI agents and human collaborators: use the 2ndBrain template, CLI, and Skill to operate an Obsidian knowledge base together.
4
+
5
+ [English](README_en.md) | [简体中文](README.md)
6
+
7
+ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
8
+ [![npm version](https://img.shields.io/npm/v/@our2ndbrain/cli.svg)](https://www.npmjs.com/package/@our2ndbrain/cli)
9
+
10
+ ## What This Repository Is
11
+
12
+ If you hand this repository to an AI agent, this README should be the first file it reads.
13
+
14
+ 2ndBrain ships three things:
15
+
16
+ - an Obsidian vault template with PARA folders, inboxes, and dashboards
17
+ - a CLI for checking the environment, initializing a vault, and updating framework files
18
+ - a 2ndBrain Skill that teaches the agent how to capture, organize, review, and process content
19
+
20
+ This README keeps only the minimum needed for AI collaboration. For the fuller project and methodology reference, see the [detailed guide](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/docs/guide_en.md).
21
+
22
+ ## Quick Start
23
+
24
+ The default path is `npx`:
25
+
26
+ ```bash
27
+ npx @our2ndbrain/cli@latest check
28
+ npx @our2ndbrain/cli@latest init my-brain
29
+ cd my-brain
30
+ npx @our2ndbrain/cli@latest member Alice
31
+ ```
32
+
33
+ If the CLI is already installed globally, you can also use:
34
+
35
+ ```bash
36
+ 2ndbrain check
37
+ 2ndbrain init my-brain
38
+ cd my-brain
39
+ 2ndbrain member Alice
40
+ ```
41
+
42
+ ## Initialize a Vault
43
+
44
+ ### Create a new 2ndBrain vault
45
+
46
+ ```bash
47
+ npx @our2ndbrain/cli@latest check
48
+ npx @our2ndbrain/cli@latest init my-brain
49
+ cd my-brain
50
+ npx @our2ndbrain/cli@latest member Alice
51
+ ```
52
+
53
+ ### Integrate into an existing Obsidian vault
54
+
55
+ ```bash
56
+ cd my-existing-vault
57
+ npx @our2ndbrain/cli@latest check
58
+ npx @our2ndbrain/cli@latest init
59
+ npx @our2ndbrain/cli@latest member Alice
60
+ ```
61
+
62
+ After initialization, guide the user to:
63
+
64
+ 1. open the directory in Obsidian
65
+ 2. click "Trust author and enable plugins"
66
+ 3. create the first daily note and start capturing tasks and thoughts
67
+
68
+ ## Install the Skill
69
+
70
+ The preferred installation path is the SSH git URL form:
71
+
72
+ ```bash
73
+ npx skills add git@github.com:Our2ndBrain/2ndBrain-Template.git --skill 2ndbrain
74
+ ```
75
+
76
+ To target a specific agent explicitly:
77
+
78
+ ```bash
79
+ npx skills add git@github.com:Our2ndBrain/2ndBrain-Template.git --skill 2ndbrain -a claude-code
80
+ npx skills add git@github.com:Our2ndBrain/2ndBrain-Template.git --skill 2ndbrain -a cursor
81
+ npx skills add git@github.com:Our2ndBrain/2ndBrain-Template.git --skill 2ndbrain -a openclaw
82
+ ```
83
+
84
+ If you are not using the `skills` CLI in that environment yet, you can still fall back to manually copying `skills/2ndbrain/`.
85
+
86
+ The skill entrypoint is [SKILL.md](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/skills/2ndbrain/SKILL.md).
87
+
88
+ ## How AI Should Collaborate With Humans
89
+
90
+ An AI agent using this template should at least follow these rules:
91
+
92
+ - Run `2ndbrain check` or `npx @our2ndbrain/cli@latest check` before setup, initialization, or takeover.
93
+ - Capture before organizing. If placement is unclear, write it to `10_Inbox/{member}/` first.
94
+ - Write tasks to `10_Inbox/{member}/00_To-Do.md`. Do not hand-edit query-driven files such as `00_Dashboard/*.md` or `10_Inbox/*/01_Tasks.md`.
95
+ - Follow the user's language instead of switching languages on your own.
96
+ - Prefer the 2ndBrain Skill for capture, organize, review, content processing, and scheduled cleanup.
97
+ - When you need detailed rules, read the Skill and its references instead of inferring policy from this README.
98
+
99
+ ## Deep Reading
100
+
101
+ - [Detailed Guide (English)](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/docs/guide_en.md)
102
+ - [详细指南(中文)](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/docs/guide.md)
103
+ - [2ndBrain Skill](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/skills/2ndbrain/SKILL.md)
104
+ - [Setup Reference](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/skills/2ndbrain/references/setup.md)
105
+ - [Operations and Daily Review](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/skills/2ndbrain/references/operations.md)
106
+ - [Content Processing](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/skills/2ndbrain/references/content-processing.md)
107
+ - [Scheduling and Automation](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/skills/2ndbrain/references/scheduling.md)
108
+ - [Task Conventions](https://github.com/Our2ndBrain/2ndBrain-Template/blob/main/skills/2ndbrain/references/task-conventions.md)
package/bin/2ndbrain.js CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  const { program } = require('commander');
10
- const { init, update, remove, member, completion } = require('../src');
10
+ const { init, update, remove, member, completion, check, watch } = require('../src');
11
11
  const pkg = require('../package.json');
12
12
 
13
13
  // ANSI color codes for terminal output
@@ -93,6 +93,35 @@ program
93
93
  }
94
94
  });
95
95
 
96
+ // Check command
97
+ program
98
+ .command('check [path]')
99
+ .description('Check environment prerequisites (Node.js, Git, Obsidian, Agent CLI)')
100
+ .action(async (targetPath = '.', options) => {
101
+ try {
102
+ const passed = await check(targetPath, options, log);
103
+ if (!passed) process.exit(1);
104
+ } catch (err) {
105
+ log.error(`Error: ${err.message}`);
106
+ process.exit(1);
107
+ }
108
+ });
109
+
110
+ // Watch command
111
+ program
112
+ .command('watch [path]')
113
+ .description('Watch To-Do files for changes and auto-trigger inbox organization')
114
+ .option('-i, --interval <minutes>', 'Debounce interval in minutes (default: 5)', parseInt)
115
+ .option('--once', 'Exit after first triggered organization')
116
+ .action(async (targetPath = '.', options) => {
117
+ try {
118
+ await watch(targetPath, options, log);
119
+ } catch (err) {
120
+ log.error(`Error: ${err.message}`);
121
+ process.exit(1);
122
+ }
123
+ });
124
+
96
125
  // Completion command
97
126
  program
98
127
  .command('completion <shell>')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@our2ndbrain/cli",
3
- "version": "1.1.3",
3
+ "version": "2026.4.5",
4
4
  "description": "CLI tool for 2ndBrain - A personal knowledge management system based on PARA + C-O-R-D + Append-and-Review",
5
5
  "keywords": [
6
6
  "2ndbrain",
@@ -24,31 +24,34 @@
24
24
  "files": [
25
25
  "bin/",
26
26
  "src/",
27
- ".obsidian/",
28
- "00_Dashboard/",
29
- "10_Inbox/Agents/",
30
- "99_System/",
27
+ "template/",
31
28
  "AGENTS.md",
32
29
  "CHANGELOG.md",
33
30
  "CLAUDE.md",
34
31
  "LICENSE",
35
- "README.md"
32
+ "README.md",
33
+ "README_en.md"
36
34
  ],
37
35
  "bugs": {
38
36
  "url": "https://github.com/Our2ndBrain/2ndBrain-Template/issues"
39
37
  },
40
38
  "engines": {
41
- "node": ">=16.0.0"
39
+ "node": ">=18.0.0"
42
40
  },
43
41
  "scripts": {
44
- "version": "node scripts/version.js && git add CHANGELOG.md",
45
- "postversion": "git push && git push --tags && npm publish"
42
+ "lint": "node scripts/lint.js && node scripts/check-workflow-pinning.js",
43
+ "pack:check": "node scripts/check-package.js",
44
+ "release": "node scripts/release.js",
45
+ "release:check": "node scripts/check-release.js",
46
+ "smoke:install": "node scripts/smoke-install.js",
47
+ "test": "node --test",
48
+ "version": "node scripts/version.js && git add CHANGELOG.md"
46
49
  },
47
50
  "dependencies": {
48
51
  "chalk": "^4.1.2",
49
52
  "commander": "^12.0.0",
50
- "diff": "^5.2.0",
51
- "fs-extra": "^11.2.0"
53
+ "diff": "^8.0.4",
54
+ "fs-extra": "^11.3.4"
52
55
  },
53
56
  "publishConfig": {
54
57
  "access": "public"
@@ -0,0 +1,199 @@
1
+ /**
2
+ * 2ndBrain CLI - Check Command
3
+ *
4
+ * Cross-platform environment check. All platform detection is handled
5
+ * in Node.js — agents never need to write shell/PowerShell scripts.
6
+ */
7
+
8
+ const { execFileSync } = require('child_process');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { is2ndBrainProject } = require('../lib/config');
12
+
13
+ const MIN_NODE_MAJOR = 18;
14
+
15
+ function commandExists(cmd) {
16
+ try {
17
+ const isWin = process.platform === 'win32';
18
+ if (isWin) {
19
+ execFileSync('where', [cmd], { stdio: 'pipe' });
20
+ } else {
21
+ execFileSync('which', [cmd], { stdio: 'pipe' });
22
+ }
23
+ return true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ function getCommandVersion(cmd, args = ['--version']) {
30
+ try {
31
+ return execFileSync(cmd, args, { stdio: 'pipe', encoding: 'utf8' }).trim();
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ function checkObsidianInstalled() {
38
+ switch (process.platform) {
39
+ case 'darwin':
40
+ return fs.existsSync('/Applications/Obsidian.app');
41
+ case 'win32': {
42
+ const localAppData = process.env.LOCALAPPDATA || '';
43
+ return fs.existsSync(path.join(localAppData, 'Programs', 'Obsidian', 'Obsidian.exe'));
44
+ }
45
+ default:
46
+ return commandExists('obsidian');
47
+ }
48
+ }
49
+
50
+ function getObsidianInstallHint() {
51
+ switch (process.platform) {
52
+ case 'darwin':
53
+ return 'brew install --cask obsidian (或从 https://obsidian.md/ 下载)';
54
+ case 'win32':
55
+ return 'winget install Obsidian.MD.Obsidian (或从 https://obsidian.md/ 下载)';
56
+ default:
57
+ return '从 https://obsidian.md/ 下载 AppImage';
58
+ }
59
+ }
60
+
61
+ function getNodeInstallHint() {
62
+ switch (process.platform) {
63
+ case 'darwin':
64
+ return 'brew install node';
65
+ case 'win32':
66
+ return 'winget install OpenJS.NodeJS.LTS (或从 https://nodejs.org/ 下载)';
67
+ default:
68
+ return 'sudo apt install nodejs npm (Debian/Ubuntu) 或从 https://nodejs.org/ 下载';
69
+ }
70
+ }
71
+
72
+ function getObsidianCLIPathHint() {
73
+ switch (process.platform) {
74
+ case 'darwin':
75
+ return '在 ~/.zshrc 中添加:\nexport PATH="$PATH:/Applications/Obsidian.app/Contents/MacOS"';
76
+ case 'win32':
77
+ return '系统设置 → 环境变量 → Path → 新增:\nC:\\Users\\<用户名>\\AppData\\Local\\Programs\\Obsidian\\';
78
+ default:
79
+ return 'sudo ln -s /opt/obsidian/obsidian /usr/local/bin/obsidian';
80
+ }
81
+ }
82
+
83
+ async function check(targetPath, _options, log) {
84
+ const results = [];
85
+ let allPassed = true;
86
+
87
+ log.info('\n🧠 2ndBrain 环境检测\n');
88
+
89
+ // 1. Node.js version
90
+ const nodeVersion = process.version;
91
+ const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0], 10);
92
+ if (nodeMajor >= MIN_NODE_MAJOR) {
93
+ results.push({ ok: true, label: `Node.js ${nodeVersion}` });
94
+ } else {
95
+ results.push({
96
+ ok: false,
97
+ label: `Node.js ${nodeVersion} (需要 >= ${MIN_NODE_MAJOR})`,
98
+ hint: getNodeInstallHint(),
99
+ });
100
+ allPassed = false;
101
+ }
102
+
103
+ // 2. Git
104
+ const gitVersion = getCommandVersion('git');
105
+ if (gitVersion) {
106
+ results.push({ ok: true, label: `Git ${gitVersion.replace('git version ', '')}` });
107
+ } else {
108
+ results.push({
109
+ ok: false,
110
+ label: 'Git 未安装',
111
+ hint: process.platform === 'win32'
112
+ ? 'winget install Git.Git'
113
+ : process.platform === 'darwin'
114
+ ? 'brew install git'
115
+ : 'sudo apt install git',
116
+ });
117
+ allPassed = false;
118
+ }
119
+
120
+ // 3. Obsidian app
121
+ if (checkObsidianInstalled()) {
122
+ results.push({ ok: true, label: 'Obsidian 已安装' });
123
+ } else {
124
+ results.push({
125
+ ok: false,
126
+ label: 'Obsidian 未安装',
127
+ hint: getObsidianInstallHint(),
128
+ });
129
+ allPassed = false;
130
+ }
131
+
132
+ // 4. Obsidian CLI
133
+ if (commandExists('obsidian')) {
134
+ const obsVersion = getCommandVersion('obsidian');
135
+ results.push({ ok: true, label: `Obsidian CLI ${obsVersion || '可用'}` });
136
+ } else {
137
+ results.push({
138
+ ok: false,
139
+ label: 'Obsidian CLI 未配置',
140
+ hint: `在 Obsidian Settings > General > CLI 中启用,然后配置 PATH:\n${getObsidianCLIPathHint()}`,
141
+ });
142
+ // Obsidian CLI is optional, don't fail the whole check
143
+ }
144
+
145
+ // 5. 2ndBrain project (if path given)
146
+ const resolvedPath = path.resolve(targetPath);
147
+ if (fs.existsSync(resolvedPath)) {
148
+ if (is2ndBrainProject(resolvedPath)) {
149
+ results.push({ ok: true, label: `2ndBrain 知识库: ${resolvedPath}` });
150
+ } else {
151
+ results.push({
152
+ ok: false,
153
+ label: `${resolvedPath} 不是 2ndBrain 知识库`,
154
+ hint: `运行: 2ndbrain init ${targetPath}`,
155
+ });
156
+ }
157
+ }
158
+
159
+ // 6. Agent CLI (openclaw or claude)
160
+ if (commandExists('openclaw')) {
161
+ results.push({ ok: true, label: 'OpenClaw CLI 可用' });
162
+ } else if (commandExists('claude')) {
163
+ results.push({ ok: true, label: 'Claude CLI 可用' });
164
+ } else {
165
+ results.push({
166
+ ok: false,
167
+ label: 'Agent CLI 未找到 (openclaw / claude)',
168
+ hint: '安装 OpenClaw: https://docs.openclaw.ai/start\n或 Claude Code: https://code.claude.com',
169
+ });
170
+ }
171
+
172
+ // Print results
173
+ for (const r of results) {
174
+ if (r.ok) {
175
+ log.success(` ✓ ${r.label}`);
176
+ } else {
177
+ log.error(` ✗ ${r.label}`);
178
+ if (r.hint) {
179
+ log.warn(` → ${r.hint.split('\n').join('\n → ')}`);
180
+ }
181
+ }
182
+ }
183
+
184
+ log.info('');
185
+
186
+ if (allPassed) {
187
+ log.success('所有必要组件已就绪 ✓\n');
188
+ } else {
189
+ log.warn('部分组件缺失,请按上方提示安装\n');
190
+ }
191
+
192
+ return allPassed;
193
+ }
194
+
195
+ module.exports = check;
196
+ module.exports.MIN_NODE_MAJOR = MIN_NODE_MAJOR;
197
+ module.exports.commandExists = commandExists;
198
+ module.exports.getCommandVersion = getCommandVersion;
199
+ module.exports.checkObsidianInstalled = checkObsidianInstalled;
@@ -25,7 +25,7 @@ _2ndbrain_completions() {
25
25
  local cmd="\${COMP_WORDS[1]}"
26
26
 
27
27
  # Top-level commands
28
- local commands="init update remove member completion"
28
+ local commands="init update remove member check watch completion"
29
29
 
30
30
  case "\$cmd" in
31
31
  init)
@@ -55,6 +55,14 @@ _2ndbrain_completions() {
55
55
  member)
56
56
  COMPREPLY=( $(compgen -W "-f --force --no-config -h --help" -- "\$cur") )
57
57
  ;;
58
+ check)
59
+ COMPREPLY=( $(compgen -W "-h --help" -- "\$cur") )
60
+ [[ -z "\$cur" || "\$cur" != -* ]] && COMPREPLY+=( $(compgen -d -- "\$cur") )
61
+ ;;
62
+ watch)
63
+ COMPREPLY=( $(compgen -W "-i --interval --once -h --help" -- "\$cur") )
64
+ [[ -z "\$cur" || "\$cur" != -* ]] && COMPREPLY+=( $(compgen -d -- "\$cur") )
65
+ ;;
58
66
  completion)
59
67
  COMPREPLY=( $(compgen -W "bash zsh fish" -- "\$cur") )
60
68
  ;;
@@ -78,6 +86,8 @@ _2ndbrain() {
78
86
  'update:Update framework files from template'
79
87
  'remove:Remove framework files (preserves user data)'
80
88
  'member:Initialize a new member directory'
89
+ 'check:Check environment prerequisites'
90
+ 'watch:Watch To-Do files and trigger inbox organization'
81
91
  'completion:Generate shell completion script'
82
92
  )
83
93
 
@@ -122,6 +132,18 @@ _2ndbrain() {
122
132
  '1:name:' \\
123
133
  '2:path:_directories'
124
134
  ;;
135
+ check)
136
+ _arguments \\
137
+ '(-h --help)'{-h,--help}'[Show help]' \\
138
+ '1:path:_directories'
139
+ ;;
140
+ watch)
141
+ _arguments \\
142
+ '(-i --interval)'{-i,--interval}'[Debounce interval in minutes]:minutes:' \\
143
+ '--once[Exit after first triggered organization]' \\
144
+ '(-h --help)'{-h,--help}'[Show help]' \\
145
+ '1:path:_directories'
146
+ ;;
125
147
  completion)
126
148
  _arguments \\
127
149
  '1:shell:(bash zsh fish)'
@@ -144,6 +166,8 @@ complete -c 2ndbrain -n '__fish_use_subcommand' -a 'init' -d 'Initialize a new 2
144
166
  complete -c 2ndbrain -n '__fish_use_subcommand' -a 'update' -d 'Update framework files from template'
145
167
  complete -c 2ndbrain -n '__fish_use_subcommand' -a 'remove' -d 'Remove framework files'
146
168
  complete -c 2ndbrain -n '__fish_use_subcommand' -a 'member' -d 'Initialize a new member directory'
169
+ complete -c 2ndbrain -n '__fish_use_subcommand' -a 'check' -d 'Check environment prerequisites'
170
+ complete -c 2ndbrain -n '__fish_use_subcommand' -a 'watch' -d 'Watch To-Do files and trigger inbox organization'
147
171
  complete -c 2ndbrain -n '__fish_use_subcommand' -a 'completion' -d 'Generate shell completion script'
148
172
 
149
173
  # Global options
@@ -173,6 +197,16 @@ complete -c 2ndbrain -n '__fish_seen_subcommand_from member' -s f -l force -d 'F
173
197
  complete -c 2ndbrain -n '__fish_seen_subcommand_from member' -l no-config -d 'Skip Obsidian config update'
174
198
  complete -c 2ndbrain -n '__fish_seen_subcommand_from member' -s h -l help -d 'Show help'
175
199
 
200
+ # check options
201
+ complete -c 2ndbrain -n '__fish_seen_subcommand_from check' -s h -l help -d 'Show help'
202
+ complete -c 2ndbrain -n '__fish_seen_subcommand_from check' -a '(__fish_complete_directories)'
203
+
204
+ # watch options
205
+ complete -c 2ndbrain -n '__fish_seen_subcommand_from watch' -s i -l interval -d 'Debounce interval in minutes' -r
206
+ complete -c 2ndbrain -n '__fish_seen_subcommand_from watch' -l once -d 'Exit after first triggered organization'
207
+ complete -c 2ndbrain -n '__fish_seen_subcommand_from watch' -s h -l help -d 'Show help'
208
+ complete -c 2ndbrain -n '__fish_seen_subcommand_from watch' -a '(__fish_complete_directories)'
209
+
176
210
  # completion options
177
211
  complete -c 2ndbrain -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish'
178
212
  `;
@@ -7,6 +7,7 @@
7
7
  const path = require('path');
8
8
  const fs = require('fs-extra');
9
9
  const {
10
+ PACKAGE_ROOT,
10
11
  TEMPLATE_ROOT,
11
12
  FRAMEWORK_FILES,
12
13
  FRAMEWORK_DIRS,
@@ -14,6 +15,7 @@ const {
14
15
  COPY_DIRS,
15
16
  SMART_COPY_DIRS,
16
17
  INIT_ONLY_FILES,
18
+ getFrameworkSourcePath,
17
19
  is2ndBrainProject,
18
20
  } = require('../lib/config');
19
21
  const { copyFiles, ensureDirs, createFile, isDirEmpty, copyFilesSmart } = require('../lib/files');
@@ -141,6 +143,7 @@ async function handleObsidianReset(obsidianSrc, obsidianDest, log, skipConfirmat
141
143
  async function init(targetPath, options, log) {
142
144
  const resolvedPath = path.resolve(targetPath);
143
145
  const templateRoot = options.template ? path.resolve(options.template) : TEMPLATE_ROOT;
146
+ const packageRoot = options.template ? path.resolve(templateRoot, '..') : PACKAGE_ROOT;
144
147
 
145
148
  log.info(`Initializing 2ndBrain project at: ${resolvedPath}`);
146
149
  log.info(`Using template from: ${templateRoot}`);
@@ -166,9 +169,9 @@ async function init(targetPath, options, log) {
166
169
 
167
170
  // Scan existing directory for smart integration
168
171
  const scan = await scanExistingDirectory(resolvedPath);
169
- const isIntegrateMode = !isEmpty && scan.hasFiles && !isExistingProject;
172
+ const isIntegrationMode = !isEmpty && scan.hasFiles && !isExistingProject;
170
173
 
171
- if (isIntegrateMode) {
174
+ if (isIntegrationMode) {
172
175
  log.info('');
173
176
  log.info('Integration mode: Merging 2ndBrain framework into existing vault');
174
177
  if (scan.hasUserDataDirs.size > 0) {
@@ -217,7 +220,7 @@ async function init(targetPath, options, log) {
217
220
  log.info('Copying framework files...');
218
221
  const fileResult = await copyFilesSmart(
219
222
  FRAMEWORK_FILES,
220
- templateRoot,
223
+ (file) => getFrameworkSourcePath(file, { templateRoot, packageRoot }),
221
224
  resolvedPath,
222
225
  {},
223
226
  (file, action, detail) => {
@@ -289,7 +292,7 @@ async function init(targetPath, options, log) {
289
292
 
290
293
  // Summary
291
294
  log.info('');
292
- log.success(isIntegrateMode ? '2ndBrain framework integrated!' : '2ndBrain project initialized!');
295
+ log.success(isIntegrationMode ? '2ndBrain framework integrated!' : '2ndBrain project initialized!');
293
296
  log.info(` Created: ${fileResult.copied.length} files`);
294
297
  if (fileResult.unchanged.length > 0) {
295
298
  log.info(` Skipped: ${fileResult.unchanged.length} existing files`);
@@ -18,6 +18,7 @@ const PLACEHOLDER = '{{MEMBER_NAME}}';
18
18
  * Member files configuration
19
19
  */
20
20
  const MEMBER_FILES = [
21
+ { template: 'tpl_member_todo.md', output: '00_To-Do.md' },
21
22
  { template: 'tpl_member_tasks.md', output: '01_Tasks.md' },
22
23
  { template: 'tpl_member_done.md', output: '09_Done.md' },
23
24
  ];
@@ -28,7 +29,7 @@ const MEMBER_FILES = [
28
29
  * @param {string} targetPath - Target project path
29
30
  * @param {Object} options - Command options
30
31
  * @param {boolean} [options.force] - Force overwrite existing member
31
- * @param {boolean} [options.noConfig] - Skip Obsidian config update
32
+ * @param {boolean} [options.config] - Whether to update Obsidian config (set false by --no-config)
32
33
  * @param {Object} log - Logger object
33
34
  */
34
35
  async function member(memberName, targetPath, options, log) {
@@ -110,6 +111,7 @@ async function member(memberName, targetPath, options, log) {
110
111
  log.success('🎉 Member init complete!');
111
112
  log.info('');
112
113
  log.info('Created files:');
114
+ log.info(` - 10_Inbox/${memberName}/00_To-Do.md (append-only task list)`);
113
115
  log.info(` - 10_Inbox/${memberName}/01_Tasks.md (personal dashboard)`);
114
116
  log.info(` - 10_Inbox/${memberName}/09_Done.md (done records)`);
115
117
  log.info('');